编译原理课程设计11.docx
《编译原理课程设计11.docx》由会员分享,可在线阅读,更多相关《编译原理课程设计11.docx(33页珍藏版)》请在冰豆网上搜索。
编译原理课程设计11
一、实验环境
在学习《程序设计语言编译原理》课程过程中,结合各章节的构造编译程序的基本理论,在基本实验完成的基础上,逐步完成课程设计。
在学习过程中,已经完成了此法分析器,语法分析器及语义分析器的设计,本课程设计是在已完成的设计的基础上构造简单的编译器(包括符号表的构造,词法分析,语法分析,目标代码生成等重要子程序), 对基本构架进行分析整合,进而完成本次课程设计。
通过本次课程设计希望能够将自己所学的理论知识与实际应用结合,训练自己软件设计和开发过程的能力,提高自身软件开发的能力。
二、课程设计的任务
(1)设计符号表
确定符号表的组织方式,一般应包括名字栏和信息栏,其中名字栏作为关键字。
要考虑能够存储有关名字的信息,并可以高效地完成如下操作:
a.查找:
根据给定的名字,在符号表中查找其信息。
如果该名字在符号表中不存在,则将其加入到符号表中,否则返回指向该名字的指针;
b.删除:
从符号表中删除给定名字的表项。
(2)设计词法分析器
设计各单词的状态转换图,并为不同的单词设计种别码。
将词法分析器设计成供语法分析器调用的子程序。
功能包括:
a.具备预处理功能。
将不翻译的注释等符号先滤掉,只保留要翻译的符号串,即要求设计一个供词法分析调用的预处理子程序;
b.能够拼出语言中的各个单词;
c.将拼出的标识符填入符号表;
d.返回(种别码,属性值)。
(3)语法分析器
要求用预测分析法、递归下降分析法、算符优先分析法、SLR分析法(几种方法任选),实现对表达式、各种说明语句、控制语句进行语法分析。
(4)目标代码生成器
能完成指定寄存器个数的情况下将一中间代码程序段翻译成汇编语言目标代码(汇编指令应包括加、减、乘、除),要求指令条数最少的情况下,尽量使用寄存器,尽量少访问内存,这样才能做到运行效率高
三、实验要求
用一种编程语言实现一个简单的编译器,其中基本的语句要求必须实现,其余部分可根据自己的实际情况选择实现。
对主要代码给予解释和理解注释,各函数和过程应有简要描述,有功能说明,有入口和出口参数说明。
四、简单编译器的实现流程图
五、课程设计的详细过程
5.1主函数的建立:
externintTESTscan();
externintTESTparse();
charScanin[300],Scanout[300],Errorfile[300],Codeout[300];
//用于接收输入输出以及错误文件名
FILE*fin,*fout;//用于指向输入输出文件的指针
voidmain(){
intes=0;
es=TESTscan();//调词法分析
if(es>0)printf("词法分析有错,编译停止!
");
elseprintf("词法分析成功!
\n");
if(es==0)
{es=TESTparse();//调语法、语义分析并生成代码
getch();
}
}
5.2设计词法分析器:
设计思想:
要求:
1.对单词的构词规则有明确的定义;
2.编写的分析程序能够正确识别源程序中的单词符号;
3.识别出的单词以<种别码,值>的形式保存在符号表中;
4.词法分析中源程序的输入以.c格式,分析后的符号表保存在.txt文件中。
5.对于源程序中的词法错误,能够做出简单的错误处理,给出简单的错误提示,保证顺利完成整个源程序的词法分析;
6.输入:
由符合规定单词类别结构的各类单词组成的源程序。
实现方法:
根据加入语义过程的状态转换图直接编写词法分析程序。
根据每一组状态转换关系(标识符)组织程序结构,并将所有公共处理过程分别实现即可。
在扫描源程序字符串时,一旦识别出关键字、运算符、标识符、无符号常数中之一,即以二元式形式(类别编码,值)输出单词。
每次调用词法分析程序,它均能自动继续扫描下去,形成下一个单词。
实现过程及主要代码:
//下面定义保留,为简化程序,使用字符指针数组保存所有保留字。
//如果想增加保留字,可继续添加,并修改保留字数目
#definekeywordSum8
char*keyword[keywordSum]={"if","else","for","while","do","int","read","write"};
//下面定义纯单分界符,如需要可添加
charsingleword[50]="+-*(){};,:
";
//下面定义双分界符的首字符
chardoubleword[10]="><=!
";
externcharScanin[300],Scanout[300];//用于接收输入输出文件名,在TEST_main.c中定义
externFILE*fin,*fout;//用于指向输入输出文件的指针,在TEST_main.c中定义
intTESTscan()//词法分析函数
{charch,token[40];//ch为每次读入的字符,token用于保存识别出的单词
intes=0,j,n;//es错误代码,0表示没有错误。
j,n为临时变量,控制组合单词时的下标等
printf("请输入源程序文件名(包括路径):
");
scanf("%s",Scanin);
printf("请输入词法分析输出文件名(包括路径):
");
scanf("%s",Scanout);
if((fin=fopen(Scanin,"r"))==NULL)//判断输入文件名是否正确
{printf("\n打开词法分析输入文件出错!
\n");
return
(1);//输入文件出错返回错误代码1
}
if((fout=fopen(Scanout,"w"))==NULL)//判断输出文件名是否正确
{printf("\n创建词法分析输出文件出错!
\n");
return
(2);//输出文件出错返回错误代码2
}
ch=getc(fin);
while(ch!
=EOF)
{while(ch==''||ch=='\n'||ch=='\t')ch=getc(fin);
if(isalpha(ch))//如果是字母,则进行标识符处理
{token[0]=ch;j=1;
ch=getc(fin);
while(isalnum(ch))//如果是字母数字则组合标识符;如果不是则标识符组合结束
{token[j++]=ch;//组合的标识符保存在token中
ch=getc(fin);//读下一个字符
}
token[j]='\0';//标识符组合结束
//查保留字
n=0;
while((nif(n>=keywordSum)//不是保留字,输出标识符
fprintf(fout,"%s\t%s\n","ID",token);//输出标识符符号
else//是保留字,输出保留字
fprintf(fout,"%s\t%s\n",token,token);//输出保留字符号
}elseif(isdigit(ch))//数字处理
{token[0]=ch;j=1;
ch=getc(fin);//读下一个字符
while(isdigit(ch))//如果是数字则组合整数;如果不是则整数组合结束
{token[j++]=ch;//组合整数保存在token中
ch=getc(fin);//读下一个字符
}
token[j]='\0';//整数组合结束
fprintf(fout,"%s\t%s\n","NUM",token);//输出整数符号
}elseif(strchr(singleword,ch)>0)//单分符处理
{token[0]=ch;token[1]='\0';
ch=getc(fin);//读下一个符号以便识别下一个单词
fprintf(fout,"%s\t%s\n",token,token);//输出单分界符符号
}elseif(strchr(doubleword,ch)>0)//双分界符处理
{token[0]=ch;
ch=getc(fin);//读下一个字符判断是否为双分界符
if(ch=='=')//如果是=,组合双分界符
{token[1]=ch;token[2]='\0';//组合双分界符结束
ch=getc(fin);//读下一个符号以便识别下一个单词
}else//不是=则为单分界符
token[1]='\0';
fprintf(fout,"%s\t%s\n",token,token);//输出单或双分界符符号
}elseif(ch=='/')//注释处理
{ch=getc(fin);//读下一个字符
if(ch=='*')//如果是*,则开始处理注释
{charch1;
ch1=getc(fin);//读下一个字符
do
{ch=ch1;ch1=getc(fin);}//删除注释
while((ch!
='*'||ch1!
='/')&&ch1!
=EOF);//直到遇到注释结束符*/或文件尾
ch=getc(fin);//读下一个符号以便识别下一个单词
}else//不是*则处理单分界符/
{token[0]='/';token[1]='\0';
fprintf(fout,"%s\t%s\n",token,token);//输出单分界符/
}
}else//错误处理
{token[0]=ch;token[1]='\0';
ch=getc(fin);//读下一个符号以便识别下一个单词
es=3;//设置错误代码
fprintf(fout,"%s\t%s\n","ERROR",token);//输出错误符号
}
}
fclose(fin);//关闭输入输出文件
fclose(fout);
return(es);//返回主程序
}
5.3设计语法、语义分析器
要求:
1.对语法规则有明确的定义;
2.编写的分析程序能够对实验一的结果进行正确的语法分析;
3.编写的分析程序能够对实验二的结果进行正确的语义分析;
4.对于遇到的语法、语义错误,能够做出简单的错误处理,给出简单的错误提示,保证语义分析过程;
实现方法:
在词法分析识别出单词符号的基础上分析并规定程序的语法结构是否符合语法规则。
其工作本质就是按文法的产生式,识别输入符号串是否为一个句子。
首先定义语法规则,然后按照规则实现语法分析。
(3)语法、语义分析及代码生成程序
intTESTparse()
{intes=0;
if((fp=fopen(Scanout,"r"))==NULL)
{printf("\n打开%s错误!
\n",Scanout);
es=10;
return(es);
}
if(es==0)es=program();
switch(es)
{case0:
printf("语法分析成功!
\n");break;
case10:
printf("打开文件%s失败!
\n",Scanout);break;
case1:
printf("缺少{!
\n");break;
case2:
printf("缺少}!
\n");break;
case3:
printf("缺少标识符!
\n");break;
case4:
printf("少分号!
\n");break;
case5:
printf("缺少(!
\n");break;
case6:
printf("缺少)!
\n");break;
case7:
printf("缺少操作数!
\n");break;
case21:
printf("符号表溢出!
\n");break;
case22:
printf("变量重复定义!
\n");break;
case23:
printf("变量未声明!
\n");break;}
fclose(fp);
fclose(fout);
return(es);
}
intprogram()
{intes=0,i;
fscanf(fp,"%s%s\n",token,token1);
printf("%s%s\n",token,token1);
if(strcmp(token,"{")){es=1;return(es);}
fscanf(fp,"%s%s\n",&token,&token1);
printf("%s%s\n",token,token1);
es=declaration_list();
if(es>0)return(es);
es=statement_list();
if(es>0)return(es);
实现过程及主要代码:
(1)定义符号表结构
struct{
charname[8];
intaddress;
}vartable[maxvartablep];
intvartablep=0,labelp=0,datap=0;
//插入符号表动作@name-def↓n,t的程序如下:
intname_def(char*name)
{inti,es=0;
if(vartablep>=maxvartablep)return(21);
for(i=vartablep-1;i==0;i--)//查符号表
{if(strcmp(vartable[i].name,name)==0)
{es=22;//22表示变量重复声明
break;
}
}
if(es>0)return(es);
strcpy(vartable[vartablep].name,name);
vartable[vartablep].address=datap;
datap++;//分配一个单元,数据区指针加1
vartablep++;
return(es);
}
(2)查询符号表返回地址
intlookup(char*name,int*paddress)
{inti,es=0;
for(i=0;i{if(strcmp(vartable[i].name,name)==0)
{*paddress=vartable[i].address;
return(es);
}
}
es=23;//变量没有声明
return(es);
}
if(es==0&&(strcmp(token,"ID")==0||strcmp(token,"NUM")==0))
es=expression_stat();//<表达式语句>
return(es);
}
//:
:
=if()[else]
/*
if()@BRF↑label1
@BR↑label2@SETlabel↓label1
[else]@SETlabel↓label2
其中动作符号的含义如下
@BRF↑label1:
输出BRFlabel1,
@BR↑label2:
输出BRlabel2,
@SETlabel↓label1:
设置标号label1
@SETlabel↓label2:
设置标号label2
intif_stat(){
intes=0,label1,label2;//if
fscanf(fp,"%s%s\n",&token,&token1);
printf("%s%s\n",token,token1);
if(strcmp(token,"("))return(es=5);//少左括号
fscanf(fp,"%s%s\n",&token,&token1);
printf("%s%s\n",token,token1);
es=expression();
if(es>0)return(es);
if(strcmp(token,")"))return(es=6);//少右括号
label1=labelp++;//用label1记住条件为假时要转向的标号
fprintf(fout,"BRFLABEL%d\n",label1);//输出假转移指令
fscanf(fp,"%s%s\n",&token,&token1);
printf("%s%s\n",token,token1);
es=statement();
if(es>0)return(es);
label2=labelp++;//用label2记住要转向的标号
fprintf(fout,"BRLABEL%d\n",label2);//输出无条件转移指令
fprintf(fout,"LABEL%d:
\n",label1);//设置label1记住的标号
if(strcmp(token,"else")==0)//else部分处理
{fscanf(fp,"%s%s\n",&token,&token1);
printf("%s%s\n",token,token1);
es=statement();
if(es>0)return(es);
}
fprintf(fout,"LABEL%d:
\n",label2);//设置label2记住的标号
return(es);
}
//:
:
=dowhile()
//:
:
=do@SET↑labellabel1
//while(<表达式>)
//@BRF↑label2@BR↑label1@SETlabel↓label2
//动作解释如下:
if(strcmp(token,"}")){es=2;return(es);}
return(es);}
//:
:
=
//|
//改成:
:
={}
intdeclaration_list()
{intes=0;
while(strcmp(token,"int")==0)
{es=declaration_stat();
if(es>0)return(es);
}
return(es);
}
intdeclaration_stat()
{intes=0;
fscanf(fp,"%s%s\n",&token,&token1);
printf("%s%s\n",token,token1);
if(strcmp(token,"ID"))return(es=3);
es=name_def(token1);//插入符号表
if(es>0)return(es);
fscanf(fp,"%s%s\n",&token,&token1);
printf("%%s\n",token,token1);
if(strcmp(token,";"))return(es=4);
fscanf(fp,"%s%s\n",&token,&token1);
printf("%s%s\n",token,token1);
return(es);
}
intstatement_list()
{intes=0;
while(strcmp(token,"}"))
{es=statement();
if(es>0)return(es);
}
return(es);
}
intstatement()
{intes=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,"do")==0)
es=do_stat();//
if(es==0&&strcmp(token,"for")==0)
es=for_stat();//
if(es==0&&strcmp(token,"read")==0)
if(strcmp(token,"("))return(es=5);
fscanf(fp,"%s%s\n",&token,&token1);
printf("%s%s\n",token,token1);
es=expression();
if(es>0)return(es);
if(strcmp(token,")"))return(es=6);
label2=labelp++;
fscanf(fp,"%s%s\n",&token,&token1);
printf("%s%s\n",token,token1);
es=statement();
if(es>0)return(es);
fscanf(fp,"%s%s\n",&token,&token1);
printf("%s%s\n",token,token1);
return(es);
}
//:
:
=for(,,)
:
:
=for(;
@SETlabel↑label1
@BRF↑label2@BR↑label3;
@SETlabel↑label4
@BR↓label1)
@SETlabel↓label3<语句>
@BR↓label4@SETlabel↓label2
动作解释:
1.@SETlabel↓label1:
设置标号label1
2.@BRF↑label2:
输出BRFlabel2,
3.@BR↑label3:
输出BRlabel3,
4.@SETlabel↓label4:
设置标号label4
5.@BR↑label1:
输出BRlabel1,
6.