本文参考自: 原文地址
程序功能描述完成以下描述赋值语句 SLR(1)文法语法制导生成中间代码四元式的过程。
G[A]:
A→V=E
E→E+T∣E-T∣T
T→T*F∣T/F∣F
F→(E)∣i
V→i
[设计说明] 终结符号i为用户定义的简单变量,即标识符的定义。
[设计要求]
(1)构造文法的SLR(1)分析表,设计语法制导翻译过程,给出每一产生式对应的语义动作;
(2)设计中间代码四元式的结构;
(3)输入串应是词法分析的输出二元式序列,即某赋值语句“专题1”的输出结果,输出为赋值语句的四元式序列中间文件;
设计两个测试用例(尽可能完备),并给出程序执行结果四元式序列。
主要数据结构描述
在实验中主要使用结构体进行各类属性的封装。此外,还用到了stack和list的结构,其中stack主要由数组进行简易实现,便于输出栈内内容;list使用C++ STL中的list进行动态的添加。以下对每一部分进行详细说明。
此处使用sentence结构体对于项目集族中的每个产生式的属性进行封装。finish表示该产生式是否完成规约,即点操作是否进行完毕;tag即用来表示当前点的位置,因产生式包含有左部和->符号,故从3开始计数;string类的str用于存储产生式的字符串。
typedef struct
{
int finish = 0; //是否结束
int tag = 3; //当前圆点的位置
string str;
}sentence;
此处用act结构体对构造的ACTION表进行存储。在实验中,我并没有使用二维数组进行ACTION表的存储,而是选用了一维的结构体:I代表输入的目前的状态,Vt代表输入的终结符号,tag用于表示是r类还是s类,action用于记录对应的r类的产生式标号或s类状态标号。
typedef struct
{
int I; //当前状态
char Vt; //终结符号
char tag; //r||s
int action; //动作
}act;
同上,此处使用结构体go对GOTO表进行存储。I表示输出的当前状态,Vn表示输入的非终结符,next_I表示GOTO的转移结果,形如GOTO(I, Vn) = next_I。
typedef struct
{
int I; //当前状态
char Vn; //非终结符
int next_I; //转移状态
}go;
此处使用I进行构造项目集族时状态的存储。number对应于该项目的序号,如I0/I1等;l则为list<sentence>类型,用于存储该状态中的产生式。
typedef struct
{
int number; //编号
list<sentence> l;
}I; //状态
此处使用siyuanshi进行四元式的存储,形同四元式:(op, arg1, arg2, result)。在此不再赘述。
typedef struct
{
char op;
char arg1;
char arg2;
char result;
}siyuanshi;
此处使用var进行符号表的存储。在构造四元式的过程中,需要对于上一次的操作中新生成的结果进行记录,以便生成新的四元式时作为参数加入其中;故结构体中的name代表某一运算结果的变量名,如A/B/C/D;string类的value表示该变量对应的算术表达式(属性)。
typedef struct
{
char name;
string value; //place
}var; //变量
以下是一些变量的定义。在这些过程中用到了大量的list:首先进行状态集的定义,以list的形式对状态进行存储;之后是list-char的FIRST与FOLLOW集的定义;之后是list形式的ACTION表和GOTO表的定义,以及list-char的Vn/Vt集。最后,input用来存储输入串,在分析过程中也会使用input来主要进行分析。
list<I> DFA; //状态集
list<char> *First; //FIRST集
list<char> *Follow; //FOLLOW集
list<act> ACTION; //ACTION[i][j]
list<go> GOTO; //GO[i][j]
list<char> Vt; //终结符号集
list<char> Vn; //非终结符号集
char input[100];
下面是分析栈、符号栈以及其属性栈的定义,分别存储状态号、符号以及其代表的属性,并且设立栈顶指针。下面使用list结构进行四元式的存储、记录可以产生四元式的产生式标号以及变量表中变量的存储。最后,在G[]数组中,进行文法的定义。本实验为自动造表,故此处可修改文法,仅作细微改动即可。
int S[100]; //分析栈
int S_top = 0; //分析栈栈顶
char T[100]; //符号栈 栈顶与分析栈总相同
string value[100]; //存储符号的属性
list<siyuanshi> L_sys; //四元式
list<int> sys; //产生四元式的状态集
list<var> V; //变量表
int result_count = 0;
string G[11] = { "S->A", "A->V=E","E->E+T","E->E-T","E->T","T->T*F","T->T/F","T->F","F->(E)","F->i","V->i" };
程序结构描述:
整个程序较为庞大,自动构造文法分析所需的表,包括Vt/Vn/FIRST/FOLLOW/ACTION/ GOTO/变量表,之后进行SLR(1)分析,并且构造四元式。在这里进行详细说明:
完成整个程序的思路是:
->读取文件(获取输入的实验一运行结果、得到表达式)
->扫描文法(构建终结符号、非终结符号集、First/Follow集)
->造表(构造项目集族、构造ACTION/GOTO、设置四元式)
->SLR分析(判断是否可以被分析程序接受、生成四元式)
读取文件:readfile()将文件中的实验一的运行结果读入,整合为字符数组,准备好以待匹配,同前几次试验,在此不做赘述。
扫描文法:scan()将文法数组G[]中的string字符串进行扫描,构造终结符号集和非终结符号集;之后按照SLR(1)文法中FIRST集和FOLLOW集的构造方法进行这两个集合的构造。在构造FIRST集时,用到了find函数,用来不断地递归查找产生式首符号为非终结符号的产生式,以构造FIRST集;在构造FOLLOW集时,使用到了findVn函数,用来返回指定的非终结符对应的标号,以进行FOLLOW集的计算。同时,通过不断的循环、加入,当所有非终结符的FOLLOW集总大小不变时,跳出循环,构造完成。至此,造表前的准备工作全部完成,接下来进入造表阶段。
造表:table()完成整个项目集族的生成以及ACTION/GOTO的构造,以及在造表过程中对于四元式的标注,也是个人认为整个程序中最难的部分。
首先,进行初始设定:添加文法的起始符号对应的产生式,用以从初态进行迭代;之后将该产生式(点还处在最开始的部位tag=3, 尚未进入规约状态finish=0)加入第一个状态I0中,接下来进行一波循环操作:首先对于当前所有的项目集族进行闭包操作,该功能被封装在闭包函数closure()中(函数中对于每一个点操作的非终结符均进行闭包操作,加入该状态中,最终返回经过闭包操作的该状态);之后对于当前状态的每一条待移进的产生式分别进行移进操作,并添加为新的状态,等待循环到该状态时的闭包操作。在移进过程中,对于不是规约的(即尚可移进的)产生式,若点操作为非终结符,置GOTO(当前状态, 非终结符) = 新状态号;若为终结符,则置ACTION(当前状态,终结符号) = s+新状态号。若存在移进时的相同符号,则加入同一状态中,不再另设新状态。之后即为对于新状态的加入判断:若新状态的闭包操作closure()已经在已形成的项目集族中出现了,则不再添加这个新状态,并且修改对应的ACTION或者GOTO(一个pop()操作+一个push()操作);否则添加。对于需要规约的产生式,首先对其进行判断是否可以产生四元式:若可以,则进行记录,以便之后判断;否则不进行记录。至此,造表结束,接下来正式进入分析阶段。
SLR分析:SLR()完成最终整个SLR(1)文法的分析任务,也包括分析过程中完成的四元式构建。在分析过程起始时,首先将状态I0压入栈中,分析开始:若ACTION(栈顶,当前符号)不是acc(在程序中为r0),则开始循环:如果是err(在程序中使用0代替),报错;否则如果是sj,状态栈、符号栈、属性栈均压栈,读取下一个符号;如果是ri,则通过需要规约第i条产生式,对于三个栈,执行第i条产生式右部的符号数次pop()操作,同时更新属性栈,用以后续的四元式生成工作;在生成四元式时,首先判断正在规约的产生式是否可以产生四元式,同时确定四元式的op,其次判断两个参数(arg1,arg2)是否为变量or变量组成的表达式。如果是表达式,则查找之前的符号表,进行代替操作。若中间不报错而跳出循环,则成功匹配。至此,按照事先约定好的输出格式进行输出,分析任务完成。
程序测试:
测试用例
在本程序中,使用文件进行读取表达式的操作。测试输入串放在text.txt中,测试用例如下:
测试用例1:
12,<i>
32,<=>
12,<a>
23,<*>
28,<(>
12,<b>
21,<+>
12,<c>
29,<)>
33,</>
12,<d>
测试用例2:
12,<r>
32,<=>
28,<(>
12,<a>
21,<+>
12,<e>
29,<)>
33,</>
12,<b>
23,<*>
28,<(>
12,<c>
22,<->
12,<d>
29,<)>
测试结果
测试1:
读入实验一结果:
12,<i>
32,<=>
12,<a>
23,<*>
28,<(>
12,<b>
21,<+>
12,<c>
29,<)>
33,</>
12,<d>
输入串为:i=a*(b+c)/d#
终结符号集:=+-*/()i#
非终结符号集:SAVETF
FIRST
S i
A i
V i
E ( i
T ( i
F ( i
FOLLOW
S #
A #
V =
E # + - )
T # + - * / )
F # + - * / )
状态0:
S->·A
A->·V=E
V->·i
状态1:
S->A·
状态2:
A->V·=E
状态3:
V->i·
状态4:
A->V=·E
E->·E+T
E->·E-T
E->·T
T->·T*F
T->·T/F
T->·F
F->·(E)
F->·i
状态5:
A->V=E·
E->E·+T
E->E·-T
状态6:
E->T·
T->T·*F
T->T·/F
状态7:
T->F·
状态8:
F->(·E)