算术表达式的语法分析及语义分析程序设计 —— LR分析法、输出三元式

来源:互联网 发布:龙卷风浏览器 mac 编辑:程序博客网 时间:2024/05/22 13:21

1.系统描述

1.1目的

通过设计、编制、调试一个算术表达式的语法及语义分析程序,加深对语法及语义分析原理的理解,并实现词法分析程序对单词序列的词法检查和分析。

1.2设计内容及步骤

1)根据要求构造相应的文法。
2)根据LR分析法的步骤在每个产生式的右部适当位置添加一个圆点构成项目。
3)构造识别活前缀的NFA。
4)LR项目集规范族的构造。
5)LR分析表的构造。
6)构造词法分析器。
7)模拟LR分析器的工作过程进行语法分析。
8)输出分析过程及三元式。

2.系统的详细设计

2.1 文法设计

本次设计具体文法需要满足的条件如下:
1) A->V=E
2) E->E+T |E-T|T
3) T->T*F|T/F|F
4) F->i|(E)
5) V->i

拓广文法:

0) A’->A; 1) A->V=E 2) E->E+T
3) E->E-T 4) E->T 5) T->T*F
6) T->T/F 7) T->F 8) F->i
9) F->(E) 10) V->i

2.2 文法项目

这里写图片描述

2.3 识别所有活前缀的DFA

FIRST和FOLLOW集如下:

A E T F V FIRST() {i} {i,(} {i,(} {i,(} {i} FOLLOW() {#} {#,),+,-} {#,),+,-,*,/} {#,),+,-,*,/} {=}

这里写图片描述

2.4 构造LR分析表

这里写图片描述

2.5 词法分析器

从键盘输入一个算数表达式,以“#”结束。对输入的字符依次进行判别,每当开始识别一个单词时,若扫视到的第一个字符为字母,则把后续输入的字母或数字字符依次进行拼接,直至扫视到非字母、数字字符为止,以期获得一个尽可能长的字母数字字符串,然后以此字符串查所谓保留字表(此保留字表已事先造好),若查到此字符串,则取出相应的类别码;反之,则表明该字符串应为一标识符。
采用上述策略后,针对表I中部分单词可以构造一个如图1所示的有限自动机(以状态转换图表示)。在图1中添加了当进行状态转移时,词法分析程序应执行的语义动作。根据图1,可用C++语言编写出符合以上几项要求的一个相应的扫描器程序。
图1 识别表I所列语言中的部分单词的DFA及相关的语义过程
图1 识别表I所列语言中的部分单词的DFA及相关的语义过程
鉴于本程序只用来进行算数表达式的语法分析,所以只对“+”、“-”、“*”、“/”、“(”、“)”、“=、“#”和变量(此处用i代替)的单独表示种别码,其余全部归为一类。具体如下:

其对应的存储方式如下:

typedef struct code{    string data;    int num;}Code;Code code[MAX];

2.6 LR分析器

将词法分析的输出即单词和种别码作为LR分析器的输入,构造状态栈state、分析栈ana、剩余输入栈sys、动作栈(ACTION词表、GOTO词表)和产生式表。其对应的存储方式分别为:

ACTION词表:

typedef struct action{    char para;    int val;}Action;

GOTO词表:

vector<Gene> gene;

产生式表:

struct Gene{//产生式    string left;    char* right;};vector<Gene> gene;

分别将对应的ACTION词表、GOTO词表和产生式词表用已有数据初始化。

根据LR分析法,将0入state栈,将“#”入ana栈,为了方便分析,sys栈直接有词法分析得到的code栈代替。

然后根据state栈的栈顶元素和code栈的栈顶元素寻找对应的ACTION词表(将state栈的栈顶元素和code栈的栈顶元素作为数组下标),找到对应的动作。若动作的para为“S”,即移进,则将code栈的栈顶元素取出,入ana栈。同时将对应动作的val入state;若动作的para为“R”,即规约,则根据对应产生式的长度,分别从ana栈和state栈中移出相应数量的单词。再根据state栈的栈顶元素和ana栈的最后一个元素查找GOTO词表(将state栈的栈顶元素和ana栈的最后一个元素作为数组下标),将其入state栈。

循环此过程,直至遇到“a”,即acc,则表明分析成功,表达式正确;否则,报错。

2.7 输出三元式和分析过程

在LR分析过程中,每循环一次,就将对应的state栈、ana栈、sys栈、动作栈和产生式表一次输出,即为分析过程。
在LR分析中,设置一个an栈,其操作与ana栈大致相同,不过只对含有规约所用的产生式中涉及到运算符时才会规约,其余时候的规约都不会执行。目的是为了保存原有操作数,方便三元式的输出。每次规约的过程中,若规约所用的产生式涉及到了含运算符的产生式的规约,则对an栈的最后三个单词进行调整(将运算符提前,第一运算数后移)输出。其中需要注意所含运算符为“(”“)”的情形,此时,不需要输出,但an栈还是要进行相应的规约。

3 用例分析

1)该用例是本分析程序的一个比较典型的简单的输入,得到结果如下:
这里写图片描述
2)当表达式包含变量或字母时,程序的运行结果:
这里写图片描述
3)当输入文法无法识别的字符时,程序会进行报错。程序的运行结果(这里是输入运算符“++”):
这里写图片描述
4)当输入的表达式不完整或不匹配时,程序会自动报错。
这里写图片描述
6)增加下难度,输入复杂表达式,输出结果如下(输入表达式为“i=(2*(1+3)+2)/2#”):
这里写图片描述

4.程序的评价

本程序最大的特点是先通过词法分析对输入的字符串进行识别,区别出一个个单词,并标以种别码,然后将其作为LR分析器的输入,由于是将输入的单词和运算符作为字符串处理,所以可以识别超过10的数字的运算,同样,由于状态栈、分析栈、剩余输入栈都是以字符串为单元来对输入处理的,所以还可以识别包括变量在内的算数表达式。功能相对而言较为全面。
在刚刚开始分析程序时,由于词法分析在上机实验时就已经实现了,而语法分析上机时时通过自顶向下的方法实现的,并且由于对原理理解得不够深刻,没有找到将词法分析的过程加入到语法分析中的方法。后来通过上网查找资料,发现通过种别码就可以将词法分析中得到的单词区分开来,同时将其作为语法分析参考的标准。但刚开始的时候在语法分析即LR分析时,是将得到的单词当做一个字符来处理,这样在分析栈和剩余输入栈中进、出栈时只需进出单个字符,比较好操作。但后来测试用例时,发现当输入“i=11+2#”时,在语法分析中“11”被当成了两个单独的字符“1”处理,而出现错误。于是,将字符串作为分析栈和剩余输入栈的基本单位,将字符串作为一个整体进栈和出栈,这样就能解决上述的错误,也能顺便解决含变量的算数表达式的分析。

2 0