/*TINY语言的词法分析器代码*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MAXN 900000005
char s[MAXN],fuhaoji[10]={'+','-','*','/','=','<','(',')',';',':'};
int main(){
int xuhao=0,zhushibiaoshi=0;
while(gets(s)){/*整行读入至数组中*/
xuhao++;/*行号+1*/
int lens=strlen(s);
cout<<xuhao<<":";
cout<<s<<endl;
for(int i=0;i<lens;i++){
if(s[i]==' ')/*如果是空格则不用分析,直接读入下一个字符*/
continue;
/*对注释的识别*/
if(s[i]=='{')
zhushibiaoshi=1;
if(s[i]=='}')
zhushibiaoshi=0;
/*对非注释的识别*/
if(0==zhushibiaoshi){/*变量zhushibiaoshi定义在程序最外层,它为0表示待识别的字符不在注释里面(注释可以跨行)*/
/*对保留字的识别*/
if(s[i]=='i'&&s[i+1]=='f'){
cout<<" "<<xuhao<<":";
puts("reserved word:if");
i++;
continue;
}
if(s[i]=='t'&&s[i+1]=='h'&&s[i+2]=='e'&&s[i+3]=='n'){
cout<<" "<<xuhao<<":";
puts("reserved word:then");
i++;i++;i++;/*提前往后面读了三个字符,要去掉这三个,不然会重复读,其他保留字同理,也需要去掉(保留字长度-1)个提前读的字符*/
continue;
}
if(s[i]=='e'&&s[i+1]=='l'&&s[i+2]=='s'&&s[i+3]=='e'){
cout<<" "<<xuhao<<":";
puts("reserved word:else");
i++;i++;i++;
continue;
}
if(s[i]=='e'&&s[i+1]=='n'&&s[i+2]=='d'){
cout<<" "<<xuhao<<":";
puts("reserved word:end");
i++;i++;
continue;
}
if(s[i]=='r'&&s[i+1]=='e'&&s[i+2]=='p'&&s[i+3]=='e'&&s[i+4]=='a'&&s[i+5]=='t'){
cout<<" "<<xuhao<<":";
puts("reserved word:repeat");
i++;i++;i++;i++;i++;
continue;
}
if(s[i]=='u'&&s[i+1]=='n'&&s[i+2]=='t'&&s[i+3]=='i'&&s[i+4]=='l'){
cout<<" "<<xuhao<<":";
puts("reserved word:until");
i++;i++;i++;i++;
continue;
}
if(s[i]=='r'&&s[i+1]=='e'&&s[i+2]=='a'&&s[i+3]=='d'){
cout<<" "<<xuhao<<":";
puts("reserved word:read");
i++;i++;i++;
continue;
}
if(s[i]=='w'&&s[i+1]=='r'&&s[i+2]=='i'&&s[i+3]=='t'&&s[i+4]=='e'){
cout<<" "<<xuhao<<":";
puts("reserved word:write");
i++;i++;i++;i++;
continue;
}
/*对符号的识别*/
for(int j=0;j<10;j++){
if(s[i]==fuhaoji[j]){
if(s[i]==':'){/*对双目符号:=进行特殊处理*/
if(s[i+1]=='='){
cout<<" "<<xuhao<<":";
cout<<s[i]<<s[i+1]<<endl;
i++;
}else{
puts(":");
}
}else{
cout<<" "<<xuhao<<":";
cout<<s[i]<<endl;
}
break;
}
}
/*对数字的识别*/
if(s[i]>='0'&&s[i]<='9'){
int count1=1;
cout<<" "<<xuhao<<":";
cout<<"num,val=";
cout<<s[i];
while(s[i+count1]>='0'&&s[i+count1]<='9'){
cout<<s[i+count1];
count1++;
}
i+=count1;
i--;
cout<<endl;
}
/*对标识符的识别*/
if((s[i]>='a'&&s[i]<='z')||(s[i]>='A'&&s[i]<='Z')){/*这里不能直接a到Z*/
int count2=1;
cout<<" "<<xuhao<<":";
cout<<"id,name=";
cout<<s[i];
while((s[i+count2]>='a'&&s[i+count2]<='z')||(s[i+count2]>='A'&&s[i+count2]<='Z')){
cout<<s[i+count2];
count2++;
}
i+=count2;
i--;
cout<<endl;
}
}
}
}
return 0;
}
本程序识别时参考的TINY语言词法:
本程序流程图:
运行情况:
实验输入:
{ sample program
in TINY language -
Computes factorial
}
read x; { input on integer }
if 0 < x then { don't compute if x <= 0 }
fact := 1 ;
repeat
fact := fact * x;
x := x - 1
until x = 0;
write fact { output factorial of x }
end
实验输出:
根据本代码所体现的思想和本代码的流程,可以将本程序拓展为一个简易的C++词法分析器,具体拓展方法如下:
①将特殊符号集拓展为C++所需要的单目特殊符号组成的集合(双目符号如+=这样的符号处理方式已于本程序中展示(:=符号的处理)(将双目符号分解为两个单目符号进行处理));
②对保留字识别代码块进行调整或加长,将C++中的保留字逐个写成如下所示的代码并加入保留字识别代码块(并去掉不存在于C++保留字中的字符串对应的代码块)。
/*对保留字的识别*/
if(s[i]=='t'&&s[i+1]=='h'&&s[i+2]=='e'&&s[i+3]=='n'){
cout<<" "<<xuhao<<":";
puts("reserved word:then");
i++;i++;i++;/*提前往后面读了三个字符,要去掉这三个,不然会重复读,其他保留字同理,也需要去掉(保留字长度-1)个提前读的字符*/
continue;
}
比如:加入对C++保留字int的识别:将如下代码加入保留字识别代码块即可。
if(s[i]=='i'&&s[i+1]=='n'&&s[i+2]=='t'){
cout<<" "<<xuhao<<":";
puts("reserved word:int");
i++;i++;
continue;
}
特殊地,对形如scanf(“%d”,&a);if(x<0){ }(与后文有关系的保留字)和a[](数组)这样特殊的C++保留字或字符,可以进行如下改动:
将'[' ']'两个字符加入数字的识别范围内:(s[i]>='0'&&s[i]<='9')改为
(s[i]>='0'&&s[i]<='9')||( s[i]=='[')|| ( s[i]==']')
这样,当输出中出现num,val=[10005] 时,我们便可以知道输入代码的这个位置是一个数组。
对形如scanf、if、printf、while、puts这样与后文有关系的保留字后面的若干个字符进行提前读入和特殊处理。
③对数字和标识符的识别代码块可以不做修改,直接用于C++词法分析器中。
④对注释的识别代码块需要进行修改以适应C++的注释体系,将注释识别代码块修改为如下程序即可:
while(gets(s)){/*整行读入至数组中*/
xuhao++;
zhushibiaoshi1=0;/*在行首将//对应的注释标识置为0*/
int lens=strlen(s);
cout<<xuhao<<":";
cout<<s<<endl;
for(int i=0;i<lens;i++){
if(s[i]==' ')/*如果是空格则不用分析,直接读入下一个字符*/
continue;
/*对注释的识别*/
if(s[i]=='/'&&s[i+1]=='*'){
zhushibiaoshi=1;
i++; /*避免第二个字符被重复读*/
continue;
}
if(s[i]=='*'&&s[i+1]=='/'){
zhushibiaoshi=0;
i++;
continue;
}
if(s[i]=='/'&&s[i+1]=='/'){
zhushibiaoshi1=1;/*利用//只能注释掉一行的特点来进行这部分处理*/
i++;
continue;
}
/*对非注释的识别*/
if(0==zhushibiaoshi&&0==zhushibiaoshi1){/*变量zhushibiaoshi、zhushibiaoshi1定义在程序最外层,这两个均为0表示待识别的字符不在注释里面(注释可以跨行)*/
/*对保留字的识别*/
/*(接TINY语言的词法分析器代码后面的部分)*/
由于C++中保留字和特殊符号种类繁多、处理方式繁多且添加或修改的方法已经在前文讨论过(单目符号加入符号集合,双目符号分解为两个单目符号进行处理(如原程序中:=符号);一般的保留字(如return、int等)可以像原程序那样识别,特殊的保留字或字符要特殊处理,如将'[' ']'两个字符加入数字的识别范围内、对如scanf、if、printf、while、puts这样与后文有关系的保留字后面的若干个字符进行提前读入和特殊处理等),故不在程序中加入这部分的处理方式。
写到这里发现没有对如scanf、if、printf、while、puts这样与后文有关系的保留字后面的若干个字符进行提前读入和特殊处理操作进行代码演示,所以在这里针对如printf这样的特殊保留字处理方式进行代码演示,本拓展核心代码(在保留字识别代码块中添加的部分如下):
(特殊地,本程序考虑了如下的情况
)
/*对保留字的识别*/
/*对保留字printf的识别*/
if(s[i]=='p'&&s[i+1]=='r'&&s[i+2]=='i'&&s[i+3]=='n'&&s[i+4]=='t'&&s[i+5]=='f'){
int countpri=6;/*跳过printf所占的字符空间*/
cout<<" "<<xuhao<<":";
puts("reserved word:printf");
while(s[i+countpri]==' ')
countpri++;/*跳过空格*/
countpri++;/*跳过左括号*/
while(s[i+countpri]==' ')
countpri++;/*跳过空格*/
countpri++;/*跳过左引号*/
cout<<" "<<xuhao<<":";
cout<<"id,name=";/*将引号之间的部分识别为字符串,输出为 reserved word:printf
id,name=XXXXX时,我们就知道输入代码的这里有一个输出XXXXX的printf函数*/
while(s[i+countpri]!='"'){
cout<<s[i+countpri];
countpri++;
}
countpri++;/*跳过右引号*/
cout<<endl;
while(s[i+countpri]==' ')
countpri++;/*跳过空格*/
countpri++;/*跳过右括号*/
while(s[i+countpri]==' ')
countpri++;/*跳过空格*/
countpri++;/*跳过;*/
cout<<" "<<xuhao<<":";
puts("id,name=;");
i+=countpri-1;/*将提前读的字符去掉*/
continue;
}
扩展过后的完整代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MAXN 900000005
char s[MAXN],fuhaoji[10]={'+','-','*','/','=','<','(',')',';',':'};
int main(){
int xuhao=0,zhushibiaoshi=0,zhushibiaoshi1=0;
while(gets(s)){/*整行读入至数组中*/
xuhao++;/*行号+1*/
zhushibiaoshi1=0;
int lens=strlen(s);
cout<<xuhao<<":";
cout<<s<<endl;
for(int i=0;i<lens;i++){
if(s[i]==' ')/*如果是空格则不用分析,直接读入下一个字符*/
continue;
/*对注释的识别*/
if(s[i]=='/'&&s[i+1]=='*'){
zhushibiaoshi=1;
i++;/*避免第二个字符被重复读*/
continue;
}
if(s[i]=='*'&&s[i+1]=='/'){
zhushibiaoshi=0;
i++;
continue;
}
if(s[i]=='/'&&s[i+1]=='/'){
zhushibiaoshi1=1;
i++;
continue;
}
/*对非注释的识别*/
if(0==zhushibiaoshi&&0==zhushibiaoshi1){/*变量zhushibiaoshi、zhushibiaoshi1定义在程序最外层,这两个均为0表示待识别的字符不在注释里面(注释可以跨行)*/
/*对保留字的识别*/
/*对保留字printf的识别*/
if(s[i]=='p'&&s[i+1]=='r'&&s[i+2]=='i'&&s[i+3]=='n'&&s[i+4]=='t'&&s[i+5]=='f'){
int countpri=6;/*跳过printf所占的字符空间*/
cout<<" "<<xuhao<<":";
puts("reserved word:printf");
while(s[i+countpri]==' ')
countpri++;/*跳过空格*/
countpri++;/*跳过左括号*/
while(s[i+countpri]==' ')
countpri++;/*跳过空格*/
countpri++;/*跳过左引号*/
cout<<" "<<xuhao<<":";
cout<<"id,name=";/*将引号之间的部分识别为字符串,输出为 reserved word:printf
id,name=XXXXX时,我们就知道输入代码的这里有一个输出XXXXX的printf函数*/
while(s[i+countpri]!='"'){
cout<<s[i+countpri];
countpri++;
}
countpri++;/*跳过右引号*/
cout<<endl;
while(s[i+countpri]==' ')
countpri++;/*跳过空格*/
countpri++;/*跳过右括号*/
while(s[i+countpri]==' ')
countpri++;/*跳过空格*/
countpri++;/*跳过;*/
cout<<" "<<xuhao<<":";
puts("id,name=;");
i+=countpri-1;/*将提前读的字符去掉*/
continue;
}
if(s[i]=='i'&&s[i+1]=='f'){
cout<<" "<<xuhao<<":";
puts("reserved word:if");
i++;
continue;
}
if(s[i]=='t'&&s[i+1]=='h'&&s[i+2]=='e'&&s[i+3]=='n'){
cout<<" "<<xuhao<<":";
puts("reserved word:then");
i++;i++;i++;/*提前往后面读了三个字符,要去掉这三个,不然会重复读,其他保留字同理,也需要去掉(保留字长度-1)个提前读的字符*/
continue;
}
if(s[i]=='e'&&s[i+1]=='l'&&s[i+2]=='s'&&s[i+3]=='e'){
cout<<" "<<xuhao<<":";
puts("reserved word:else");
i++;i++;i++;
continue;
}
if(s[i]=='e'&&s[i+1]=='n'&&s[i+2]=='d'){
cout<<" "<<xuhao<<":";
puts("reserved word:end");
i++;i++;
continue;
}
if(s[i]=='r'&&s[i+1]=='e'&&s[i+2]=='p'&&s[i+3]=='e'&&s[i+4]=='a'&&s[i+5]=='t'){
cout<<" "<<xuhao<<":";
puts("reserved word:repeat");
i++;i++;i++;i++;i++;
continue;
}
if(s[i]=='u'&&s[i+1]=='n'&&s[i+2]=='t'&&s[i+3]=='i'&&s[i+4]=='l'){
cout<<" "<<xuhao<<":";
puts("reserved word:until");
i++;i++;i++;i++;
continue;
}
if(s[i]=='r'&&s[i+1]=='e'&&s[i+2]=='a'&&s[i+3]=='d'){
cout<<" "<<xuhao<<":";
puts("reserved word:read");
i++;i++;i++;
continue;
}
if(s[i]=='w'&&s[i+1]=='r'&&s[i+2]=='i'&&s[i+3]=='t'&&s[i+4]=='e'){
cout<<" "<<xuhao<<":";
puts("reserved word:write");
i++;i++;i++;i++;
continue;
}
/*对符号的识别*/
for(int j=0;j<10;j++){
if(s[i]==fuhaoji[j]){
if(s[i]==':'){/*对双目符号:=进行特殊处理*/
if(s[i+1]=='='){
cout<<" "<<xuhao<<":";
cout<<s[i]<<s[i+1]<<endl;
i++;
}else{
puts(":");
}
}else{
cout<<" "<<xuhao<<":";
cout<<s[i]<<endl;
}
break;
}
}
/*对数字的识别*/
if(s[i]>='0'&&s[i]<='9'){
int count1=1;
cout<<" "<<xuhao<<":";
cout<<"num,val=";
cout<<s[i];
while(s[i+count1]>='0'&&s[i+count1]<='9'){
cout<<s[i+count1];
count1++;
}
i+=count1;
i--;
cout<<endl;
}
/*对标识符的识别*/
if((s[i]>='a'&&s[i]<='z')||(s[i]>='A'&&s[i]<='Z')){/*这里不能直接a到Z*/
int count2=1;
cout<<" "<<xuhao<<":";
cout<<"id,name=";
cout<<s[i];
while((s[i+count2]>='a'&&s[i+count2]<='z')||(s[i+count2]>='A'&&s[i+count2]<='Z')){
cout<<s[i+count2];
count2++;
}
i+=count2;
i--;
cout<<endl;
}
}
}
}
return 0;
}
完整代码输入输出演示:
输入(自拟):
printf ( " /*sum // " ) ;
/* printf ( "qaq " ) ;
read X;
*/ printf ( " read awa // */ " ) ;//read m;
printf ( " /* printf zhongyuxiewanle " ) ;
printf("//HAHAHA//") ;
输出:
总结C++各部分的词法分析方法:
①数字和标识符:与原代码(词法分析展示.cpp,下文简称为原代码)相同。
②注释:见扩展代码(词法分析扩展-C++注释.cpp,下文简称为扩展代码)。
③一般的保留字(int等):与原代码相同(举例见上文)。
④特殊的保留字(printf、cin等):见扩展代码,按扩展代码的设计思路来设计即可设计出能够分析其他特殊的保留字的代码块。
⑤符号:处理方法与原代码相同(将特殊符号集拓展为C++所需要的单目特殊符号组成的集合(双目符号如+=这样的符号处理方式已于本程序中展示(:=符号的处理)(将双目符号分解为两个单目符号进行处理)))
⑥特殊字符串(a[]等):通过对特定的字符进行专门的识别(如列入特定的识别范围内)来实现词法分析。
特殊地,对于cin这样后面跟着>>的C++保留字,我们可以将 >>(可能被程序误认为是位运算符号) 视为printf保留字后 “ 一样的东西在保留字识别代码块中直接跳过以避免被误认,跳过的方法可以参考上文的代码,空格问题也可以参考上文的代码解决。
/*空格问题处理代码块*/
while(s[i+countpri]==' ')
countpri++;/*跳过空格*/
综上所述,可以运用本扩展代码(词法分析扩展-C++注释.cpp)中的方法和设计思路来实现完整的C++词法分析器。