ImageVerifierCode 换一换
格式:DOCX , 页数:18 ,大小:358.79KB ,
资源ID:7310186      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/7310186.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(广工编译原理课程设计报告.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

广工编译原理课程设计报告.docx

1、广工编译原理课程设计报告课 程 设 计 课程名称 编译原理 题目名称 PL/0编译器的扩充 学生学院 计算机学院 专业班级 计算机科学与技术12(4)学 号 3112005901 学生姓名 柏石先 指导教师 李杨 程序功能完成情况测试用例全面程度学生对所编程序熟悉程度报告格式是否与要求相符报告内容是否准确、全面2014 年 12 月 28日一、 实验目的与要求基本内容(成绩范围:“中”、“及格”或“不及格”)(1)扩充赋值运算:*= 和 /=(2)扩充语句(Pascal的FOR语句): FOR :=STEP UNTILDo选做内容(成绩评定范围扩大到:“优”和“良”)(1)增加类型: 字符类型

2、; 实数类型。(2)增加 注释; 注释由/*和*/包含;(3)扩充函数: 有返回值和返回语句; 有参数函数。(4)增加一维数组类型(可增加指令)。(5)其他典型语言设施。二、 实验环境与工具1、源语言:PL/0语言,PL/0语言是PASCAL语言的子集,它的编译程序是一个编译解析执行系统,后缀名为.PL0;2、目标语言:生成文件后缀为*.COD的目标代码3、实现平台:Borland C+Builder 64、运行平台:Windows 8.1三、 设计概述1、 结构设计说明(1)PL/0编译系统的结构框架源语言:源语言是基于C语言写的PL/0编译程序PL0语言(可 以看成 Pascal语言的子集

3、)目标语言:假想的栈式计算机计算语言,即类指令代码。指令格式如下:其中f代表功能码,l表示层次差,a的含意对不同的指令有所区别。具体的指令功能表:LIT 0 a将常数值取到栈顶,a为常数值LOD l a将变量值取到栈顶,a为偏移量,l为层差STO l a将栈顶内容送入某变量单元中,a为偏移量,l为层差CAL l a调用过程,a为过程地址,l为层差INT 0 a在运行栈中为被调用的过程开辟a个单元的数据区JMP 0 a无条件跳转至a地址JPC 0 a条件跳转,当栈顶布尔值非真则跳转至a地址,否则顺序执行OPR 0 0过程调用结束后,返回调用点并退栈OPR 0 1栈顶元素取反OPR 0 2次栈顶与

4、栈顶相加,退两个栈元素,结果值进栈OPR 0 3次栈顶减去栈顶,退两个栈元素,结果值进栈OPR 0 4次栈顶乘以栈顶,退两个栈元素,结果值进栈OPR 0 5次栈顶除以栈顶,退两个栈元素,结果值进栈OPR 0 6栈顶元素的奇偶判断,结果值在栈顶OPR 0 7OPR 0 8次栈顶与栈顶是否相等,退两个栈元素,结果值进栈OPR 0 9次栈顶与栈顶是否不等,退两个栈元素,结果值进栈OPR 0 10次栈顶是否小于栈顶,退两个栈元素,结果值进栈OPR 0 11次栈顶是否大于等于栈顶,退两个栈元素,结果值进栈OPR 0 12次栈顶是否大于栈顶,退两个栈元素,结果值进栈OPR 0 13次栈顶是否小于等于栈顶,

5、退两个栈元素,结果值进栈OPR 0 14栈顶值输出至屏幕OPR 0 15屏幕输出换行OPR 0 16从命令行读入一个输入置于栈顶四、 设计分析(一) 扩充赋值运算:*= 和 /=需要增加2个运算符*= 和 /=,用下面表格定义的SYM代替运算符*=/=SYM表示TIMESBECOMESSLASHBECOMES*= 和 /=的语法描述图:(二) 扩充语句(Pascal的FOR语句)因为在Pascal中的FOR语句描述为:FOR := STEP UNTIL DO 所以增加FOR,STEP,UNTIL,DOFOR语句语法描述图为:五、 程序设计1) 增加所需要的保留字和运算符,实现*=和/=,以及F

6、OR语句,应该增加TIMESBECOMES,SLASHBECOMES,FOR,STEP,UNTIL,DO。注:因为要求课设和之前实验的内容合并在一起,所以保留了课程实验已经添加的保留字和运算符,之前的已经添加了的不再赘述。具体实现的语句如下所示:typedef enum NUL, IDENT, NUMBER, PLUS, MINUS, TIMES, SLASH, ODDSYM, EQL, NEQ, LSS, LEQ, GTR, GEQ, LPAREN, RPAREN, COMMA, SEMICOLON, PERIOD, BECOMES, BEGINSYM, ENDSYM, IFSYM, THE

