广东工业大学2015编译原理课程设计

编译原理课程设计报告

课程名称____编译原理课程设计_ 题目名称PL/0编译器的扩充 学生学院__ 计算机学院______ 专业班级___ 学 号______ 学生姓名____ 指导教师______ 张巍

2015年 12 月 27日

一. 课程设计目的及要求

基本内容(成绩范围:“中”、“及格”或“不及格”) (1)扩充赋值运算:+=,-=, *= 和 /=

(2)扩充语句(Pascal的FOR语句):

FOR :=STEPUNTILDo

选做内容(成绩评定范围扩大到:“优”和“良”)

增加注释; 注释由/*和*/包含;

二.概述

1、源语言:PL/0语言,PL/0语言是PASCAL语言的子集,它的编译程序是一个编译

解析执行系统,后缀名为.PL0;

2、目标语言:生成文件后缀为*.COD的目标代码 3、实现平台(平台):Borland C++Builder 6 4、运行平台:Windows xp

三.结构设计说明

各功能模块概述

四.主要成分描述

(1)符号表

为了组成一条指令,编译程序必须知道其操作码及其参数(数或地址)。这些值是由编译程序本身联系到相应标识符上去的。这种联系是在处理常数、变量和过程说明完成的。为此,标识符表应包含每一标识符所联系的属性;如果标识符被说明为常数,其属性值为常数值;如果标识符被说明成变量,其属性就是由层次和修正量(偏移量)组成的地址;如果标识符被说明为过程,其属性就是过程的入口地址及层次。常数的值由程序正文提供,编译的任务就是确定存放该值的地址。我们选择顺序分配变量和代码的方法;每遇到一个变量说明,就将数据单元的下标加一(PL/0 机中,每个变量占一个存贮单元)。开始编译一个过程时,要对数据单元的下标dx 赋初值,表示新开辟一个数据区。dx 的初值为3,因为每个数据区包含三个内部变量RA,DL 和SL。

(2)运行时的存储组织和管理

当源程序经过语法分析,如果未发现错误时,由编译程序调用解释程序,对存放在CODE中的代码CODE[0]开始进行解释执行.当废弃结束后,记录源程序中标识符的TABLE表已没有作用.因此存储区只需以数组CODE存主的只读目标程序和运行机制时的数据区S,S是由解释程序定义的一维整数型数组. 解释执行时的数据空间S为栈式计算机的在座空间,遵循后进先出规则,对每个过程(包括主程序)当调用 时,才分配数据空间,退出过程进, 则所分配原则的数据空间被释放. 解释程序还定义了4个寄存器:1、指令寄存器.存放当前正在解释的一条目标指令2、程序地址寄存器.指向下一条要执行的目标程序的地址3、栈顶寄存器.4、基址寄存器.指向每个过程被调用时,在数据区S中给它分配原则的数据段起始地址,也称基地址.为了实现过程被调用时给它分配数据段,过程运行结束后释放数据段以及嵌套过程之间结标志符引用的问题,当过程被调用时,在栈顶分配三个联系单元,这三个联系单元存放的内容分别为:SL静态链,动态链DL,RA返回地址。

(3)语法分析方法

语法分析的任务是识别由词法分析给出的单词符号序列在结构上是否符合给定的文法规则.PL/0编译程序的语法分析采用了自顶向下的递归子程序法.粗略地说:就是对应每个非终结符语法单元,编一个独立的处理过程(或子程序).语法分

析研究从读入第一个单词开始由非终结符’程序即开始符出发,沿语法描述图箭头所指出的方向进行分析.当遇到非终结符时,则调用相应的处理过程,从语法描述图看也就进入了一个语法单元,再沿当前所进入的语法描述图的箭头方向进行分析,当遇到描述图中是终结符时,则判断当前读入的单词是否与图中的终结符相匹配,若匹配,则执行相应的语义程序(就是翻译程序).再读取下一个单词继续分析.遇到分支点时将当前的单词与分支点上的多个终结符逐个相比较,若都不匹配时可能是进入下一非终结符语法单位或是出错.

如果一个PL/0语言程序的单词序列在整修语法分析中,都能逐个得到匹配,直到程序结束’.’,这时就说所输入的程序是正确的.对于正确的语法分析做相应的语义翻译,最终得出目标程序.

.

变量说明部分>

>

VAR

标识符>

复合语句>

语句> END

READ ( )

PL/0编译程序对语法错误的处理采用两种办法:

