SNL编译器设计实验报告.docx

上传人:b****4 文档编号:11579175 上传时间:2023-03-19 格式:DOCX 页数:31 大小:928.16KB
下载 相关 举报
SNL编译器设计实验报告.docx_第1页
第1页 / 共31页
SNL编译器设计实验报告.docx_第2页
第2页 / 共31页
SNL编译器设计实验报告.docx_第3页
第3页 / 共31页
SNL编译器设计实验报告.docx_第4页
第4页 / 共31页
SNL编译器设计实验报告.docx_第5页
第5页 / 共31页
点击查看更多>>
下载资源
资源描述

SNL编译器设计实验报告.docx

《SNL编译器设计实验报告.docx》由会员分享,可在线阅读,更多相关《SNL编译器设计实验报告.docx(31页珍藏版)》请在冰豆网上搜索。

SNL编译器设计实验报告.docx

SNL编译器设计实验报告

目录

 

第一部分实验成果统计表…………………………………………………………1

第二部分实验简介…………………………………………………………………2

第三部分词法分析…………………………………………………………………3

第四部分语法分析…………………………………………………………………6

4.1LL

(1)法语法分析…………………………………………………………7

4.2递归下降法语法分析……………………………………………………10

第五部分语义分析…………………………………………………………………19

第六部分程序测试…………………………………………………………………22

第七部分实验总结与体会…………………………………………………………28

第一部分实验成果统计表

姓名

性别

班级

学号

所占比例

个人成绩

任务分工:

(请用小四号宋体填写)

词法分析部分程序的调试与实现;

LL

(1)语法分析部分程序的调试与实现;

递归下降语法分析部分程序的调试与实现;

语义分析部分程序的调试与实现;

成绩评定:

词法分析

递归下降

LL

(1)

语义分析

团队成绩

教师签章

备注

填写说明:

1、请将首页红色部分信息填全,其中:

班级为2位数字,保留首位的0;学号为8位数字,计算机科学与技术学院以53开头;所占比例为百分数,精确到个位数,且所有人的所占比例之和为100%;不足四人的分组请保留后面的多余空行,请勿修改该表的结构。

2、请根据实际情况填写任务分工部分,主要任务包括:

编译系统的总体分析与设计,四个具体功能的设计与实现,对应的测试与验证过程(报告正文需要列出若干组具体测试样例与对应结果),系统界面的设计与美工,以及辅助工具、视图和文件等。

3、成绩评定部分由指导教师填写,请勿填写和修改。

 

第二部分实验简介

本实验中实现了SNL编译系统中的词法分析、语法分析和语义分析。

其中语法分析包括递归下降分析方法和LL

(1)分析方法。

词法分析,以源程序为输入,生成单词的内部表示TOKEN序列。

语法分析,以TOKEN序列为输入进行语法分析,并生成整个源程序的语法分析树。

在SNL编译程序中,采用了两种语法分析方法实现:

LL

(1)和递归下降法。

两种语法分析的结果是一样的。

语义分析,以语法树为输入生成标识符的属性符号表以及相关的各类信息表,如数组信息表,并进行相关的语义检查。

它们三者的关系如下:

语义分析

第三部分词法分析

源程序一般表现为字符串(机器语言称其为ASCII码)序列的形式,而编译程序的翻译工作应该在单词一级上进行,这与自然语言的翻译理解过程是类似的。

因此要进行编译工作,首先要把源程序的字符序列翻译成单词序列。

词法分析是编译过程的第一阶段。

它的任务就是对输入的字符串形式的源程序按顺序进行扫描,根据源程序的词法规则识别具有独立意义的单词(符号),并输出与其等价的TOKEN序列。

TOKEN是单词(符号)的内部表示。

完成词法分析任务的程序称为词法分析程序,通常也称为词法分析器或扫描器(scanner)。

TOKEN是单词在编译程序处理过程中的一种内部表示,也是词法分析程序对程序中各类单词进行处理之后的输出形式。

对于一种语言而言,如何对它的单词进行分类,每一类单词的TOKEN数据结构的形式如何,都没有固定的模式,可以随编译程序的不同而不同。

通常TOKEN的结构可以分成两部分,单词的语法信息和语义信息。

其中语法信息记录的是这个单词的种类,语义信息则记录着这个单词的具体信息。

