欢迎来到三一办公! | 帮助中心 三一办公31ppt.com(应用文档模板下载平台)
三一办公
全部分类
  • 办公文档>
  • PPT模板>
  • 建筑/施工/环境>
  • 毕业设计>
  • 工程图纸>
  • 教育教学>
  • 素材源码>
  • 生活休闲>
  • 临时分类>
  • ImageVerifierCode 换一换
    首页 三一办公 > 资源分类 > DOCX文档下载  

    PL0课程设计 多种思路.docx

    • 资源ID:3163881       资源大小:49.43KB        全文页数:37页
    • 资源格式: DOCX        下载积分:6.99金币
    快捷下载 游客一键下载
    会员登录下载
    三方登录下载: 微信开放平台登录 QQ登录  
    下载资源需要6.99金币
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    PL0课程设计 多种思路.docx

    PL0课程设计 多种思路语句 Ident := += -= + - 表达式 + Ident - If 条件 Then 语句 Else 语句 因子 To For 语句 Downto 表达式 To 语句 Number Ident 表达式 )+ - 1. 增加一维数组类型 l 增加一维数组后的声明语句语法描述图: l EBNF语法描述:<声明语句>:=var<标识符><number>,<标识符><number> l 识别数组部分代码实现,在getsym和enter里改动 if(ch='')/如果是则认为是数组 getchdo; getsymdo; sum=num;/把getsym取出的数字赋给sum,作为数组的维数 if(ch='')/维数后的应该是 getchdo; sym=arrayp; else error(33);/如果没有报错 if(sum<1) error(34);/数组维数不能为0 /-把一维数组当作变量,把数组填入名字表table里,在enter- switch(k) case array: /数组时 table(*ptx)-1.size=sum;/为数组变量开辟一个长度sum的空间 for(i=0;i<sum;i+) table(*ptx).level=lev; /填入层次 table(*ptx).adr=(*pdx); /填入地址 strcpy(table(*ptx).name,"#"); /用#做为该数组的名字 table(*ptx).kind=variable; /类型为变量 (*ptx)+; (*pdx)+; sum=0; /清空sum (*ptx)-; break; l 增加数组后赋值语句语法描述图: if(sym=varsym)/*收到变量声名符号,开始处理变量声名*/ getsymdo; do vardeclarationdo(&tx,lev,&dx); if(sym=arrayp)/ enter(array,&tx,lev,&dx); getsymdo; / while(sym=comma) getsymdo;/ vardeclarationdo(&tx,lev,&dx); if(sym=arrayp) / enter(array,&tx,lev,&dx); getsymdo; / if(sym=semicolon) getsymdo;/ else error(5); while(sym=ident); l 数组单元在名字表中的位置: 因为把数组当成一种特殊的变量,所以也可以当做因子使用,不用做任何修改。 当使用数组中的单元时候,找到单元在名字表位置时调用position。 l 代码实现:在position里改动 int position(char * idt,int tx) if(ch='') getsymdo;/如果取到的符号为则认为是数组 if(sym=arrayp)/判断是否为数组 if(tablei.size>sum) i=i+sum+1;/使用单元为数组首单元+该单元编号+1 else error(35); sum=0; return i; 详细版 添加一维数组需要对PL0进行如下修改: 添加一维数组变量类型的声明的处理以及识别; 处理一维数组作为变量时在表达式及语句中的变量引用情况; 处理一维数组进行+和运算; 处理一维数组在运行栈里的存取问题; 检测一维数组下标界合法性问题。 完成数组的添加的主导思想为,把数组元素转化为变量处理,从而充分利用之前为变量设定的各种操作。 (1) 添加一维数组变量类型的声明的处理以及识别 首先在枚举类型kind中添加array及null。 修改后kind为: enum object constant, variable, procedur, array, null, ; 同时结构体类型中tablestruct中的size除了表示procedure的大小外,同时用于表示数组的大小。 添加数组左右中括号的symbol,左中括号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中添加对一维数组的声明处理。若变量后接左中括号则为数组。修改如下: 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中的中括号里是变量的话 /要检查是不是以声明的常量 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);/填写名字表 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); /*全局变量id中已存有当前名字的名字*/ table(*ptx).kind=k; switch(k) case constant: /*常量名字*/ if (num>amax) 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; case 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解释函数中添加对上述新增功能的解释.代码如下: 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<0|st-1>=st) error(41);/定义数组下标溢出错误 printf("数组下标溢出错误"); break; 接下来可以修改stament语句,如下: int statement(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; /处理里的表达式 memcpy(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 /省略+=,-=等语句判别的代码,同时在各代码中取消存储到变量的语句用下面的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; 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); /*把数组元素的值压入栈*/ 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时对其赋值。 扩充一维数组类型功能 扩充一维数组类型功能需要:一维数组变量类型的声明;一维数组的识别分析;一维数组 作为变量时在表达式中的变量引用情况;一维数组可以进行+和运算;一维数组在运行 栈里的存取问题;一维数组下标界合法性问题。 一维数组的文法如下: <数组变量声明>:=<标识符> <数组变量引用>:=<标识符> <表达式> <数字>|<已声明的常量> 扩充的语法描述见结构设计中的 PL/0 分程序和主要语句的语法描述中的描述图。 在标识符的属性类型里增加了 array 数组类型,详细见报告上面的说明。 在虚拟机代码指令操作码中增加了六条指令: gar,/根据栈顶的偏移地址从数组中取值到新的栈顶 sar,/根据次栈顶的偏移地址把栈顶的值存入数组 shd,/将栈顶的值下移到次栈顶,栈顶出栈,即次栈顶成为栈顶 del,/出栈顶 jud,/判断数组下标合法性 tra,/将数组的下标范围入栈,gendo(tra,0,数组下标最大值); 其中增加的指令中 gar,sar,shd,del 是用于数组变量引用时的处理,jud,tra 是用于数组下标合法 性的判断处理。详细运用见下面分析。 下面一一分析扩充一维数组的情况: 一维数组变量类型的声明: 编译器一开始运作的时候,先对声明部分进行分析后填名字表 table。数组类型属于变 量,所以,在分析声明部分的时候,遇到变量 var 声明时,读完变量标识符后再读下一 个字符,如果下个字符 SYM=lepa 即时,就将变量认定为数组变量,后采用数组变 量的分析处理。 在变量分析函数:int vardeclaration(int * ptx,int lev,int * pdx);中对数组变量声明的处理 如下流程: 一维数组的识别分析: 一维数组的识别分析过程是数组变量引用处理之前的基础,识别过程大体如下: 1) 读到 SYM=ident,读下个单词; 2)如果该单词是 lepa,即,判断为数组变量,后读下个单词,采用表达式 expression 函数处理 里的式子,后读下个单词,遇到 ripa,即,识别数组变量完毕; 3) 如果该单词不是 lepa,即判断为正常变量,采用正常变量的情况处理。 一维数组在表达式中的变量引用情况: 一维数组的变量引用情况,先识别是一维数组变量,后处理生成中间代码。下面分析一 维数组中间代码的生成问题。 1)一维数组变量作为语句的开始符号, 即赋值语句 ai:=<表达式>或者 ai+;这种形 式的语句 对于赋值语句 aE1:=E2 的情况,中间代码结构如下图: 2) 一维数组变量作为表达式里面的某个因子参加表达式的运算情况 在因子处理函数 int factor(bool*fsys,int *ptx,int lev);中,如果遇到数组变量时,后读 下个单词,如果是+,-的情况,分析情况见下面;如果不是+,-的情况,按下面 分析处理。 即对于表达式:E1<运算符> aE2 <运算符>E3 中,中间代码生成情况如下图: 以例子 b:=2*a1-4;解析一下因子 a1中间代码的生成情况。 3) 对于一维数组变量与运算符+,-的情况见下面。 一维数组的+和运算分析: 对一维数组的+和运算分析和普通变量的+、-运算分析的流程是基本相同的,只是 在识别一维数组变量和生成中间代码有些差别。 生成中间代码的差别在于:分两种情况讨论。 作为表达式里面的因子 aE+或者 aE 生成的中间代码结构如下: 下面给出表达式中因子 aE+的中间代码 作为表达式里面的因子+aE或者aE 生成的中间代码结构如下: 下面给出表达式中因子+aE的中间代码: 一维数组在运行栈里的存取问题,包括指令增加和优化: 对于优化运行栈的存取问题,是因在调试的时候,在调试程序: var a2; Begin a1:=1; while a1<1000 do begin +a1; end; write(a1); end. 输出的结果是 1,后经过细细检查发现是运行栈溢出的问题。 运行栈的大小为:#define stacksize 500,在一般的简单程序是足够的,但是为什么会溢 出呢?原来在运行的过程中,忘记删除多余的数据,从而导致运行栈在每次循环总会将 栈顶指针上移,从而运行栈溢出了。 后根据这个问题增加了指令 shd 和 del,对多余的数据进行删除,达到中间代码的优化。 一维数组下标界合法性问题: 对于处理一维数组下标界的合法性问题,增加了指令 tra 和 jud,指令 tra 是将数组变量 的下标最大值取到栈顶,后指令 jud 是根据栈顶的下标界限值与次栈顶的实际下标值相 比较检查下标是否合法。 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 i<E2 then 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,"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,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(nxtlev,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); /生成比较指令,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); /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,如大于等于,继续循环, 小于的话,跳出循环*/ 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,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还是downto的区别 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记录当前代码段作为开始循环位置 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(

    注意事项

    本文(PL0课程设计 多种思路.docx)为本站会员(牧羊曲112)主动上传,三一办公仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一办公(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    备案号:宁ICP备20000045号-2

    经营许可证:宁B2-20210002

    宁公网安备 64010402000987号

    三一办公
    收起
    展开