“编译原理,经过对普通编译原理分析,形成了以词典为核心,结合词的数据结构完成编译过程的自成体系技术理论。通过分析词法生成词典(词典发生器),分析语法生成局部代码,并建立数据指针(翻译机),组装局部代码生成目标机器语言流并建立程序调用过的对象、指针等数据流空间,最终完成可执行程序(装配器)三个模糊过程(因为这些过程都离不开词典,编译中间会发生相互影响),把高级计算机语言加工成为计算机机器语言代码流,这就是我们提出的编译原理。”
经过一学期的编译原理学习,对这门课的认识也从一知半解到逐渐理解。学习过程中,做了许多内容相关的实验,如词法分析程序、语法分析程序、语义分析程序,以及TEST虚拟机。这些部分组合成了一个简易的TEST语言编译器。现将代码上传至博客。如果对你有帮助,欢迎点赞。
词法分析:
词法分析是编译器前端设计的基础阶段, 在这一阶段, 编译器会根据设定的语法规则, 对源程序进行标记, 在标记的过程中, 每一处记号都代表着一类单词, 在做记号的过程中, 主要有标识符、关键字、特殊符号等类型, 编译器中包含词法分析器、输入源程序、输出识别记号符, 利用这些功能可以将字号转化为熟悉的单词。
建议使用DEVC++作为编程工具,可避免不必要的错误。
#include<bits/stdc++.h> //这是一个万能头文件。不想使用的话可更改为自己需要的
#define keyNum 12
using namespace std;
char keyWord[keyNum][11]={"call","do","else","enum","for","function","if","int","main","read","while","write"};
char singleWord[]="+-*();,{}";//纯单分界符
char doubleWord[]=">=<!";
FILE *Fin,*Fout;//文件指针
char cifa_in[300];//输入文件名
char cifa_out[300];
int init();//初始化
int scan();//输入
int isSaved(char *s){//是否为保留字 (二分)
int l=0,r=keyNum;
while(l<r){
int mid=(l+r)/2;
if(strcasecmp(s,keyWord[mid])==0) return 1;
if(strcasecmp(s,keyWord[mid])>0) l=mid+1;
else r=mid;
}
return 0;
}
int init(){
cout<<"输入文件名称:"<<endl;
cin>>cifa_in;
strcat(cifa_in,".txt");
if((Fin=fopen(cifa_in,"r"))==NULL){
cout<<"打开输入文件出错"<<endl;
return 0;
}
strcpy(cifa_out,"out_cifa_");
strcat(cifa_out,cifa_in);
if((Fout=fopen(cifa_out,"w"))==NULL){
cout<<"创建词法输出文件出错"<<endl;
return 0;
}
return 1;
}
int scan(){//词法分析
char ch,buff[40];
int es=1,j;
int cnt=1;//用于记录当前行号
ch=getc(Fin);
while(ch!=EOF){
while(ch==' '||ch=='\t'||ch=='\n'){
if(ch=='\n') cnt++;
ch=getc(Fin);
}
if(ch==EOF) break;
if(isalpha(ch))
{//如果是标识符
buff[0]=ch;
j=1;
ch=getc(Fin);
while(isalnum(ch))
{
buff[j++]=ch;
ch=getc(Fin);
}
buff[j]='\0';
int num;
if(isSaved(buff))
fprintf(Fout,"%s\t%s\n",buff,buff);
else fprintf(Fout,"%s\t%s\n","ID",buff);
}
else if(isdigit(ch)){//如果是数字
buff[0]=ch;ch=getc(Fin);j=1;
while(isdigit(ch)){
buff[j++]=ch;
ch=getc(Fin);
}
buff[j]='\0';
fprintf(Fout,"%s\t%s\n","NUM",buff);
}
else if(strchr(singleWord,ch)>0){//单分界符
buff[0]=ch;
buff[1]='\0';
ch=getc(Fin);
fprintf(Fout,"%s\t%s\n",buff,buff);
}
else if(strchr(doubleWord,ch)>0)
{//双
buff[0]=ch; ch=getc(Fin);
if(ch=='='){
buff[1]=ch;buff[2]='\0';
ch=getc(Fin);
}
else buff[1]='\0';
fprintf(Fout,"%s\t%s\n",buff,buff);
}
else if(ch=='/'){
ch=getc(Fin);
if(ch=='*'){
char ch1;
ch1=getc(Fin);
do{
ch=ch1;ch1=getc(Fin);
}while((ch!='*'||ch1!='/')&&ch1!=EOF);
ch=getc(Fin);
}
else{
buff[0]='/';buff[1]='\0';
fprintf(Fout,"%s\t%s\n",buff,buff);
}
}
else if(ch=='&'){
buff[0]=ch;
ch=getc(Fin);
if(ch=='&'){
buff[1]=ch;buff[2]='\0';
fprintf(Fout,"%s\t%s\n",buff,buff);
ch=getc(Fin);
}
else{
buff[1]='\0';
ch=getc(Fin);
es=0;
fprintf(Fout,"%s\t%d\t%s\n","ERROR at line",cnt,buff);
}
}
else if(ch=='|'){
buff[0]=ch;
ch=getc(Fin);
if(ch=='|'){
buff[1]=ch;buff[2]='\0';
fprintf(Fout,"%s\t%s\n",buff,buff);
ch=getc(Fin);
}
else{
buff[1]='\0';
ch=getc(Fin);
es=0;
fprintf(Fout,"%s\t%d\t%s\n","ERROR at line ",cnt,buff);
}
}
else{
buff[0]=ch;buff[1]='\0';
ch=getc(Fin);
es=0;
fprintf(Fout,"%s\t%d\t%s\n","ERROR at line ",cnt,buff);
}
}
fclose(Fin);
fclose(Fout);
return es;
}
语法分析:
语法分析是指利用设定的语法规则, 对记号中的结构进行标识, 这包括句子、短语等方式, 在标识的过程中, 可以形成特殊的结构语法树。语法分析对编译器功能的发挥有着重要影响, 在设计的过程中, 一定要保证标识的准确性。
语法分析主要是以单词串形式的源程序作为分析与输入对象, 其最为根本的目标和任务就是为了以设计语言的语法规则为标准, 对源程序的语法结构进行具体的分析, 根据设计语言的语法规则, 对组成这些源程序的语法成分进行分析, 如函数、下标变量、各种程序语句、各种表达式等等, 并且要通过正确性的语法检查, 将中间代码进行阶段处理。但是要注意的一点是根据需要进行了归约处理, 必然存在着相应语法错误, 那么可以将其中全部输入的符号删除, 改变上述格局, 进行移进和归约分析, 并且在此基础上, 不断地寻找一个相应的策略, 从而形成有效的语法分析方法
语义分析,生成中间代码:
语义分析也需要借助语法规则, 在对语法单元的静态语义进行检查时, 要保证语法规则设定的准确性。在对词法或者语法进行转化时, 一定要保证语法结构设置的合法性。在对语法、词法进行检查时, 语法结构设定不合理, 则会出现编译错误的问题。前端设计对精确性要求比较好, 设计人员能够要做好校对工作, 这会影响到编译的准确性, 如果前端设计存在失误, 则会影响C语言编译的效果。
将编译程序转换为一种内部表现形式后, 我们将该种内部表现形式称之为中间语言或者是中间代码, 它含义明确、结构简单, 属于一种记号系统。比如一些编译程序, 基本上没有中间代码, 但是为了在实际应用中, 确保机器的独立运行, 易于实现目标代码的优化, 在许多的编译程序中均设置了中间语言。这种中间语言介于机器语言和源程序语言中, 程序相对复杂, 而C语言编译器却在很大程度上改变以上现状, 其语义分析和语法分析相对成熟, 理论和算法比较完善, 可仍旧存在的问题是没有公认的形式系统, 中间代码仍旧接近于形式化的方法。
同教材一样,我将语法以及语义分析中间代码生成写成一体,方便修改编辑。
代码如下(需要以头文件的形式包含前面的词法分析程序):
#include<bits/stdc++.h>
#include"词法分析.h"
using namespace std;
const int maxvartablep=500;
char token[20],token1[40];
char yuyi_in[300];//词法分析文件名
char yuyi_out[300];
FILE *fin,*fout;
int program();
int declaration_list();
int declaration_stat();
int statement_list();
int statement();
int if_stat();
int while_stat();
int for_stat();
int do_while_stat();
int read_stat();
int write_stat();
int compound_stat();
int expression_stat();
int expression();
int bool_expr();
int additive_expr();
int term();
int factor();
int fun_declaration();//
int fun_body();//
int main_declaration();//
int call_stat();//
int name_def(char *name,int isenum);
char enum_name[15]; //存enum类型的枚举名
int isenum=0;
//连续查错
int line = 3; //定义行数,用于错误处理 从2开始,一般main和{分行写 2指向{
int errorFlag=0; //errorFlag表示是否检测到"可连续查错的"错误 为0表示没有,为1表示有
char errors[50][50]; //errors数组用来储存编译过程中出现的错误
char errorNum = 0; //用来储存错误序号
//sprintf(errors[errorNum], "第%d行: 未定义的符号", line);
// errorNum++;
struct{
char name[8];
int address;
int xisenum;
} vartable[maxvartablep],enum_chart[maxvartablep];
int vartablep=0,labelp=0,datap=0;
int e_vartablep=0,e_labelp=0; //用来插入enum符号表
//把枚举值存入enum符号表
int insert_enum(char *name)
{
int i,es=0;
if(e_vartablep>=maxvartablep) return(26);
for(i=e_vartablep-1;i>=0;i--)
{
if(strcmp(enum_chart[i].name,name)==0)
{
es=25; //枚举值重复
break;
}
}
if(es>0) return(es);
strcpy(enum_chart[e_vartablep].name,name);
e_vartablep++;
return(es);
}
//插入符号表动作
int name_def(char*name,int isenum)
{
int i,es=0;
if(vartablep>=maxvartablep) return(21);
for(i=vartablep-1;i>=0;i--)
{
if(strcmp(vartable[i].name,name)==0)
{
es=22;
break;
}
}
if(es>0) return(es);
strcpy(vartable[vartablep].name,name);
vartable[vartablep].address=datap;
vartable[vartablep].xisenum=isenum;
datap++;
vartablep++;
return(es);
}
//查枚举变量赋值是否在enum_chart中
int lookenum(char *name)
{
int i,es=0;
for(i=0;i<e_vartablep;i++)
{
if(strcmp(enum_chart[i].name,name)==0)
{
fprintf(fout," LOADI %s\n",name);
return(es);
}
}
es=30; //值不在枚举值中
return(es);
}
int lookup(char* name,int* paddress){
int i,es=0;
for(i=0;i<vartablep;i++){
if(strcmp(vartable[i].name,name)==0){
*paddress=vartable[i].address;
return es;
}
}
return es=23;
}
int TESTparse(){
int es=0;
strcpy(yuyi_in,cifa_out);
if((fin=fopen(yuyi_in,"r"))==NULL){
printf("打开%s文件错误\n",yuyi_in);
es=10;
}
strcpy(yuyi_out,"out_yuyi_");
strcat(yuyi_out,cifa_in);
if((fout=fopen(yuyi_out,"w"))==NULL){
printf("打开yuyi_out文件错误\n");
es=10;
}
if(es==0) es=program();
printf("=====语法分析结果:======\n");
switch(es){
case 0:printf("语法分析成功\n");break;
case 10:cout<<"打开输入文件失败"<<endl;break;
case 1:cout<<"缺少{!"<<endl;break;
case 2:cout<<"缺少}!"<<endl;break;
case 3:cout<<"缺少标识符!"<<endl;break;
//case 4:cout<<"缺少分号!"<<endl;break;
case 5:cout<<"缺少(!" <<endl;break;
case 6:cout<<"缺少)!"<<endl;break;
case 7:cout<<"缺少操作数!"<<endl;break;
case 9:cout<<"缺少main函数"<<endl;break;
case 11:cout<<"非法声明,缺少fun/main"<<endl;break;
case 21:cout<<"符号表溢出"<<endl;break;
case 22:cout<<"重复定义"<<endl;break;
case 23:cout<<"变量未声明"<<endl;break;
case 24:printf("do后缺少while!\n"); break;
case 25: printf("枚举值重复!\n"); break;
case 26: printf("枚举值个数溢出!\n"); break;
case 27: printf("不允许给枚举值赋值!\n"); break;
case 30: printf("给枚举变量赋的值只能为%s枚举值中的一个!\n",enum_name); break;
default: cout<<es<<endl;
}
return es;
}
int program(){
int es=0;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"function")&&strcmp(token,"main")){
return es=11;
}
while(strcmp(token,"function")==0){
es=fun_declaration();
if(es>0) return es;
}
if(strcmp(token,"main")){
return es=9;
}
es=main_declaration();
fprintf(fout," STOP\n");
if(es>0) return es;
printf(" 符号表\n");
printf(" 名字 地址\n");
for(int i=0;i<vartablep;i++)
{
printf(" %s %d\n",vartable[i].name,vartable[i].address);
}
return es;
}
int fun_declaration(){
int es=0;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"ID")) return es=3;
es=name_def(token1,0);
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"(")) return es=5;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,")")) return es=5;
es=fun_body();
if(es>0) return es;
return es;
}
int main_declaration(){
int es=0;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"(")) return es=5;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,")")) return es=6;
es=fun_body();
if(es>0) return es;
fscanf(fin,"%s %s\n",token,token1);
return es;
}
int fun_body(){
int es=0;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"{")) return es=1;
fscanf(fin,"%s %s\n",token,token1);
es=declaration_list();
if(es>0) return es;
es=statement_list();
if(es>0) return es;
if(strcmp(token,"}")) return es=2;
fscanf(fin,"%s %s\n",token,token1);
return es;
}
int declaration_list(){
int es=0;
while(strcmp(token,"int")==0||strcmp(token,"enum")==0){
es=declaration_stat();
if(es>0) return es;
}
return es;
}
int declaration_stat(){
int es=0;
if(strcmp(token,"int")==0) //是int
{
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"ID")) return es=3;
es=name_def(token1,0);
if(es>0) return es;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,";")){ //读到不是;的符号时,写入错误文件,再继续向下取 减少一步fscanf
errorFlag=1; //置全局错误标志为1
sprintf(errors[errorNum], "第%d行: 语句末尾缺少分号", line);
line++;
errorNum++;
}
if(!strcmp(token,";")){ //读到;了,正常继续fscanf
fscanf(fin,"%s %s\n",token,token1);
line++;
}
return es;
}
if(strcmp(token,"enum")==0)
{
fscanf(fin, "%s %s\n", token, token1); //识别枚举名 存入enum_name数组
printf("%s %s\n",token,token1);
strcpy(enum_name,token1);
fscanf(fin, "%s %s\n", token, token1); //识别枚举名后面的枚举值
printf("%s %s\n",token,token1);
if (strcmp(token,"{")) {
return (es=4);
}
fscanf(fin, "%s %s\n", token, token1);
printf("%s %s\n",token,token1);
while(strcmp(token,"}")) //枚举值存入符号表
{
if((strcmp(token,",")==0))
{
fscanf(fin, "%s %s\n", token, token1);
printf("%s %s\n",token,token1);
}
else
{
es=insert_enum(token1); //扫描进枚举符号表
if(es>0) return(es);
fscanf(fin, "%s %s\n", token, token1);
printf("%s %s\n",token,token1);
}
}
fscanf(fin, "%s %s\n", token, token1);
printf("%s %s\n",token,token1);
while(strcmp(token,";"))
{
if((strcmp(token,",")==0))
{
fscanf(fin, "%s %s\n", token, token1);
printf("%s %s\n",token,token1);
}
else
{
es=name_def(token1,1);//扫描进符号表并标识xisenum=1
if(es>0) return(es);
fscanf(fin, "%s %s\n", token, token1);
printf("%s %s\n",token,token1);
}
}
fscanf(fin, "%s %s\n", token, token1);
printf("%s %s\n",token,token1);
return(es);
}
}
int statement_list(){
int es=0;
while(strcmp(token,"}")){
es=statement();
if(es>0) return es;
}
return es;
}
int statement(){
int es=0;
if(es==0&&strcmp(token,"if")==0) es=if_stat();
if(es==0&&strcmp(token,"while")==0) es=while_stat();
if(es==0&&strcmp(token,"for")==0) es=for_stat();
if(es==0&&strcmp(token,"read")==0) es=read_stat();
if(es==0&&strcmp(token,"write")==0) es=write_stat();
if(es==0&&strcmp(token,"{")==0) es=compound_stat();
if(es==0&&strcmp(token,"call")==0) es=call_stat();
if(es==0&&(strcmp(token,"ID")==0||strcmp(token,"NUM")==0||strcmp(token,"(")==0)) es=expression_stat();
if(es == 0 && strcmp(token, "do") == 0) es = do_while_stat(); //<do_while语句>
return es;
}
int if_stat(){
int es=0,label1,label2;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"(")) return es=5;
fscanf(fin,"%s %s\n",token,token1);
es=expression();
if(es>0) return es;
if(strcmp(token,")")) return es=6;
label1=labelp++;
fprintf(fout," BRF LABEL%d\n",label1);
fscanf(fin,"%s %s\n",token,token1);
es=statement();
if(es>0) return es;
label2=labelp++;
fprintf(fout," BR LABEL%d\n",label2);
fprintf(fout,"LABEL%d:\n",label1);
if(strcmp(token,"else")==0){
fscanf(fin,"%s %s\n",token,token1);
es=statement();
if(es>0) return es;
}
fprintf(fout,"LABEL%d:\n",label2);
return es;
}
int do_while_stat(){
int es = 0,label1,label2;
label1=labelp++; //BR
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"{")) //少{
return(es=1);
//设置LABEL1:
line++;
fprintf(fout,"LABEL%d:\n",label1);
fscanf(fin,"%s %s\n",token,token1);
es=statement(); // statement语句,do内部
if(strcmp(token,"}")) //少}
return(es=2); //do的执行范围是从第一句一直到}左边为止
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"while")) //少while
return(es=24);
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"(")) //少(
return(es=5);
fscanf(fin,"%s %s\n",token,token1); //while()括号内表达式 expression语句
es=expression();
if(es>0) return(es);
if(strcmp(token,")")) //少)
return(es=6);
label2=labelp++; //存BRF
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,";")){ //读到不是;的符号时,写入错误文件,再继续向下取 减少一步fscanf
errorFlag=1; //置全局错误标志为1
sprintf(errors[errorNum], "第%d行: 语句末尾缺少分号", line);
line++;
errorNum++;
}
//设置LABEL2:
fprintf(fout," BRF LABEL%d:\n",label2);
fprintf(fout," BR LABEL%d:\n",label1);
fprintf(fout,"LABEL%d:\n",label2);
if(!strcmp(token,";")){ //读到;了,正常继续fscanf
fscanf(fin,"%s %s\n",token,token1);
line++;
} //少;
return(es);
}
int while_stat()
{
int es=0,label1,label2;
label1=labelp++;
fprintf(fout,"LABEL%d:\n",label1);
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"(")) return es=5;
fscanf(fin,"%s %s\n",token,token1);
es=expression();
if(es>0) return es;
if(strcmp(token,")")) return es=6;
label2=labelp++;
fprintf(fout," BRF LABEL%d\n",label2);
fscanf(fin,"%s %s\n",token,token1);
es=statement();
if(es>0) return es;
fprintf(fout," BR LABEL%d\n",label1);
fprintf(fout,"LABEL%d:\n",label2);
return es;
}
int for_stat(){
int es=0,label1,label2,label3,label4;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"(")) return es=5;
fscanf(fin,"%s %s\n",token,token1);
es=expression();
if(es>0) return es;
fprintf(fout," POP\n");
if(strcmp(token,";")){ //少分号
errorFlag=1; //置全局错误标志为1
sprintf(errors[errorNum], "第%d行: 语句末尾缺少分号", line);
line++;
errorNum++;
}
label1=labelp++;
fprintf(fout,"LABEL%d:\n",label1);
if(!strcmp(token,";")){ //读到;了,正常继续fscanf
fscanf(fin,"%s %s\n",token,token1);
line++;
}
es=expression();
if(es>0) return es;
label2=labelp++;
fprintf(fout," BRF LABEL%d\n",label2);
label3=labelp++;
fprintf(fout," BR LABEL%d\n",label3);
if(strcmp(token,";")){ //少分号
errorFlag=1; //置全局错误标志为1
sprintf(errors[errorNum], "第%d行: 语句末尾缺少分号", line);
errorNum++;
line++;
}
label4=labelp++;
fprintf(fout,"LABEL%d:\n",label4);
if(!strcmp(token,";")){ //读到;了,正常继续fscanf
fscanf(fin,"%s %s\n",token,token1);
line++;
}
es=expression();
if(es>0) return es;
fprintf(fout," POP\n");
fprintf(fout," BR LABEL%d\n",label1);
if(strcmp(token,")")) return es=6;
fprintf(fout,"LABEL%d:\n",label3);
fscanf(fin,"%s %s\n",token,token1);
es=statement();
if(es>0) return es;
fprintf(fout," BR LABEL%d\n",label4);
fprintf(fout,"LABEL%d:\n",label2);
return es;
}
int write_stat(){
int es=0;
fscanf(fin,"%s %s\n",token,token1);
es=expression();
if(es>0) return es;
if(strcmp(token,";")){ //少分号
errorFlag=1; //置全局错误标志为1
sprintf(errors[errorNum], "第%d行: 语句末尾缺少分号", line);
errorNum++;
line++;
}
fprintf(fout," OUT\n");
if(!strcmp(token,";")){ //读到;了,正常继续fscanf
fscanf(fin,"%s %s\n",token,token1);
line++;
}
return es;
}
int read_stat(){
int address;
int es=0;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"ID")) return es=3;
es=lookup(token1,&address);
if(es>0) return es;
fprintf(fout," IN \n");
fprintf(fout," STO %d\n",address);
fprintf(fout," POP\n");
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,";")){ //少分号
errorFlag=1; //置全局错误标志为1
sprintf(errors[errorNum], "第%d行: 语句末尾缺少分号", line);
errorNum++;
line++;
}
if(!strcmp(token,";")){ //读到;了,正常继续fscanf
fscanf(fin,"%s %s\n",token,token1);
line++;
}
return es;
}
int compound_stat(){
int es=0;
fscanf(fin,"%s %s\n",token,token1);
es=statement_list();
if(strcmp(token,"}")) return es=2;
fscanf(fin,"%s %s\n",token,token1);
return es;
}
int expression_stat(){
int es=0;
if(strcmp(token,";")==0){ //有;
fscanf(fin,"%s %s\n",token,token1);
return es;
}
es=expression();
if(es>0) return es;
fprintf(fout," POP\n");
if(es==0&&strcmp(token,";")==0){
fscanf(fin,"%s %s\n",token,token1);
line++;
return es;
}
if(es==0&&(strcmp(token,";")!=0)){ //没取到;
errorFlag=1; //置全局错误标志为1
sprintf(errors[errorNum], "第%d行: 语句末尾缺少分号", line);
line++;
errorNum++;
return es;
}
}
int call_stat(){
int es=0;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"ID")) return es=3;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,"(")) return es=5;
fscanf(fin,"%s %s\n",token,token1);
if(strcmp(token,")")) return es=6;
fscanf(fin,"%s %s\n",token,token1);
return es;
}
int expression(){
int es=0,fileadd;
char token2[20],token3[40];
if(strcmp(token,"ID")==0){
for(int i=0;i<e_vartablep;i++) //遍历枚举值符号表,看是不是在给枚举值赋值,是的话返回错误信息,退出
{
if(strcmp(token1,enum_chart[i].name)==0)
{
return (es=27);
}
}
fileadd=ftell(fin);
fscanf(fin,"%s %s\n",token2,token3);
if(strcmp(token2,"=")==0){
int address;
es=lookup(token1,&address);
if(es>0) return es;
if(vartable[address].xisenum==1) //看前面的ID是普通变量还是enum变量
{
fscanf(fin, "%s %s\n", token, token1);
printf("%s %s\n",token,token1);
es=lookenum(token1);
if(es>0)
return(es);
fprintf(fout," STO %d\n",address);
fscanf(fin, "%s %s\n", token, token1);
printf("%s %s\n",token,token1);
}
else
{
fscanf(fin, "%s %s\n", token, token1);
printf("%s %s\n",token,token1);
es=bool_expr();
if(es>0)
return (es);
fprintf(fout," STO %d\n",address);
}
}
else{
fseek(fin,fileadd,0);
es=bool_expr();
if(es>0) return es;
}
}
else es=bool_expr();
return es;
}
int bool_expr(){
int es=0;
es=additive_expr();
if(es>0) return es;
if(strcmp(token,">")==0||strcmp(token,">=")==0||strcmp(token,"<")==0||strcmp(token,"<=")==0||strcmp(token,"==")==0||strcmp(token,"!=")==0){
char token2[20];
strcpy(token2,token);
fscanf(fin,"%s %s\n",token,token1);
es=additive_expr();
if(es>0) return es;
if(strcmp(token2,">")==0)fprintf(fout," GT\n");
if(strcmp(token2,">=")==0)fprintf(fout," GE\n");
if(strcmp(token2,"<")==0)fprintf(fout," LES\n");
if(strcmp(token2,"<=")==0)fprintf(fout," LE\n");
if(strcmp(token2,"==")==0)fprintf(fout," EQ\n");
if(strcmp(token2,"!=")==0)fprintf(fout," NOTEQ\n");
}
return es;
}
int additive_expr(){
int es=0;
es=term();
if(es>0) return es;
while(strcmp(token,"+")==0||strcmp(token,"-")==0){
char token2[20];
strcpy(token2,token);
fscanf(fin,"%s %s",token,token1);
// printf("%s %s\n",token,token1);
es=term();
if(es>0) return es;
if(strcmp(token2,"+")==0) fprintf(fout," a ADD\n");
if(strcmp(token2,"0")==0) fprintf(fout," SUB\n");
}
return es;
}
int term(){
int es=0;
es=factor();
if(es>0) return es;
while(strcmp(token,"*")==0||strcmp(token,"/")==0){
char token2[20];
strcpy(token2,token);
fscanf(fin,"%s %s\n",token,token1);
es=factor();
if(es>0) return es;
if(strcmp(token2,"*")==0) fprintf(fout," MULT\n");
if(strcmp(token2,"/")==0) fprintf(fout," DIV\n");
}
return es;
}
int factor(){
int es=0;
if(strcmp(token,"(")==0){
fscanf(fin,"%s %s\n",token,token1);
es=expression();
if(es>0) return es;
if(strcmp(token,")")) return es=6;
fscanf(fin,"%s %s\n",token,token1);
}
else{
if(strcmp(token,"ID")==0){
int address;
es=lookup(token1,&address);
if(es>0) return es;
fprintf(fout," LOAD %d\n",address);
fscanf(fin,"%s %s\n",token,token1);
return es;
}
if(strcmp(token,"NUM")==0){
fprintf(fout," LOADI %s\n",token1);
fscanf(fin,"%s %s\n",&token,&token1);
return es;
}
else{
es=7;
return es;
}
}
return es;
}
int main()
{
if(!init()){cout<<"词法分析错误"<<endl;return 0;}
int flag=scan();
if(flag) cout<<"词法分析成功"<<endl;
else cout<<"词法分析失败"<<endl;
int es=0;
if(flag) es=TESTparse();
if(es==0&&flag){
printf("语义分析成功\n");
}
else if(flag&&es) printf("语法分析失败\n");
if (errorFlag!=0) {
printf("有遗漏性错误\n");
printf("\n错误数量: %d \n", errorNum);
for (int i = 0; i < errorNum; i++)
printf("%s\n", errors[i]);
}
fclose(fin);
fclose(fout);
return 0;
}
TEST虚拟机:
#include<bits/stdc++.h>
int TESTmachine(){
int es=0,i=0,k=0,codecount,stack[1000],stacktop=0;
int data[1000];
char Codein[300],code[1000][20];
int label[100]={0};
char lno[4];
FILE* fin;
printf("输入文件名:");
scanf("%s",Codein);
strcat(Codein,".txt");
char s[300]="out_yuyi_";
strcat(s,Codein);strcpy(Codein,s);
if((fin=fopen(Codein,"r"))==NULL){
printf("打开%s文件出错\n",Codein);
es=10;
return 10;
}
codecount=0;
i=fscanf(fin,"%s",&code[codecount]);
while(!feof(fin)){
i=strlen(code[codecount])-1;
if(code[codecount][i]==':'){
i=i-5;
strncpy(lno,&code[codecount][5],i);
lno[i]='\0';
label[atoi(lno)]=codecount;
// printf("label[%d]=%d\n",atoi(lno),label[atoi(lno)]);
code[codecount][0]=':';
code[codecount][1]='\0';
strcat(code[codecount],lno);
k++;
}
codecount++;
i=fscanf(fin,"%s",&code[codecount]);
}
fclose(fin);
// for(i=0;i<codecount;i++) printf("code[%d] %s\n",i,code[i]);
for(i=0;i<codecount;i++){
int l;
l=strlen(code[i]);
if((l>1)&&(code[i][1]=='A')){
strncpy(lno,&code[i][5],l-5);
lno[l-5]='\0';
itoa(label[atoi(lno)],code[i],10);
}
}
// for(i=0;i<codecount;i++) printf("code[%d] %s\n",i,code[i]);
i=0;
while(i<codecount){
// printf("code %d %s\n",i,code[i]);
if(strcmp(code[i],"LOAD")==0){
i++;
stack[stacktop]=data[atoi(code[i])];
stacktop++;
}
if(strcmp(code[i],"LOADI")==0){
i++;
stack[stacktop++]=atoi(code[i]);
}
if(strcmp(code[i],"STO")==0){
i++;
data[atoi(code[i])]=stack[stacktop-1];
}
if(strcmp(code[i],"POP")==0){
stacktop--;
}
if(strcmp(code[i],"ADD")==0){
stack[stacktop-2]=stack[stacktop-2]+stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"SUB")==0){
stack[stacktop-2]=stack[stacktop-2]-stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"MULT")==0){
stack[stacktop-2]=stack[stacktop-2]*stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"DIV")==0){
stack[stacktop-2]=stack[stacktop-2]/stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"BR")==0){
i++;
i=atoi(code[i]);
}
if(strcmp(code[i],"BRF")==0){
i++;
if(stack[stacktop-1]==0) i=atoi(code[i]);
stacktop--;
}
if(strcmp(code[i],"EQ")==0){
stack[stacktop-2]=stack[stacktop-2]==stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"NOTEQ")==0){
stack[stacktop-2]=stack[stacktop-2]!=stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"GT")==0){
stack[stacktop-2]=stack[stacktop-2]>stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"LES")==0){
stack[stacktop-2]=stack[stacktop-2]<stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"GE")==0){
stack[stacktop-2]=stack[stacktop-2]>=stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"LE")==0){
stack[stacktop-2]=stack[stacktop-2]<=stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"AND")==0){
stack[stacktop-2]=stack[stacktop-2]&&stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"OR")==0){
stack[stacktop-2]=stack[stacktop-2]||stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"NOT")==0){
stack[stacktop-1]=!stack[stacktop-1];
stacktop--;
}
if(strcmp(code[i],"IN")==0){
printf("输入:");
scanf("%d",&stack[stacktop]);
stacktop++;
}
if(strcmp(code[i],"OUT")==0){
printf("程序输出##%d\n",stack[stacktop-1]);
stacktop--;
}
if(strcmp(code[i],"STOP")==0){
break;
}
i++;
}
return es;
}
int main()
{
int es=TESTmachine();
return 0;
}
结语:编译原理这门课对科班学生还是很重要的。知道了编译器是如何实现的,当自己的代码出问题后可以更好的定位问题,解决问题。同时,学习这门课不建议单单为了考试而背课件,而是老老实实做实验。虽然过程很艰难,但是收获真的很大。