实验三语义分析报告.docx
《实验三语义分析报告.docx》由会员分享,可在线阅读,更多相关《实验三语义分析报告.docx(17页珍藏版)》请在冰豆网上搜索。
![实验三语义分析报告.docx](https://file1.bdocx.com/fileroot1/2022-12/1/200774f2-cdbe-459e-a6b2-d65d6a2a1e01/200774f2-cdbe-459e-a6b2-d65d6a2a1e011.gif)
实验三语义分析报告
编译原理实验报告
实验名称:
分析调试语义分析程序
实验类型:
验证型
指导教师:
专业班级:
姓名:
学号:
实验地点:
实验成绩:
日期:
2016年6月3日
实验三分析调试语义分析程序
一、实验目的
通过分析调试TEST语言的语义分析和中间代码生成程序,加深对语法制导翻译思想的理解,掌握将语法分析所识别的语法范畴变换为中间代码的语义翻译方法。
2、实验知识
1.语法制导基本思想
语法制导就是对文法中的每个产生式都附加一个语义动作或语义子程序,且在语法分析过程中,每当需要使用一个产生式进行推导或归约时,语法分析程序除执行相应的语法分析动作外,还要执行相应的语义动作或调用相应的语义子程序。
基本思想是,根据翻译的需要设置文法符号的属性,以描述语法结构的语义。
例如,一个变量的属性有类型,层次,存储地址等。
表达式的属性有类型,值等。
属性值的计算和产生式相联系。
随着语法分析的进行,执行属性值的计算,完成语义分析和翻译的任务。
2.翻译方案设计
1)设计原理:
在实验二的基础上为文法符号引进一组属性及相应求值规则和动作,得到属性翻译文法,并引进一个符号表(包括变量名,变量数据是否有效,变量地址,变量的具体数据,数据类型等),在进行语法分析的同时,结合符号表完成语义分析与检测,同时根据属性翻译文法的属性及相关动作得到中间代码(抽象机式汇编指令),最后通过模拟的抽象机运行出结果。
2)设计方法:
(@为动作标志,↓为继承属性,↑为综合属性)
结合课本语法制导相关内容对文法增加属性和动作如下:
以下列出有修改的属性翻译文法:
①↓vartablep,datap,codep→intID↑n@name-def↓n,t;
其中动作符号的含义如下
@name-def↓n,t:
插入符号表;
②→if()@BRF↑label1@BR↑label2@SETlabel↓label1|if()@BRF↑label1@BR↑label2@SETlabel↓label1else@SETlabel↓label2
其中动作符号的含义如下
@BRF↑label1:
输出BRFlabel1;
@BR↑label2:
输出BRlabel2;
@SETlabel↓label1:
设置标号label1;
@SETlabel↓label2:
设置标号label2;
③→while@SETlabel↑label1()@BRF↑label2@BR↓label1@SETlabel↓label2
其中动作符号的含义如下
@SETlabel↑label1:
设置标号label1;
@BRF↑label2:
输出BRFlabel2;
@BR↓label1:
输出BRlabel1;
@SETlabel↓label2:
设置标号label2;
④→for(@POP;@SETlabel↑label1@BRF↑label2@BR↑label3;@SETlabel↑label4@POP@BR↓label1)@SETlabel↓label3@BR↓label4@SETlabel↓label2
其中动作符号的含义如下
@SETlabel↓label1:
设置标号label1;
@BRF↑label2:
输出BRFlabel2;
@BR↑label3:
输出BRlabel3;
@SETlabel↓label4:
设置标号label4;
@BR↑label1:
输出BRlabel1;
@SETlabel↓label3:
设置标号label3;
@BR↑label4:
输出BRlabel4;
@SETlabel↓label2:
设置标号label2;
⑤→write@OUT;
其中动作符号的含义如下
@OUT:
输出OUT
⑥→readID↑nLOOK↓n↑d@IN@STO↓d@POP;
其中动作符号的含义如下
@LOOK↓n↑d:
查符号表n,给出变量地址d;没有,变量没定义;
@IN:
输出IN;
@STO↓d:
输出指令代码STOd;
@POP:
将栈顶元素出栈
⑦→ID↑n@LOOK↓n↑d@ASSIGN=@STO↓d@POP|
其中动作符号的含义如下
@LOOK↓n↑d:
查符号表n,给出变量地址d;没有,变量没定义;
@ASSIGN:
记住当前文件位置;
@STO↓d:
输出指令代码STOd;
⑧→
|>@GT
|<@LES
|>=@GE
|<=@LE
|==@EQ
|!
=@NOTEQ
其中动作符号的含义如下
@GT:
次栈顶与栈顶作大于比较;
@LES:
次栈顶与栈顶作小于比较;
@GE:
次栈顶与栈顶作大于等于比较;
@LE:
次栈顶与栈顶作小于等于比较;
@EQ:
次栈顶与栈顶作等于比较;
@NOTEQ:
次栈顶与栈顶作不等于比较;
B→+B@ADD|-B@SUB|ε
⑨→+@ADD|-@SUB|ε
其中动作符号的含义如下
@ADD:
操作数相加;
@SUB:
操作数相减;
C→*C@MULT|/C@DIV|ε
⑩→*@MULT|/@DIV|ε
其中动作符号的含义如下
@MULT:
操作数相乘;
@DIV:
操作数相除;
⑪→()|ID↑n@LOOK↓n↑d@LOAD↓d|NUM↑i@LOADI↓i
其中动作符号的含义如下
@LOOK↓n↑d:
查符号表n,给出变量地址d;没有,变量没定义;
@LOAD↓d:
将地址d的变量入栈;
@LOADI↓i:
将常量i入栈;
3)设计结果:
1)→{}
2)→|ε
3)↓vartablep,datap,codep→intID↑n@name-def↓n,t;
4)→|ε
5)→|||
|||
6)→if()@BRF↑label1@BR↑label2@SETlabel↓label1|if()@BRF↑label1@BR↑label2@SETlabel↓label1else@SETlabel↓label2
7)→while@SETlabellabel1()@BRF↑label2@BR↓label1@SETlabel↓label2
8)→for(;@SETlabel↑label1@BRF↑label2@BR↑label3;@SETlabel↑label4@BR↓label1)@SETlabel↓label3@BR↓label2
9)→write@OUT;
10)→readID↑nLOOK↓n↑d@IN@STO↓d@POP;
11)→{}
12)→@POP;|;
13)→ID↑n@LOOK↓n↑d@ASSIGN=@STO↓d@POP|
14)→
15)→>@GT|<@LES|>=@GE
|<=@LE|==@EQ|!
=@NOTEQ|ε
16)→
17)→+@ADD|-@SUB|ε
18)→
19)→*@MULT|/@DIV|ε
20)→()|ID↑n@LOOK↓n↑d@LOAD↓d|NUM↑i@LOADI↓i
三、实验过程
首先,理解书上的代码和观看相关的知识的PPT,深入理解属性反应文法的作用,据此在我之前实验写好的语法分析基础上进行修改,写出语义分析代码。
在语义分析里增加“不可引用未赋初值变量”的规则,在init()、showVarTable()、checkInitValue()中增加了相关操作。
然后,结合栈式抽象机及其汇编指令相关命令的操作含义,模拟写出TEST语言的抽象机模型用以运行文法生成的中间代码。
最后,写出执行中间代码的虚拟机程序。
最后,编写TEST语言程序进行代码实例测试,调试观察运行过程及结果,并调试修改程序BUG。
代码完成后,测试、完善。
//定义符号表结构,添加未赋初值记录
struct{
charname[8];
intaddress;
intnotInit;//未赋初值
}vartable[maxvartablep];//改符号表最多容纳maxvartablep个记录
//在插入符号表动作@name-def↓n,t的程序中给notInit赋值为0,
vartable[vartablep].notInit=0;
//在该函数给中判断notInit的值是否为0和查询表中是否有变量名,若查询表中有该变量名,且notInit为0时,将该变量的notInit赋值为1,表示已经判断已经赋值
voidinit(char*name)
//在该函数中首先判定变量是否声明,再判定notInit的值是否为0,若变量是声明了且notInit为0时,则判定变量未赋初值。
voidcheckInitValue(char*name)
4、实验结果
1)测试数据及结果(初始程序)
2)修改后的测试数据:
(给sum和product赋初值)运行结果如下
3)中间代码如下:
五、讨论与分析
1、通过实验对课程知识点的理解
实验后知识梳理与总结:
通过TEST属性文法的翻译设计,了解了语法制导的基本过程及方法,明白了属性文法的基本推导与构造原理,了解了不同属性之间的传递关系,动作的执行流程,也了解了栈式抽象机及其汇编指令的执行原理,了解了TEST语言抽象机的原理。
语法制导:
语法制导就是对文法中的每个产生式都附加一个语义动作或语义子程序,且在语法分析过程中,每当需要使用一个产生式进行推导或归约时,语法分析程序除执行相应的语法分析动作外,还要执行相应的语义动作或调用相应的语义子程序。
每个语义子程序都指明了相应产生式中各个符号的具体含义,并规定了使用该产生式进行分析时所应采取的语义动作(如传送或处理信息、查填符号表、计算值、产生中间代码等等)。
这种分析模式既把语法分析与语义处理分开,又令其平行地进行,从而在同一遍扫描中同时完成语法分析和语义处理两项工作。
语法制导的属性翻译:
以语法分析为基础,伴随语法分析的各步骤,执行相应的语义动作;具体方法如下:
1.将文法符号所代表的语言成分的意思,用属于该文法符号的属性表示;
2.用语义规则(语义规则的执行就是语义动作)规定产生式所代表的语言成分之间的关系(即属性之间的关系),即用语义规则实现属性计算。
3.语义动作(语义规则的执行):
在语法分析的适当时刻(如推导或归约)执行附在对应产生式上的语义规则,以实现对语言结构语义的处理,如计算、查填符号表、生成中间代码、发布出错信息等。
2、回答实验指导书的实验思考提出的问题。
(1)语义及代码生成程序中的符号表管理方案存在什么问题?
提出改进方案。
答:
按照原来书上的代码,符号表用的是数组栈,空间上容易造成浪费或溢出,可以使用map数组。
但是我没有实现使用map数组。
此外,原来的代码还没有检查是否赋初值,对引用了未赋初值的变量也检测不出来,为此我在符号表结构中增加了一个int类型的变量notInit来标记变量是否赋初值。
(2)给出产生式所添加的动作含义。
答:
↓vartablep,datap,codep→intID↑n@name-def↓n,t;
其中动作符号的含义如下:
@name-def↓n,t:
插入符号表;
(3)给出产生式中所添加的动作含义。
答:
→()|ID↑n@LOOK↓n↑d@LOAD↓d|NUM↑i@LOADI↓i
其中动作符号的含义如下
@LOOK↓n↑d:
查符号表n,给出变量地址d;没有,变量没定义;
@LOAD↓d:
将地址d的变量入栈;
@LOADI↓i:
将常量i入栈;
(4)在抽象机模拟程序中是如何统计指令总数的?
画出流程图并做说明
答:
由于抽象机使用的是书上后面的代码进行修改的,使用的用while循环来执行每条指令,用i来统计指令总数,将每一条指令与code进行比较,若两者相同则执行相应的操作。
执行操作后i的次数加一,从而统计出指令总数。
流程图如下:
(5)如果要检查变量在参与运算时是否有值,应当如何处理?
给出详细的解决方案
答:
在符号表结构中增加了一个int类型的变量notInit来标记变量是否赋初值,当变量声明的时候给标记赋值为0,当表达式为其赋初值时(如for_stat()和read_stat()),就将其变为1,在checkInitValue(char*name)函数中检测用到的变量对应的属性,如果notInit是0则报错,变量未赋值。
6、附录:
关键代码(给出适当注释,可读性高)
7、TESTparse:
//定义符号表结构
struct{
charname[8];
intaddress;
intnotInit;//未赋初值
}vartable[maxvartablep];//改符号表最多容纳maxvartablep个记录
//插入符号表动作@name-def↓n,t的程序如下:
voidname_def(char*name)
{
inti,es=0;
if(vartablep>=maxvartablep)
{
printf("符号表溢出\n");
exit(0);
}
for(i=vartablep-1;i==0;i--)//查符号表
{
if(strcmp(vartable[i].name,name)==0)
{
deal_Error("变量重复定义",wordLine);
break;
}
}
strcpy(vartable[vartablep].name,name);
vartable[vartablep].address=datap;
vartable[vartablep].notInit=0;
datap++;//分配一个单元,数据区指针加1
vartablep++;
}
//查询符号表返回地址
voidlookup(char*name,int*paddress)
{
inti;
for(i=0;i{
if(strcmp(vartable[i].name,name)==0)
{
*paddress=vartable[i].address;
exit(0);
}
}
deal_Error("变量未声明!
",wordLine);
}
//↓vartablep,datap,codep→intID↑n@name-def↓n,t;
voiddeclaration_stat()
{
if(strcmp("int",wordType)==0)
{
fscanf(fp,"%s%s%d",wordType,wordValue,&wordLine);
wordAll++;
if(strcmp("ID",wordType)==0)
{
fscanf(fp,"%s%s%d",wordType,wordValue,&wordLine);
wordAll++;
name_def(wordValue);
if(strcmp(";",wordType)==0)
{
fscanf(fp,"%s%s%d",wordType,wordValue,&wordLine);
wordAll++;
}
elsedeal_Error("声明语句缺少;",wordLine-1);
}
elsedeal_Error("声明语句ID错误",wordLine);
}
elsedeal_Error("声明语句缺少int",wordLine);
}
/*
if()@BRF↑label1@BR↑label2@SETlabel↓label1
[else]@SETlabel↓label2*/
voidif_stat(){
intes=0,label1,label2;
if(strcmp("if",wordType)==0)
{
fscanf(fp,"%s%s%d",wordType,wordValue,&wordLine);
wordAll++;
if(strcmp("(",wordType)==0)//读到if表达式的左括号
{
fscanf(fp,"%s%s%d",wordType,wordValue,&wordLine);
wordAll++;
expression();
if(strcmp(")",wordType)==0)//读到if表达式的右括号
{
label1=labelp++;//用label1记住条件为假时要转向的标号
fprintf(fout,"BRFLABEL%d\n",label1);//输出假转移指令
fscanf(fp,"%s%s%d",wordType,wordValue,&wordLine);
wordAll++;
statement();
label2=labelp++;//用label2记住要转向的标号
fprintf(fout,"BRLABEL%d\n",label2);//输出无条件转移指令
fprintf(fout,"LABEL%d:
\n",label1);//设置label1记住的标号
if(strcmp("else",wordType)==0)//读到if语句中的else部分
{
fscanf(fp,"%s%s%d",wordType,wordValue,&wordLine);
wordAll++;
statement();
}
}
elsedeal_Error("缺少)",wordLine);
}
elsedeal_Error("if语句缺少(",wordLine);
}
elsedeal_Error("if语句错误",wordLine);
fprintf(fout,"LABEL%d:
\n",label2);//设置label2记住的标号
}
//:
:
=for(,,)
/*
:
:
=for(;
@SETlabel↑label1@BRF↑label2@BR↑label3;
@SETlabel↑label4@BR↓label1)
@SETlabel↓label3<语句>@BR↓label4@SETlabel↓label2
*/
voidfor_stat()
{
intlabel1,label2,label3,label4;
if(strcmp("for",wordType)==0)
{
fscanf(fp,"%s%s%d",wordType,wordValue,&wordLine);
wordAll++;
if(strcmp("(",wordT