编译原理修改后地PL0报告材料

上传人:痛*** 文档编号:82868703 上传时间:2022-04-30 格式:DOC 页数:19 大小:199KB
返回 下载 相关 举报
编译原理修改后地PL0报告材料_第1页
第1页 / 共19页
编译原理修改后地PL0报告材料_第2页
第2页 / 共19页
编译原理修改后地PL0报告材料_第3页
第3页 / 共19页
点击查看更多>>
资源描述
word一、上机实践要求“编译原理与技术的上机实验要求你对PL/0语言与其编译器进展扩大和修改。每个扩大或修改方式可得到不同的分数,总分为为100分。完成上机作业后,必须提交如下文档:(1) 修改后的PL/0语言文本。包含词法分析正规式,语法分析BNF。(2) 有关修改后的PL/0编译/解释器的说明。详细说明你的编译器是如何编译新的PL/0语言程序的。指出你的程序中最精彩的局部,以与你为什么这样做,你是如何控制和恢复语义错误的。(3) 给出你所改动后的编译器源程序清单,并标记出你所修改的局部。比拟你的编译器和原来的编译器之间的差异。(4) 说明你的编译器中可能存在的错误。(5) 总结经验与教训,如果重做一遍,你会有哪些新的改良?对现存的PL/0编译程序可做如下修改或扩大,其中1、2、11和12必须完成,剩余的均可任意选择,但总分必须超过70分。(1) 注释5分注释由(*和*)包含,不允许嵌套。(2) 布尔类型的数据10分布尔类型的BNF为:var_option | var var_decl_listvar_decl_list var_decl | var_decl_list var_declvar_decl ident_list : data_typedata_type integer | boolean这种修改包括:(i) 区别整型与布尔型变量、常量和表达式。(ii) 增加按严格计算的布尔类型运算符and、or和not。这些算符以与己有的运算符的优先级与Pascal语言一样。(iii) 能够使用布尔常量true和false。(iv) 把PL/0语言中的“条件概念一般化为Pascal语言那样。(v) 布尔表达式可以比拟大小:false =表达式1.1(e)分程序=identnumberconst,;varident,;identprocedure;程序体语句1.1(b)语句:=表达式ident语句序列条件if语句thendowhile条件语句identcallendbegin1.1(d)表达式+项-+-项1.1(f)项/因子因子*1.1(g)因子identnumber)(表达式1.1(h)如:*表示*重复任意次,*38表示*重复3-8次。 :方括号表示其内的成分为任选项。( ) :表示圆括号内的成分优先。例:用EBNF描述文法的定义 :=+|-=0|1|2|3|4|5|6|7|8|9或更好的写法=+|-|0=1|2|3|4|5|6|7|8|9 =0| PL/0语言文法的EBNF表示为:程序=分程序分程序=常量说明局部变量说明局部过程说明局部语句常量说明局部=CONST常量定义 ,常量定义;常量定义=标识符=无符号整数无符号整数=数字数字变量说明局部=VAR标识符,标识符;标识符=字母字母|数字过程说明局部=过程首部分程序;过程说明局部;过程首部=PROCEDURE标识符;语句=赋值语句|条件语句|当型循环语句|过程调用语句|读语句|写语句|复合语句|空赋值语句=标识符=表达式复合语句=BEGIN语句;语句END条件=表达式关系运算符表达式|ODD表达式表达式=+|-项加法运算符项 项=因子乘法运算符因子因子=标识符|无符号整数|(表达式)加法运算符=+|-乘法运算符=*|/关系运算符=#|=|=|=条件语句=IF条件THEN语句过程调用语句=CALL标识符当型循环语句=WHILE条件DO语句读语句=READ(标识符,标识符)写语句=WRITE(表达式,表达式)字母=a|b|X|Y|Z数字=0|1|2|8|9EBNF表示的符号说明。 :用左右尖括号括起来的中文字表示语法构造成分,或称语法单位,为非终结符。= :该符号的左部由右部定义,可读作定义为。| :表示或,为左部可由多个右部定义。 :花括号表示其内的语法成分可以重复。在不加上下界时可重复0到任意次数,有上下界时为可重复次数的限制。用语法图描述语言的语法与EBNF描述相比拟:语法图描述: 直观,易读,易写。EBNF表示:形式化强,易机器识别。无论用语法图或EBNF作为描述程序设计语言语法的工具,对描述的语法可以检查哪些符号序列是所给语言的合法程序,一个程序设计语言如C或JAVA,用户可用它写出成千上万个不同的程序,但C或JAVA它们各自的语法只有一个,这就使得无穷的句子集可用有穷的文法语法描述。PL/0编译程序的使用限制 标识符的有效长度是10 数字最多为14位 过程最多可嵌套三层,可递归调用 标识符的作用域同PASCAL,内层可引用包围它的外层定义的标识符如:变量名,过程名,常量名目标指令有8条: LIT:将常量值取到运行栈顶。a域为常数值。 LOD:将变量放到栈顶。a域为变量在所说明层中的相对位置,l为调用层与说明层的层差值。 STO:将栈顶的内容送入某变量单元中。a,l域的含意同LOD指令。 CAL:调用过程的指令。a为被调用过程的目标程序入口地址,l为层差。 INT:为被调用的过程(或主程序)在运行栈中开辟数据区。a域为开辟的单元个数。 JMP:无条件转移指令,a为转向地址。 JPC:条件转移指令,当栈顶的布尔值为非真时,转向a域的地址,否如此顺序执行。 OPR:关系运算和算术运算指令。将栈顶和次栈顶的内容进展运算,结果存放在次栈顶,此外还可以是读写等特殊功能的指令,具体操作由a域值给出。详见解释执行程序。类pcode代码指令的详细解释指令功能表 认识目标代码类pcode 目标代码类pcode是一种假想栈式计算机的汇编语言。指令格式:flaf 功能码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次栈顶与栈顶相加,退两个栈元素,结果值进栈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次栈顶是否小于等于栈顶,退两个栈元素,结果值进栈OPR 0 14栈顶值输出至屏幕OPR 0 15 屏幕输出换行OPR 0 16从命令行读入一个输入置于栈顶PL/0语言可看成是PASCAL语言的子集,它的编译程序是一个编译解释执行系统。PL/0的目标程序为假想栈式计算机的汇编语言,与具体计算机无关。PL/0的编译程序和目标程序的解释执行程序都是用PASCAL语言书写的,因此PL/0语言可在配备PASCAL语言的任何机器上实现。其编译过程采用一趟扫描方式,以语法分析程序为核心,词法分析程序和代码生成程序都作为一个独立的过程,当语法分析需要读单词时就调用词法分析程序,而当语法分析正确需生成相应的目标代码时,如此调用代码生成程序。此外,用表格管理程序建立变量、常量和过程标识符的说明与引用之间的信息联系。用出错处理程序对词法和语法分析遇到的错误给出在源程序中出错的位置和错误性质。当源程序编译正确时,PL/0编译程序自动调用解释执行程序,对目标代码进展解释执行,并按用户程序要求输入数据和输出运行结果。本书所提供的PL/0语言编译器的根本工作流程如图1-1所示:语法分析词法分析语义分析代码生成代码执行源程序执行结果符号表管理错误诊断处理图1-1 PL/0编译器根本工作流程由于PL/0编译程序采用一趟扫描方法,所以语法分析过程BLOCK是整个编译过程的核心。下面我们将在图2.4中先给出编译程序的总体流程,以弄清BLOCK过程在整个编译程序中的作用。在流程图2.4中可以看出,主程序置初值后先调用读单词过程GETSYM取一个单词,然后再调用语法分析过程BLOCK,直到遇源程序的完毕符为止。语法分析过程BLOCK是整个编译过程的核心,是指开始由主程序调用GETSYM取一个单词,再调用语法分析过程BLOCK, BLOCK由当前单词根据语法规如此再调用其它过程,如说明处理、代码生成或出错处理等过程进展分析,当分析完一个单词后,BLOCK再调用GETSYM取下一个单词,一直重复到当前单词为完毕符明确源程序已分析完毕。假如未取到完毕符,而源程序已没有输入符号,这时明确源程序有错误,无法再继续分析。PL/0的词法分析程序GETSYM(图2.5)是一个独立的过程,其功能是为语法分析提供单词用的,是语法分析的根底,它把输入的字符串形式的源程序分割成一个个单词符号。为此PL/0编译程序设置了三个全程量的公用单元如下:SYM:存放每个单词的类别,用内部编码形式表示。ID:存放用户所定义的标识符的值。即标识符字符串的机内表示。NUM:存放用户定义的数。单词的种类有五种。根本字:也可称为保存字或关键字,如BEGIN,END,IF,THEN等。运算符:如:+、-、*、=、#、=、=等。标识符:用户定义的变量名、常数名、过程名。常数:如:10,25,100等整数。界符:如:,、.、;、(、)等。如果我们把根本字、运算符、界符称为语言固有的单词,而对标识符、常数称为用户定义的单词。那么经词法分析程序分出的单词,对语言固有的单词只给出类别存放在SYM中,而对用户定义的单词(标识符或常数)既给类别又给值,其类别放在SYM中,值放在ID或NUM中,全部单词种类由编译程序定义的纯量类型SYMBOL给出,也可称为语法的词汇表。如下面提到的IFSYM,THENSYM,IDENT,NUMBER均属SYMBOL中的元素。当识别到字母开头的字母数字串时,先查关键字表。假如查不到如此为标识符,查到如此为关键字。PL/0编译程序文本中主程序开始对关键字表置初值如下:关键字表为:word1:=begin ;word2:=call ;.word13:=write ;每个数组元素的字符长度为10,不满10个字符时,以空格补满。查到时找到关键字相应的内部表示为:Wsym1:=beginsym; wsym2:=callsym;wsym13:=writesym;PL/0编译程序文本中开始对类型的定义中给出单词定义为:type symbol=(nul,ident,number,plus,varsym,procsym);定义单词是纯量/枚举类型,又定义了3个全程量为: sym:symbol;id:alfa;num:integer;因此词法分析程序GETSYM将完成如下任务:(1) 滤空格:空格在词法分析时是一种不可缺少的界符,而在语法分析时如此是无用的,所以必须滤掉。(2) 识别保存字:设有一X保存字表。对每个字母打头的字母、数字字符串要查此表。假如查着如此为保存字,将对应的类别放在SYM中。如IF对应值IFSYM,THEN对应值为THENSYM。假如查不着,如此认为是用户定义的标识符。(3) 识别标识符:对用户定义的标识符将IDENT放在SYM中,标识符本身的值放在ID中。(4) 拼数:当所取单词是数字时,将数的类别NUMBER放在SYM中,数值本身的值存放在NUM中。(5) 拼复合词:对两个字符组成的算符 如:=、=、= 等单词,识别后将类别送SYM中。(6) 输出源程序:为边读入字符边输出(可输出在文件中)。GETCH 所用单元说明:CH: 存放当前读取的字符,初值为空。LINE: 为一维数组,其数组元素是字符,界对为180。用于读入一行字符的缓冲区。LL和CC为计数器,初值为0。GETSYM流程图的工作单元说明:A:一维数组,数组元素为字符,界对110。ID:同A。WORD:保存字表,一维数组,数组元素是以字符为元素的一维数组。界对为113。查表方式采用二分法。单个字符对应的单词表的建立是,首先在主程序中定义了下标为字符的数组ssym,数组ssym的元素为单词,主程序开始对下标为字符的所有数组元素置初值为nul,对PL/0语言用到的单个字符为单词的,将其字符作为数组下标的元素置初值为对应的单词。如:ssym+:=plus; ssym-:=minus;ssym;:=semicolon;PL/0编译程序的语法分析PL/0编译程序语法、语义分析是整个编译程序设计与实现的核心局部,要求学员努力学习掌握实现技术和方法。现分别说明语法分析实现的主要思想方法和语义分析的实现。 语法分析的任务是识别由词法分析给出的单词符号序列在结构上是否符合给定的文法规如此。PL/0语言的文法规如此已在2.1节中给出。本节将以语法图描述的语法形式为依据,给出语法分析过程的直观思想。PL/0编译程序的语法分析采用了自顶向下的递归子程序法。什么是自顶向下的语法分析?可形象地对该程序自顶向下构造一棵倒挂着的语法分析树,其构造方法是:1 由开始符号非终结符程序作为分析树的根结点,由非终结符程序规如此的右部为子结点。2 对分析树中的每个非终结符结点,选择它规如此的一个右部为子结点构造分析树的下一层。3 重复2直到分析树中的末端结点只有终结符。4 假如分析树中的末端结点从左到右连接的终结符号串刚好是输入的程序终结符号串,如此说明所给程序在语法上是正确的。粗略地说:自顶向下的递归子程序法就是对应每个非终结符语法单元,编一个独立的处理过程(或子程序)。语法分析从读入第一个单词开始由非终结符程序即开始符号出发,沿语法描述图箭头所指出的方向进展分析。当遇到非终结符时,如此调用相应的处理过程,从语法描述图看也就进入了一个语法单元,再沿当前所进入的语法描述图的箭头方向进展分析,当遇到描述图中是终结符时,如此判断当前读入的单词是否与图中的终结符相匹配,假如匹配,如此执行相应的语义程序(就是翻译程序)。再读取下一个单词继续分析。遇到分支点时将当前的单词与分支点上的多个终结符逐个相比拟,假如都不匹配时,可能是进入下一非终结符语法单位或是出错。如果一个PL/0语言的单词序列在整个语法分析中,都能逐个得到匹配,直到程序完毕符.,这时就说所输入的程序是正确的。对于正确的语法分析做相应的语义翻译,最终得出目标程序。以上所说语法分析过程非常直观粗浅,实际上应用递归子程序法构造语法分析程序时,对文法有一定的要求和限制,这个问题我们将在第5章详细讨论。此外,从PL/0的语法描述图中可以清楚地看到,当对PL/0语言进展语法分析时,各个非终结符语法单元所对应的分析过程之间必须存在相互调用的关系。这种调用关系可用图1-2表示。也可称为PL/0语法的依赖图,在图中箭头所指向的程序单元表示存在调用关系,从图中不难看出这些子程序在语法分析时被直接或间接递归调用。 由图1-2PL/0语法调用关系图可以看出对分程序和语句为直接递归调用,对表达式为间接递归调用。程序程序体语句条件表达式项因子图1-2 语法分析过程依赖关系表达式的EBNF表达式=+|-项+|-项 项=因子*|/因子 因子=标识符|无符号整数|表达式表达式的递归子程序实现procedure expr; begin if sym in plus, minus then begingetsym; term; end else term; while sym in plus, minus do begin getsym; term; end end; 项的递归子程序实现procedure term; beginfactor; while sym in times, slash do begingetsym; factor; end end; 因子的递归子程序实现procedure factor;begin if sym ident thenbegin if sym number thenbeginif sym = ( then begingetsym;expr;if sym = ) then getsym else error end else errorendendend; 语法分析程序除总控外主要有两大局部的功能,即对说明局部的处理和对程序体局部的处理,也就是在语法单元中的分程序功能。PL/0编译程序语法、语义分析的的核心程序是BLOCK过程,在BLOCK过程内又定义了许多嵌套与并列的过程。在过程BLOCK内对说明局部与程序体局部的分析说明如下:(1) 说明局部的分析由于PL/0语言允许过程调用语句,且允许过程嵌套定义。因此每个过程应有过程首部以定义局部于它自己过程的常量、变量和过程标识符,也称局部量。每个过程所定义的局部量只能供它自己和它自己定义的内过程引用。对于同一层并列过程的调用关系是先定义的可以被后定义的引用,反之如此不行。说明局部的处理任务就是对每个过程的说明对象造名字表,填写所在层次、标识符的属性和分配的相对位置等。标识符的属性不同时,所需要填写的信息也有所不同。登录信息是调用ENTER过程完成的。说明局部的处理对主程序看成是第0层过程,主程序定义的过程为第1层,随着嵌套的深度增加而层次数加大。PL/0允许最大层次为3。所造名字表放在全程量一维数组TABLE表中。TX为索引表的指针,表中的每个元素为记录型数据。LEV给出层次,DX给出每层局部量当前已分配到的相对位置,可称地址指示器,每说明完一个变量后DX指示器加1。PL/0编译程序文本中对名字表定义有:说明类型的定义:type object= (constant, variable, procedur)定义为纯量/枚举类型名字表的定义:table:array0.txmax of recordname:alfa;case kind:object ofconstant:(val:integer);variable:procedur:(level,adr,size:integer);end;例如:一个过程的说明局部为:CONST A=35,B=49;VAR C,D,E;PROCEDUREP;VAR G, .对常量,变量和过程说明分析后,在TABLE表中的信息如表2.2所示。在说明处理后TABLE表中的信息对于过程名的ADR域,是在过程体的目标代码生成后再反填过程体的入口地址。例中在处理P过程的说明时对LEV就增加1。在P过程中的变量名的层次为LEV+1后的值。对过程还有一项数据SIZE,是记录该过程体运行时所需的数据空间。至于在造表和查表的过程中,如何保证每个过程的局部量不被它的外层引用,请读者阅读完PL/0编译程序后自己总结。(2) 过程体的分析程序的主体是由语句构成的。处理完过程的说明后就处理由语句组成的过程体,从语法上要对语句逐句分析。当语法正确时就生成相应语句功能的目标代码。当遇到标识符的引用时就调用POSITION函数查TABLE表,看是否有过正确的定义,假如已有,如此从表中取相应的有关信息,供代码的生成用。假如无定义如此出错。PL/0编译程序的目标代码结构和代码生成 编译程序的目标代码是在分析程序体时生成的,在处理说明局部时并不生成目标代码,而当分析程序体中的每个语句时,当语法正确如此调用目标代码生成过程以生成与PL/0语句等价功能的目标代码,直到编译正常完毕。PL/0语言的代码生成是由过程GEN完成的。GEN过程有三个参数,分别代表目标代码的功能码、层差和位移量(对不同的指令含意不同)。生成的代码顺序放在数组CODE中。CODE为一维数组,数组元素为记录型数据。每一个记录就是一条目标指令。CX为指令的指针,由0开始顺序增加。实际上目标代码的顺序是内层过程的排在前边,主程序的目标代码在最后。下面我们给出一个PL/0源程序和对应的目标程序的清单。PL/0编译程序的目标代码生成是由GEN过程完成的 ,当语法分析正确如此调用目标代码生成过程以生成与PL/0语句等价功能的目标代码,直到编译正常完毕。 除了过程说明局部外,变量和常量的说明都不产生目标代码。在block入口处生成一条(jmp,0,0)指令,作为过程体入口指令,该指令的第3区域的0需分析到过程体入口时才返填。目标代码生成时所用到的变量地址和层差等信息是由名字表table提供的,而名字表的信息是在说明时填写的。在代码生成时查名字表,这就是表格管理的作用。这些信息之间的连接关系学员必须弄清。下面对一些重要程序段给予扼要的解释。gen过程的实现很简单不再解释对分程序体人口的处理见程序文本block 的过程体begin (*block*)dx:=3;tx0:=tx; *保存当前table表指针值,实际为过程名在table表中的位置*tabletx.adr:=cx;*保存当前code指针值到过程名的adr域*gen(jmp,0,0);*生成转向过程体入口的指令,该指令的地址为cx已保存在过程名的adr域,真正的过程体入口地址,等生成过程体入口的指令时,再由tabletx.adr中取出 cx将过程体入口返填到cx所指目标代码,即:jmp,0,0的第3区域,同时填到tabletx.adr 中*)程体入口时的处理codetabletx0.adr.a:=cx;cx为过程入口地址,填写在code中with tabletx0 do begin adr:=cx; 过程的入口填写在table表的过程名中size:=dx; 过程需要的空间填写在table中end;cxo:=cx; 保存过程在code中的入口地址在输出目标代码时用 gen(int,0,dx);生成过程入口指令请特别注意dx、 tx、 cx的作用和如何处理信息之间的连接关系。PL/0编译程序的语法错误处理 编写一个程序,往往难于一次成功,常常会出现各种类型的错误。一般有语法错、语义错与运行错。出错的原因是多方面的,这就给错误处理带来不少困难。就语法错来说,任何一个编译程序在进展语法分析遇到错误时,总不会就此停止工作,而是希望能准确地指出出错位置和错误性质并尽可能进展校正,以便使编译程序能继续工作。但对所有的错误都做到这样的要求是很困难的,主要困难在校正上,因为编译程序不能完全确定程序人员的意图。例如在一个表达式中,圆括号不配对时,就不能确定应补在何处。有时由于校正得不对反而会影响到后边,导致出现误判错误的情况。因此编译程序只能采取一些措施,对源程序中的错误尽量查出,加以修改,以便提高调试速度。PL/0编译程序对语法错误的处理采用两种方法:(1) 对于一些易于校正的错误,如丢了逗号、分号等,如此指出出错位置,并加以校正。校正的方式就是补上逗号或分号。(2) 对某些错误编译程序难于确定校正的措施,为了使当前的错误不致影响整个程序的崩溃,把错误尽量局限在一个局部的语法单位中。这样就需跳过一些后面输入的单词符号,直到读入一个能使编译程序恢复正常语法分析工作的单词为止。具体做法是:当语法分析进入以某些关键字(保存字)或终结符集合为开始符的语法单元时,通常在它的入口和出口处,调用一个测试程序TEST(见图2.9)。例如:语句的开始符是begin,if,while,call,read,write;说明的开始符是var,const,procedure;因子的开始符是(,ident,number。当语法分析进入这样的语法单元前,可用测试程序检查当前单词符号是否属于它们开始符号的集合,假如不是如此出错。请读者对照图 2.1各语法描述图直观地找出每个非终结符语法单元的开始符号集合,与表2.3进展比拟,验证对开始符号集合理解的正确性。对于一个文法符号的开始符号集合的形式定义将在第5章详细介绍。现给出PL/0文法局部非终结符语法单元的开始符号和后继符号的集合。另外由于PL/0编译程序采用自顶向下的分析方法,一个语法单元分析程序调用别的语法单元的分析程序时,以参数形式(文本中以FSYS定义为单词符号集合作为形参)给出被调用的语法分析程序出口时合法的后继单词符号集合(如表2.3所给出),在出口处也调用测试程序。假如当前单词符号是属于所给集合,如此语法分析正常进展,否如此出错。单词符号集合FSYS参数是可传递的,随着调用语法分析程序层次的深入,FSYS的集合逐步补充合法单词符。非终结符SFIRST(S)FOLLOW(S)程序体const var procedure ident call if begin while. ;语句ident call begin if while. ; end条件odd + - ( ident numberthen do表达式+ - ( ident number. ; ) R end then do项ident number (. ; ) R + - end then do因子ident number (. ; ) R + - * / end then do*注: 表2.3中 R 表示关系运算符集合,如=,=,=。在进入某个语法单位时,调用TEST,检查当前符号是否属于该语法单位的开始符号集合。假如不属于,如此滤去开始符号和后继符号集合外的所有符号。在语法单位分析完毕时,调用TEST,检查当前符号是否属于调用该语法单位时应有的后继符号集合。假如不属于,如此滤去后继符号和开始符号集合外的所有符号。TEST测试过程有三个参数,它们的含意是: S1:当语法分析进入或退出某一语法单元时当前单词符号应属于的集合,它可能是一个语法单元开始符号的集合或后继符号的集合。 S2:在某一出错状态时,可恢复语法分析继续正常工作的补充单词符号集合。因为当语法分析出错时,即当前单词符号不在S1集合中,为了继续编译,需跳过后边输入的一些单词符号,直到当前输入的单词符号是属于S1或S2集合的元素。 n:整型数,出错信息编号。 为了进一步明确S1,S2集合的含意,现以因子(FACTOR)的语法分析程序为例,在过程FACTOR的入口处调用了一次TEST过程,它的实参S1是因子开始符号的集合(文本中的FACBEGSYS)。S2是每个过程的形参FSYS调用时实参的传递值。当编译程序第一次调用BLOCK时,FSYS实参为与说明开始符和语句开始符集合的和。以后随着调用语法分析程序层次的深入逐步增加。如调用语句时增加了;和endsym,在表达式语法分析中调用项时又增加了+和-,而在项中调用因子时又增加了*和/,这样在进入因子分析程序时即使当前符号不是因子开始符,出错后只要跳过一定的符号,遇到当时输入的单词符号在FSYS中或在因子开始符号集合中,均可继续正常进展语法分析。而在因子过程的出口处也调用了测试程序,不过这时S1和S2实参恰恰相反。说明当时的FSYS集合的单词符号都是因子正常出口时允许的单词符号,而因子的开始符号为可恢复正常语法分析的补充单词符号。从PL/0编译程序文本中因子过程的处理片段说明上述问题。1PL/0编译程序文本中给出关于某些语法单位开始符号集合的定义为:symset=set of symbol; 见PL/0文本类型说明局部declbegsys, statbegsys, facbegsys:symset;declbegsys:=constsym,varsym,procsym;见PL/0文本主程序置初值局部statbegsys:=beginsym,callsym,ifsym,whilesym,readsym,writesym;facbegsys:=ident,number,lparen;2后继符号集合fsys作为参数传递见PL/0文本相应过程的说明局部procedure test(s1,s2:symset; n:integer);procedure block(lev,tx:integer; fsys:symset); procedure statement(fsys:symset);procedure expression (fsys:symset);procedure term (fsys:symset);procedure factor (fsys:symset);3因子过程的处理片段test过程有三个参数:可允许的下一个符号集合S1,如果当前符号不在此集合中,当即得到一个错误号;另加的停止符号集合S2,有些符号的出现,虽然无疑是错的,但它们绝对不应被忽略而跳过;整数n,表示有关错误的诊断号:void test(symset s1, symset s2, int n)symset s;if (! inset(sym, s1)error(n);s = uniteset(s1, s2);while(! inset(sym, s)getsym();destroyset(s);4由于后继符号集合fsys作为参数传递,随着调用语法分析程序层次的深入后继符号集合逐步增加,但对调用同一个过程所需增加的后跟符与调用位置有关。例如: 在write语句和factor中调用expression(fsys);所增加的后继符号不完全一样。 write语句的语法: = write(,); 处理在 内调用expression时在fsys中应增加 rparen,ma。expression(rparen,ma+fsys); factor的语法:=.|( exp )在处理 内调用expression时在fsys中应增加rparen。expression(rparen+fsys);然而PL/0编译程序对测试程序TEST的调用有一定的灵活性。对语义错误,如标识符未加说明就引用,或虽经说明,但引用与说明的属性不一致。这时只给出错误信息和出错位置,编译工作可继续进展。而对运行错,如溢出,越界等,只能在运行时发现,由于PL/0编译程序的功能限制无法指出运行错在源程序中的错误位置。出错编号出错原因1:常数说明中的=写成=。2:常数说明中的=后应是数字。3:常数说明中的标识符后应是=。4:const ,var, procedure后应为标识符。5:漏掉了,或;。6:过程说明后的符号不正确(应是语句开始符,或过程定义符)。7:应是语句开始符。8:程序体内语句局部的后跟符不正确。9:程序结尾丢了句号.。10:语句之间漏了;。11:标识符未说明。12:赋值语句中,赋值号左部标识符属性应是变量。13:赋值语句左部标识符后应是赋值号=。14:call后应为标识符。15:call后标识符属性应为过程。16:条件语句中丢了then。17:丢了end或;。18:while型循环语句中丢了do。19:语句后的符号不正确。20:应为关系运算符。21:表达式内标识符属性不能是过程。22:表达式中漏掉右括号)。23:因子后的非法符号。24:表达式的开始符不能是此符号。31:数越界。32:read语句括号中的标识符不是变量。PL/0编译程序的目标代码解释执行时的存储分配当源程序经过语法分析,如果未发现错误时,由编译程序调用解释程序,对存放在CODE中的目标代码CODE0开始进展解释执行。当编译完毕后,记录源程序中标识符的TABLE表已没有作用。因为计算每个变量在运行栈中相对本过程基地址的偏移量dx 的值,放在table表中的adr域,生成目标代码时再从adr域中取出基地址的偏移量 ,放在code中的a域。因此数据空间只需以数组CODE存放的只读目标程序和运行时的数据栈S。S是由解释程序定义的一维整型数组。由于PL/0语言的目标程序是一种假想的栈式计算机的汇编语言,仍用PASCAL语言解释执行。解释执行时的数据空间S为栈式计算机的存储空间。遵循后进先出规如此,对每个过程(包括主程序)当被调用时,才分配数据空间,退出过程时,如此所分配的数据空间被释放。解释程序还定义了4个存放器。(1) I:指令存放器。存放着当前正在解释的一条目标指令。(2) P:程序地址存放器。指向下一条要执行的目标程序的地址(相当目标程序CODE数组的下标)。(3) T:栈顶存放器。由于每个过程当它被运行时,给它分配的数据空间(下边称数据段)可分成两局部。静态局部:包括变量存放区和三个联系单元(联系单元的作用见后)。动态局部:作为临时工作单元和累加器用。需要时随时分配,用完后立即释放。栈顶存放器T指出了当前栈中最新分配的单元(T也是数组S的下标)。(4) B:基址存放器。指向每个过程被调用时,在数据区S中给它分配的数据段起始地址,也称基地址。 为了实现对每个过程调用时给它分配数据段,也就是对即将运行的过程所需数据段进栈;过程运行完毕后释放数据段,即该数据段退栈;以与嵌套过程之间对标识符引用的寻址问题。每个过程被调用时,在栈顶分配三个联系单元,这三个单元存放的内容分别为:(1) SL:静态链:它是指向定义该过程的直接外过程(或主程序)运行时最新数据段的基地址。(2) DL:动态链:它是指向调用该过程时正在运行过程的数据段基地址。(3) RA:返回地址:记录调用该过程时目标程序的断点,即当时的程序地址存放器P的值。也就是调用过程指令的下一条指令的地址。具体的过程调用和完毕,对上述存放器与三个联系单元的填写和恢复由如下目标指令完成。(1) INT 0 A每个过程目标程序的入口都有这样一条指令,用以完成开辟数据段的工作。A域的值指出数据段的大小,即为局部变量个数+3(联系单元个数为3)。由编译程序的代码生成给出。开辟数据段的结果是改变栈顶存放器T的值,即T=T+A;。(2) OPR 0 0是每个过程出口处的一条目标指令。用以完成该过程运行完毕后释放数据段的工作,即退栈工作。恢复调用该过程前正在运行的过程(或主程序)的数据段基地址存放器的值,和栈顶存放器T的值,并将返回地址送到指令地址存放器P中,以使调用前的程序从断点开始继续执行。(3) CAL L A;为调用过程的目标指令,其中L: 为层次差,它是寻找静态链的依据。在解释程序中由BASE(L)函数来计算,L为参数,实参为所给层差。A: 为被调用过程的目标程序入口。CAL指令还完成填写静态链、动态链、返回地址,给出被调用过程的基地址值,送入基址存放器B中,目标程序的入口地址A的值送指令地址存放器P中,使指令从A开始执行。由于PL/0编译程序是用PASCAL语言编写的(假如文件名为PL0.PAS),所以要对PL/0语言的源程序进展编译,如在PC机上,首先必须对PL0.PAS进展编译、汇编、连接得到PL0.EXE文件。运行PL0.EXE文件才是启动PL/0的编译程序。因此执行命令。RUN PL0启动PL/0编译程序,输出一些询问信息,需用户回答。输出信息 回答信息INPUT FILE?PL/0源程序文件名LIST OBJECT CODE?Y或N目标程序输出的次序是,最内层的过程体在最前边,主程序体在最后。源程序清单中的序号,是该语句在目标程序中对应的起始位置。但需指出例题中序号为0,1指令的内容为:0 jmp 0 8 8为主程序入口1 jmp 0 2 2为过程P的入口三、实验结果:测试文件TEXT.TXT:const m=7, n = 85;var x, y, z;procedure multiply;var a, b;begina := x ; b := y ; z := 0;while b 0 dobeginif odd b then z := z + a;a := 2 * a; b := b / 2;endend;beginx := m; y := n; call multiply;end.乘法过程经过编译程序产生以下代码: 2INT05-allocate storagea := x 3LOD13-x 4STO03-ab := y 5LOD14-y 6STO04-bz := 0 7LIT00-0 8STO15-zb 0 9LOD04-b10LIT00-011OPR012-12JPC029-if b 0 dobeginif odd b then z := z + a;a := 2 * a; b := b / 2;endend;beginx := n; y := m; call plusplus;end.测试文件EXAMPLE.TXT:const m = 7, n = 85;var x, y, z, q, r;procedure multiply;var a, b;procedure plus;var ss;procedure plusplus;var s;begins := xend;beginss :=xend;begina := x; b := y; z := 0;while b 0 dobeginif odd b then z := z + a;a := 2 * a; b := b / 2;endend;procedure divide;var w;beginr := x; q := 0; w := y;while w y dobeginq := 2 * q; w := w / 2;if w = r thenbeginr := r - w;q := q + 1;end;endend;procedure gcd;var f, g;beginf := x;g := y;while f g dobeginif f g then g := g - f;if g f then f := f - g;endend;beginx := m; y := n; call plus;x := n; y := m; call plusplus;x := 25; y := 3; ca
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 管理文书 > 施工组织


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!