《编译原理课设报告版.doc》由会员分享,可在线阅读,更多相关《编译原理课设报告版.doc(20页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、编译原理课程设计PL0 Experiment Report(燕山大学 信息科学与工程学院) 姓 名 班 级 : 计算机* 学 生 学 号 : 130104010*&* 课 程 名 称 : 编译原理 指 导 教 师 : 888 2015年 12 月 24 日 一、设计目的研究、 改进或自行设计、 开发一个简单的编译程序或其部分功能, 加深对编译理论和编译过程的理解。 编程语言不限。二、设计任务扩展 PL/0 编译程序功能目的: 扩充 PL/0 编译程序功能要求: (1)阅读、 研究 PL/0 编译程序源文件。 (2)在上述工作基础上, 可有选择地补充、完善其中词法分析、语法分析、语义分析、目标代
2、码生成、目标代码解释执行等部分的功能。如以语法分析部分为例,则可以增加处理更多语法成分的功能,如可处理一维数组、+、-、+=、-=、*=、/=、%(取余)、!(取反)、repeat、for、else、开方、处理注释、错误提示、标示符或变量中可以有下划线等。还可以增加类型,如增加字符类型、实数类型; 扩充函数如有返回值和返回语句的,有参数函数等; (3)设计编制典型的运行实例,以便能反映出自己所作的改进。三、设计思想:PL/0 语言可以看成PASCAL 语言的子集,它的编译程序是一个编译解 释执行系统。PL/0 的目标程序为假想栈式计算机的汇编语言,与具体计算 机无关。 PL/0的编译程序和目标
3、程序的解释执行程序都是用PASCAL语言书写 的,因此PL/0 语言可在配备PASCAL 语言的任何机器上实现 。其编译过程采用一趟扫描方式,以语法分析程序为核心,词法分析和代码生成程序都作为一个独立的过程,当语法分析需要读单词时就调用词法分析程序, 而当语法分析正确需要生成相应的目标代码时,则调用代码生成程序。 用表格管理程序建立变量、常量和过程表示符的说明与引用之间的信息联系。当源程序编译正确时,PL/0 编译程序自动调用解释执行程序,对目标 代码进行解释执行,并按用户程序的要求输入数据和输出运行结果。四、设计内容:1 扩充语句for(;);2 扩充语句if then else ;3 扩充
4、语句repeat ;until ;4 增加自增自减运算+和和+=,-=运算;5 修改不等号#,为!=;6 增加一维数组声明格式:/:/;赋值格式::=;调用格式:五、程序结构:程序pl0程序block语句statement条件condition表达式expression项term因子factorPL/0源程序 词法分析程序表格管理程序语法分析程序出错管理程序代码生成程序目标程序图1 编译程序结构 图2功能模块调用1.功能模块作用如下:Pl0.c:主程序Error:出错处理,打印出错位置和错误编码Getsym:词法分析,读取一个单词Getch:漏掉空格,读取一个字符Gen:生成目标代码,并送入目
5、标程序区Test:测试当前符号是否合法Block:分程序分析处理过程,词法语法分析Enter:登陆名字表Position:查找标识符在名字表中的位置Constdeclaration:常量定义处理Vardeclaraction:变量说明处理Listcode:列出目标代码清单Statement:语句处理Expression:表达式处理Term:项处理Factor:因子处理Condition:条件处理Interpret:对目标代码的解释执行程序Base:通过静态链求出数据取得基地址增加两个功能:Arraydeclaration:数组声明处理Arraycoef:数组索引计算和“虚拟机”动作生成2.保留
6、字:enum symbol nul,ident,number,plus,minus, times,slash,oddsym,eql,neq, lss,leq,gtr,geq,lparen, rparen,comma,semicolon,period,becomes, beginsym,endsym,ifsym,thensym,elsesym,forsym, inc,dec,whilesym, writesym, readsym,dosym,callsym, constsym,varsym, procsym,repeatsym, untilsym, plusbk,minusbk, lbrack,
7、rbrack,colon,共43个,其中补充保留字为:else, for, repeat, until, plusbk, minusbk, Lbrack, rbrack, colon3.名字表中的类型enum object constant, variable, procedure, arrays, 共4个,扩充arrays,以便实现数组4.虚拟机代码enum fct lit, opr, lod, sto, cal, inte, jmp, jpc,lda, sta, 共10个,补充的lda,sta用于数组操作6. 错误信息(1)const,var,procedure后应为标识符(2) 常数说明
8、中的=后应是数字(3)常数说明中的标识符后应是=(4)常数说明中的=写成了:=(5)漏掉了,或;(6)过程说明后的符号不正确(应是语句开始符,或过程定义符)(7)应是语句开始符(8)标识符未说明(9)程序结尾丢了句号。(10)语句之间漏了;(11)call后应为标识符(12)赋值语句中,赋值号左部标识符属性应是变量(13)赋值号左部标识符属性应是赋值号(14)程序体内语句部分的后跟符不正确(15)call后标识符属性应为过程(16)条件语句中丢了then(17)丢了end或;(18)while循环语句中丢了do(19)语句后的符号不正确6.名字表结构struct tablestructchar
9、 nameal;enum object kind;int val;int level;int adr;int size;/扩充名字表结构,增加一个data域保存数组的下界int data; /* 其他数据,对arrays来说是下界*/7.语法描述图:程序分程序 .图3 程序语法描述图,。;,;=语句constidentnumbervaridentprocedureident分程序图4 分程序语法描述图语句语句until表达式表达式:=if条件endthen语句条件do语句(表达式),(ident,)begin语句语句;identcallidentwhilerepeatreadwritedecs
10、incsincsdecselse语句for条件语句图5 语句语法描述图 条件=#=odd表达式表达式=图6条件语法描述图表达式项项+ 图7 表达式语法描述图项因子因子/*%图8 项语法描述图因子)(表达式identnumberincsdecsdecsincs图9 因子语法描述图四、功能扩充1.语句处理中加入for循环语句if(sym = forsym)getsymdo;if(sym != lparen) error(34);/没有左括号出错else getsymdo;statementdo(nxtlev, ptx, lev); /S1代码if(sym != semicolon) error(1
11、0); /语句缺少分号出错elsecx1=cx;getsymdo;conditiondo(nxtlev, ptx, lev); /E代码if(sym!=semicolon)error(10);/语句缺少分号出错else cx2=cx;gendo(jpc,0,0);cx3=cx;gendo(jmp,0,0);getsymdo;cx4=cx;statementdo(nxtlev, ptx, lev);/S2代码if(sym != rparen) error(22);/缺少右括号出错else gendo(jmp,0,cx1);getsymdo;cx5=cx;statementdo(nxtlev, p
12、tx, lev); /S3代码codecx3.a=cx5;gendo(jmp,0,cx4);codecx2.a=cx;2.在语句处理中增加repeat-until语句if(sym = repeatsym)cx1 = cx;getsymdo;statementdo(nxtlev, ptx, lev);if(sym = untilsym)getsymdo;conditiondo(nxtlev, ptx, lev);cx2=cx;gendo(jpc, 0, 0);codecx2.a=cx1; else error(33); /没有写until出错3.扩充+和运算符对于+和-运算符,扩充时要注意存在两
13、个情况:1)作为语句的时候;2)作为表达式中的因子的时候。 注意:扩充时增加因子开始符facbegsysincs=true和facbegsysdecs=true。 扩充的语法描述见结构设计中的PL/0分程序和主要语句的语法描述中的描述图,详细代码见程序。 1)作为语句的时候,有四种情况: a+; a-; +a; -a; 文法的EBNF表示形式为: :=+ |-|+|- 文法分析过程大体如下图: 语句开始符SYM=+或者-读下个SYM,如是ident,确定为自增自减语句语句开始符SYM=ident读下个SYM,如是+或者-,确定为自增自减语句 +a和a a+和a生成中间代码对于a+;+a;和a-
14、;-a;语句的处理如下: 先将变量的值取出放在栈顶,后将1入栈,后执行加法或减法运算oprv指令的2(加法)、3(减法),后将运算后的栈顶值存回变量。 a+;和+a;语句的中间代码:lod 0 3;lit 0 1;opr 0 2;sto 0 3;a-;和-a;语句的中间代码:lod 0 3;lit 0 1;opr 0 3;sto 0 3; 2) 作为因子的时候,有两种情况: a+和a-作为因子,比如:b:=a+*a-;语句 +a和-a作为因子,比如:b:=-a+2*+a;语句 文法的EBNF表示形式为: :=. +|-|+| -. 其中的.表示前后都可以有其他的项或因子 生成中间代码A对于因子
15、+a和-a的中间代码生成处理和a+;等语句处理一样; B对于因子a+和a的中间代码生成处理如下:a+:lod 0 3;lit 0 1;opr 0 2;sto 0 3;lod 0 3;lit 0 1;opr 0 3; a-:lod 0 3;lit 0 1;opr 0 3;sto 0 3;lod 0 3;lit 0 1;opr 0 2; 先将变量的值取出放在栈顶,后将1入栈,后执行加法或减法运算opr指令的2(加法)、3(减法),后将运算后的栈顶值存回变量,后将变量的值又取出来放入栈顶,后将1入栈,如果是a+就执行减法,如果是a就执行加法,以实现先用a的值后再加1。4.语句处理中加入if-then
16、-else语句在原有程序if(sym=then).后加入下列代码:cx1 = cx;gendo(jpc, 0, 0);statementdo(fsys, ptx, lev);if(sym = elsesym)getsymdo;cx2 = cx;gendo(jmp, 0, 0);codecx1.a = cx;statementdo(fsys, ptx, lev);codecx2.a = cx;elsecodecx1.a = cx;5.修改不等号#为!=注释源程序中的ssym# = neq语句,在getsym中加入下列代码:/修改不等号为!=else if(ch=!)getchdo;if(ch=)
17、sym=neq;getchdo; else sym=nul; 6.加入对一维数组的支持本程序将数组看做变量的一种,由var声明函数调用array声明函数完成数组声明,这样就处加入文件输出的相关语句外,可以完全保留block函数和enter函数;通过改写factor函数使数组因子包括了后缀的索引号,这样就可以调用通用的表达式函数赋值数组了。为了方便完成数组相关功能,扩充了虚拟机处理代码。数组的越界及非法调用错误处理没有完善,仅给出了错误代码。在头文件pl0.h中:/*定义两个全局变量,用来保存数组定义的下界和容量*/static int g_arrBase = 0;static int g_ar
18、rSize = 0;/* 虚拟机代码*/增加lda,sta专门由于数组的处理/增加两个虚拟机指令lda,sta,分别用来从数组中取数和存到数组中/数组元素的访问和存储,是将()后的当成表达式,先处理,得到元素的索引,放在栈顶/最后根据数组的首地址,得到某个元素的地址enum fct .lda, sta /扩充名字表结构,增加一个data域保存数组的下界struct tablestruct.int data; /* 其他数据,对arrays来说是下界*/* 名字表中的类型*/enum object .arrays /添加数组类型/数组声明处理, 下界和上界允许已经定义过的常量标识符int arr
19、aydeclaration(int* ptx, int lev, int* pdx);/数组元素索引计算与“虚拟机”生成int arraycoef(bool *fsys,int *ptx,int lev);在源程序文件pl0.c中:编写相关的arraydeclaration,arraycoef两个功能函数:/* 数组声明处理, 下界和上界允许已经定义过的常量标识符*/int arraydeclaration(int* ptx, int lev, int* pdx) char arrIdal; /* 暂存数组标识名,避免被覆盖*/ int cstId; /* 常量标识符的位置*/ int arr
20、Base=-1, arrTop=-1; /* 数组下界、上界的数值*/getsymdo; if(sym=lbrack) /* 标识符之后是,则识别为数组*/ strcpy(arrId, id); /* 检查下界*/ getsymdo; if(sym=ident) if(cstId=position(id,(*ptx)!=0)arrBase=(constant=tablecstId.kind)?tablecstId.val:-1; elsearrBase=(sym=number)?num:-1; if(-1=arrBase)error(50);return -1; /* 检查冒号*/getsym
21、do;if(sym!=colon) error(50);return -1; /* 检查上界*/ getsymdo; if(sym=ident) if(cstId=position(id,(*ptx)!=0)arrTop=(constant=tablecstId.kind)?tablecstId.val:-1; elsearrTop=(number=sym)?num:-1; if(arrTop=-1)error(50);/随意指定,因为原程序对错误号的规划极差!return -1; /* 检查 */ getsymdo; if(sym!=rbrack) error(50);return -1;
22、/* 上下界是否符合条件检查*/ g_arrSize=arrTop-arrBase+1; g_arrBase=arrBase; if(g_arrSizen; write(a); write(b);end.当输入的n为3时,repeat.until.语句中的循环体执行3次,所以a=4,b=72.测试增加的+,-功能 测试文件:2.txt 测试结果:var a,b;begin a:=1; b:=3; a+; b-; write(a); write(b);end.3.测试if-then-else功能 测试文件:3.txt 测试结果:var s,i,n;begin i:=4; s:=0; n:=7;
23、if in then s:=i else s:=n; write(s);end.4.测试for功能测试文件:5.txt 测试结果:var s,i,n; begin s:=0; n:=1; for(i:=1;ia2; for(i:=1;i=5;i:=i+1) begin a1+=2; a2:=a2*i; end; write(a1); write(a2);end.五、设计技巧 编程的过程是脚踏实地地去做,才能读懂,才能改进,才能发展。只有相对的技巧,就是对原程序的错误编号,标记。对改动的错误利用编译程序的断点功能检查错误。书写程序的时候,首先画语法图列出框架,在根据框架书写程序,这是我从中总结的
24、技巧。六、设计心得PL0编译程序的课程设计是在读懂PL/0编译器程序的基础上进行扩展功能后完善程序,使我们了解到PL/0编译器的工作原理和实现机制。 从源程序到目标程序的翻译工作的整个过程中,遇到的困难有很多,读代码,解决遇到的问题,从而去认识了解整个过程的流程,对词法分析有了深刻的理解,在语法分析阶段中,充分运用到文法这个知识点。对于文法,课本上单纯的理论学习在本次课程设计中国得到了深刻的应用,在这次课程设计中,让我认识到整个编译程序的主体架构是文法。中间代码的生成是代码优化的作用,一个好的中间代码省时间、空间。在调试程序的时候通过解决问题,对中间代码生成和执行过程有了更加深刻的理解。 综上所述,对PL/0编译程序的扩充让我学到了很多东西,对于编译程序的理解更深刻、更透彻。