这样,就能为以后的语法分析和语义分析处理单词做好准备。

SNL语法分析对每类单词的分析结果的TOKEN结构为三元组(词法信息、语义信息以及该单词在源程序中的行号)。

本SNL编译器单词的内部表示如下:

行号(便于出错处理)

TOKEN的内部表示

语法信息及其字符串表示

语义信息及其字符串表示

 

SNL语言的单词分类如下:

(1):

保留字保留字一般是由语言系统自身定义的,SNL语言中的保留字有program,while,if,fi等等。

(2):

标识符用来标识程序中各个对象的名称。

由用户自己定义用来表示变量名,数组名或者过程名等。

SNL语言中标识符由字母开头,字母、数字的任意组合组成的。

(3):

常量用来表示各类常数。

如字符型常量,实行常量,布尔常量等。

(4):

运算符表示程序中算术运算、逻辑运算、字符运算的确定字符或字符串。

SNL语言中的运算符有+,—,*,/,<和:

=六种运算符。

(5):

界限符包括逗号分号等。

SNL语言中的‘;’表示一条语句结束,‘.’表示程序结束等等。

实现词法分析器的注意事项:

1.保留字和标识符名字的区分

2.复合单词的处理

3.向前搜索及回退

4.数字的转换

5.输入时边界的处理

6.注释的处理

词法分析主要的函数有:

getTokenlist()、getNextChar()、ungetNextChar()、reservedLookup()、ChainToFile()、printTokenlist()。

getTokenlist()函数是最主要的函数,它每次从输入缓冲区lineBuf字符串序列中超前读一个(有时是两个)字符,使用确定有限自动机DFA的直接转向处理方法。

超前读的字符如不匹配的话,还需要回退。

如果得到的TOKEN是标识符的话,还需要查询保留字表以判断该标识符是否是保留字。

产生词法错误的时候,仅仅略过产生错误的字符,不加改正。

然后将得到的TOKEN存入链表,当整个源程序都分析完成的时候,将TOKEN链表中各个TOKEN存入文件Tokenlist.txt中,将来输出显示TOKEN时再从Tokenlist.txt中读取。

getNextChar()函数被getTokenlist()函数调用,每次从输入缓冲区lineBuf中返回一个字符,如果lineBuf中的字符全部读完后,再从TOKEN文件中读取下一批。

如此重复下去,直到源程序字符全部读完为止。

ungetNextChar()函数被getTokenlist()函数调用,当文件结束标志FileEnd没有置位的时候,将lineBuf缓冲区中的读取指针回退一个字符。

reservedLookup()函数用在当前TOKEN是标识符的时候,需要查保留字表来判断该TOKEN是保留字还是普通标识符。

ChainToFile()函数用在源文件处理完毕后,将TOKEN链表中的TOKEN一个一个地存入文本文件Tokenlist.txt中。

将来可以直接在文本文件中查看tokenlist,也可以在需要输出显示的时候从该文件中读取TOKEN。

printTokenlist()函数用在需要在命令行输出显示Tokenlist的时候,从Tokenlist.txt中读取TOKEN,然后显示出来。

 

主要函数getTokenlist()的流程图如下:

Y

N

结束

调用ChainToFile将链表信息存入文件

curToken.Lex=ENDFILE?

将curToken的信息存入链表

入口

Y

从lineBuf中超前读入

下一个字符,

根据state

状态进行处理

N

state=DONE?

第四部分语法分析

语法分析是编译程序的第二阶段,也是编译程序的核心部分。

语法分析的任务是,根据语言的语法规则,对源程序进行语法检查,并识别出相应的语法成分。

按照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)分析程序工作过程首先初始化,即把开始符压入栈中,以后的每步分析必是下面的四种情况之一:

(1)分析栈的栈顶元素是终极符,则看其是否与输入流的头符相匹配,如果匹配成功,则去掉栈顶元素并读入下一个单词;若匹配不成功,则报错。

(2)栈顶是非终极符,则用栈顶和输入流的当前单词去查当前矩阵,如果查得的值是产生式编号,则把对应的产生式右部逆序压入栈中;如果查得的值为错误信息,则报错。

(3)栈已空,输入流不空,这时输入流报错。

(4)若栈已空,输入流也空,则语法分析成功。

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.txt文件中读取一个TOKEN

priosity()用于判断当前操作符的优先级。

