C/C++实现词法分析程序的示例代码
作者:Aricl.
一、实验内容
通过完成词法分析程序,了解词法分析的过程。编制一个读单词程序,对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; //创建四个表,储存符号 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++; } //执行完之后指向第一个不为空格的字符 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; //读取连续的字母数字序列 while(isalpha(instring[p])||isnumber(instring[p])){ id[i++]=instring[p++];//p指向连续序列之后的第一个字符 } 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:出错*/
三、实验结果
测试一
测试二
四、实验总结
整体的代码思路是创建四个数组分别存放关键字表、界符表、运算符号表、关系运算符号表,这样若想新增符号只需要在数组中修改即可,实现动态变化而非写死的。
下面进行模块化设计,总共分为analysis()、symbol()、constant()、alphabet()、show()、isnumber()、isalpha()七个函数,analysis函数作为总的分析函数,通过分析当前字符是否是字母还是数字或者其它符号,分别调用不同的函数进行具体的分析,然后将结果存储在全局变量outtoken和t中,前者代表输出的二元组,后者代表该单词的类型,难点在于两个符号的运算符的判断。
本题还有一个细节之处,在输入完代码后敲的那个回车键会多余需要使用一个getchar()函数来吸收掉,并且结束符@为自定义的,不参与到词法分析中,所以在输入完程序代码后使用instring[p-1]='\0';//抵消掉@,这样就使得instring数组里存放的是完整的有效的程序代码,不含结束符。
到此这篇关于C/C++实现词法分析程序的示例代码的文章就介绍到这了,更多相关C++词法分析内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!