(1)对于一些易于校正的错误,如丢了逗号、分号等,指出出错的位置,加以校正,继续进行分析。 (2)对于难于校正的错误,给出错误的位置与性质,跳过后面一些单词,直到下一个可以进行正常语法分析的语法单位。 错误类型如下

0 过程开始部分说明不正确 1 常数说明中"="写成":="

2 常数说明中"="后应为整数或实数或字符 3 常数说明中的标识符后应是"="

4 const, var, procedure后应为标识符 5 漏掉了","或";"

6 过程说明后的符号不正确(应该是语句开始符,或过程定义符) 7 应是语句开始符

8 程序体内语句部分的后跟符不正确 9 程序结尾丢了句号"."

10 语句间漏了";" 11 标识符未说明

12 赋值语句中,赋值号左部标识符属性应是变量 13 变量后不能是此符号 14 call后应为标识符

15 call后标识符属性应为过程 16 条件语句中丢了"then" 17 丢了"end"或";"

18 while型循环语句丢了"do" 19 语句后的符号不正确 20 应为关系运算符

21 表达式内标识符属性不能是过程 22 表达式中漏掉右括号"(" 23 因子后的非法符号

24 表达式的开始符不能是此符号 31 数越界

(4)中间代码表示

PL/0编译程序所产生的目标代码是一个假想栈式计算机的汇编语言,可称为类pcode指令

其中f代表功能码,l表示层次差,也就是引用变量胡哦哦过程的分程序与说明该变量或过程的分程序之间的层次差。A的含义对不同的指令有所区别。对每条指令的解释说明如下。

目标指令有8条:

①LIT 将常数值取到栈顶,a为常数值

② LOD 将变量值取到栈顶,a为偏移量,l为层差

③ STO 将栈顶内容送入某变量单元中,a为偏移量,l为层差 ④ CAL 调用过程,a为过程地址,l为层差

⑤ INT 在运行栈中为被调用的过程开辟a个单元的数据区 ⑥ JMP无条件跳转至a地址

⑦ JPC 条件跳转,当栈顶布尔值非真则跳转至a地址,否则顺序执行

⑧ OPR的l域为0,当栈顶的布尔值为非真时,转向a域的地址,否则顺序执行 opr 0 0 过程调用结束后,返回调用点并退栈 opr 0 1 栈顶元素取反

opr 0 2 次栈顶与栈顶相加,退两个栈元素,结果值进栈 opr 0 3 次栈顶减去栈顶,退两个栈元素,结果值进栈 opr 0 4 次栈顶乘以栈顶,退两个栈元素,结果值进栈 opr 0 5 次栈顶除以栈顶,退两个栈元素,结果值进栈 opr 0 6 栈顶元素的奇偶判断,结果值在栈顶

opr 0 7

opr 0 8 次栈顶与栈顶是否相等,退两个栈元素,结果值进栈 opr 0 9 次栈顶与栈顶是否不等,退两个栈元素,结果值进栈 opr 0 10 次栈顶是否小于栈顶,退两个栈元素,结果值进栈 opr 0 11 次栈顶是否大于等于栈顶,退两个栈元素,结果值进栈 opr 0 12 次栈顶是否大于栈顶,退两个栈元素,结果值进栈 opr 0 13 次栈顶是否小于等于栈顶,退两个栈元素,结果值进栈 opr 0 14 栈顶值输出至屏幕 opr 0 15 屏幕输出换行

opr 0 16 从命令行读入一个输入置于栈顶

五.测试用例

本次课设的测试用例共分为三个

Test1,用于测试扩充赋值运算:+=,-=, *= 和 /=

Test2用于测试扩充语句(Pascal的FOR语句):

FOR :=STEPUNTILDo

Test3用于测试/* */

六.开发过程和完成情况

1.扩充赋值运算:+=,-=,*=和/=

代码修改:

else if (SYM == PLUSEQL) // +=语义分析 {