优先级由高到低排序为:

乘法运算符>加法运算符>关系运算符>左括号>栈底标志END。

predict()函数根据产生式编号选择一个要执行的函数。

newnode()函数用于生成树节点。

TeeToFile()函数用于将最后产生的语法树写入SynTree.txt文件,该文

件可以查看语法树,也可以在需要显示输出时从中读取语法树。

printTree()函数用于从SynTree.txt中读出语法树,然后输出显示。

StrToEnum()用于将从Tokenlist.txt中读取的以字符串形式存在的TOKEN的Lex域转换成LexType枚举类型。

PushSym()用于将当前符号入栈到符号栈。

PopSym()用于将符号栈栈顶符号出栈。

PushSynTree()用于将当前语法树节点中指向儿子或兄弟节点的指针的地址入栈到语法树栈。

PopSynTree()用于将语法树栈栈顶指向树节点的指针的地址出栈。

PushOp()用于将当前操作符入栈到操作符栈。

PopOp()用于将操作符栈栈顶操作符出栈。

ReadOpStack()用于读取操作符栈栈顶操作符。

PushNum()用于将当前操作数入栈。

PopNum()用于将操作数栈栈顶操作数出栈。

LL

(1)语法分析主函数parseLL1()的算法框图如下:

4.2递归下降法

(一)、递归下降语法基本原理综述

语法分析是编译程序的第二阶段,语法分析的任务是根据语言的语法规则对源程序进行语法检查,并识别出相应的语法成分。

简单地说,语法分析就是根据语言的语法规则对语言的句子结构进行分析,看源程序的句子结构是否符合语言语法规则的要求。

语法分析的输入是从词法分析器输出的TOKEN序列形式,然后根据语言的文法规则进行分析处理,语法分析的输出是无语法错误的语法分析树的形式。

这一部分主要介绍递归下降法语法分析的原理及实现。

递归下降法是语法分析中比较容易理解的一种方法,因为其采用了递归的结构和思想,但这也在一定程度上影响了程序的执行效率,所以这种方法的优点是容易实现,容易理解,缺点就是这种方法的执行效率不如LL

(1)高。

递归下降法的主要原理是对每个终极符和非终极符按其产生式结构构造相应的语法分析子程序,其中终极符产生匹配命令,而非终极符则产生过程调用命令。

其中子程序结构与产生式的结构几乎是一致的。

前面我们已经提到对于终极符将产生匹配命令match(),match()命令的定义如下:

Match(x)——无定义,x属于Vn

——检查token=x?

若是,则移位;否则,报错,x属于Vt

根据递归下降法的基本原理和思想,我们可以将递归下降法语法分析主程序写成如下的形式,这个程序形式就是我我们所编写的递归下降法语法分析程序的主程序格式,这样就可以容易构造出一个文法的语法分析程序:

Programp

token:

类型;

Begin

读入一个toke

iftoken属于First(S)

thenS

else报错并停机

End

 

(二)、递归下降语法分析程序的主要处理对象及其依赖关系

我们编写的递归下降语法分析程序主要对一个程序的主要部分进行分析和检查,其中主要包括程序头、程序声明、类型声明、变量声明、过程声明、程序体(包括各种语句序列、表达式)等,下面对各部分做简要的说明:

程序头:

程序的开始,包括保留字以及程序的名字。

程序声明:

包括程序的整个声明部分,又可以细分为类型声明、变量声明、过程声明等。

类型声明:

用于定义类型,其中类型可以分为基本类型(整型、字符型等)、结构类型(数组类型、记录类型等)和自定义类型(类型名称为标识符,实际类型为上述两种类型)。

变量声明:

用于声明变量,定义变量类型。

过程声明:

用于声明过程,包括参数(变参、值参)、过程中的声明和过程体。

程序体:

由语句序列组成,语句主要包括赋值语句、条件语句、循环语句、输入语句、输出语句、返回语句、过程调用语句(可由读入的标识符或者其它相应的保留字来判定属于哪一种语句,并选择相应的程序进行分析。

)对语句的分析可能会涉及到对表达式的处理。

给出了递归下降语法分析程序所要分析处理的各种语法成分,为了能更清楚

直接地反映出各语法成分之间的依赖关系,参考实验教材上的内容,我们给出了

递归下降语法分析中各语法成分的依赖图,如下图所示:

