用C語言編寫一個PL/0詞法分析器,為語法語義分析提供單詞,使之能把輸入的字符串形式的源程序分割成一個個單詞符號傳遞給語法語義分析,並把分析結果(基本字,運算符,標識符,常數以及界符)輸出。
PL/0的詞法分析程序GETSYM是一個獨立的過程,其功能是為語法語義分析提供單詞,把輸入的字符串形式的源程序分割成一個個單詞符號傳遞給語法語義分析。其主要任務為:1、濾空格;2、識別基本字;3、識別標識符;4、拼數;5、拼復合詞;6、輸出源程序。
PL/0編譯程序一般設置3個全程變量:
SYM:存放每個單詞的類別,用內部編碼形式表示;
ID: 存放用戶所定義的標識別符的值;
NUM:存放用戶定義的數。
PL/0語言的單詞的種類分成基本字(亦稱保留字)、運算符、標識符、常數、界符5個大類,以下是針對這5類單詞的一種EBNF描敘:
<無符號整數>::=<數字>{<數字>}
<標識符> ::=<字母>{<字母>|<數字>}
<字母> ::=a|b|……|X|Y|Z
<數字> ::=0|1|2|……|8|9
<保留字> ::= const | var | procedure | begin | end | odd | if | then | call | while | do | read |write
<運算符> ::= + | - | * | / | = | # | < | <= | > | >= | :=
<界符> ::= ( | ) | , | ; | .
保留字、運算符和界符這幾類在實踐中一般將每個單詞符號設計為獨立的詞法單元,即每個單詞符號擁有獨立的類別。這樣PL/0編譯程序所設計的單詞符號就對應到31個單詞種別,這31個單詞種別采用枚舉類型表示。
例:PL/0程序片段:
const a<=10;
var b,c;
procedure p;
begin
……
end.
**************************************************************
詞法分析器運行結果:
const 基本字/constsym
a 標識符
<= 運算符/小於等於號
10 常數
; 界符/分號
var 基本字/varsym
b 標識符
c 標識符
; 界符/分號
……
end 基本字/endsym
. 界符/結束符
//Analysis.h
#ifndef ANALYSIS_H_INCLUDED
#define ANALYSIS_H_INCLUDED
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<cstring>
char stayWord[13][15]={"const","var","procedure","begin","end","odd","if","then","call"," while","do","read","write"};
Char enumStayWord[13][15]={"constsym","varsym","proceduresym","beginsym","endsym",
"oddsym"," ifsym","thensym","callsym"," whilesym","dosym","readsym","writesym"};
bool isNumber(char ch);
bool isCase(char ch);
bool isCaculationSymbol(char ch);
bool isBandSymbol(char ch);
int isStayWord(char *str);
void getInputStreamFromFile(char *fileName, char *str);
void calulationString(char *str);
void bandString(char *str);
void Analysis(char *InputFileName, char *str, char *outputFileName);
#endif // ANALYSIS_H_INCLUDED
//main.cpp
#include <iostream>
#include"Analysis.h"
using namespace std;
int main()
{
char str[100000],input[15]="input.txt",output[15]="output.txt";
Analysis(input, str,output);
return 0;
}
bool isNumber(char ch){
//判斷該位置的符號是否是數字
if(ch>='0'&&ch<='9') return true;
else return false;
}
bool isCase(char ch){
//判斷該位置的符號是否是字母
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
return true;
else return false;
}
bool isCaculationSymbol(char ch){
判斷該位置是否是運算符的基本單位
if(ch=='+'||ch=='-'||ch=='*'||ch=='/'||ch=='>'||ch=='<'||ch=='='||ch=='#'||ch==’:’)
return true;
else return false;
}
bool isBandSymbol(char ch){
//判斷該位置是否是邊界符
if(ch=='('||ch==')'||ch==','||ch==';'||ch=='.')
return true;
else return false;
}
int isStayWord(char *str){
//判斷該位置是否是保留字
int aa;
for(aa=0;aa<13;aa++){ if(!strcmp(str,stayWord[aa])) break;}
return aa;
}
void getInputStreamFromFile(char *fileName, char *str){
//從文件中獲取將被分析的代碼段
char ch;
int i=0;
FILE *fp;
fp=fopen(fileName,"r");
while((ch=fgetc(fp))!=EOF){
if(ch!='\n'&&(int)ch==9) str[i++]=ch;
//去掉換行、Tab
else str[i++]=' ';
}
str[i]='\0';
fclose(fp);
}
void calulationString(char *str){//連續的運算符處理
int aa=strlen(str);
for(int i=0;i<aa;i++){
if(str[i]=='+'){ printf("+\t\t運算符/加號\n");}
else if(str[i]=='-'){ printf("-\t\t運算符/減號\n");}
else if(str[i]=='*'){ printf("*\t\t運算符/乘號\n");}
else if(str[i]=='/'){ printf("/\t\t運算符/除號\n");}
else if(str[i]=='='){ printf("=\t\t邏輯運算符/等於號\n");}
else if(str[i]==':'){
//如果是i位置是 ’:’,若i+1位置不是 ‘=’,那么該符號非法
if(i+1<aa&&str[i+1]=='='){ printf(":=\t\t運算符/賦值號\n");i++;}
else{ printf("%c\t\t非法字符!!!\n",str[i]); }
}
else if(str[i]=='#'){ printf("#\t\t運算符/不等於號\n");}
else if(str[i]=='>'){
if(i+1<aa&&str[i+1]=='='){ printf(">=\t\t邏輯運算符/大於等於號\n"); i++;}
else{ printf(">\t\t邏輯運算符/大於號\n");}
}
else if(str[i]=='<'){
if(i+1<aa&&str[i+1]=='='){ printf("<=\t\t邏輯運算符/小於等於號\n"); i++;}
else{ printf("<\t\t邏輯運算符/小於號\n");}
}
else ;
}
}
void bandString(char *str){
//獲取一段連續的邊界符號后,依次將其分解開
int i,k=strlen(str);
for( i=0;i<k;i++){
switch(str[i]){
case '(': printf("(\t\t界符/左刮胡\n"); break;
case ')': printf(")\t\t界符/右刮胡\n"); break;
case ',': printf(",\t\t界符/逗號\n"); break;
case ';': printf(";\t\t界符/分號\n"); break;
case '.': printf(".\t\t界符/結束號\n"); break;
default: break;
}
}
}
void Analysis(char *InputFileName, char *str, char *outputFileName){
//詞法分析函數
getInputStreamFromFile(InputFileName,str);
int lenth=strlen(str),i,j,bb,cc,dd,ee;
char tempStr[100],smallStr[100];
//每種類型的符號 其連續的個數是有限的……
i=0;
while(i<lenth){
//進入大循環……
j=0;
while(str[i]==' '&&i<lenth) { i++; }//去掉開頭的空格符號
while(str[i]!=' '&&i<lenth) { tempStr[j++]=str[i++]; }//截取一段代碼字符串
tempStr[j]='\0';
bb=strlen(tempStr);
cc=0;
while(cc<bb){
if(isCase(tempStr[cc])){//如果以字母開頭,則執行…… while(( !isCaculationSymbol(tempStr[cc]))&&(!isBandSymbol(tempStr[cc]))&&cc<bb){
smallStr[dd++]=tempStr[cc++]; }//截取全是字母的一段
smallStr[dd]='\0';
ee=isStayWord(smallStr);
if(ee<13) {//如果字母串是否是保留字,則……
printf("%s\t\t基本字/%s\n",stayWord[ee],enumStayWord[ee]);
strcpy(smallStr,"");
dd=0;}
else{否則……
printf("%s\t\t標識符\n",smallStr);
strcpy(smallStr,"");
dd=0; }
//所截取的短串沒處理完,回到 while(cc<bb){} 的開始,有當前所指向位置的
//符號作為判斷條件(即什么樣的開始決定什么樣的處理)……
}
else if(isNumber(tempStr[cc])){//如果以數字開頭,則執行……
while(isNumber(tempStr[cc])&&cc<bb){
smallStr[dd++]=tempStr[cc++];}
smallStr[dd]='\0';
printf("%s\t\t常數\n",smallStr);
strcpy(smallStr,"");
dd=0;
}else if(isCaculationSymbol(tempStr[cc])){//如果以運算符開頭,則執行……
while(isCaculationSymbol(tempStr[cc])&&cc<bb){
smallStr[dd++]=tempStr[cc++];}
smallStr[dd]='\0';
calulationString(smallStr); //連續的運算符處理……
strcpy(smallStr,"");
dd=0;
}else if(isBandSymbol(tempStr[cc])){//如果以邊界符開頭,則執行……
while(isBandSymbol(tempStr[cc])&&cc<bb){
smallStr[dd++]=tempStr[cc++];}
smallStr[dd]='\0';
bandString(smallStr); //l連續的界限符的處理……
strcpy(smallStr,"");
dd=0;
}else{
printf("無法識別的符號……\n");cc++;
}
}
strcpy(tempStr,"");
}
fclose(stdout);
}
輸入文件:
const a<=10;
var b,c;
procedure p;
begin:
var aa:=100;
bb#cc;
dfghfhg<hj=gh;
end.
運行結果:
, 界符/逗號
c 標識符
; 界符/分號
procedure 基本字/proceduresym
p 標識符
; 界符/分號
begin 基本字/beginsym
: 非法字符!!!
var 基本字/varsym
aa 標識符
:= 運算符/賦值號
100 常數
; 界符/分號
bb 標識符
# 運算符/不等於號
cc 標識符
; 界符/分號
dfghfhg 標識符
< 邏輯運算符/小於號
hj 標識符
= 邏輯運算符/等於號
gh 標識符
; 界符/分號
end 基本字/endsym
. 界符/結束號
Process returned 0 (0x0) execution time : 0.172 s
Press any key to continue.