GetSym(); GEN(LOD, LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //左值入栈 EXPRESSION(FSYS,LEV,TX); //右表达式结果入栈 GEN(OPR, 0, 2) ; //加法运算 GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将结果送回左值 }

else if (SYM == MINUSEQL) // -=语义分析 {

GetSym(); GEN(LOD, LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //左值入栈 EXPRESSION(FSYS,LEV,TX); //右表达式结果入栈 GEN(OPR, 0, 3) ; //乘法运算 GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将结果送回左值 }

else if (SYM == MULSEQL) // *=语义分析 {

GetSym(); GEN(LOD, LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //左值入栈 EXPRESSION(FSYS,LEV,TX); //右表达式结果入栈 GEN(OPR, 0, 4) ; //乘法运算 GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将结果送回左值 }

else if(SYM == DIVSEQL) // /=语义分析 { GetSym(); GEN(LOD, LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //左值入栈 EXPRESSION(FSYS,LEV,TX); //右表达式结果入栈 GEN(OPR, 0, 5) ; //除法运算,注意原始的是求余运算!!! GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将结果送回左值 }

语法树:

2.扩充语句(Pascal的FOR语句):

FOR :=STEPUNTILDo

代码修改:

case FORSYM:

GetSym();

if(SYM != IDENT) //for后面必须跟变量来进行赋值

Error(47);

else

i=POSITION(ID,TX); //查找标识符在名字表中的位置并赋值给i

if(i == 0) //对i进行检查是否存在于符号表中

Error(11);

else if (TABLE[i].KIND!=VARIABLE) //不是变量类型,错误

{ /*ASSIGNMENT TO NON-VARIABLE*/

Error(12);

i=0;

}

GetSym(); //读取下一个符号:=

if(SYM == BECOMES) //读取到:=

GetSym();

else

Error(13);

EXPRESSION(SymSetUnion(SymSetNew(STEPSYM),FSYS),LEV,TX); //设置后跟符为step

if(i != 0) //变量存在

GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将栈顶的值传给变量 if(SYM==STEPSYM) //读取到step

GetSym();

else //没有读到step,出错

Error(46);

CX1=CX; //记录此处地址

GEN(JMP,0,0); //条件跳转,地址未知,后面回填

CX3=CX;

EXPRESSION(SymSetUnion(SymSetNew(UNTILSYM),FSYS),LEV,TX); //设置后跟符为until

GEN(LOD,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将变量送回栈顶 GEN(OPR,0,2); //栈顶与次栈顶相加,即for语句的变量加上step中的步数 GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR);//把栈顶的值送到变量 if(SYM==UNTILSYM) //读取到until

GetSym();

else

Error(47);

CODE[CX1].A=CX; //地址回填

EXPRESSION(SymSetUnion(SymSetNew(DOSYM),FSYS),LEV,TX); //设置后跟符为do,终止条件

GEN(LOD,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将变量送回栈顶

GEN(OPR,0,11); //比较次栈顶与栈顶的大小,即比较条件是否还满足循环条件 CX2=CX;

GEN(JPC,0,0); //条件跳转,地址未知,后面回填地址

if(SYM==DOSYM) //读取到do

GetSym();

else

Error(48);

STATEMENT(FSYS,LEV,TX); //对do语句的内容进行处理

GEN(JMP,0,CX3); //执行完后无条件跳转到cx3处的地址,即对循环条件进行比较,是否还继续执行循环

CODE[CX2].A=CX; //地址回调

break;

增加注释; 注释由/*和*/包含;

else if (CH=='*') // /*~*/注释的实现

{

while(1) //处于此循环内,直到获取到*/之前不做任何处理 {

GetCh();

if (CH=='*')

{

GetCh();

if(CH=='/') //右边的*/读取完毕,退出循环

{

GetCh();

break;

}

}

}

}

此段代码位于分析‘/’内,对/**/截获内容不做处理

七.实验结果

1.扩充赋值运算: +=,-=,*=和/=

2.扩充语句(Pascal的FOR语句):

FOR :=STEPUNTILDo

增加注释; 注释由/*和*/包含;

八.心得体会

不得不说,编译原理这门课程是我上大学以来觉得最难的一门课程了,并且也是专业课,也就显得极其重要和尤为有意义了。编译原理的课程设计,给了我们实践课内知识最好的一个平台了。相比之前的编译原理实验课,课程设计要求实现的功能更多,难度更高。有了上次的编译原理实验的体验,这次课程设计显然是熟练了很多,也没有摸不着头脑的感觉,脉络还是比较清晰。对于+=,-=,*=,/=的语义分析,由于在之前的实验课*=,/=已经完成了,所以这次实现起来也比较容易,实现的步骤几乎和之前完全一样。但是比较难的就是for语义分析的实现,花了较多时间去调试和测试结果,实现起来也是比较繁琐的。而对于/**/注释的实现,一开始我是想在getch内实现的,试了好多次还是不行,后来还是和同学交流了一下在以’/’符号开始的词法分析内内部实现,才成功了。对于本次的课程设计,需要理解编译程序的核心和基本思想,包括基本的语法分析和句法分析以及中间代码的生成,还有十分重要的堆栈应用。只有把这些基础知识联系串联起来,才能更顺利的完成后边的设计内容。我觉得课程设计是一个巩固课内知识的好方法,只有自己动手去做,去想,才能真正的去理解课程的内容及消化。最后谢谢老师本学期的编译原理课程的指导!!

编译原理课程设计报告

课程名称____编译原理课程设计_ 题目名称PL/0编译器的扩充 学生学院__ 计算机学院______ 专业班级___ 学 号______ 学生姓名____ 指导教师______ 张巍

2015年 12 月 27日

一. 课程设计目的及要求

基本内容(成绩范围:“中”、“及格”或“不及格”) (1)扩充赋值运算:+=,-=, *= 和 /=

(2)扩充语句(Pascal的FOR语句):

FOR :=STEPUNTILDo

选做内容(成绩评定范围扩大到:“优”和“良”)

增加注释; 注释由/*和*/包含;

二.概述

1、源语言:PL/0语言,PL/0语言是PASCAL语言的子集,它的编译程序是一个编译

解析执行系统,后缀名为.PL0;

2、目标语言:生成文件后缀为*.COD的目标代码 3、实现平台(平台):Borland C++Builder 6 4、运行平台:Windows xp

三.结构设计说明

各功能模块概述

四.主要成分描述

(1)符号表

为了组成一条指令,编译程序必须知道其操作码及其参数(数或地址)。这些值是由编译程序本身联系到相应标识符上去的。这种联系是在处理常数、变量和过程说明完成的。为此,标识符表应包含每一标识符所联系的属性;如果标识符被说明为常数,其属性值为常数值;如果标识符被说明成变量,其属性就是由层次和修正量(偏移量)组成的地址;如果标识符被说明为过程,其属性就是过程的入口地址及层次。常数的值由程序正文提供,编译的任务就是确定存放该值的地址。我们选择顺序分配变量和代码的方法;每遇到一个变量说明,就将数据单元的下标加一(PL/0 机中,每个变量占一个存贮单元)。开始编译一个过程时,要对数据单元的下标dx 赋初值,表示新开辟一个数据区。dx 的初值为3,因为每个数据区包含三个内部变量RA,DL 和SL。

(2)运行时的存储组织和管理

当源程序经过语法分析,如果未发现错误时,由编译程序调用解释程序,对存放在CODE中的代码CODE[0]开始进行解释执行.当废弃结束后,记录源程序中标识符的TABLE表已没有作用.因此存储区只需以数组CODE存主的只读目标程序和运行机制时的数据区S,S是由解释程序定义的一维整数型数组. 解释执行时的数据空间S为栈式计算机的在座空间,遵循后进先出规则,对每个过程(包括主程序)当调用 时,才分配数据空间,退出过程进, 则所分配原则的数据空间被释放. 解释程序还定义了4个寄存器:1、指令寄存器.存放当前正在解释的一条目标指令2、程序地址寄存器.指向下一条要执行的目标程序的地址3、栈顶寄存器.4、基址寄存器.指向每个过程被调用时,在数据区S中给它分配原则的数据段起始地址,也称基地址.为了实现过程被调用时给它分配数据段,过程运行结束后释放数据段以及嵌套过程之间结标志符引用的问题,当过程被调用时,在栈顶分配三个联系单元,这三个联系单元存放的内容分别为:SL静态链,动态链DL,RA返回地址。

(3)语法分析方法

语法分析的任务是识别由词法分析给出的单词符号序列在结构上是否符合给定的文法规则.PL/0编译程序的语法分析采用了自顶向下的递归子程序法.粗略地说:就是对应每个非终结符语法单元,编一个独立的处理过程(或子程序).语法分

析研究从读入第一个单词开始由非终结符’程序即开始符出发,沿语法描述图箭头所指出的方向进行分析.当遇到非终结符时,则调用相应的处理过程,从语法描述图看也就进入了一个语法单元,再沿当前所进入的语法描述图的箭头方向进行分析,当遇到描述图中是终结符时,则判断当前读入的单词是否与图中的终结符相匹配,若匹配,则执行相应的语义程序(就是翻译程序).再读取下一个单词继续分析.遇到分支点时将当前的单词与分支点上的多个终结符逐个相比较,若都不匹配时可能是进入下一非终结符语法单位或是出错.

如果一个PL/0语言程序的单词序列在整修语法分析中,都能逐个得到匹配,直到程序结束’.’,这时就说所输入的程序是正确的.对于正确的语法分析做相应的语义翻译,最终得出目标程序.

.

变量说明部分>

>

VAR

标识符>

复合语句>

语句> END

READ ( )

PL/0编译程序对语法错误的处理采用两种办法:

(1)对于一些易于校正的错误,如丢了逗号、分号等,指出出错的位置,加以校正,继续进行分析。 (2)对于难于校正的错误,给出错误的位置与性质,跳过后面一些单词,直到下一个可以进行正常语法分析的语法单位。 错误类型如下

0 过程开始部分说明不正确 1 常数说明中"="写成":="

2 常数说明中"="后应为整数或实数或字符 3 常数说明中的标识符后应是"="

4 const, var, procedure后应为标识符 5 漏掉了","或";"

6 过程说明后的符号不正确(应该是语句开始符,或过程定义符) 7 应是语句开始符

8 程序体内语句部分的后跟符不正确 9 程序结尾丢了句号"."

10 语句间漏了";" 11 标识符未说明

12 赋值语句中,赋值号左部标识符属性应是变量 13 变量后不能是此符号 14 call后应为标识符

15 call后标识符属性应为过程 16 条件语句中丢了"then" 17 丢了"end"或";"

18 while型循环语句丢了"do" 19 语句后的符号不正确 20 应为关系运算符

21 表达式内标识符属性不能是过程 22 表达式中漏掉右括号"(" 23 因子后的非法符号

24 表达式的开始符不能是此符号 31 数越界

(4)中间代码表示

PL/0编译程序所产生的目标代码是一个假想栈式计算机的汇编语言,可称为类pcode指令

其中f代表功能码,l表示层次差,也就是引用变量胡哦哦过程的分程序与说明该变量或过程的分程序之间的层次差。A的含义对不同的指令有所区别。对每条指令的解释说明如下。

目标指令有8条:

①LIT 将常数值取到栈顶,a为常数值

② LOD 将变量值取到栈顶,a为偏移量,l为层差

③ STO 将栈顶内容送入某变量单元中,a为偏移量,l为层差 ④ CAL 调用过程,a为过程地址,l为层差

⑤ INT 在运行栈中为被调用的过程开辟a个单元的数据区 ⑥ JMP无条件跳转至a地址

⑦ JPC 条件跳转,当栈顶布尔值非真则跳转至a地址,否则顺序执行

⑧ OPR的l域为0,当栈顶的布尔值为非真时,转向a域的地址,否则顺序执行 opr 0 0 过程调用结束后,返回调用点并退栈 opr 0 1 栈顶元素取反

opr 0 2 次栈顶与栈顶相加,退两个栈元素,结果值进栈 opr 0 3 次栈顶减去栈顶,退两个栈元素,结果值进栈 opr 0 4 次栈顶乘以栈顶,退两个栈元素,结果值进栈 opr 0 5 次栈顶除以栈顶,退两个栈元素,结果值进栈 opr 0 6 栈顶元素的奇偶判断,结果值在栈顶

opr 0 7

opr 0 8 次栈顶与栈顶是否相等,退两个栈元素,结果值进栈 opr 0 9 次栈顶与栈顶是否不等,退两个栈元素,结果值进栈 opr 0 10 次栈顶是否小于栈顶,退两个栈元素,结果值进栈 opr 0 11 次栈顶是否大于等于栈顶,退两个栈元素,结果值进栈 opr 0 12 次栈顶是否大于栈顶,退两个栈元素,结果值进栈 opr 0 13 次栈顶是否小于等于栈顶,退两个栈元素,结果值进栈 opr 0 14 栈顶值输出至屏幕 opr 0 15 屏幕输出换行

opr 0 16 从命令行读入一个输入置于栈顶

五.测试用例

本次课设的测试用例共分为三个

Test1,用于测试扩充赋值运算:+=,-=, *= 和 /=

Test2用于测试扩充语句(Pascal的FOR语句):

FOR :=STEPUNTILDo

Test3用于测试/* */

六.开发过程和完成情况

1.扩充赋值运算:+=,-=,*=和/=

代码修改:

else if (SYM == PLUSEQL) // +=语义分析 {

GetSym(); GEN(LOD, LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //左值入栈 EXPRESSION(FSYS,LEV,TX); //右表达式结果入栈 GEN(OPR, 0, 2) ; //加法运算 GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将结果送回左值 }

else if (SYM == MINUSEQL) // -=语义分析 {

GetSym(); GEN(LOD, LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //左值入栈 EXPRESSION(FSYS,LEV,TX); //右表达式结果入栈 GEN(OPR, 0, 3) ; //乘法运算 GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将结果送回左值 }

else if (SYM == MULSEQL) // *=语义分析 {

GetSym(); GEN(LOD, LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //左值入栈 EXPRESSION(FSYS,LEV,TX); //右表达式结果入栈 GEN(OPR, 0, 4) ; //乘法运算 GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将结果送回左值 }

else if(SYM == DIVSEQL) // /=语义分析 { GetSym(); GEN(LOD, LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //左值入栈 EXPRESSION(FSYS,LEV,TX); //右表达式结果入栈 GEN(OPR, 0, 5) ; //除法运算,注意原始的是求余运算!!! GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将结果送回左值 }

语法树:

2.扩充语句(Pascal的FOR语句):

FOR :=STEPUNTILDo

代码修改:

case FORSYM:

GetSym();

if(SYM != IDENT) //for后面必须跟变量来进行赋值

Error(47);

else

i=POSITION(ID,TX); //查找标识符在名字表中的位置并赋值给i

if(i == 0) //对i进行检查是否存在于符号表中

Error(11);

else if (TABLE[i].KIND!=VARIABLE) //不是变量类型,错误

{ /*ASSIGNMENT TO NON-VARIABLE*/

Error(12);

i=0;

}

GetSym(); //读取下一个符号:=

if(SYM == BECOMES) //读取到:=

GetSym();

else

Error(13);

EXPRESSION(SymSetUnion(SymSetNew(STEPSYM),FSYS),LEV,TX); //设置后跟符为step

if(i != 0) //变量存在

GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将栈顶的值传给变量 if(SYM==STEPSYM) //读取到step

GetSym();

else //没有读到step,出错

Error(46);

CX1=CX; //记录此处地址

GEN(JMP,0,0); //条件跳转,地址未知,后面回填

CX3=CX;

EXPRESSION(SymSetUnion(SymSetNew(UNTILSYM),FSYS),LEV,TX); //设置后跟符为until

GEN(LOD,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将变量送回栈顶 GEN(OPR,0,2); //栈顶与次栈顶相加,即for语句的变量加上step中的步数 GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR);//把栈顶的值送到变量 if(SYM==UNTILSYM) //读取到until

GetSym();

else

Error(47);

CODE[CX1].A=CX; //地址回填

EXPRESSION(SymSetUnion(SymSetNew(DOSYM),FSYS),LEV,TX); //设置后跟符为do,终止条件

GEN(LOD,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); //将变量送回栈顶

GEN(OPR,0,11); //比较次栈顶与栈顶的大小,即比较条件是否还满足循环条件 CX2=CX;

GEN(JPC,0,0); //条件跳转,地址未知,后面回填地址

if(SYM==DOSYM) //读取到do

GetSym();

else

Error(48);

STATEMENT(FSYS,LEV,TX); //对do语句的内容进行处理

GEN(JMP,0,CX3); //执行完后无条件跳转到cx3处的地址,即对循环条件进行比较,是否还继续执行循环

CODE[CX2].A=CX; //地址回调

break;

增加注释; 注释由/*和*/包含;

else if (CH=='*') // /*~*/注释的实现

{

while(1) //处于此循环内,直到获取到*/之前不做任何处理 {

GetCh();

if (CH=='*')

{

GetCh();

if(CH=='/') //右边的*/读取完毕,退出循环

{

GetCh();

break;

}

}

}

}

此段代码位于分析‘/’内,对/**/截获内容不做处理

七.实验结果

1.扩充赋值运算: +=,-=,*=和/=

2.扩充语句(Pascal的FOR语句):

FOR :=STEPUNTILDo

增加注释; 注释由/*和*/包含;

八.心得体会

不得不说,编译原理这门课程是我上大学以来觉得最难的一门课程了,并且也是专业课,也就显得极其重要和尤为有意义了。编译原理的课程设计,给了我们实践课内知识最好的一个平台了。相比之前的编译原理实验课,课程设计要求实现的功能更多,难度更高。有了上次的编译原理实验的体验,这次课程设计显然是熟练了很多,也没有摸不着头脑的感觉,脉络还是比较清晰。对于+=,-=,*=,/=的语义分析,由于在之前的实验课*=,/=已经完成了,所以这次实现起来也比较容易,实现的步骤几乎和之前完全一样。但是比较难的就是for语义分析的实现,花了较多时间去调试和测试结果,实现起来也是比较繁琐的。而对于/**/注释的实现,一开始我是想在getch内实现的,试了好多次还是不行,后来还是和同学交流了一下在以’/’符号开始的词法分析内内部实现,才成功了。对于本次的课程设计,需要理解编译程序的核心和基本思想,包括基本的语法分析和句法分析以及中间代码的生成,还有十分重要的堆栈应用。只有把这些基础知识联系串联起来,才能更顺利的完成后边的设计内容。我觉得课程设计是一个巩固课内知识的好方法,只有自己动手去做,去想,才能真正的去理解课程的内容及消化。最后谢谢老师本学期的编译原理课程的指导!!


相关文章

  • 大学工学专业介绍:软件工程
  • 主要课程:C/C++程序设计.Java 程序设计.数据结构.计算方法.编译原理.软件工程.操作系统原理.数据库系统原理.网络数据库.计算机网络技术.微机原理及汇编语言程序设计.基于Web 的程序设计.软件开发技术.软件测试技术.多媒体技术. ...查看


  • 软件编译技术课程总结
  • 软件编译技术论文 学号: 姓名: 班级: 摘要 软件编译技术是计算机及相关专业的一门重要专业课程,在计算机科学中有很重要的地位和作用,已被国内外高校列为计算机专业的主要课程.它主要介绍了高级程序设计语言编译程序构造的一般原理.基本设计方法. ...查看


  • 电子技术综合设计与实践课程设计
  • 课程设计 课程名称电子技术综合设计与实践 题目名称交通灯控制器 学生学院自动化学院 专业班级13级自动化 学 号 学生姓名 组员_____ 指导教师 2015 年11月26日 广东工业大学课程设计任务书 题目名称 学生学院 专业班级 姓名 ...查看


  • 编译原理学习导论 [和讯博客]
  • 文章来源: 转贴www.csdn.net 大学课程为什么要开设编译原理呢?这门课程关注的是编译器方面的产生原理和技术问题,似乎和计算机的基础领域不沾边,可是编译原理却一直作为大学本科的必修课程,同时也成为了研究生入学考试的必考内容.编译原理 ...查看


  • 2016编译原理课程设计心得体会 2篇
  • 2016编译原理课程设计心得体会 2篇 经过一个星期的编译原理课程设计,本人在刘贞老师的指导下,顺利完成该课程设计.通过该课程设计,收获颇多. 一.对实验原理有更深的理解 通过该课程设计,掌握了什么是编译程序,编译程序工作的基本过程及其各阶 ...查看


  • 高级语言编译过程可视化研究
  • 高级语言编译过程可视化研究 摘要:针对编译原理教学中存在的知识点多.概念抽象.算法难 于理解的情况,本文设计了一种可视化编译系统,实现了类c 语言 的文法编辑与检查.词法分析.语法分析.语义处理的过程展示. 系统界面布局一致.操作简便,为便 ...查看


  • 智能抢答器设计
  • 能力拓展训练 题 目 学 院 专 业 班 级 姓 名 指导教师 智能抢答器设计 2013年6月24日 能力拓展训练任务书 学生姓名: 专业班级: 指导教师: 工作单位: 自动化学院 题 目: 智能抢答器设计 初始条件:计算机.Max+plu ...查看


  • 原码一位乘法器
  • 课 程 设 计 报 告 课程设计名称:课程设计题目: 院(系):专 业:班 级:学 号:姓 名:指导教师:完成日期: 计算机组成原理课程设计 定点原码一位乘法器的设计 目 录 第1章 总体设计方案 ..................... ...查看


  • 电子秤的设计与制作毕业论文
  • 分类号 单位代码 密 级 公 开 学 号[1**********]3 学士学位论文 论文题目:电子秤的设计与制作 论 文 作 者:甘清华 指 导 教 师:谭菊(讲师) 专 业:电子信息科学与技术 (微电子制造工程) 提交论文日期:2015年 ...查看


热门内容