7、NSYM, WHILESYM, WRITESYM, READSYM, DOSYM, CALLSYM, CONSTSYM, VARSYM, PROCSYM, PROGSYM,ELSESYM, FORSYM,STEPSYM,UNTILSYM,RETURNSYM, TIMESBECOMES,SLASHBECOMES,ANDSYM,ORSYM,NOTSYM SYMBOL;这里需要考虑需要增加保留字的个数,以及如何命名,再将新增的保留字添加对应的保留字的集合中。具体实现的语句如下所示:char *SYMOUT = NUL, IDENT, NUMBER, PLUS, MINUS, TIMES, SLASH

8、, ODDSYM, EQL, NEQ, LSS, LEQ, GTR, GEQ, LPAREN, RPAREN, COMMA, SEMICOLON, PERIOD, BECOMES, BEGINSYM, ENDSYM, IFSYM, THENSYM, WHILESYM, WRITESYM, READSYM, DOSYM, CALLSYM, CONSTSYM, VARSYM, PROCSYM, PROGSYM ,ELSESYM, FORSYM,STEPSYM,UNTILSYM,RETURNSYM,TIMESBECOMES, SLASHBECOMES, ANDSYM, ORSYM, NOTSYM;2

9、) 将新增的保留字按照字母表升序的方式添加,运算符参照已有的运算符来进行添加,注意好符号与SYM的对应。具体实现的语句如下所示:特别注意点:此处一定要考虑到PLO编译器采用了折半查找算法来进行操作,如果新增的保留字没有按照既定的升序规则来插入,会造成在编译过程中,编译器无法识别某些保留字。strcpy(KWORD 1,BEGIN); strcpy(KWORD 2,CALL); strcpy(KWORD 3,CONST); strcpy(KWORD 4,DO); strcpy(KWORD 5,ELSE); strcpy(KWORD 6,END); strcpy(KWORD 7,FOR); str

10、cpy(KWORD 8,IF); strcpy(KWORD 9,ODD); strcpy(KWORD 10,PROCEDURE); strcpy(KWORD 11,PROGRAM); strcpy(KWORD12,READ); strcpy(KWORD13,RETURN); strcpy(KWORD14,STEP); strcpy(KWORD15,THEN); strcpy(KWORD16,UNTIL); strcpy(KWORD17,VAR); strcpy(KWORD18,WHILE); strcpy(KWORD19,WRITE); WSYM 1=BEGINSYM; WSYM 2=CALL

11、SYM; WSYM 3=CONSTSYM; WSYM 4=DOSYM; WSYM 5=ELSESYM; WSYM 6=ENDSYM; WSYM 7=FORSYM; WSYM 8=IFSYM; WSYM 9=ODDSYM; WSYM 10=PROCSYM; WSYM 11=PROGSYM; WSYM12=READSYM; WSYM13=RETURNSYM; WSYM14=STEPSYM; WSYM15=THENSYM; WSYM16=UNTILSYM; WSYM17=VARSYM; WSYM18=WHILESYM; WSYM19=WRITESYM; SSYM+=PLUS; SSYM-=MINUS

12、; SSYM*=TIMES; SSYM/=SLASH; SSYM(=LPAREN; SSYM)=RPAREN; SSYM=EQL; SSYM,=COMMA; SSYM.=PERIOD; SSYM;=SEMICOLON; SSYM&=ANDSYM; SSYM|=ORSYM; SSYM!=NOTSYM;3) 特别需要注意的两点,这个是很容易被忽略的地方,就是在完成保留字和运算符的增加以后,一定要对PLO编译器对保留字个数已经单词总数定义进行相应的修改。保留字总数比如说在不添加任何保留字的情况下,PL0编译器的原保留字应该是14个,所以在Unit1.CPP中有定义const NORW = 14; /

13、* # OF RESERVED WORDS */而在实验中因为新增加保留字5个,故此处应改为:const NORW = 19; /* # OF RESERVED WORDS */单词总数与保留字总数一样,我们增加完保留字和运算符以后,要修改单词总是,比如原单词总数是33,因为原编译器中并未定义一个常量来进行统一管理,所以需要对所有“i33”的地方进行修改。因为实验中新增加单词10个,故应改为“i43”。如下面代码所示举例: SYMSET SymSetUnion(SYMSET S1, SYMSET S2) SYMSET S=(SYMSET)malloc(sizeof(int)*43); for

