PL0课程设计 多种思路.docx

上传人:牧羊曲112 文档编号:3163881 上传时间:2023-03-11 格式:DOCX 页数:37 大小:49.43KB
返回 下载 相关 举报
PL0课程设计 多种思路.docx_第1页
第1页 / 共37页
PL0课程设计 多种思路.docx_第2页
第2页 / 共37页
PL0课程设计 多种思路.docx_第3页
第3页 / 共37页
PL0课程设计 多种思路.docx_第4页
第4页 / 共37页
PL0课程设计 多种思路.docx_第5页
第5页 / 共37页
亲,该文档总共37页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《PL0课程设计 多种思路.docx》由会员分享,可在线阅读,更多相关《PL0课程设计 多种思路.docx(37页珍藏版)》请在三一办公上搜索。

1、PL0课程设计 多种思路语句 Ident := += -= + - 表达式 + Ident - If 条件 Then 语句 Else 语句 因子 To For 语句 Downto 表达式 To 语句 Number Ident 表达式 )+ - 1. 增加一维数组类型 l 增加一维数组后的声明语句语法描述图: l EBNF语法描述::=var, l 识别数组部分代码实现,在getsym和enter里改动 if(ch=)/如果是则认为是数组 getchdo; getsymdo; sum=num;/把getsym取出的数字赋给sum,作为数组的维数 if(ch=)/维数后的应该是 getchdo;

2、sym=arrayp; else error(33);/如果没有报错 if(sum1) error(34);/数组维数不能为0 /-把一维数组当作变量,把数组填入名字表table里,在enter- switch(k) case array: /数组时 table(*ptx)-1.size=sum;/为数组变量开辟一个长度sum的空间 for(i=0;isum) i=i+sum+1;/使用单元为数组首单元+该单元编号+1 else error(35); sum=0; return i; 详细版 添加一维数组需要对PL0进行如下修改: 添加一维数组变量类型的声明的处理以及识别; 处理一维数组作为变

3、量时在表达式及语句中的变量引用情况; 处理一维数组进行+和运算; 处理一维数组在运行栈里的存取问题; 检测一维数组下标界合法性问题。 完成数组的添加的主导思想为,把数组元素转化为变量处理,从而充分利用之前为变量设定的各种操作。 (1) 添加一维数组变量类型的声明的处理以及识别 首先在枚举类型kind中添加array及null。 修改后kind为: enum object constant, variable, procedur, array, null, ; 同时结构体类型中tablestruct中的size除了表示procedure的大小外,同时用于表示数组的大小。 添加数组左右中括号的sy

4、mbol,左中括号lepa,右中括号ripa,同时修改symnum 变为43(41+2),以及init函数中进行如下修改 ssym+=plus; ssym-=minus; ssym*=times; ssym/=slash; ssym(=lparen; ssym)=rparen; ssym=eql; ssym,=comma; ssym.=period; /ssym#=neq; ssym;=semicolon; ssym=lepa;/数组用新增 ssym=repa;/数组用新增 添加int类型全局变量arraysize。用于暂时存储数组大小。 在变量声明处理过程vardeclaration中添加对

5、一维数组的声明处理。若变量后接左中括号则为数组。修改如下: int vardeclaration(int * ptx,int lev,int * pdx) int i; char idtempal+1;/临时保存数组名字 if(sym=ident) strcpy(idtemp,id); getsymdo; /如果是数组 if(sym=lepa)/数组的左中括号 getsymdo; if(sym=number)/a中的中括号里是数字的话 *pdx=*pdx+num;/为数组分配空间 arraysize=num;/保存数组的长度 else if(sym=ident)/a中的中括号里是变量的话 /要

6、检查是不是以声明的常量 i=position(id,*ptx);/查找名字表 if(i=0) error(11);/标识符未说明 else if(tablei.kind=constant)/标识符的属性是常量 *pdx=*pdx+tablei.val;/为数组分配空间 arraysize=tablei.val;/保存数组的长度 else error(25);/数组下标定义不符合规定,应为常量 /if else error(25);/数组下标定义不符合规定,应为常量 /else strcpy(id,idtemp);/恢复数组名字id enter(array,ptx,lev,pdx);/填写名字表

