SNL编译器设计实验报告_第1页
SNL编译器设计实验报告_第2页
SNL编译器设计实验报告_第3页
SNL编译器设计实验报告_第4页
SNL编译器设计实验报告_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

-.z.---.可修编.编译原理课程设计实验报告目录第一部分实验成果统计表…………1第二部分实验简介…………………2第三部分词法分析…………………3第四部分语法分析…………………64.1LL(1)法语法分析…………74.2递归下降法语法分析……………………10第五部分语义分析…………………19第六部分程序测试…………………22第七部分实验总结与体会…………28-.z.--第一部分实验成果统计表性别班级**所占比例个人成绩任务分工:(请用小四号宋体填写)词法分析部分程序的调试与实现;LL(1)语法分析部分程序的调试与实现;递归下降语法分析部分程序的调试与实现;语义分析部分程序的调试与实现;成绩评定:词法分析递归下降LL(1)语义分析团队成绩教师签章备注填写说明:1、请将首页红色部分信息填全,其中:班级为2位数字,保留首位的0;**为8位数字,计算机科学与技术学院以53开头;所占比例为百分数,精确到个位数,且所有人的所占比例之和为100%;不足四人的分组请保留后面的多余空行,请勿修改该表的结构。2、请根据实际情况填写任务分工部分,主要任务包括:编译系统的总体分析与设计,四个具体功能的设计与实现,对应的测试与验证过程(报告正文需要列出若干组具体测试样例与对应结果),系统界面的设计与美工,以及辅助工具、视图和文件等。3、成绩评定部分由指导教师填写,请勿填写和修改。-.z.第二部分实验简介本实验中实现了SNL编译系统中的词法分析、语法分析和语义分析。其中语法分析包括递归下降分析方法和LL(1)分析方法。词法分析,以源程序为输入,生成单词的部表示TOKEN序列。语法分析,以TOKEN序列为输入进行语法分析,并生成整个源程序的语法分析树。在SNL编译程序中,采用了两种语法分析方法实现:LL(1)和递归下降法。两种语法分析的结果是一样的。语义分析,以语法树为输入生成标识符的属性符号表以及相关的各类信息表,如数组信息表,并进行相关的语义检查。它们三者的关系如下:LL(1)LL(1)语法分析递归下降语法分析词法分析语义分析-.z.第三部分词法分析源程序一般表现为字符串(机器语言称其为ASCII码)序列的形式,而编译程序的翻译工作应该在单词一级上进行,这与自然语言的翻译理解过程是类似的。因此要进行编译工作,首先要把源程序的字符序列翻译成单词序列。词法分析是编译过程的第一阶段。它的任务就是对输入的字符串形式的源程序按顺序进行扫描,根据源程序的词法规则识别具有独立意义的单词(符号),并输出与其等价的TOKEN序列。TOKEN是单词(符号)的部表示。完成词法分析任务的程序称为词法分析程序,通常也称为词法分析器或扫描器(scanner)。TOKEN是单词在编译程序处理过程中的一种部表示,也是词法分析程序对程序中各类单词进行处理之后的输出形式。对于一种语言而言,如何对它的单词进行分类,每一类单词的TOKEN数据结构的形式如何,都没有固定的模式,可以随编译程序的不同而不同。通常TOKEN的结构可以分成两部分,单词的语法信息和语义信息。其中语法信息记录的是这个单词的种类,语义信息则记录着这个单词的具体信息。这样,就能为以后的语法分析和语义分析处理单词做好准备。SNL语法分析对每类单词的分析结果的TOKEN结构为三元组(词法信息、语义信息以及该单词在源程序中的行号)。本SNL编译器单词的部表示如下:行号(便于出错处理)行号(便于出错处理)TOKEN的内部表示语法信息及其字符串表示TOKEN的内部表示语法信息及其字符串表示语义信息及其字符串表示语义信息及其字符串表示SNL语言的单词分类如下:(1):保留字保留字一般是由语言系统自身定义的,SNL语言中的保留字有program,while,if,fi等等。(2):标识符用来标识程序中各个对象的名称。由用户自己定义用来表示变量名,数组名或者过程名等。SNL语言中标识符由字母开头,字母、数字的任意组合组成的。(3):常量用来表示各类常数。如字符型常量,实行常量,布尔常量等。(4):运算符表示程序中算术运算、逻辑运算、字符运算的确定字符或字符串。SNL语言中的运算符有+,—,*,/,<和:=六种运算符。(5):界限符包括逗号分号等。SNL语言中的‘;’表示一条语句结束,‘.’表示程序结束等等。实现词法分析器的注意事项:保留字和标识符名字的区分复合单词的处理向前搜索及回退数字的转换输入时边界的处理注释的处理词法分析主要的函数有:getTokenlist()、getNe*tChar()、ungetNe*tChar()、reservedLookup()、ChainToFile()、printTokenlist()。getTokenlist()函数是最主要的函数,它每次从输入缓冲区lineBuf字符串序列中超前读一个(有时是两个)字符,使用确定有限自动机DFA的直接转向处理方法。超前读的字符如不匹配的话,还需要回退。如果得到的TOKEN是标识符的话,还需要查询保留字表以判断该标识符是否是保留字。产生词法错误的时候,仅仅略过产生错误的字符,不加改正。然后将得到的TOKEN存入链表,当整个源程序都分析完成的时候,将TOKEN链表中各个TOKEN存入文件Tokenlist.t*t中,将来输出显示TOKEN时再从Tokenlist.t*t中读取。getNe*tChar()函数被getTokenlist()函数调用,每次从输入缓冲区lineBuf中返回一个字符,如果lineBuf中的字符全部读完后,再从TOKEN文件中读取下一批。如此重复下去,直到源程序字符全部读完为止。ungetNe*tChar()函数被getTokenlist()函数调用,当文件结束标志FileEnd没有置位的时候,将lineBuf缓冲区中的读取指针回退一个字符。reservedLookup()函数用在当前TOKEN是标识符的时候,需要查保留字表来判断该TOKEN是保留字还是普通标识符。ChainToFile()函数用在源文件处理完毕后,将TOKEN链表中的TOKEN一个一个地存入文本文件Tokenlist.t*t中。将来可以直接在文本文件中查看tokenlist,也可以在需要输出显示的时候从该文件中读取TOKEN。printTokenlist()函数用在需要在命令行输出显示Tokenlist的时候,从Tokenlist.t*t中读取TOKEN,然后显示出来。

