C/C++實現詞法分析程序的示例代碼
一、實驗內容
通過完成詞法分析程序,了解詞法分析的過程。編制一個讀單詞程序,對PL/0語言進行詞法分析,把輸入的字符串形式的源程序分割成一個個單詞符號,即基本保留字、標識符、常數、運算符、分界符五大類。
對PL/0語言進行詞法分析,把輸入的字符串形式的源程序分割成一個個單詞符號,其詞法描述如下:
(1)關鍵字:
begin,call,const,do,end,if,odd,procedure,read,then,var,while,write
(2) 標識符:用來表示各種名字,必須以字母開頭小于10位字符組成
(3) 數字:以0-9組成小于14位的數字
(4) 運算符:+,-,*,/,:=,<,<=,>,>=,#
(5) 分界符:, ,. ,; ,( ,)
二、實驗代碼
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<iomanip>
using namespace std;
//創(chuàng)建四個表,儲存符號
const char *k[13]={"begin","call","const","do","end","if","odd","procedure","read","then","var","while","write"};//關鍵字表
const char *s1[5]={",",".",";","(",")"};//界符表
const char *s2[6]={"+","-","*","/","++","--",};//運算符號表
const char *s3[9]={"<=",">",">=","=",">",">=","<>",":=","#"}; //關系運算符號表
//定義全局變量
int row=1,line=1;
int t,p=0;//單詞類別碼以及記錄移動指針
char instring[100];//保存輸入的程序代碼緩存數組
char outtoken[10];//輸出
char ci[8],id[10];//暫時保存數字和字符
//函數的聲明
void analysis();//分析函數,決定調用哪個函數進行分析
void symbol();//分析以非字母數字開頭的字符
void constant();//分析常數
void alphabet();//分析標識符和關鍵字
void show();//打印輸出函數
bool isnumber(char x);//判斷是否是數字
bool isalpha(char x);//判斷是否是字母
int main(){
cout<<"請輸入一段程序代碼并以@結束:"<<endl;
//輸出程序代碼
do{
instring[p++]=getchar();
} while(instring[p-1]!='@');
getchar();//吸收回車鍵
instring[p-1]='\0';//抵消掉@
p=0;//移動指針歸零
cout<<left;
cout<<"------------------------------------------------------------------------------"<<endl;
cout<<setw(6)<<"單詞"<<" "<<setw(6)<<"二元序列"<<" "<<setw(6)<<"類型"<<" "<<endl;
//掃描輸入的字符
while(p<strlen(instring)){
analysis();
show();
}
cout<<"------------------------------------------------------------------------------"<<endl;
cout<<"[注]:"<<endl;
cout<<"t=1:關鍵字,"<<"t=2:分界符,"<<"t=3:算術運算符,"<<"t=4:關系運算符,"<<"t=5:常數,"<<"t=6:標識符,"<<"t==7:詞法出錯"<<endl;
cout<<"@為結束符,不參與到詞法分析中"<<endl;
cout<<endl;
return 0;
}
//判斷是否是數字
bool isnumber(char x){
return x>='0'&&x<='9';
}
//判斷是否是字母
bool isalpha(char x){
return (x>='a'&&x<='z'||x>='A'&&x<='Z');
}
//分析函數,決定調用哪個函數進行分析
void analysis(){
strcpy(outtoken,"");//清空outtoken數組
while(instring[p]==' '||instring[p]=='\n'){
if(instring[p]=='\n'){
row++;
line=1;
}
p++;
}
//執(zhí)行完之后指向第一個不為空格的字符
char ch=instring[p];
//按照字符的類別調用不同的分析處理函數
if(isalpha(ch))
alphabet();
else if(isnumber(ch))
constant();
else
symbol();
}
//常數處理函數
void constant(){
strcpy(ci,"");//清空ci
t=5;//類別碼
int i=0;
while(isnumber(instring[p])){
ci[i++]=instring[p++];
}
while(isalpha(instring[p])||isnumber(instring[p])){
ci[i++]=instring[p++];
t=7;//出錯
}
ci[i]='\0';//結束符
//strcpy_s(outtoken,strlen(ci)+1,ci);
strcpy(outtoken,ci);
line++;
return;
}
//標識符和關鍵字的分析函數
void alphabet(){
strcpy(id,"");//清空id
int i=0;
//讀取連續(xù)的字母數字序列
while(isalpha(instring[p])||isnumber(instring[p])){
id[i++]=instring[p++];//p指向連續(xù)序列之后的第一個字符
}
id[i]='\0';
//查關鍵字表
for(i=0;i<8;i++){
if(strcmp(id,k[i])==0){
t=1;//表示關鍵字
line++;
strcpy(outtoken,id);
return; //是關鍵字的話,直接退出
}
}
//查看是否是標識符
for(i=0;i<strlen(id);i++){
if(!(isalpha(id[i])||isnumber(id[i]))){
t=7;
strcpy(outtoken,id);
line++;
return;
}
}
line++;
t=6;//不是關鍵字且沒有出錯即為標識符
strcpy(outtoken,id);
}
//其它運算符的分析函數
void symbol(){
char ch=instring[p++];
char ch2=instring[p];
t=7;
switch(ch){
case '+':
if(ch2=='+')
t=3;
break;
case '-':
if(ch2=='-')
t=3;
break;
case '>':
if(ch2=='=')
t=4;
break;
case '<':
if(ch2=='='||ch2=='>')
t=4;
break;
case ':':
if(ch2=='=')
t=3;
break;
}
//判斷是否具有兩個符號的運算符
if(ch=='>'&&ch2=='='||ch=='<'&&ch2=='='||ch=='<'&&ch2=='>'||ch=='+'&&ch2=='+'||ch=='-'&&ch2=='-'||ch==':'&&ch2=='='){
p++;
outtoken[0]=ch;
outtoken[1]=ch2;
outtoken[2]='\0';
line++;
return;
} else{
char chq[2];
chq[0]=ch;
chq[1]='\0';
//分界符比較
for(int i=0;i<6;i++){
if(strcmp(chq,s1[i])==0){
t=2;
break;
}
}
//算術運算符比較
for(int i=0;i<6;i++){
if(strcmp(chq,s2[i])==0){
t=3;
break;
}
}
//關系運算符比較
for(int i=0;i<9;i++){
if(strcmp(chq,s3[i])==0){
t=4;
break;
}
}
}
line++;
outtoken[0]=ch;
outtoken[1]='\0';
return;
}
//輸出函數,根據以上分析函數進行打印輸出分析的結果
void show(){
cout<<left;
//setw(6)表示占位寬度為6個字符
if(t==7){
cout<<setw(6)<<outtoken<<" "<<setw(6)<<"ERROR!"<<setw(11)<<" "<<setw(10)<<"ERROR!";
}else{
cout<<left;
cout<<setw(6)<<outtoken<<" "<<"<"<<t<<","<<outtoken;
cout<<setw(6-strlen(outtoken))<<">"<<" ";
switch(t){
case 1:cout<<left<<setw(10)<<"關鍵字";break;
case 2:cout<<left<<setw(10)<<"分界符";break;
case 3:cout<<left<<setw(10)<<"算術運算符";break;
case 4:cout<<left<<setw(10)<<"關系運算符";break;
case 5:cout<<left<<setw(10)<<"常數";break;
case 6:cout<<left<<setw(10)<<"標識符";break;
}
}
cout<<endl;
}
/*變量說明:
k數組:關鍵字表; s數組:分界符表,其中分界符,算術運算符,關系運算符分別存放在s1,s2,s3數組中
id:標識符; ci:常數 ;row:行 line:列,單詞的位置
instring數組:為輸入源程序代碼的單詞緩存; outtoken數組:記錄為輸出內部表示緩存
symbol:分析//后的注釋;constant:常數分析;alphabet:標識符和關鍵字分析
analysis:分析函數,根據輸入字符判斷調用哪一個函數 ;show:輸出打印函數
t:單詞的種類 t=1:關鍵字 t=2:分界符 t=3:算術運算符 t=4:關系運算符 t=5:常數 t=6:標識符 t=7:出錯*/三、實驗結果
測試一