7、 getsymdo; if(sym!=ripa)/如果不是结尾 error(26); else getsymdo; /if /下个字符是逗号或者分号,则不是数组,是变量 else if(sym=comma|sym=semicolon) enter(variable,ptx,lev,pdx);/填写名字表 /getsymdo; else error(4); return 0; 同时修改enter函数,增加对数组的支持 void enter (enum object k,int *ptx,int lev, int *pdx) (*ptx)+; strcpy(table(*ptx).name,id)

8、; /*全局变量id中已存有当前名字的名字*/ table(*ptx).kind=k; switch(k) case constant: /*常量名字*/ if (numamax) error(31); num=0; table(*ptx).val=num; break; case variable: /*变量名字*/ table(*ptx).level=lev; table(*ptx).adr=(*pdx); table(*ptx).size=0; (*pdx)+; break; /*过程名字*/ case procedur: table(*ptx).level=lev; break; ca

9、se array: /*数组名字*/ table(*ptx).level=lev; table(*ptx).adr=(*pdx)-arraysize; table(*ptx).size=arraysize; break; (2)处理一维数组作为变量时在表达式及语句中的变量引用情况,以及完成对数组越界的处理 在修改statement与factor函数前,需对虚拟机的中间代码添加对数组相关操作的支持。 修改FCT枚举类型如下 enum fct lit, opr, lod, sto, cal, inte, jmp, jpc, tra,jud,wta,rda, ; 在interup解释函数中添加对上述

10、新增功能的解释.代码如下: case tra:/将数组的下标范围入栈 st=i.a; t+; break; case rda:/读数组数据 tmd=base(i.l,s,b); st=stmd+i.a+st-1; t+; break; case wta:/写数组数据 tmd=base(i.l,s,b); stmd+i.a+st-2=st-1; t-; break; case jud:/判断数组下标的合法性 t-; if(st-1=st) error(41);/定义数组下标溢出错误 printf(数组下标溢出错误); break; 接下来可以修改stament语句,如下: int stateme

11、nt(bool* fsys,int * ptx,int lev) int i,cx1,cx2; bool nxtlevsymnum; enum symbol addop;/用于存储运算类型,新增 object type=null; if(sym=ident) i=position(id,*ptx); if(i=0) error(11); else /数组元素或变量 if(tablei.kind!=variable&tablei.kind!=array) error(12); i=0; else getsymdo; if(sym=lepa)/如果是数组 getsymdo; /处理里的表达式 me

12、mcpy(nxtlev,fsys,sizeof(bool)* symnum); nxtlevripa=true;/表达式后接符号为右中括号 expressiondo(nxtlev,ptx,lev); gendo(tra,0,tablei.size);/生成将数组下标范围入栈指令 gendo(jud,0,0);/作用判断下标合法性,完成了数组越界的处理 if(sym!=ripa) error(26);/接下来的单词不为右中括号 getsymdo; /if(sym=lepa) if(sym=becomes)/省略未改动部分 else /省略+=,-=等语句判别的代码,同时在各代码中取消存储到变量的

13、语句用下面的IF语句统一代替 if(type=array) gendo(wta,lev-tablei.level,tablei.adr); /*把数组元素的值压入栈*/ else gendo(sto,lev-tablei.level,tablei.adr); /*把变量的值压入栈*/ 在因子处理程序中,添加对一维数组的支持,在判断第一个单词为ident后的switch语句中,添加如下代码 case array: getsymdo; if(sym=lepa) memcpy(nxtlev,fsys,sizeof(bool)* symnum); nxtlevripa=true; getsymdo;