总程序

 

程序头

 

程序声明

类型声明

 

变量声明

 

过程声明

 

参数声明

 

过程中的声明

 

过程体(主体程序)

 

语句序列

 

语句

 

表达式

 

图1.递归下降语法分析中各语法成分的依赖关系

(三)、递归下降语法分析输出结果——语法分析树

根据上面的叙述,我们知道递归下降语法分析程序的输入是通过词法分析程序产生的TOKEN序列,而作为语法分析程序的输出,我们给出了递归下降语法分析程序的统一规范化输出结果——语法分析树的基本形式。

我们编写的递归下降语法分析程序最终的输出结果就是下面语法树的形式,只不过我们是以另一种形式组织并保存在.txt文件中,但核心的思想就是下图所示的语法树形式。

该语法树形式不仅体现了语法分析程序的输出结果,还在一定程度上反映出了各语法分析成分之间的关系。

图2.语法分析树

(四)、递归下降语法分析程序主要函数介绍

根据之前的分析和叙述,我们已经对递归下降语法分析程序进行了最基本的阐述,这些基本的阐述可以使得我们很清楚地了解递归下降语法分析程序所要实现的功能、所要分析的对象以及优缺点等。

下面我们将对递归下降语法分析程序中的主要函数进行介绍,主要介绍函数的声明和功能等,并在最后给出各主要函数之间的调用关系。

按照这些函数的声明和调用关系,我们便可以基本实现递归下降语法分析程序的功能。

主要函数声明:

主要函数功能描述:

TreeNode*parseMain(void)

1.调用总程序处理分析函数:

创建语法分析树,同时处理文件的提前结束错误。

TreeNode*program(void);

2总程序处理分析程序:

生成语法分析树的根节点root,分别调用程序头部分、声明部分、程序体部分分析函数并成为语法树的3个节点

TreeNode*programHead(void)

3程序头部分分析处理函数:

根据读入的单词,匹配保留字PROTGRAM,记录程序名于程序头结点,匹配ID

TreeNode*declarePart(void)

4程序声明部分分析函数:

根据文法产生式创建新的类型声明标志节点、变量声明标志节点。

调用类型声明部分处理函数TypeDec()、变量声明部分处理函数VarDec()、过程声明部分处理函数ProcDec()

TreeNode*typeDec(void)

5类型声明部分处理函数:

根据读入的下个单词,若当前单词为TYPE,调用函数typeDeclaration()并赋值给t,则函数返回t,否则返回NULL

TreeNode*typeDeclaration(void)

6类型声明中的其他函数:

根据文法产生式,匹配保留字TYPE,调用函数TypeDecList()并赋值给t,若t为NULL则显示提示信息。

函数返回t

TreeNode*typeDecList(void)

7函数声明中的其它函数:

根据文法产生式,创建新的声明类型节点t。

若申请成功,调用函数TypeId(),匹配保留字EQ,调用函数TypeDef(),匹配保留字SEMI,调用函数TypeDecMore(),返回值赋值给t的成员sibling,函数返回t

TreeNode*typeDecMore(void)

8类型声明中的其它函数:

该函数根据读入的单词,或者调用函数TypeDecList()并赋值给t,则函数返回t;或者什么都不做,或者跳过此错误单词,读入下一个单词,返回NULL

voidtypeId(TreeNode*t)

9类型声明中类型标识符处理分析程序:

该函数根据读入的单词,判断TOKEN.Lex=ID的值,如果为真则将TOKEN.Sem字符串拷贝到参数t的成员attr.name[tnum]中,其中tnum是用来记录namne个数的临时变量。

然后再将tnum加1送回参数t的成员attr.idnum

voidtypeDef(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序列,函数返回t

voididList(TreeNode*t)

17记录类型域中标识符名处理分析程序:

该函数根据文法产生式,匹配标识符ID,记录标识符名称,调用递归处理函数IdMore()

voididMore(TreeNode*t)

18记录类型域中其他标识符名处理分析程序:

该函数根据文法产生式判断读入的单词,处理调用相应的函数

TreeNode*varDec(void)

19变量声明处理分析程序:

该函数根据文法产生式判断读入的单词,处理调用相应的函数。

如果处理成功则返回t,否则返回NULL。

TreeNode*varDeclara

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 成人教育 > 自考

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1