主要函数getTokenlist()的流程图如下:YN结束调用ChainToFile将链表信息存入文件curToken.Le*=ENDFILE?将curToken的信息存入链表入口Y从lineBuf中超前读入下一个字符,根据state状态进行处理Nstate=DONE?YN结束调用ChainToFile将链表信息存入文件curToken.Le*=ENDFILE?将curToken的信息存入链表入口Y从lineBuf中超前读入下一个字符,根据state状态进行处理Nstate=DONE?-.z.第四部分语法分析语法分析是编译程序的第二阶段,也是编译程序的核心部分。语法分析的任务是,根据语言的语法规则,对源程序进行语法检查,并识别出相应的语法成分。按照SNL编译程序的模型,语法分析的输入时从词法分析器输出的源程序的TOKEN序列形式,然后根据语言的文法规则进行分析处理,语法分析的输出是无语法错误的语法成分,表示成语法树的形式。语言是具有独立意义的单词根据一定的语法规则组成的句子的集合,句子的结构由语法规则给出,句子的含义由语义规则给出,而对语言的语法分析就是对语言的句子结构的分析。归于程序设计语言而言,它的句子就是程序,程序设计语言定义的是符合其语法规则的程序的集合,因此程序设计语言的语法分析的关键是识别程序(句子)的语法结构。完成语法分析任务的程序成为语法分析程序,也称为语法分析器或简称分析器。本编译器的语法分析采用自顶向下的语法分析。自顶向下分析是从文法的开始符号出发,试图为输入串建立一个最左推导,或为输入串构造一个语法树。这种分析是通过对当前句型的最左非终极符,反复使用他的不同的规则进行替换和展开,以匹配输入串来实现的。语法分析检查的错误有:程序的开始单词错,表达式的开始单词错,语句的卡是单词错,表达式的后记单词错,语句的后记单词错等。标识符和常量单词错。如域名不是标识符等。括号类错误。如begin_end不匹配,(-)不配对,[-]不配对,case-end不配对,if-then-end不配对等。分隔符错。如赋值语句后面不是赋值号,标识符表或表达式的分隔符(或后继符)错。4.1LL(1)语法分析LL(1)语法分析方法是一种自顶向下的语法分析方法,它是LL(k)分析方法的特例,其中k表示向前看k个符号的意思。LL(1)语法分析程序由两部分组成的:第一部分是语法分析表,也称为LL(1)分析矩阵;第二部分是语法分析驱动程序。LL(1)矩阵的作用是帮助当前非终极符和当前输入符确定应该选择的语法规则,它的行对应非终极符,列对应终极符,矩阵的值有两种:一种是产生式编号。另外一种是错误编号。LL(1)分析程序工作过程首先初始化,即把开始符压入栈中,以后的每步分析必是下面的四种情况之一:分析栈的栈顶元素是终极符,则看其是否与输入流的头符相匹配,如果匹配成功,则去掉栈顶元素并读入下一个单词;若匹配不成功,则报错。栈顶是非终极符,则用栈顶和输入流的当前单词去查当前矩阵,如果查得的值是产生式编号,则把对应的产生式右部逆序压入栈中;如果查得的值为错误信息,则报错。栈已空,输入流不空,这时输入流报错。若栈已空,输入流也空,则语法分析成功。LL(1)工作原理如下图:SNL语法程序的实现采用手工操作构造LL(1)分析表。LL(1)分析表用一个二维矩阵表示,其中每个非终极符对应一行,每个终极符对应一列,一个非终极符和一个终极符可以确定矩阵中的一个元素,元素的值表示该非终极符和该终极符对应的产生式。SNL的LL(1)语法分析程序共用到四个栈,分别称为:符号栈、语法树栈、操作符栈和操作数栈。其中,符号栈用于进行SNL的LL(1)语法分析;其他的栈是为了在语法分析过程中同时生成与源程序结构对应的语法树而设置的。语法树栈用于声明部分和语句部分的语法树;操作符栈和操作数栈用于生成表达式部分的语法树。处理声明部分和语句部分的语法树生成时,设置一个语法树栈,存放语法树节点中指向儿子或者兄弟节点的指针的地址。在生成当前语法树节点时,如果以后需要对其儿子节点或者兄弟节点赋值,则按照处理顺序的逆序将这些儿子节点或者兄弟节点的指针的地址压入语法树栈,后面生成它的儿子节点或者兄弟节点时,只需弹栈,并对相应的指针进行赋值,就可以完成所需的语法树节点的。处理表达式时,需要另外设置两个栈,操作数栈和操作符栈,遇到操作数压入操作数栈,遇到操作符,则进行判断,如果当前操作符的优先级高于操作符栈的栈顶操作符,直接压入操作符栈;否则,弹出栈顶操作符,并弹出操作数栈顶的两个操作数,生成相应的子树,并对新生成的子树的父节点(操作符节点)进行循环判断。LL(1)语法分析的主要函数有:parseLL1()、CreatLL1Table()、gettoken()、priosity()、predict()、newnode()、TreeToFile()、printTree()、StrToEnum()、PushSym()、PopSym()、PushSynTree()、PopSynTree()、PushOp()、PopOp()、ReadOpStack()、PushNum()、PopNum()等等parseLL1()函数是最主要的函数。它利用LL(1)分析表和符号栈进行语法分析,并处理终极符不匹配和文件提前结束错误。函数处理完成后,得到整个语法树。CreatLL1Table()用于创建LL(1)分析表。用二维数组表示LL(1)分析表,初始化二维数组所有元素为0,根据给定的LL(1)文法,对产生式编号。对于每个产生式,左部的非终极符作为行号,其Predict集中每个元素分别作为列号,二维数组中行号、列号对应的元素赋值为该产生式的编号。getToken()函数每次从Tokenlist.t*t文件中读取一个TOKENpriosity()用于判断当前操作符的优先级。优先级由高到低排序为:乘法运算符>加法运算符>关系运算符>左括号>栈底标志END。predict()函数根据产生式编号选择一个要执行的函数。newnode()函数用于生成树节点。TeeToFile()函数用于将最后产生的语法树写入SynTree.t*t文件,该文件可以查看语法树,也可以在需要显示输出时从中读取语法树。printTree()函数用于从SynTree.t*t中读出语法树,然后输出显示。StrToEnum()用于将从Tokenlist.t*t中读取的以字符串形式存在的TOKEN的Le*域转换成Le*Type枚举类型。PushSym()用于将当前符号入栈到符号栈。PopSym()用于将符号栈栈顶符号出栈。PushSynTree()用于将当前语法树节点中指向儿子或兄弟节点的指针的地址入栈到语法树栈。PopSynTree()用于将语法树栈栈顶指向树节点的指针的地址出栈。PushOp()用于将当前操作符入栈到操作符栈。PopOp()用于将操作符栈栈顶操作符出栈。ReadOpStack()用于读取操作符栈栈顶操作符。PushNum()用于将当前操作数入栈。PopNum()用于将操作数栈栈顶操作数出栈。LL(1)语法分析主函数parseLL1()的算法框图如下:4.2递归下降法(一)、递归下降语法基本原理综述语法分析是编译程序的第二阶段,语法分析的任务是根据语言的语法规则对源程序进行语法检查,并识别出相应的语法成分。简单地说,语法分析就是根据语言的语法规则对语言的句子结构进行分析,看源程序的句子结构是否符合语言语法规则的要求。语法分析的输入是从词法分析器输出的TOKEN序列形式,然后根据语言的文法规则进行分析处理,语法分析的输出是无语法错误的语法分析树的形式。这一部分主要介绍递归下降法语法分析的原理及实现。递归下降法是语法分析中比较容易理解的一种方法,因为其采用了递归的结构和思想,但这也在一定程度上影响了程序的执行效率,所以这种方法的优点是容易实现,容易理解,缺点就是这种方法的执行效率不如LL(1)高。递归下降法的主要原理是对每个终极符和非终极符按其产生式结构构造相应的语法分析子程序,其中终极符产生匹配命令,而非终极符则产生过程调用命令。其中子程序结构与产生式的结构几乎是一致的。前面我们已经提到对于终极符将产生匹配命令match(),match()命令的定义如下:Match(*)——无定义,*属于Vn——检查token=*?若是,则移位;否则,报错,*属于Vt根据递归下降法的基本原理和思想,我们可以将递归下降法语法分析主程序写成如下的形式,这个程序形式就是我我们所编写的递归下降法语法分析程序的主程序格式,这样就可以容易构造出一个文法的语法分析程序:Programptoken:类型;Begin读入一个tokeiftoken属于First(S)thenSelse报错并停机End(二)、递归下降语法分析程序的主要处理对象及其依赖关系我们编写的递归下降语法分析程序主要对一个程序的主要部分进行分析和检查,其中主要包括程序头、程序声明、类型声明、变量声明、过程声明、程序体(包括各种语句序列、表达式)等,下面对各部分做简要的说明:程序头:程序的开始,包括保留字以及程序的名字。程序声明:包括程序的整个声明部分,又可以细分为类型声明、变量声明、过程声明等。类型声明:用于定义类型,其中类型可以分为基本类型(整型、字符型等)、结构类型(数组类型、记录类型等)和自定义类型(类型名称为标识符,实际类型为上述两种类型)。变量声明:用于声明变量,定义变量类型。过程声明:用于声明过程,包括参数(变参、值参)、过程中的声明和过程体。程序体:由语句序列组成,语句主要包括赋值语句、条件语句、循环语句、输入语句、输出语句、返回语句、过程调用语句(可由读入的标识符或者其它相应的保留字来判定属于哪一种语句,并选择相应的程序进行分析。)对语句的分析可能会涉及到对表达式的处理。给出了递归下降语法分析程序所要分析处理的各种语法成分,为了能更清楚直接地反映出各语法成分之间的依赖关系,参考实验教材上的容,我们给出了递归下降语法分析中各语法成分的依赖图,如下图所示:总程序总程序程序头程序头程序声明程序声明类型声明类型声明变量声明变量声明过程声明过程声明参数声明参数声明过程中的声明过程中的声明过程体(主体程序)过程体(主体程序)语句序列语句序列语句语句表达式表达式图1.递归下降语法分析中各语法成分的依赖关系(三)、递归下降语法分析输出结果——语法分析树根据上面的叙述,我们知道递归下降语法分析程序的输入是通过词法分析程序产生的TOKEN序列,而作为语法分析程序的输出,我们给出了递归下降语法分析程序的统一规化输出结果——语法分析树的基本形式。我们编写的递归下降语法分析程序最终的输出结果就是下面语法树的形式,只不过我们是以另一种形式组织并保存在.t*t文件中,但核心的思想就是下图所示的语法树形式。该语法树形式不仅体现了语法分析程序的输出结果,还在一定程度上反映出了各语法分析成分之间的关系。图2.语法分析树(四)、递归下降语法分析程序主要函数介绍根据之前的分析和叙述,我们已经对递归下降语法分析程序进行了最基本的阐述,这些基本的阐述可以使得我们很清楚地了解递归下降语法分析程序所要实现的功能、所要分析的对象以及优缺点等。下面我们将对递归下降语法分析程序中的主要函数进行介绍,主要介绍函数的声明和功能等,并在最后给出各主要函数之间的调用关系。按照这些函数的声明和调用关系,我们便可以基本实现递归下降语法分析程序的功能。主要函数声明:主要函数功能描述:TreeNode*parseMain(void)1.调用总程序处理分析函数:创建语法分析树,同时处理文件的提前结束错误。TreeNode*program(void);2总程序处理分析程序:生成语法分析树的根节点root,分别调用程序头部分、声明部分、程序体部分分析函数并成为语法树的3个节点TreeNode*programHead(void)3程序头部分分析处理函数:根据读入的单词,匹配保留字PROTGRAM,记录程序名于程序头结点,匹配IDTreeNode*declarePart(void)4程序声明部分分析函数:根据文法产生式创建新的类型声明标志节点、变量声明标志节点。调用类型声明部分处理函数TypeDec()、变量声明部分处理函数VarDec()、过程声明部分处理函数ProcDec()TreeNode*typeDec(void)5类型声明部分处理函数:根据读入的下个单词,若当前单词为TYPE,调用函数typeDeclaration()并赋值给t,则函数返回t,否则返回NULLTreeNode*typeDeclaration(void)6类型声明中的其他函数:根据文法产生式,匹配保留字TYPE,调用函数TypeDecList()并赋值给t,若t为NULL则显示提示信息。函数返回tTreeNode*typeDecList(void)7函数声明中的其它函数:根据文法产生式,创建新的声明类型节点t。若申请成功,调用函数TypeId(),匹配保留字EQ,调用函数TypeDef(),匹配保留字SEMI,调用函数TypeDecMore(),返回值赋值给t的成员sibling,函数返回tTreeNode*typeDecMore(void)8类型声明中的其它函数:该函数根据读入的单词,或者调用函数TypeDecList()并赋值给t,则函数返回t;或者什么都不做,或者跳过此错误单词,读入下一个单词,返回NULLvoidtypeId(TreeNode*t)9类型声明中类型标识符处理分析程序:该函数根据读入的单词,判断TOKEN.Le*=ID的值,如果为真则将TOKEN.Sem字符串拷贝到参数t的成员[tnum]中,其中tnum是用来记录namne个数的临时变量。然后再将tnum加1送回参数t的成员attr.idnumvoidtypeDef(TreeNode*t)10具体类型处理分析函数:该函数根据读入的单词,或者调用BaseType(),或者调用structureType(),或者复制标识符名称、匹配标识符,或者跳过错误单词,读入下一个单词voidbaseType(TreeNode*t)11基本类型分析处理函数:该函数根据读入的单词判断执行哪个分支voidstructureType(TreeNode*t)12结构类型处理分析函数:该函数根据读入的单词判断选择调用函数ArrayType(),或者调用函数RecType()voidarrayType(TreeNode*t)13数组类型的分析处理函数:该函数对读入的单词进行匹配,并记录数组的上界和下界,再匹配保留字,最后调用基本类型函数ºyBaseType(),记录数组的子类型voidrecType(TreeNode*t)14记录类型的处理分析函数:该函数对读入的单词进行匹配,调用记录中的域函数FieldRec(),最后再对读入的单词进行匹配TreeNode*fieldDecList(void)15记录类型中的域声明处理分析函数:该函数根据读入的单词判断记录域中的变量属于哪种类型,根据产生式的select集合判断执行哪个分支程序。其中相同类型的变量id用“,”分隔开,其名称记录在语法书的同一个节点中,不同类型的变量节点互为兄弟节点TreeNode*fieldDecMore(void)16记录类型中的其他域声明处理分析函数:该函数根据文法产生式判断读入的单词,若为END,则不做任何处理;若为INTEGER,CHAR或者ARRAY,则调用FieldDecList()递归处理函数;否则读入下一个TOKEN序列,函数返回tvoididList(TreeNode*t)17记录类型域中标识符名处理分析程序:该函数根据文法产生式,匹配标识符ID,记录标识符名称,调用递归处理函数IdMore()voididMore(TreeNode*t)18记录类型域中其他标识符名处理分析程序:该函数根据文法产生式判断读入的单词,处理调用相应的函数TreeNode*varDec(void)19变量声明处理分析程序:该函数根据文法产生式判断读入的单词,处理调用相应的函数。如果处理成功则返回t,否则返回NULL。TreeNode*varDeclaration(void)20变量声明部分处理程序:该函数根据文法产生式,匹配保留字VAR,调用函数VarDecList()函数。处理成功返回t,否则返回NULLTreeNode*varDecList(void)21变量声明部分处理程序:该函数根据文法产生式调用相应的变量声明部分的处理函数TypeDef(),VarIdList(),VarDecMore()。处理成功返回t,否则返回NULLTreeNode*varDecMore(void)22变量声明部分处理程序:该函数根据文法产生式。由读入的单词判断执行哪个分支,最后返回tvoidvarIdList(TreeNode*t)23变量声明部分变量名部分处理程序:该函数根据文法产生式,匹配标识符名ID,记录标识符名称,调用函数VarIdMore()。voidvarIdMore(TreeNode*t)24变量声明部分变量名部分处理程序:该函数根据文法产生式,由读入的单词判断执行哪个分支(判断是否还有其他变量)TreeNode*procDec(void)25过程声明部分总的处理分析程序:该函数根据文法产生式判断当前单词,若为BEGIN则不做任何动作,若为PROCEDURE,则调用过程声明函数ProcDeclaration(t),否则读入下一个单词。函数返回tTreeNode*procDeclaration(void)26该函数根据文法产生式匹配保留字PROCEDURE,调用过程名函数Proame(),参数函数ParamList(),过程体部声明部分函数ProcDecPart(),过程体函数ProcBody()。成功则返回t,否则返回NULL。voidparamList(TreeNode*t)27过程声明中的参数声明部分的处理分析程序:该函数根据文法产生式判断读入的单词,若为RPAREN,则不做任何动作;若为INTEGER,CHAR,ARRAY,RECORD,ID,VAR,则调用参数声明序列处理函数ParamDecList(t),否则读下一个TOKEN序列TreeNode*paramDecList(void)28过程声明中的参数声明序列的处理分析程序:该函数根据文法产生式调用函数Param(),返回指针t;调用函数paramMore(),返回指针p。若p不为NULL,则将p赋值给t的成员变量sibling。若处理成功则返回指针t,否则返回NULLTreeNode*paramMore(void)29过程声明中的参数声明其他部分的处理分析程序:该函数根据文法产生式,由读入的单词判断执行哪个分支(判断是否还有其他参数声明)。TreeNode*Param(void)30过程声明中的参数声明中参数部分的处理分析程序:该函数根据文法产生式,由读入的单词判断执行哪个分支程序,即判断是值参还是变参。处理成功返回t,否则返回NULLvoidformList(TreeNode*t)31过程声明中的参数声明中参数名部分的处理分析程序:该函数根据文法产生式,记录参数声明中的参数名称,匹配保留字ID,调用FidMore()函数。voidfidMore(TreeNode*t)32过程声明中的参数声明中参数名部分的处理分析程序:该函数根据文法产生式,有读入的单词判断执行哪个分支程序。TreeNode*procDecPart(void)33过程中声明部分的分析处理程序:该函数根据文法产生式,调用声明部分函数DeclarationPart(),如果处理成功则返回t,否则返回NULL。TreeNode*procBody(void)34过程体部分处理分析程序:该函数根据文法产生式,调用程序体部分函数ProgramBody().成功则返回t,否则返回NULL。TreeNode*programBody(void)35主程序体部分处理分析程序:根据文法产生式创建新的语句标志类型语法树节点,并匹配保留字,调用语句序列函数StmList()TreeNode*stmList(void)36语句序列部分处理分析程序:根据文法产生式,调用语句处理函数Stm(),返回指针t;调用更多语句处理函数StmMore(),返回指针p,并使p成为t的兄弟节点。TreeNode*stmMore(void)37更多语句部分处理分析程序:根据文法产生式,由读入的单词判断执行哪个分支,即是否有更多的语句。TreeNode*stm(void)38语句递归处理分析程序:根据读入的单词和每条产生式的Predict集选择调用相应的语句处理函数。(书中有更详细的说明)TreeNode*assCall(void)39赋值语句和过程调用语句部分的处理分析程序:根据读入的单词选择相应的处理程序(赋值语句处理程序和过程调用语句处理程序)TreeNode*assingmentRest(void)40赋值语句处理分析函数:根据文法产生式,创建新的赋值语句类型语法树节点。匹配保留字EQ,调用表达式函数E*p()。TreeNode*conditionalStm(void)41条件语句处理分析程序:该函数根据文法产生式匹配保留字IF,调用表达式函数E*p();匹配保留字THEN,调用语句序列函数StmL()(此处的语句序列函数不同于前处的StmList()函数。具体更详尽的说明见书P104)TreeNode*loopStm(void)42循环语句处理分析程序:根据文法产生式,匹配保留字WHILE,调用表达式处理函数E*p(),再匹配保留字DO,调用语句序列处理函数,最后匹配保留字ENDWH。TreeNode*inputStm(void)43输入语句的处理分析函数:根据文法产生式,创建新的输入语句类型语法树节点t,匹配保留字READ,LPAREN,记录标识符名称,匹配ID,RPAREN。TreeNode*outputStm(void)44输出语句处理分析程序:根据文法产生式,创建新的输出语句类型语法树节点t,匹配保留字WRITE,LPAREN,调用函数E*p(),匹配保留字RPAREN。TreeNode*returnStm(void)45过程返回语句处理分析程序:根据文法产生式,创建新的过程返回类型的语法树节点t,匹配保留字RETURN。TreeNode*callStmRest(void)46过程调用语句处理分析程序:根据文法产生式,创建新的过程调用类型语法树节点t,匹配保留字LPAREN,调用实参处理分析函数ActParamList(),匹配RPAREN。TreeNode*actParamList(void)47实参部分处理分析程序:根据读入的单词选择执行哪段程序(有参数或者无参数)TreeNode*actParamMore(void)48更多实参部分处理分析程序:根据读入的单词决定执行哪段分支程序。TreeNode*e*p(void)49表达式递归处理分析程序:根据文法产生式,调用相应的递归处理函数。TreeNode*simple_e*p(void)50根据文法产生式,调用相应的递归处理函数。TreeNode*term(void)51项递归处理分析程序:根据文法产生式,调用相应的递归处理函数。TreeNode*factor(void)52因子递归处理分析程序:根据文法产生式,调用相应的递归处理函数。TreeNode*variable(void)53变量处理程序:根据产生式,生成一个新的表达式节点t,如果创建成功且当前单词为ID,则记录并匹配ID,调用变量其余部分处理函数variMore(t),返回t。voidvariMore(TreeNode*t)54变量其余部分处理程序:根据文法产生式,调用相应的递归处理函数。如果当前单词为ASSIGN等,则不作任何处理;如果当前单词为LMIDPAREN,则调用E*p()处理表达式;如果当前单词为DOT,则匹配DOT处理域变量函数fieldvar();否则读下一个TOKENTreeNode*fieldvar(void)55域变量部分处理过程:根据文法产生式,生成一个新的表达式节点t,如果创建成功且当前单词为ID,则记录并匹配ID,调用域变量其余部分处理函数fieldvarMore(t),返回tvoidfieldvarMore(TreeNode*t)56域变量其他部分处理过程:根据文法产生式,调用相应的递归处理函数。如果当前单词为ASSIGN,则不作任何处理;如果当前单词为LMIDPAREN,则调用E*p()处理表达;否则读入下一个TOKENvoidmatch(Le*Typee*pected)57终极符匹配处理程序:该函数将当前单词与函数参数给定的单词相比较,如果一致则取下一个单词,都则退出语法分析程序。(五)、递归下降语法分析程序中各函数的调用关系函数parseMain()函数parseMain()函数Program()函数Program()函数ProgramHead()函数ProgramHead()函数DeclarePart()函数DeclarePart()函数TypeDec()函数TypeDec()函数TypeName()函数TypeName()函数VarDec()函数VarDec()函数ProcDec()函数ProcDec()函数ParamList()函数ParamList()函数ProcDecPart()函数ProcDecPart()函数ProcBody()函数ProcBody()函数ProgramBody()函数ProgramBody()函数StmList()函数StmList()函数Stm()函数Stm()函数E*p()函数E*p()函数simple_e*p()函数simple_e*p()函数term()函数term()函数factor()函数factor()图3.主要函数的调用关系(六)、递归下降语法分析实验总结与体会通过将近8周的学习和编程,我实现了递归下降语法分析程序的基本功能并且通过调试可以使得程序能正确运行并产生预期的结果。虽然我参考了书中很多容才将本模块程序实现,但是这个过程使我受益匪浅:我学会了上网搜集相关容和源程序;也锻炼了我的学习和调试程序的能力;同时对于一些不完善的细节我通过自己的努力进行重新编程和纠错,最终完成了程序的相关功能;更重要的是我学会了与小组成员更好更高效地交流和合作,从而使得我们能顺利地完成了这次编程任务。大学三年以来,我们还是第一次接触这么大型的程序开发,感觉很新鲜,很有成就感,这个过程也使得我们对高级语言编程更加熟练。不过由于时间和个人能力有限,我的源程序很大一部分都参考了实验书中的容,所以实验效果不是很理想。不过通过这次实验确实让我学到很多,我也会借此继续完善和强化自己,争取能不断地提高自己编程和实践能力,为以后的研究生学习和工作打下基础。第五部分符号表管理与语义分析一、概述语义分析(SemanticAnalysis)是任何编译程序必不可少的一个阶段,也是编译程序中最实质性的工作。在整个编译过程中,词法分析和语法分析是对源程序形式上的识别和处理,而语义分析程序是对源程序的语义做相应的处理工作。语义分析的主要任务是针对语法分析后得到的部结构进行语义处理,既要做相应的语义分析检查工作,还要为后面的编译工作提供足够的信息,这些信息包括标识符的语义信息、类型信息、过程信息等。二、语义分析的主要任务构造符号表和信息表进行语义错误检查,主要包括标识符重复定义、标识符未声明、标符类型不符、引用不合法等如果语义分析无错误,输出符号表容,否则将错误信息输出到指定文件中符号表格式标识符名:类型信息层号偏移直/间接变量示例a:intTyvarkindLevel=0Offset=7dir错误信息格式(行号):标识符名错误信息示例(5):a未声明的类型标识符三、具体实现符号表设计与实现本程序的符号表的构建是使用驻留法进行实现的,从而在程序运行结束时,所有标识符的信息均能继续保存在符号表中,方便进行后续操作,且使用驻留法实现的符号表更易理解和操作。SymbTable*scope[128];//符号标定义,大小为128intsymbtop;//存储符号表末尾下标,初始为-1typedefstructsymbtable{ charidname[10];//存储标识符名称,为“*”是表示一层的结束 AttributeIRattrIR;//存储标识符详细信息 intne*t;//当标识符名为“*”时有效,存储该层入口结点在scope中的位置}SymbTable;//符号表结点定义private:intstack[10];//存储每一层入口结点在scope中的位置voidPush(ints);//每进入新的一层时,将入口结点下标入栈intPop();//没离开一层时,将入口结点下标出栈intGetTop();//获取当前层的起始位置示例:SNL源代码为:programptypet1=integer;vart1v1;procedureq(varintegeri);procedurer(integerj)begin write(j);endbegin r(i); i:=i+10;endbegin read(v1); q(v1)end.则生成的符号表为:位置IdnameattrIRne*t0t1详细信息-11v1详细信息-12q详细信息-13i详细信息-14r详细信息-15j详细信息-16*NULL27*NULL4语义分析部分设计与实现整体设计有语义错误输出错误信息无语义错误打印

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论