14、expressiondo(nxtlev,ptx,lev); gendo(tra,0,tablei.size);/生成将数组下标范围入栈指令 gendo(jud,0,0);/判断下标合法性 gendo(rda,lev-tablei.level,tablei.adr); /*把数组元素的值压入栈*/ if(sym!=ripa)error(42);/定义中括号不匹配错误 elsegetsymdo; break; 同时把所有读取变量存取变量的代码替换为如下代码 读变量的替换代码 if(type=array) gendo(rda,lev-tablei.level,tablei.adr); /*把数组元素

15、的值压入栈*/ else gendo(lod,lev-tablei.level,tablei.adr);/*把变量的值压入栈*/ 写变量的替换代码 if(type=array) gendo(wta,lev-tablei.level,tablei.adr); /*把数组元素的值压入栈*/ else gendo(sto,lev-tablei.level,tablei.adr); /*把变量的值压入栈*/ 上述代码中type为object类型变量。在判断变量是否为array时对其赋值。 扩充一维数组类型功能 扩充一维数组类型功能需要:一维数组变量类型的声明;一维数组的识别分析;一维数组 作为变量时在

16、表达式中的变量引用情况;一维数组可以进行+和运算;一维数组在运行 栈里的存取问题;一维数组下标界合法性问题。 一维数组的文法如下: := := | 扩充的语法描述见结构设计中的 PL/0 分程序和主要语句的语法描述中的描述图。 在标识符的属性类型里增加了 array 数组类型,详细见报告上面的说明。 在虚拟机代码指令操作码中增加了六条指令: gar,/根据栈顶的偏移地址从数组中取值到新的栈顶 sar,/根据次栈顶的偏移地址把栈顶的值存入数组 shd,/将栈顶的值下移到次栈顶,栈顶出栈,即次栈顶成为栈顶 del,/出栈顶 jud,/判断数组下标合法性 tra,/将数组的下标范围入栈,gendo(

17、tra,0,数组下标最大值); 其中增加的指令中 gar,sar,shd,del 是用于数组变量引用时的处理,jud,tra 是用于数组下标合法 性的判断处理。详细运用见下面分析。 下面一一分析扩充一维数组的情况: 一维数组变量类型的声明: 编译器一开始运作的时候,先对声明部分进行分析后填名字表 table。数组类型属于变 量,所以,在分析声明部分的时候,遇到变量 var 声明时,读完变量标识符后再读下一 个字符,如果下个字符 SYM=lepa 即时,就将变量认定为数组变量,后采用数组变 量的分析处理。 在变量分析函数:int vardeclaration(int * ptx,int lev,

18、int * pdx);中对数组变量声明的处理 如下流程: 一维数组的识别分析: 一维数组的识别分析过程是数组变量引用处理之前的基础,识别过程大体如下: 1) 读到 SYM=ident,读下个单词; 2)如果该单词是 lepa,即,判断为数组变量,后读下个单词,采用表达式 expression 函数处理 里的式子,后读下个单词,遇到 ripa,即,识别数组变量完毕; 3) 如果该单词不是 lepa,即判断为正常变量,采用正常变量的情况处理。 一维数组在表达式中的变量引用情况: 一维数组的变量引用情况,先识别是一维数组变量,后处理生成中间代码。下面分析一 维数组中间代码的生成问题。 1)一维数组变

19、量作为语句的开始符号, 即赋值语句 ai:=或者 ai+;这种形 式的语句 对于赋值语句 aE1:=E2 的情况,中间代码结构如下图: 2) 一维数组变量作为表达式里面的某个因子参加表达式的运算情况 在因子处理函数 int factor(bool*fsys,int *ptx,int lev);中,如果遇到数组变量时,后读 下个单词,如果是+,-的情况,分析情况见下面;如果不是+,-的情况,按下面 分析处理。 即对于表达式:E1 aE2 E3 中,中间代码生成情况如下图: 以例子 b:=2*a1-4;解析一下因子 a1中间代码的生成情况。 3) 对于一维数组变量与运算符+,-的情况见下面。 一维

