![pl0语法分析词法分析语义分析_第1页](http://file4.renrendoc.com/view10/M02/39/33/wKhkGWXtl5OAFg4VAAKhPTlRZxo005.jpg)
![pl0语法分析词法分析语义分析_第2页](http://file4.renrendoc.com/view10/M02/39/33/wKhkGWXtl5OAFg4VAAKhPTlRZxo0052.jpg)
![pl0语法分析词法分析语义分析_第3页](http://file4.renrendoc.com/view10/M02/39/33/wKhkGWXtl5OAFg4VAAKhPTlRZxo0053.jpg)
![pl0语法分析词法分析语义分析_第4页](http://file4.renrendoc.com/view10/M02/39/33/wKhkGWXtl5OAFg4VAAKhPTlRZxo0054.jpg)
![pl0语法分析词法分析语义分析_第5页](http://file4.renrendoc.com/view10/M02/39/33/wKhkGWXtl5OAFg4VAAKhPTlRZxo0055.jpg)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
PL/0语言是Pascal语言的一个子集,我们这里分析的PL/0的编译程序包括了对PL/0PL/0语言编译程序接受以语法分析为核心、一遍扫描的编译方法。词法分析和代码生成作为独立的子程序供语法分析程序调用。语法分析的同时,供应了出错报告和出错恢复的功能。在源程序没有错误编译通过的状况下,调用类PCODE解释程序解释执行生成的类词法分析子程序名为getsym,功能是从源程序中读出一个单词符号〔token〕,把它的信息放入全局变量sym.id和num中,语法分析器需要单词时,直接从这三个变量中(留意!语法分析器每次用完这三个变量的值就马上调用getsym子程序猎取新的单词供下一次使用。而不是在需要新单词时才调用getsymiS程。〕getsym过程通过反复调用getch子过程从源程序过猎取字符,并把它们拼成单词。getch过程中使用了行缓冲区技术以提词法分析器的分析过程:调用getsym时,它通过getch过程从源程序中获得一个字符。假如这个字符是字母,那么连续猎取字符或数字,最终可以拼成一个单词,查保存字表,假如查到为保存字,那么把sym变量赋成相应的保存字类型值;假如没有查到,那么这个单词应是一个用户自定义的标识符(可能是变量名、常量名或是过程的名字),把sym置为ident,把这个单词存入id变量。查保存字表时使用了二分法查找以提高效率。假如getch获得的字符是数字,那么连续用getch猎取数字,并把它们拼成一个整数,然后把sym置为number,并把拼成的数值放入num变量。假如识别出其它合法的符号(比方:赋值号、大于号、小于等于号等),那么把sym那么成相应的类型。假如遇到不合法的字符,把sym置成nul。语法分析子程序接受了自顶向下的递归子程序法,语法分析同时也依据程序的语意生成相应的代码,并供应了出错处理的机制。语法分析主要山分程序分析过程(block)>常量定义分析过程(constdeclaration)>变量定义分析过程(vardeclaration)>语句分析过程(statement)>表达式处理过程(expression)>项处理过程(term)>因子处理过程(factor)和条件处理过程(condition)构成。这些过程在结构上构成一个嵌套的层次结构。除此之外,还有出错报告过程(error)、代码生成过程(gen)>测试单词合法性及出错恢复过程(test)、登录名字表过程本编译程序在运行的时候,通过主程序中调用分程序处理过程block来分析分程序局部(分程序分析过程中还可能会递归调用block过程),然后,推断最终读入的符号是否为句号。假如是句号且分程序分析中未出错,那么是一个合法的PL/0程序,可以运行生成的代码,否那么就说明源PL/0程序是不合法的,输出出错提示即下面按各语法单元分析PL/0编译程序的运行机制。语法分析开头后,首先调用分程序处理过程(block)处理分程序。过程入口参数置为:0层、符号表位置0、出错恢复单词集合为句号、声明符或语句开头符。进入block过程后,首先把局部数据段安排指针设为3,预备安排3个单元供运行期存放静态链SL、动态链DL和返回地址RA。然后用tx0生一条jmp指令,预备跳转到主程序的开头位置,由于当前还没有知到主程序到底在何处开头,所以jmp的LI标临时填为0,稍后再改。同时在符号表的当前位置记录下这个jmp指令在代码段中的位置。在推断了嵌套层数没有超过规定的层数后,开始分析源程序。首先推断是否遇到了常量声明,假如遇到那么开头常量定义,把常量存入符号表。接下去用同样的方法分析变量声明,变量定义过程中会用dx变量记录下局部数据段安排的空间个数。然后假如遇到procedure保存字那么进行过程声明和定义,声明的方法是把过程的名字和所在的层次记入符号表,过程定义的方法就是通过递归调用block过程,由于每个过程都是一个分程序。由于这是分程序中的分程序,因此调用block时需把当前的层次号lev加一传递给block过程。分程序声明局部完成后,即将进入语句的处理,这时的代码安排指针ex的值正好指向语句的开头位置,这个位置正是前面的jmp指令需要跳转到的位置。于是通过前面记录下来的地址值,把这个jmp指令的跳转位置改成当前ex的位置。并在符号表中记录下当前的代码段安排地址和局部数据段要安排的大小〔dx的值〕。生成一条int指令,安排dx个空间,作为这个分程序段的第一条指令。下面就调用语句处理过程statement分析语句。分析完成后,生成操作数为0的opr指令,用于从分程序返回(对于0层的主程序来说,就是程序运行完成,退出)。通过循环,反复获得标识符和对应的值,存入符号表标识符的名字、它所在的层及它在所在层中的偏移地址。语句处理过程是一个嵌套子程序,通过调用表达式处理、项处理、因子处理等过程及递归调用自己来实现对语句的分析。语句处理过程可以识别的语句包括赋值语句、read语句、write语句、call语句、过语句、while语句。当遇到begin/end语首先猎取赋值号左边的标识符,从符号表中找到它的信息,并确认这个标识符确为变量名。然后通过调用表达式处理过程算得赋值号右部的表达式的值并生成相应的指令保证这个值放在运行期的数据栈顶。最终通过前面查到的左部变量的位置信息,生成相应的sto指令,把栈顶值存入指定的变量的空间,实现了赋值操作。确定read语句语法合理的前提下(否那么报错),生成相应的指令:第一条是的opr指令,实现从标准输入设备上读一个整数值,放在数据栈顶。其次条是sto指令,把栈顶的值存入read语句括号中的变量所在的单元。与read语句相像。在语法正确的前提下,生成指令:通过循环调用表达式处理过程分析write语句括号中的每一个表达式,生成相应指令保证把表达式的值算出并放到数据栈顶并生成14号操作的opr指令,输出表达式的值。最终生成15号操从符号表中找到call语句右部的标识符,获得其所在层次和偏移地址。然后生成相应的cal指令。至于调用子过程所需的爱护现场等工作是111类PCODE解释程按if语句的语法,首先调用规律表达式处理过程处理过语句的条件,把相应的真假值放到数据栈顶。接下去记录下代码段安排位置(即下面生成的jpc指令的位置),然后生成条件转移jpc指令〔遇0或遇假转移〕,转移地址未知临时填0o然后调用语句处理过程处理then语句后面的语句或语句块。then后的语句处理完后,当前代码段安排指针的位置就应当是上面的jpc指令的转移位置。通过前面记录下的jpc指令的位置,把它的跳转位置改成当前的代码段指针位置。通过循环遍历begin/end语句块中的每一个语句,通过递归调用语句分析过程首先用cxl变量登记当前代码段安排位置,作为循环的开头位置。然后处理while语句中的条件表达式生成相应代码把结果放在数据栈顶,再用cx2变量登记当前位置,生成条件转移指令,转移位置未知,填0。通过递归调用语句分析过程分析do语句后的语句或语句块并生成相应代码。最终生成一条无条件跳转指令jmp,跳转到cxl所指位置,并把cx2所指的条件跳转指令的跳转位置改成当前代码段安排位置。依据PL/0语法可知,表达式应当是山正负号或无符号开头、由假设干个项以加减号连接而成。而项是山假设干个因子以乘除号连接而成,因子那么可能是一个标识符或一个数字,或是一个以括号括起来的子表达式。依据这样的结构,构造出相应的过程,递归调用就完成了表达式的处理。把项和因子独立开处理解决了加减号与乘除号的优先级问题。在这儿个过程的反复调用中,始终传递fsys变量的值,保证可以在出错的状况下跳过出错的符号,使分析过程得以进行下去。首先推断是否为一元规律表达式:判奇偶。过程分析计算表达式的值,然后生成判奇指令。假如不是,那么确定是二元规律运算处理过程依次分析运算符左右两局部的值,放在栈顶的两个空间中,然后依不同的规律运算符,生成相应的规律推断指令,放入代码段。推断單词合法性与出错恢复过程分析:测试当前符号(即sym變量中的值)是否在si集合中,假如不在,就通过调用出错报告过程输出出错代码n,并放弃当前符号,通过词法分析过程猎取一下單词,直到这个單词消灭在si或s2集合中为止。这个过程在实际使用中很机敏,主要有两个用法:在进入某个语法單位时,调用本过程,检查当前符号是否属于该语法單位的开始符号集合。假设不属于,那么滤去开头符号和后继符号集合外的全部符号。在语法單位分析结束时,调用本过程,检查当前符号是否属于调用该语法單位时应有的后继符号集合。假设不属于,那么滤去后继符号和开头符号集合外的全部通过这样的机制,可以在源程序消灭错误时,准时跳过出错的局部,保证语法分析可以连续下去。语法分析过程中调用的其它子过程相比照拟简洁,请参考源程序的注释。数据段用于存放运行期数据、拥有一个代码段用于存放类PCODE程序代码。同时还拥用数据段安排指针、指令指针、指令存放器、局部段基址指针等存放器。对于源程序的每一个过程(包括主程序),在被调用时,首先在数据段中开辟三个空间,存放静态链SL、动态链DL和返回地址RA。静态链记录了定义该过程的直接外过程(或主程序)运行时最新数据段的基地址。动态链记录调用该过程前正在运行的过程的数据段基址。返回地址记录了调用该过程时程序运行的断点要引用它的直接或间接父过程(这里的父过程是按定义过程时的嵌套悄况来定的,而不是按执行时的调用挨次定的)的变量时,可以通过静态链,跳过个数为层差的数据段,找到包含要引用的变量所在的数据段基址,然后通过偏移地址访问它。在过程返回时,解释程序通过返回地址恢复指令指针的值到调用前的地址,通过当前段基址恢复数据段安排指针,通过动态链恢复局部段基址指针。实现子过程的返回。对于主程序来说,解释程序会遇到返回地址为0的请况,这时就认为程序运行结束。层数的局部数据段基址。这在使用st。、lod等访问局部变量的指令中会经常用相应的动作。当遇到主程序中的返回指令时,指令指针会指到0位置,把这样一个条件作为终至循环的条件,保证程序运行可以正常的结束。programpl0(input,output));(*PL/0编译程序与代码生成解释运行程序*)label99;(*声明出错跳转标记*)norw二11;(*ofreservedwords*)(*保存字的个数*)txmax=100;(*lengthofidentifiertable*)(*标识符表的长度(容ft)*)nmax=14;(水maxnumberofdigitsinnumbers*)(*数字允许的最长位数*)al=10;(*lengthofidentifiers*)(*标识符最长长度*)amax=2047;(*maximumaddress*)(水寻址空间水)levmax=3;(*maxdepthofblocknesting*)(*最大允许的块嵌套层数*)容纳代码行数)*)symbol=(nul,ident,number,plus,minus,times,sllss,leq,gtr,geq,lparen,rparen,comma,semicolon,pbeginsym,endsym,ifsym,thensconstsym,varsym,procsym);(*symobl类型标识了不同类型的词汇*)alfa=packedarray[1..al]ofchar;(*alfa类型用于标识符*)objectl=(constant,variable,procedur);(*objectl为三种标识符的fct=(lit,opr,lod,sto,cal,int,jmp,jpc);(*fct类型分别标识a:0.·amax;(*displacementaddrjmp0,ajumptoajpc0,ajumpconditionaltoaid:alfa;(*lastcc:integer;(*charactercount*)(*行缓冲区指针*)块总在ex所指位置生成新的代码*)line:array[1..81]ofchar;(*行缓冲区,用于从文件读出一行,供词法分析猎取单词时之用*)a:alfa;&词法分析器中用于临时存放正在分析的词*)word:array[1..norw]ofalfa;(*保存字表*)类型*)ssym:array]ofsymbol:(*一些符号对应的symbol类型表*)和项开头符号集合*)table:array[0..txmaxZofrecord(*constant:(*假如是常量名*)variable,procedur:(*假如是变量名或过程名*)(level,adr,size:integer)(*存放层差、偏移地址和大小*)fin,fout:text;(*fin文本文件用于指向输入没有用到*)sfile:string;(*存放PL/0源程序文件的文件名*)procedureerror〔n:integer〕;writein('****',’’:ccT,n:2〕;(*在屏幕ccT位置显示!与出错代码提示,由于ccwritein(fal,’:cc~1,n:2);(*在文件与出错代码提示*)err:二err+1(*出错总次数加一*)(*读取原程序中下一个字符过程getch*)ifcc二11then〔*假如行缓冲区指针指向行缓冲区最终一个字符就从文件读一行到行缓冲区*)ifeof(fin)then(*假如到达文件末尾*)writeProgramincomplete');(*出错,退出程序水)11:=0;(*行缓冲区长度置0*)cc:=0;(*行缓冲区指针置行首*)write〔cx:4,’’〕;&输出ex值,宽度为4*〕write(fal,ex:4,’’);(*输出ex值,宽度为4到文件*)whilenoteoln(fi11:=11+1;(*行缓冲区长度加一*)(*可见,PL/0源程序要求每行的长度都小于81个字符*)read(fin,ch);(*我添加的代码。由于PC上文本文件换行是以所以要把多余的LF从文件读出,这里放在ch变量中是由于ch变量的值在下面即将被转变,把这个多余值放在ch中没有问题*)writein(fal);ifchin[’d’..’z’]then(*假如读出的字符是一个字母,说明是保存字或k:=0;(廉标识符缓冲区指针置0*)repeat(*这个循环用于依次读出源文件中的字符构成标识符粉过k<althen(*假如标识符长度没有超过最大标识符长度(假如超过,就取前面一局部,把多余的抛弃)*)以字母开头,后面跟假设干个字母或数字*)ifk>=kkthen(*假如当前获得的标识符长度大于等于kk*)repeat〔*这个循环用于把标识符缓冲后部没有填入相应字母或空格的空间识符长度小于kk,就把a数组的后部没有字母的空间用空格补足。假如读到的标识符长度大于等于kk,这时就不必在后面填空格了,由于它的后面确定全是空格。反之假如最近读到的标识符长度小于kk,那就需要从kk位置向前,把超过当前标识长度的空间填满空格。以上的这样一个规律,完全是出于程序性能的上考虑。其实完全可以简洁的把a数组中a[k]元素以后的空间不管三七二十一全填空格。(*下面开头二分法查找看读出的标识符是不是保存字之一*)ifid<=word[k]then(*假如当前的标识符小于k所指的保存字*)j:=k-1;(*移动j指针*)ifid>=word[k]then(*假如当前的标识符大于k所指的保存字*)中存的是保存字*)sym:=wsym[k]&找到保存字,把sym置为相应的保存字值*)sym:二ident〔*未找到保存字,把sym置为ident类型,表示是标识符*〕end(*至此读出字符为字母即对保存字或标识符的处理结束*)else(*假如读出字符不是字母*)ifchin[’0'..’9’]then(*假如读出字符是数字*)begin(*number*)(*开头对数字进行处理*)k:二0;(*数字位数*)num:二0;(*数字置为0*)sym:=number:(*置sym为number,表示这一次读到的是数字*)repeat(*这个循环依次从源文件中读出字符,组成数字粉num:=10*num+(ord(ch)-ord('0'));〔*num*10加上最近读出的k:二k+1;(*数字位数加一*)〔*发出30号错*〕end(*至此对数字的识别处理结束时过ch二’:then(*假如读出的不字母也不是数字而是冒号时sym:二becomes;〔*sym的类型设为赋值号becomes*〕sym:=nul;(*假如不是读到等号,那单独的一个冒号就什么也不是*)end仕以上完成对赋值号的处理*)else〔*假如读到不是字母也不是数字也不是冒号粉ifch=*<*then(*假如读到小于号*)getch;〔*再读一个字符*〕ifch=*二’then(*假如读到等号*)sym:=leq;(*购成一个小于等于号*)else(*假如小于号后不是跟的等号*)sym:=1ss〔*那就是一个单独的小于号*〕else(*假如读到不是字母也不是数字也不是冒号也不是小于号*)过ch二’〉’then(*假如读到大于号,处理过程类似getch;(*再读一个字符欢)sym:二geq;(*购成一个大于等于号*)else〔*假如大于号后不是跟的等号*〕sym:=gtr〔*那就是一个单独的大于号*〕else〔*假如读到不是字母也不是数字也不是冒号也不是小于号也不是大于号begin(*那就说明它不是标识符/保存字,也不是简单的双字节操作符,应该是一个一般的符号*)sym:=ssym[ch]:〔*直接成符号表中查到它的类型,赋给sym*〕getch(*读下一个字符*)(*整个过语句推断结束*)(*词法分析过程getsym总结:从源文件中读出假设干有效字符,组成一个token串,识别它的类型为保存字/标识符/数字或是其它符号。假如是保存字,把sym置成相应的保存字类型,假如是标识符,把sym置成ident表示是标识符,于此同时,id变量中存放的即为保留字字符串或标识那么直接把sym置成相应类型。经过本过程后ch变量中存放的是下一个即将被识别的字符*)(*y,z:代码的两个操作数*)(*本过程用于把生成的U标代码写入U标代码数组,供后面的解释器解释执行*)proceduregen(x:fct;y,z:integer);ifex>exmaxthen〔*假如cx>cxmax表示半前生成的代码行号大于允许的最大代码行数*)(*测试当前单词是否合法过程test*)集(*s2:在某一出错状态下,可恢复语法分析正常工作的补充单词集合*)(*n:出错信息编号,当当前符号不属于合法的si集合时发出的出错信息时proceduretest(si,s2:symset:n:integer):ifnot(syminsi)then(*假如当前符号不在si中*)error〔n〕;(*发出n号错误水)si:二si+s2;(*把s2集合补充进si集合*)whilenot(syminsi)do(*通过循环找到下一个合法的符号,以恢复语法分析工作*)getsym(*fsys:用于出错恢复的单词集合*)procedureblock(lev,tx:integer;fsys:symset);dx:integer;〔*dataallocationindex*〕(*数据段内存安排指针,指向下一个被安排空间在数据段中的偏移位置*)位置时(*记录本层开头时代码段安排(*登陆符号表过程enter◆)begin(*enterobjecttx:=tx+1;(*符号表指针指向一个新的空位纣withtable[tx]do(*开头登录*)name:=id;(*name是符号的名字,对于标识符,这里就是标识符的名字时casekof(*依据不同的类型进行不同的操作*)constant:(*假如是常量名*)过num>amaxthen(*在常量的数值大于允许的最大值的惜况下*)抛出31号错误时实际登陆的数字以0代替*)val:二num(*如是合法的数值,就登陆到符号表*)dx:=dx+1;(*偏移量自增一,为下一次做好预备*)procedur:(*假如要登陆的是过程名*)(*登录符号过程没有考虑到重复的定义的问题。假如消灭重复定义,那么以最终一次的定义为准。*〕(*在符号表中查找指定符号所在位置的函数position*)(*返回值:要找的符号在符号表中的位置,假如找不到就返回0*)table[01.name:=id;〔*先把id放入符号表0号位置*〕position:=i(*返回找到的位置号,假如没找到那么肯定正procedureconstdeclaration;符*)getsym;(水猎取下一个token*)〔1〕;〔*抛出1号错误*)(*这里其实自动进行了错误订正使编译连续进行,把赋值号当作等号处理getsym;〔*猎取下一个token,等号或赋值号后应接上数字*〕过sym=numberthen〔*假如的确是数字*〕enter〔constant〕;(*把这个常量登陆到符号表*)error(2)(*假如等号后接的不是数字,抛出2号错误时error(3)仕假如常量标识符后接的不是等号或赋值号,抛出3号错误水)error(4)(*假如常量声明过程遇到的第一个符号不为标识符,抛出4号错误procedurevardeclaraifsym二identthen(*变量声明过程开头遇到的第一个符号必定应为标识符enter(variable);(*将标识符登陆到符号表中*)getsym(*猎取下一个token,为后面作预备*)error(4)(*假如变量声明过程遇到的第一个符号不是标识符,抛出4号错误end(*vardeclaration水);iflistswitchthen〔*假如用户选择是要列出代码的状况下才列出代码*〕fori:二cx0toex-1do(*从当前层代码开头位置到当前代码位置T处,即为本分程序块*)助记符和L与A操作数*)(*我修改的代码:原程序此处在输出i时,没有指定占4个字符宽度,不美观也与下面一句不配套。*)procedurestatemeexpression〔fsys:symset〕;procedureterm〔fsys:symset〕;procedurefactor(fsys:symset);test(facbegsys,fsys,24);(*开头因子处理前,先检查当前token是否在(*假如不是合法的token,抛24号错误,并通过fsys集恢复使语法处理可以ifsym=identthen(*假如遇到的是标识符*)ifi=0then(*假如查符号表返回为0,表示没有找到标识符*)error(11)(*抛出11号错误*)WithtableEi]do〔*假如在符号表中找到了当前标识符的位置,开头生成相casekindofconstant:gen(lit,0,val):〔*假如这个标识符对应的是常量,值为val,生成lit指令,把Vdl放到栈顶*)variable:gen(lod,lev-level,adr);〔*假如标识符是变量名,生成(*把位于距离当前层level的层的偏移地址为adr的变量放到栈顶*)procedur:error〔21〕&假如在因子处理中遇到的标识符是过程名,出错了,抛21号错*]ifnum>amaxthen〔*假如数字的大小超过允许最大值num:二0(*把数字按0值处理*)gen(lit,0,num);〔*生成lit指令,把这个数值字面常量放到栈顶*〕ifsym=lparenthen(*假如遇到的是左括号*)getsym;(*猎取一个token*)expression([rparen]+fsys);〔*递归调用expression子程序分析一个子表达式*)ifsym=rparenthen(*子表达式getsym〔*假如的确遇到右括号,读取下一个tokerror〔22〕(*否那么抛出22test(fsys,facbegsys,23)(*一个因子处理完毕,遇到的token应在fsys集合中*)(*假如不是,抛23号错,并找到下一个因子的开头,使语法分析可以连续运行下去*)factor([times,slash]+fsys);〔*每一个项都应当由因子开头,因此调用factor子程序分析因子*)mulop:二sym;(*保存当前运算符*)factor〔fsys+[times,slash]〕;〔*运算符后应是一个因子,故调factor(*生成乘法指令*)(*不是乘号肯定是除号,生成除法指令*)ifsymin[plus,minus]then示正负号*)beginaddop:二sym;〔*把当前的正号或负号保存起getsym;(*猎取一个toterm〔fsys+[plus,minus]〕:〔*正负号后面应当是一个项,调term子程序分析*)ifaddop=minusthen(*假如保存下来的符号是负号*)gen〔opr,0,1〕(*生成一条1号操作指令:取反运算*)(*假如不是负号就是正号,不需生成相应的指令*)else(*假如不是山正负号开头,就应是一个项开头*)whilesymin[plus,minus]do〔*项后应是加运算或减运算*〕getsym;〔*猎取下一个token,加减运算符后应跟的是一个项*〕ifaddop=plusthegen(opr,0,3)(*生成3号操作指令:减法*)(*条件处理过程condition◆)procedurecondition(frelop:symbol;〔*用于临时记录token(这里肯定是一个二元规律运算符)的内容*)begirifsym=oddsymthen(*假如是odd运算符(一元)*)getsym;(*猎取下一个token*)gen〔opr,0,6〕;〔生成6号操作指令:奇偶推断运算*〕else(*假如不是odd运算符(那就肯定是二元规律运算符)*)expression([eql,neq,1ss,leq,gtr,geq]+fsys);(*对表达式左部进行处理计算*)ifnot(symin[eql,neq,lss,leg,gtr,geq])then(*假如token不是逻辑运算符中的一个*)error(20)(*抛出20号错误*)relop:二sym;(*记录下当前的规律运算符*)getsym;(*猎取下一个token*)expression(fsys);(*对表达式右部进行处理讣算*)caserelopof(*假如刚刚的运算符是下面的一种粉eql:gen(opr,0,8);(*等号:产生8号判等指令时neq:gen(opr,0,9);(*不等号:产生9号判不等指令粉lssgen(opr,0,1(欢大于等号号:产生11号判不小于指令*)leq:gen(opr,0,13);(*小于等于号:产生13号判不大于指令*)i:二position(id);(*在符号表中查到该标识符所在位置*)ifi=0then(*假如没找到*)error(11)(*抛出11号错误*)iftable[i].kind<>variablethen(*假如在符号表中找到该标识符,但该标识符不是变量名*)error(12);(*抛出12号错误水)error(34)(*抛出34号错误*)ifsym=identthen(*假如确为一个标识符*)(*这里略有问题,还应推断一下这个标识符是不是变量名,假如是常量名或过程名应出错*)i:=position(id)(*查符号表,找到它所在位置给i,找不到时i会为0i:二0;(*不是标识符那么有问题,i置0作为出错标志*)ifi=0then(*假如有错误*)error〔35〕(*抛出35号错误*)else〔*否那么生成相应的口标代码*〕gen(sto,lev-level,adr)(*生成sto指令,把读入的值存入指定变量所在的空间*)右括号*)untilsym<>comma;〔*不断生成代码直到read语句的参数表中的变量遍历完为止,这里遇到不是逗号,应为右括号*)ifsym<>rparenthen(*假如不是我们预想中的右括号*)whilenot〔syminfsys〕do(*依靠fsys集,找到下一个合法的token,恢复语法分析*)getsym〔*假如read语句正常结束,得到下一个token,一般为分号或end*〕过sym=writesymthen〔*假如遇到/write语句*〕getsym;(*猎取下一token,应为左括号*)repeat〔*依次猎取括号中的每一个值,进行输出*〕getsym;〔*获得一个token,这里应是一个标识符*〕expression〔[rparen,comgen(opr,0,14)(*生成14号指令:向屏幕输出*)untilsym>comma;〔*循环直到遇到的不再是逗号,这时应是右括号*〕error(33)(*抛出33号错误*)gen〔opr,0,15〕&生成一个13号操作的目标代码,功能是输出一个换行*)(*由此可知PL/0中的write语句与Pascal中的writein语句类似,是带有输出换行的水)error(11)(*抛出11号错误*)error(15);&假如call后跟的不是过程名,抛出15号错误粉getsym;〔*猎取一token应是一个规律表达式*〕错恢复集中参与then和do语句*)cxl:二ex;&登记当前代码安排指针位置*)生成条件跳转指令,跳转位置临时填0,分析完语句后再statement〔fsys〕;(*分析thencodeEcxl1.a-cx(*上一行指令(cxl所指的)的跳转位置应为当前ex所指位置进行分析处理*)whilesymin[semicolon]+statbegsysdo〔*假如分析完一句后遇到分号或语句开头符循环分析下一句语句*)ifsym=semicolonthen〔*假如语句是分号(可能是空语句〕*)getsym〔*猎取下一token连续分析*〕error(10);&假如语句与语句间没有分号,出10号错*)statement([semicolon,error(17)(*假如不是end,抛出17号错*)cxl:二ex;&登记当前代码安排位置,这是while循环的开头位置*)condition〔[dosym]+fsys〕;〔*对这个规律表达式进行分析汁算*)cx2:=ex;&登记当前代码安排位置,这是while的do中的语句的开头位置*)gen(jpc,0,0);&生成条件跳转指令,跳转位置临时填0*)过sym=dosymthen〔*规律表达式后应为do语句*〕error(18);(*if后缺少then,抛出18号错误*)statement(fsys);(*分析do后的语句块*)gen(jmp,0,cxl);住循环跳转到cx1位置,即再次进行规律推断时code[cx2].a:=ex(*把刚刚填0的跳转位置改成当前位置,完成while语号,假如没有遇到,就抛19号错*)dx:二3;(*地址指示器给出每层局部量当前已安排到的相对位置。置初始值为3的缘由是:每一层最开头的位置有三个空间用于存放静态链tx0:二tx;&初始符号表指针指向当前层的符号在符号表中的开头位置*)table[txl.adr:=ex;(*符号表当前位置登记当前层代码的开头位置*)gen(jmp,0,0);&产生一行跳转指令,跳转位置临时未知填0*)iflev>levmaxthen&假如当前过程嵌套层数大发出32号错误*)repeat(*开头循环处理源程序中全部的声明局部*)ifsym=constsymthen〔*假如当前token是const保存字,开头进行常量声getsym;〔*猎取下一个token,正常应为用作常量名的标识符*〕repeat(*反复进行常量声明*)constdeclaration;(*声明以肖前token为标识符的常量*)whilesym=commado(*假如遇到了逗号那么反复声明下一个常量*)声明以的前token为标识符的常量*)end;ifsym=semicolonthen(*假如常量声明结束,应遇到分号*)getsym〔*猎取下一个token,为下一轮循环做好预备*〕error(5)(*假如常量声明语句结束后没有遇到分号那么发出5号错误*)untilsym>ident(*假如遇到非标识符,那么常量声明结束*)它可以接受像下面的声明方法,而依据课本上的EBNF范式不行得出下面的语即它可以接受分号或逗号隔开的常量声明,而依据EBNF范式只可接受用逗号getsym;(*猎取下一个token,此处正常应为用作变量名的一个标识符*)vardeclaration;whilesym=commado(*假如遇到了逗号那么反复声明下一个变量*)getsym;〔*猎取下一个token,这里正好应当是标识符*〕vardeclaration;〔*声明以肖前token为标识符的变量*〕ifsym=semicolonthen〔水假如变量声明结束,应遇到分号*〕getsym(*猎取下一个token,error(5)(*假如变量声明语句结束后没有遇到分号那么发出5号错误*)untilsym>ident;(*假如遇到非标识符,那么变量声明结束*)(*这里也存在与上面的常量声明一样的毛病:与PL/0的语法标准有冲突。*)ifsym=identthen(*假如token确为enter(procedur);〔*把这个过程登录到名字表中*)error〔4〕;&否那么抛出4号错误水)ifsym=semicolonthengetsym〔*猎取下一,个token,预备进行语法分析的递归调用*〕error〔5〕;(*否那么抛出5号错误*)block〔lev+1,tx,[semicolon]+fsys〕前层次加一,同时传递表头索引、合法单词符*)个end后的分号*)test(statbegsys+[ident,procsym],fsys,6);(*检查当前tok合法,不合法那么用fsys恢复语法分析同时抛6号错*)error(5)(*假如过程声明后的符号不是分号,抛出5号错误*)test(statbegsys+[ident],不合法那么用声明开头符号作出错恢复、抛7号错*〕untilnot(symindeclbegsys);(*直到声明性的源程序分析完毕,连续向下执行,分析主程序*)codeEtableEtx01.adrl.a:二ex;&把前面生成的跳转语句的跳转位置改成当前位置*)withtable[tx0]do(*在符号表中记录*)size:二dx;&长度为当前数据代安排位置粉gen(int,0,dx);(*生成安排空间指令,安排dx个空间水)块*)gen(opr,0,O):(*生成从子程序返回操作指令*)test(fsys,[],8);&用fsys检查当前状态是否合法,不合法那么抛8号错水)procedureinterpret;p,b,t:integer;(*programbass:array[1..stacksize]ofinteger;(*datastore◆)while1>0d
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《做真正的管理者》课件
- 二零二五版内墙刮瓷施工安全监管合同
- 二零二五年度终止劳务派遣合同:针对交通运输行业员工的协议3篇
- 《超赔保险费率》课件
- DB37-T 4518-2022 河湖水域岸线遥感监测技术规范
- 《天线怎样发射信号》课件
- 二零二五年度皮革产品环保认证采购合同范本4篇
- 《妊娠诊断》课件
- 2025年教育机构租赁承包合同样本解析3篇
- 充电桩合作协议书
- 《排球正面双手传球》课件
- 五官科眼耳鼻咽喉科医疗常用器械的认识
- 企业清产核资报表
- 浅谈建筑工程机电安装施工技术运用论文
- 2023年新改版教科版四年级下册科学练习题(一课一练+单元+期中+期末)
- 《字体设计》模块五 装饰性变化设计技巧的训练
- 《摔跤吧爸爸》观后感PPT
- meyer压裂模拟及功能培训
- FRENIC 5000G11S、P11S富士变频器操作说明书
- 机构编制重要事项的报告范文(5篇)
- Q-GDW 11274-2014 风电无功电压自动控制技术规范
评论
0/150
提交评论