測試二

四、實驗總結
整體的代碼思路是創(chuàng)建四個數組分別存放關鍵字表、界符表、運算符號表、關系運算符號表,這樣若想新增符號只需要在數組中修改即可,實現動態(tài)變化而非寫死的。
下面進行模塊化設計,總共分為analysis()、symbol()、constant()、alphabet()、show()、isnumber()、isalpha()七個函數,analysis函數作為總的分析函數,通過分析當前字符是否是字母還是數字或者其它符號,分別調用不同的函數進行具體的分析,然后將結果存儲在全局變量outtoken和t中,前者代表輸出的二元組,后者代表該單詞的類型,難點在于兩個符號的運算符的判斷。
本題還有一個細節(jié)之處,在輸入完代碼后敲的那個回車鍵會多余需要使用一個getchar()函數來吸收掉,并且結束符@為自定義的,不參與到詞法分析中,所以在輸入完程序代碼后使用instring[p-1]='\0';//抵消掉@,這樣就使得instring數組里存放的是完整的有效的程序代碼,不含結束符。
到此這篇關于C/C++實現詞法分析程序的示例代碼的文章就介紹到這了,更多相關C++詞法分析內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
VSCode 配置C++開發(fā)環(huán)境的方法步驟
這篇文章主要介紹了VSCode 配置C++開發(fā)環(huán)境的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-03-03