20、数组的+和运算分析: 对一维数组的+和运算分析和普通变量的+、-运算分析的流程是基本相同的,只是 在识别一维数组变量和生成中间代码有些差别。 生成中间代码的差别在于:分两种情况讨论。 作为表达式里面的因子 aE+或者 aE 生成的中间代码结构如下: 下面给出表达式中因子 aE+的中间代码 作为表达式里面的因子+aE或者aE 生成的中间代码结构如下: 下面给出表达式中因子+aE的中间代码: 一维数组在运行栈里的存取问题,包括指令增加和优化: 对于优化运行栈的存取问题,是因在调试的时候,在调试程序: var a2; Begin a1:=1; while a11000 do begin +a1; e

21、nd; write(a1); end. 输出的结果是 1,后经过细细检查发现是运行栈溢出的问题。 运行栈的大小为:#define stacksize 500,在一般的简单程序是足够的,但是为什么会溢 出呢?原来在运行的过程中,忘记删除多余的数据,从而导致运行栈在每次循环总会将 栈顶指针上移,从而运行栈溢出了。 后根据这个问题增加了指令 shd 和 del,对多余的数据进行删除,达到中间代码的优化。 一维数组下标界合法性问题: 对于处理一维数组下标界的合法性问题,增加了指令 tra 和 jud,指令 tra 是将数组变量 的下标最大值取到栈顶,后指令 jud 是根据栈顶的下标界限值与次栈顶的实际

22、下标值相 比较检查下标是否合法。 6.扩充取余运算符% 取余运算符%采用了指令 opr 0 7,只需在初始化函数里增加 ssym%=mod;/取余的语句, 后在解释函数里增加指令 opr 0 7 的功能就行了。 2. 增加for语句支持 增加Pascal的FOR语句: FOR := TO DO FOR := DOWNTO DO 其中,语句的循环变量的步长为1, 语句的循环变量的步长为-1 for 语句的语句语法图如下: For i:= E1 to E2 do S1 循环语句ALGOL等价于: i:= E1; goto OVER; AGAIN :i:= i+1 OVER : if iE2 the

23、n Begin S1; goto again end; (注意程序中基础用到循环控制变量i,因此 entry必须被保存下来,而Pascal这样的语言中,循环变量在循环外也是可见的,本次扩充约定循环步长为 1或者-1。具体需要在程序staement添加for的句法判断) 首先,词法分析部分增加关键字 在SYMBOL里增加FORSYM, TOSYM, DOWNTOSYM,对应SYMMAX=44 ; NORW = 25; 初始化中 strcpy(KWORD10,FOR); WSYM 10=IFSYM; strcpy(KWORD22,TO); WSYM22=TOSYM; strcpy(KWORD 7,

24、DOWNTO); WSYM 7=DOWNTOSYM; 其次,修改STATEMENT,代码如下: else if(sym=forsym) /检测到for语句 getsymdo; if(sym=ident) i=position(id,*ptx); if(i=0) error(11); else if(tablei.kind!=variable) /赋值语句中,赋值号左部标识符属性应是变量 error(12);i=0; else getsymdo; if(sym!=becomes) error(13); /赋值语句左部标识符后应是赋值号:= else getsymdo; memcpy(nxtlev

25、,fsys,sizeof(bool)*symnum); nxtlevtosym=true; /后跟符to和downto nxtlevdowntosym=true; expressiondo(nxtlev,ptx,lev); /处理赋值语句右部的表达式E1 gendo(sto,lev-tablei.level,tablei.adr); /保存初值 switch(sym) case tosym: /步长为的向上增加 getsymdo; cx1=cx; /保存循环开始点 gendo(lod,lev-tablei.level,tablei.adr); /将循环判断变量取出放到栈顶 memcpy(nxt

26、lev,fsys,sizeof(bool)*symnum); /处理表达式E2 nxtlevdosym=true; /后跟符do expressiondo(nxtlev,ptx,lev); /*判断循环变量条件,比如for i:=E1 to E2 do S中,判断i是否小于E2,如小于等于,继续循环,大于的话,跳出循环*/ cx2=cx; /保存循环结束点 /生成条件跳转指令,跳出循环,跳出的地址未知 gendo(jpc,0,0); getsymdo; statement(fsys,ptx,lev); /循环体处理 if(sym=dosym) /处理循环体S gendo(opr,0,13);