14、(int i=0; i43; i+) if (S1i | S2i) Si=1; else Si=0; return S;特别是这个地方也要更改数目为43,不然会发生异常信息。 DECLBEGSYS=(int*)malloc(sizeof(int)*43); STATBEGSYS=(int*)malloc(sizeof(int)*43); FACBEGSYS =(int*)malloc(sizeof(int)*43); for(int j=0; j43; j+) DECLBEGSYSj=0; STATBEGSYSj=0; FACBEGSYSj =0; 体会:在这里的细节导致出现了很多意料不到的问

15、题,很多时候调试了很久才发现,有些地方漏改了,其实可以考虑设置一个全局变量,进行统一修改,不过考虑到对编译器增加全局变量不是很好的一种方式,难免会发生出现可能的篡改,而且不够直观,故只是在原来基础上做出修改。4) 新增的运算符需要被编译器识别,必须满足编译器做词法分析时,能够正确得到对应的SYM,因此在GetSym()函数中在相应位置增加相应的运算符分析判断,具体实现如下面所示的语句: else if(CH=*) GetCh(); if(CH=) SYM=TIMESBECOMES; GetCh(); else SYM=TIMES; else if(CH=/) GetCh(); if(CH=)S

16、YM=SLASHBECOMES; GetCh(); else SYM=SLASH;体会:这里是双字符判断,需要注意判别的连贯性,处理不同情况下对于的SYM或者报错方式。5) 完成*=和/=的语法分析以后,需要考虑其语义的实现,也就是考虑如何实现语句的处理,这里根据前面设计的语法描述图来进行相应的语句分析。依据语法描述图,首选找到STATEMENT函数中IDENT语句体,即case IDENT:语句中加入对*=和/=的语句描述。注意这里有三种情况,要考虑到相互不能影响。表达式的分析采用EXPRESSION(FSYS,LEV,TX);语句就行。从指令功能表可知:OPR,0,4代表次栈顶乘以栈顶,退

17、两个栈元素,结果值进栈OPR,0,5代表次栈顶除以栈顶,退两个栈元素,结果值进栈一定要注意是次栈顶对栈顶的操作,所以要注意被除数(被乘数)和除数(乘数)的入栈的先后次序,不可颠倒。 case IDENT: i=POSITION(ID,TX); if (i=0) Error(11); else if (TABLEi.KIND!=VARIABLE) /*ASSIGNMENT TO NON-VARIABLE*/ Error(12); i=0; GetSym(); if (SYM=BECOMES|TIMESBECOMES|SLASHBECOMES) OPSYM=SYM; if (SYM=TIMESBE

18、COMES) GEN(LOD,LEV-TABLEi.vp.LEVEL,TABLEi.vp.ADR); /取值到栈顶 else if (SYM=SLASHBECOMES) GEN(LOD,LEV-TABLEi.vp.LEVEL,TABLEi.vp.ADR); /取值到栈顶 GetSym(); else Error(13); EXPRESSION(FSYS,LEV,TX); if (OPSYM = TIMESBECOMES) GEN(OPR,0,4); / OPR,0,4代表次栈顶乘以栈顶,退两个栈元素,结果值进栈 else if (OPSYM = SLASHBECOMES) GEN(OPR,0,

19、5); / OPR,0,5代表次栈顶除以栈顶,退两个栈元素,结果值进栈 if (i!=0) GEN(STO,LEV-TABLEi.vp.LEVEL,TABLEi.vp.ADR); break;体会:这里在调试过程中发现了编译程序中的一个BUG:当我测试除等运算时,发现目标代码的生成没有问题,可是结果变成了取余运算,调试许久以后,才发现原来在编译器源码中Interpret()函数的语句中的除运算变成了取余运算。也就造成了OPR 0,5语句本来应该是表示除运算指令,变成了取余运算指令。所以把该语句修改为:case 5: T-; ST=ST / ST+1; break;结果就可以正常输出。所以面对这

20、种BUG,还好老师提醒一下,才发现是这个原因。6) 在增加了所有FOR循环需要的保留字以后,根据FOR循环的语法描述进行语义分析。因为在Pascal中的FOR语句描述为:FOR := STEP UNTIL DO 。所以按照这个语义,进行相应的语句处理。case FORSYM: GetSym(); if (SYM!=IDENT) Error(13);/是否为 变量符号 else i=POSITION(ID,TX); if (i=0) Error(11); else if (TABLEi.KIND!=VARIABLE) /赋值语句中,赋值左部标识符是变量 Error(12); i=0; GetSy

