编译原理——递归下降语法分析器

实验项目

完成以下描述赋值语句的LL(1)文法的递归下降分析程序

1
2
3
4
5
6
7
8
9
S→V=E
E→TE′
E′→ATE′|ε
T→FT′
T′→MFT′|ε
F→ (E)|i
A→+|-
M→*|/
V→i

设计说明

终结符号i为用户定义的简单变量,即标识符的定义

设计要求

(1)输入串应是词法分析的输出二元式序列,即某算术表达式“专题1”的输出结果,输出为输入串是否为该文法定义的算术表达式的判断结果;
(2)递归下降分析程序应能发现简单的语法错误;
(3)设计两个测试用例(尽可能完备,正确和出错),并给出测试结果

设计思路

接收输入

词法分析器输出文件

由设计要求中的(1)可知,语法分析器所能接受的输入为词法分析器所输出的二元组
首先我们来回顾一下词法分析器所输出的二元组的格式:
Alt text
可以看到,语句i=i+i)对应的词法分析输出二元组格式为:<number,token>的格式

number:编码数字
token:符号/字符串

显然,我们可以通过一个结构体来接受词法分析器所输入的二元组!

数据存储结构

我构造的结构体如下:

1
2
3
4
5
//记录词法分析器生成的二元组
typedef struct {
int num;
string token;
}two;

考虑到语句长度总是变化的,所以为了提高程序的鲁棒性,总的数据结构应定义为可变数组:

1
2
//记录全部的二元组
vector<two> text;

到此,数据储存结构的准备工作1已全部完成。

构造执行函数

根据递归下降分析的思想,我们首先需要对所有非终结符对应的产生式构造执行函数。
再者,根据设计要求,我们在进行字符匹配时仅使用编码two->num进行匹配即可

编码映射

在本题中所使用到的主要编码如下:

字符 编码
# -1
id 10
= 18
+ 19
- 21
* 23
/ 24
( 27
) 28

S()

对产生式:S→V=E,可以得到如下的递归调用流程。

由词法分析器的编码可知,match(18)中的18代表字符=

Alt text

E()

对产生式:E→TE′,可以得到如下的递归调用流程。
Alt text

E’()

对产生式:`E′→ATE′|ε,可以得到如下的递归调用流程。

由于产生式包含空动作E′→ε,所以在程序执行时需要判断是否读到E’的follow集元素,
其中编码28代表字符),编码-1代表字符#

Alt text

T()

对产生式:T→FT′,可以得到如下的递归调用流程。
Alt text

T’()

对产生式:T′→MFT′|ε,可以得到如下的递归调用流程。

由于产生式包含空动作T′→ε,所以在程序执行时需要判断是否读到E’的follow集元素,
其中编码19代表字符+,编码21代表字符-,编码28代表字符),编码-1代表字符#

Alt text

F()

对产生式:F→ (E)|i,可以得到如下的递归调用流程:

10代表用户定义标识符,27代表字符,28代表字符

Alt text

A()

对产生式:A→+|-,可以得到如下的递归调用流程:

19代表字符+,21代表字符-

Alt text

M()

对产生式:M→*|/,可以得到如下的递归调用流程:

23代表字符*,24代表字符/

Alt text

V()

对产生式:V→i,可以得到如下的递归调用流程:

10代表用户定义标识符

Alt text

测试

正确用例

对于语句:

1
i = i + i

编译结果为:
Alt text

对于语句:

1
i = i * (i/i)

编译结果为:
Alt text

错误用例

对于语句:

1
i = i + i )

编译结果为:
Alt text

对于语句:

1
i = i + -

编译结果为:
Alt text

至此,该递归下降语法分析器已全部符合题目要求

小手一抖⬇️