27、/生成比较指令,i是否小于等于E2的值 /增加循环变量步长为 gendo(lod,lev-tablei.level,tablei.adr); /将循环变量取出放在栈顶 gendo(lit,0,1); /将步长取到栈顶 gendo(opr,0,2); /循环变量加步长 /将栈顶的值存入循环变量 gendo(sto,lev-tablei.level,tablei.adr); gendo(jmp,0,cx1); /无条件跳转到循环开始点 /*回填循环结束点的地址,cx为else后语句执行完的位置,它正是前面未定的跳转地址*/ codecx2.a=cx; else break; error(29);

28、/for语句中少了do case downtosym: /步长为的向下减少 getsymdo; cx1=cx; /保存循环开始点 /将循环判断变量取出放到栈顶 gendo(lod,lev-tablei.level,tablei.adr); memcpy(nxtlev,fsys,sizeof(bool)*symnum); /处理表达式E2 nxtlevdosym=true; /后跟符do expressiondo(nxtlev,ptx,lev); /*判断循环变量条件,比如for i:=E1 downto E2 do S中,判断i是否大于等于E2,如大于等于,继续循环, 小于的话,跳出循环*/

29、gendo(opr,0,11); /生成比较指令,i是否大于等于E2的值 cx2=cx; /保存循环结束点 /生成条件跳转指令,跳出循环,跳出的地址未知 gendo(jpc,0,0); getsymdo; statement(fsys,ptx,lev); /循环体处理 /将循环变量取出放在栈顶 gendo(lod,lev-tablei.level,tablei.adr); gendo(lit,0,1); /将步长取到栈顶 gendo(opr,0,3); /循环变量加步长 /将栈顶的值存入循环变量 gendo(sto,lev-tablei.level,tablei.adr); gendo(jmp

30、,0,cx1); /无条件跳转到循环开始点 /*回填循环结束点的地址,cx为else后语句执行完的位置,它正是前面未定的跳转地址*/ if(sym=dosym) /处理循环体S /增加循环变量步长为 codecx2.a=cx; else break; error(29);/for语句中少了do else error(19); /for语句后跟赋值语句,赋值语句左部是变量,缺少变量 其跳转图为 ) 在statement中添加,类似else的方式,主要是处理好如何出入栈的问题,代码中有详细的注释。 if(sym=forsym) enum symbol temp;/定义临时变量,来判断是to还是do

31、wnto的区别 getsym; if(sym=ident) i=position(id,*ptx); /查找变量标识符 if(i=0) error(11); /标识符没有找到 else if(tablei.kind=constant|tablei.kind=procedur) error(12); /变量不能为常数或过程 i=0; getsym; if(sym=becomes) /变量赋初始值 getsym; /处理第一个运算表达式,放入栈顶 expressiondo(fsys,ptx,lev); if(sym=tosym) /如果为TO cx1=cx; /CX1记录当前代码段作为开始循环位置

32、 gen(sto,lev-tablei.level,tablei.adr); /变量赋值 gen(lod,lev-tablei.level,tablei.adr); /变量的值放入栈顶 getsym; /计算第二个运算表达式,放入栈顶 expressiondo(fsys,ptx,lev); gen(opr,0,13); /判断运算是否大于 cx2=cx; /CX2记录当前代码段,用于JPC的跳转地址的回填 gen(jpc,0,0); temp=tosym; else /DOWNTO 与 TO 几乎一样,区别在于判断运算是否小于 if(sym=downtosym) cx1=cx; gen(sto,lev-tablei.level,tablei.adr); /变量赋值 gen(lod,lev-tablei.level,tablei.adr); /变量的值放入栈顶 getsym; expressiondo(fsys,ptx,lev); gen(opr,0,11);/判断运算是否小于 cx2=cx; gen(jpc,0,0); temp=downtosym; else error(19); if(sym=dosym) getsym; /做DO后面的 else error(18); /缺少do statementdo(

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号