21、m(); if(SYM!=BECOMES) Error(13); else GetSym(); /*处理赋值语句右部的表达式*/ EXPRESSION(SymSetUnion(SymSetNew(UNTILSYM,STEPSYM,DOSYM),FSYS),LEV,TX); if(i!=0) GEN(STO,LEV-TABLEi.vp.LEVEL,TABLEi.vp.ADR); /保持初始值 CX1=CX;/如果是赋值语句,跳转到untilsym执行 GEN(JMP,0,0); CX2=CX; /保持循环开始点 if(SYM!=STEPSYM) Error(19); else GetSym();

22、 EXPRESSION(SymSetUnion(SymSetNew(UNTILSYM,STEPSYM,DOSYM),FSYS),LEV,TX); GEN(LOD,LEV-TABLEi.vp.LEVEL,TABLEi.vp.ADR); /循环变量放到栈顶 GEN(OPR,0,2); GEN(STO,LEV-TABLEi.vp.LEVEL,TABLEi.vp.ADR);/将栈顶的值放入循环变量 /GetSym(); if(SYM!=UNTILSYM) Error(19); else CODECX1.A=CX; /如果是赋值语句,跳转到untilsym执行 GetSym(); EXPRESSION(

23、SymSetUnion(SymSetNew(UNTILSYM,STEPSYM,DOSYM),FSYS),LEV,TX); GEN(LOD,LEV-TABLEi.vp.LEVEL,TABLEi.vp.ADR); GEN(OPR,0,12); /次栈顶是否比栈顶小,是,1进栈,否,0进栈 CX3=CX; GEN(JPC,0,0);/ 栈顶布尔值非0则转移 if(SYM=DOSYM) GetSym(); STATEMENT(FSYS,LEV,TX); GEN(JMP,0,CX2);/跳回step CODECX3.A=CX; else Error(19); /跳出for循环 break;体会:这里的语

24、句分析的次序应该是按照语句出现的先后,而不是语义的先后,不然逻辑很难处理,而且根据循环的方式,进行相应的跳转回填操作比较多,因为要求是对表达式进行操作,所以EXPRESSION()语句的处理也比较多。六、 测试用例因为这个整合了前面课程实验和课程设计的全部内容,因此对代码的检测也写了很多的测试用例。下面是相关说明:七、 运行结果1. 测试/=运算(结果截图,测试用例E05.PL0)运行代码截图:结果输出截图:测试*=运算(结果截图,测试用例E06.PL0)结果输出截图:目标代码生成: 0 JMP 0 1 1 INI 0 7 2 LIT 0 4 3 STO 0 5 4 OPR 0 16 5 ST

25、O 0 3 6 OPR 0 16 7 STO 0 4 8 LOD 0 3 9 LIT 0 1 10 OPR 0 2 11 STO 0 6 12 JMP 0 19 13 LOD 0 4 14 LIT 0 2 15 OPR 0 2 16 LOD 0 6 17 OPR 0 2 18 STO 0 6 19 LOD 0 3 20 LIT 0 10 21 OPR 0 2 22 LOD 0 6 23 OPR 0 12 24 JPC 0 31 25 LOD 0 5 26 LOD 0 6 27 OPR 0 4 28 OPR 0 14 29 OPR 0 15 30 JMP 0 13 31 OPR 0 0测试FOR

26、循环语句(结果截图,测试用例E07.PL0)结果输出截图:八、 心得体会 总的来说,编译原理课程设计,难度挺大,加上本身时间比较重叠,所以很多问题都没有时间去尝试。而且也遇到像编译器存在某些小BUG的问题(比如OPR 0,5),导致调试了很久,才发现问题不在自己的代码。最印象深刻的是对FOR语句的处理,因为FOR语句的语义分析比较繁琐,老师虽然在课堂讲了两次,也听明白了,但是实际动手还是发现有比较大的困难。这也在一些方面体现了知识的活学活用还是不够。 对于编译原理这门课,我觉得考虑问题的分析方式还是很有意思,也对如何编译这个过程的处理有了一个大致的摸索。切实的进行编译器编程之后,对这个编译器如何实现我们的代码转换有了新的认识,站在这个基础上去理解高级语言的语法分析的过程,去想象高级语言的语义分析的思路,对自己的编程感悟也有了一些提高。 编译原理实验是一个要很注意细节的事情,因为在处理细节上必须注意,比如我在处理保留字数量和单词数量上就一直摸索了好久,期间也造成了一些像内存溢出,无法识别保留字的错误。而对于像FOR语句,主要是对于语义处理,和语句之间的衔接跳转回调的理解。 总之,编译原理是一门需要细心和耐心的学科,对于我们理解高级语言的实现由很大的促进。衷心感谢在学习过程中李扬老师的耐心指导。

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1