编译原理实验报告课程名称______编译原理_____题目名称PL0上机作业学生学院__计算机学院______专业班级__12级计科8班学号____3112006110____学生姓名____谢偲灏_指导教师______杨劲涛___2015年1月3日1一、概述目的:在分析理解一个教学型编译程序(如PL/0)的基础上,对其词法分析程序、语法分析程序和语义处理程序进行部分修改扩充。达到进一步了解程序编译过程的基本原理和基本实现方法的目的。1.要求:课内实验(考试前交报告)对PL/0作以下修改扩充:(1)增加单词:保留字:FOR,TO,DOWNTO,+=,-=,++,--;RETURN要求:词法识别即可(2)替换单词:不等号#改为,(3)增加条件语句的ELSE子句,要求:写出相关文法,语法图,语义规则。二、实验环境与工具(1)源:C语言(2)程序目标语言:PL/0(3)实现工具(平台):vc++6.0(4)运行平台:PC机,Windows7三、设计方案1、结构设计说明(1)PL/0语言编译器PL/0语言可看成是PASCAL语言的子集,它的编译程序是一个编译解释执行系统。PL/0的目标程序为假想栈式计算机的汇编语言,与具体计算机无关。2PL/0源程序目标代码生成程序目标代码生成程序目标代码生成程序目标代码生成程序目标代码生成程序出错处理函数表格管理函数(2)PL/0编译程序的语法分析过程BLOCK是整个编译过程的核心。这里根据编译程序的总体流程图,来弄清BLOCK过程在整个编译程序中的作用。总流程图如下图所示:3启动置初值调用GETSYM取单词调用block过程当前单词是否为源程序结束符“。”?源程序中是否有错误?调用解释过程interpret解释执行目标程序结束出错打印错误YNYNPL/0的编译程序采用一趟扫描方式,以语法分析程序为核心,词法分析程序和代码生成程序都作为一个独立的过程,当语法分析需要读单词时就用词法分析程序,而当语法分析正确需生成相应的目标代码时,则调用代码生成程序。此外,用表格管理程序建立变量,常量和过程标识符的说明与引用之间的信息联系。用出错处理程序对词法和语法分析遇到的错误给出在源程序中出错的位置和错误性质。2.各功能模块描述过程或函数名简要功能说明pl0主程序4error出错处理,打印出错位置和错误编码getsym词法分析,读取一个单词getch漏掉空格,读取一个字符gen生成目标代码,并送入目标程序区test测试当前单词符号是否合法block分程序分析处理过程enter登录名字表position(函数)查找标识符在名字表中的位置constdeclaration常量定义处理vardeclaration变量说明处理listode列出目标代码清单statement语句处理expression表达式处理term项处理factor因子处理condition条件处理interpret对目标代码的解释执行程序base(函数)通过静态链求出数据区的基地址四、主要成分描述○1符号表为了组成一条指令,编译程序必须知道其操作码及其参数(数或地址)。这些值是由编译程序本身联系到相应标识符上去的。这种联系是在处理常数、变量和过程说明完成的。为此,标识符表应包含每一标识符所联系的属性;如果标识符被说明为常数,其属性值为常数值;如果标识符被说明成变量,其属性就是由层次和修正量(偏移量)组成的地址;如果标识符被说明为过程,其属性就是过程的入口地址及层次。常数的值由程序正文提供,编译的任务就是确定存放该值的地址。我们选择顺序分配变量和代码的方法;每遇到一个变量说明,就将数据单元的下标加一(PL/0机中,每个变量占一个存贮单元)。开始编译一个过程时,要对数据单元的下标dx赋初值,表示新开辟一个数据区。dx的初值为3,因为每个数据区包含三个内部变量RA,DL和SL。○2运行时存储组织和管理对于源程序的每一个过程(包括主程序),在被调用时,首先在数据段中开辟三个空间,存放静态链SL、动态链DL和返回地址RA。静态链记录了定义该过程的直接外过程(或主程序)运行时最新数据段的基地址。动态链记录调用该过程前正在运行的过程的数据段基址。返回地址记录了调用该过程时程序运行的断点位置。对于主程序来说,SL、DL和RA的值均置为0。静态链的功能是在一个子过程要引用它的直接或间接父过程(这里的父过程是按定义过程时的嵌套情况来定的,而不是按执行时的调用顺序定的)的变量时,可以通过静态链,跳过个数为层差的数据段,找到包含要引用的变量所在的数据段基址,然后通过偏移地址访问它。5在过程返回时,解释程序通过返回地址恢复指令指针的值到调用前的地址,通过当前段基址恢复数据段分配指针,通过动态链恢复局部段基址指针。实现子过程的返回。对于主程序来说,解释程序会遇到返回地址为0的情况,这时就认为程序运行结束。解释程序过程中的base函数的功能,就是用于沿着静态链,向前查找相差指定层数的局部数据段基址。这在使用sto、lod、stoArr、lodArr等访问局部变量的指令中会经常用到。类PCODE代码解释执行的部分通过循环和简单的case判断不同的指令,做出相应的动作。当遇到主程序中的返回指令时,指令指针会指到0位置,把这样一个条件作为终至循环的条件,保证程序运行可以正常的结束。○3语法分析方法语法分析子程序采用了自顶向下的递归子程序法,语法分析同时也根据程序的语义生成相应三元代码,并提供了出错处理的机制。语法分析主要由分程序分析过程(BLOCK)、参数变量分析过程(ParaDeclaration)、参数变量处理过程(ParaGetSub)、数组处理过程(ParaGetSub)、常量定义分析过程(ConstDeclaration)、变量定义分析过程(Vardeclaration)、语句分析过程(Statement)、表达式处理过程(Expression)、项处理过程(Term)、因子处理过程(Factor)和条件处理过程(Condition)构成。这些过程在结构上构成一个嵌套的层次结构。除此之外,还有出错报告过程(Error)、代码生成过程(Gen)、测试单词合法性及出错恢复过程(Test)、登录名字表过程(Enter)、查询名字表函数(Position)以及列出类PCODE代码过程(Listcode)作过语法分析的辅助过程。○4中间代码表示中间代码是是源程序的一种内部表示,复杂性介于源语言和目标机语言之间。中间代码的表示方法有逆波兰式、三元式、树形、四元式等。(1)逆波兰记号是最简单的一种中间代码表示形式,早在编译程序出现之前,它就用于表示算术表达式。后缀表示法表示表达式,其最大的优点是易于栈式计算机处理表达式。(2)每个三元式由三个部分组成:A.算符opB.第一运算对象ARG1C.第二运算对象ARG2运算对象可能是源程序中的变量,也可能是某个三元式的结果,用三元式的编号表示。(3)树形表示是三元式表示的翻版。(4)四元式是一种比较普遍采用的中间代码形式:算符op,运算对象ARG1,运算对象ARG2,运算结果RESULT五、开发过程和完成情况1.增加单词:保留字:FOR,TO,DOWNTO,RETURN运算符:+=,-=,++,--;新增4个保留字和4个运算符,合计8个单词。6其中保留字FOR,TO,DOWNTO,RETURN分别对应downtosym,forsym,returnsym,tosym;运算符+=,-=,++,--分别对应pluseql,minuseql,plusplus,minusminus1.1保留字enumsymbol{nul,ident,number,plus,minus,times,slash,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,downtosym,forsym,returnsym,tosym,elsesym,pluseql,plusplus,minuseql,minusminus//增加的语句};/*设置保留字名字,按照字母顺序,便于折半查找*/strcpy(&(word[0][0]),begin);strcpy(&(word[1][0]),call);strcpy(&(word[2][0]),const);strcpy(&(word[3][0]),do);strcpy(&(word[4][0]),downto);/*增加保留字DOWNTO*/strcpy(&(word[5][0]),else);/*增加保留字ELSE*/strcpy(&(word[6][0]),end);strcpy(&(word[7][0]),for);/**增加保留字FOR*/strcpy(&(word[8][0]),if);strcpy(&(word[9][0]),odd);strcpy(&(word[10][0]),procedure);strcpy(&(word[11][0]),read);strcpy(&(word[12][0]),return);/*增加保留字RETURN*/strcpy(&(word[13][0]),then);strcpy(&(word[14][0]),to);/*增加保留字TO*/strcpy(&(word[15][0]),var);strcpy(&(word[16][0]),while);strcpy(&(word[17][0]),write);/*设置保留字符号*/wsym[0]=beginsym;wsym[1]=callsym;wsym[2]=constsym;wsym[3]=dosym;wsym[4]=downtosym;/*增加保留字符号downtosym*/wsym[5]=elsesym;/*增加保留字符号elsesym*/wsym[6]=endsym;wsym[7]=forsym;/*增加保留字符号forsym*/wsym[8]=ifsym;wsym[9]=oddsym;7wsym[10]=procsym;wsym[11]=readsym;wsym[12]=returnsym;/*增加保留字符号returnsym*/wsym[13]=thensym;wsym[14]=tosym;/*增加保留字符号tosym*/wsym[15]=varsym;wsym[16]=whilesym;wsym[17]=writesym;1.2保留字和单词的个数1),原保留字个数是14,#definenorw14。因为新增加保留字4个,所以#definenorw182),原单词总数是33,#definesymnum33。因为新增加单词9个,所以#definesymnum421.3运算符在GetSym()函数中增加:elseif(ch=='+')//增加检测++和+=符号{getchdo;if(ch=='+'){sym=plusplus;//构成++号getchdo;}elseif(ch=='='){sym=pluseql;//构成+=号getchdo;}else{sym=plus;}}elseif(ch=='-')//增加检测--和-=符号{getchdo;if(ch=='-'){sym=minusminus;//构成--号getchdo;}elseif(ch=='='){sym=minuseql;//构成-=号8getchdo;}else{sym=minus;}}2.修改单词:不等号#改为(1)//ssym['#']=neq;把这段删除(2)在GetSym()过程中把分析到的定义为不等号#,从“”切入修改。eif(ch=='')/*检测小于或小于等于符号*/{getchdo;if(ch=='='){sym=leq;getchdo;}