一、实验目的
设计实现一个LR编译器或一个完整的编译程序,掌握编译器的基本原理和实现技术。
二、实验内容
- LR(0)、SLR或LR(1)等语法分析器。 2. 基于实验一(词法分析器)和实验二(LL<1>等语法分析器)的基础上,完成一个完整的编译器。 3. 自行选题
三、实验环境
WIN10 + Python3.8
四、实验要求
1.对于内容一LR分析器的要求
(1)自行定义两种文法,可参照教材和实验指导书。
(2)LR分析表需要自动生成。
2.对于内容二编译器的要求
(1)输入源代码可参考实验一,自行定义文法。
(2)输入源代码可以根据兴趣确定,自行定义文法。如数学计算式的识别。
3.对于内容三的要求 自行选题难度不少于内容一和内容二。
五、实验原理(及其相关流程图)
构造SLR(1)分析表的步骤流程图:
已知有文法G[E]如下:
则其拓广文法G’[S’]如图:
应该得到的该文法G’[S’]的DFA为:
实验代码分析及实现流程图:
第一步:拓广文法
(1)数据结构:
kuoguangsentences = [] # 存放拓广后的语句列表 如B->BD|d => ["B->BD","B->d"]
Sentences = {} # 语句字典,方便查找 如B->BD|d => {B:["BD","d"]}
(2)关键代码:
def kuoguang(sentences):
Sentences["S'"] = sentences[0].split("->")[0] # 文法开头增加S’-> ?
for i, value in enumerate(sentences): # 遍历语句,将每句语法中的|分开,并存放在语法字典中
begin = value.split("->")[0] # begin存放语句的开头,即 B->aA 去掉”->“拆分成 ["B", "aA"], 存放B
Sentences[begin] = [] # 初始化语法字典存放的非终结符,即 {B:[]}
for j in value.split("->")[1].split("|"):
Sentences[begin].append(j) # 语法字典增加非终结符对应的语句,即 {B:[]} => {B:["aA"]}
for i in Sentences:
for j in Sentences[i]:
kuoguangsentences.append(i + "->" + j) # 根据语法字典还原语句放入拓广语句列表中,方便打印,即{B:["aA"]} => "B->aA"
(3)流程图:
第二步:求LR0项目集
(1)数据结构:
itemsentences = [] # 存放所有项目语句的列表,方便打印
Item = {} # 项目集的字典,方便查找
(2)关键代码:
# 遍历语法字典,将每个位置的情况都添加,即B->aA => ["B->·aA","B->a·A","B->aA·"]
def LR0item(Sentences):
for i in Sentences: # 遍历语法字典中的非终结符
Item[i] = [] # 初始化对应非终结符的项目集列表
for j in Sentences[i]: # 遍历对应非终结符的语句
for n in range(len(j) + 1): # 读取语句中的每一个符号对应的位置
list_p = list(j) # 将语句转化为列表方便操作
list_p.insert(n, "·") # 在相应的位置插入"·"
q = "".join(list_p) # 再将语句转化为str类型
itemsentences.append(i + "->" + q) # 存放列表当中
Item[i].append(q) # 存放字典中
(3)流程图:
第三步:求LR(0)项目集规范族
(1)数据结构:
StandardLR0item = {} # 存放DFA的字典,即:{I0:["B->bD".....],I1:["D->·cB".....],....}
num = 0 # 记录项目集的下标,即:I0,I1.....In
after = [] # 记录当前项目集其中一个语句进入下一状态集的符号,即: E->a·A => E->aA· ,存放A => ["A"]
itemfirst = [] # 存放用于求闭包集合的语句的列表
check = {} # 存放当前集合前往另一状态集合的记录的字典, 即: I0 --A--> I1 , 存放{I0:["A","I1"]}
(2)关键代码:
def Standarditem(item, cengshu): # item为对应的项目集In,层数为项目集的下标数字n
global num # 记录项目集的下标
after = {} # 记录当前项目集其中一个语句进入下一状态集的符号,即: E->a·A => E->aA· ,存放A => ["A"]
clourse = [] # 记录查询过的闭包集合
for i in item: # 遍历项目集中的语句
last = i.split("·")[1] # 获得"·"之后的符号串
if last == '': # 如果"·"之后没有符号串,则不进入下一状态,直接遍历下一语句
continue
str1 = i.replace('·', "") # str1存放去除"·"的语句,即:B->a·D => B->aD
str1 = list(str1) # 将str1转化为列表,方便操作
str1.insert(i.find("·") + 1, "·") # 在后一位符号插入"·", 即:B->a·D => B->aD·
str1 = "".join(str1) # 将str1转化为符号串
if last[0] not in after: # last[0]即为原语句中"·"的后一位符号,判断是否已经根据这个符号探寻过
for p in range(1, len(StandardLR0item) - 1): # 判断上一次探寻的项目集是否已经包含在另一个项目集当中
if is_subsequence(StandardLR0item[num], StandardLR0item[p]): # 使用is_subsequence()函数来判断前一列表是否包含在另一个列表当中,如果包含,则返回真,并进行如下操作
StandardLR0item.pop(num) # 若包含,则删除这一项目集合
num -= 1 # 项目集合下标-1
if cengshu in check: # 记录这一个集合根据状态n,指向前面已经探寻过的项目集当中
check[cengshu].update({StandardLR0item[p][0].split("·")[0][-1]: p})
else:
check.update({cengshu: {StandardLR0item[p][0].split("·")[0][-1]: p}})
break
num += 1 # 如果该状态没探寻过,则下标+1,创建新的项目集合
num1 = num # num1记录上一个项目集的下标
after[last[0]] = num # 记录来源,状态+原项目集合的下标
clourse = [] # 初始化记录查询过的闭包集合
if cengshu in check: # 记录这一个集合根据状态n,指向后面已经未探寻过的项目集
check[cengshu].update({last[0]: num})
else:
check.update({cengshu: {last[0]: num}})
else:
num1 = after[last[0]] # 如果存在,那么返回对应状态指向的项目集的下标
for j in itemsentences: # 遍历项目集的语句
if str1 == j: # 如果遍历的语句与新的语句str1相等,那么存放对应项目集的列表当中
if num1 not in StandardLR0item:
StandardLR0item[num1] = [j]
else:
if j not in StandardLR0item[num1]:
StandardLR0item[num1].append(j)
else:
continue
if i.index("·") + 2 < len(i): # 判断是否增加闭包集合,假如新语句的"·"已经是最后一位,则不用判断
v = last[1] # v是原语句中"·"对应后两位的符号
if v.isupper() and num in StandardLR0item and v not in clourse: # 如果是非终结符,则在对应的项目集中增加其闭包集合
p = Closure(v) # 求原语句中"·"对应后两位的符号的闭包集合
p.Create()
StandardLR0item[num][1:1] = CLOUSRE[v] # 在对应的项目集中增加其闭包集合
clourse.append(v)
for p in range(1, len(StandardLR0item) - 1): # 最后一句语句中需要再次判断上一次探寻的项目集是否已经包含在另一个项目集当中
if is_subsequence(StandardLR0item[num], StandardLR0item[p]): # 使用is_subsequence()函数来判断前一列表是否包含在另一个列表当中,如果包含,则返回真,并进行如下操作
StandardLR0item.pop(num) # 若包含,则删除这一项目集合
num -= 1 # 项目集合下标-1
if cengshu in check: # 记录这一个集合根据状态n,指向前面已经探寻过的项目集当中
check[cengshu].update({StandardLR0item[p][0].split("·")[0][-1]: p})
else:
check.update({cengshu: {StandardLR0item[p][0].split("·")[0][-1]: p}})
break
(3)流程图:
第四步:生成SLR分析表
(1)数据结构:
VC = [] # 存放非终结符
VT = [] # 存放终结符
ACTION = {} # 存放ACTION表
GOTO = {} # 存放GOTO表
(2)关键代码:
def CREATETABLE(): # 根据项目集更新ACTION和GOTO表
for v in VT: # 初始化ACTION表
for n in range(len(StandardLR0item)):
if v in ACTION:
ACTION[v].update({n: ""})
else:
ACTION.update({v: {n: ""}})
for v in VC: # 初始化GOTO表
for n in range(len(StandardLR0item)):
if v in GOTO:
GOTO[v].update({n: ""})
else:
GOTO.update({v: {n: ""}})
if "S'" in GOTO: # GOTO表去除S‘
GOTO.pop("S'")
for i in StandardLR0item: # 循环项目集
for j in StandardLR0item[i]: # 循环项目集中的语句
if j.find("·") + 1 == len(j): # 若·符号是结尾,判断是否执行acc操作还是归约操作
str1 = j.split("·")[0]
n = str1.split("->")[0] # 存放语句对应的非终结符
if n == "S'": # 如果非终结符为S'则更新acc到ACTION表中
TABLE('#', i, "acc")
else:
for p in range(len(kuoguangsentences)):
if str1 == kuoguangsentences[p]:
for m in FOLLOW[n]: # 循环在该非终结符对应的FOLLOW集合中的终结符,更新其对应的归约操作到ACTION表中
TABLE(m, i, "r" + str(p))
else:
v = j.split("·")[1][0] # 判断是移进操作,更新到对应的ACTION表中
TABLE(v, i, check[i][v])
(3)流程图:
六、实验运行结果图
本小组在完成SLR语法分析器的同时也实现了其可视化工作,如下图:
实例文法为:
E->E+T|T
T->TF|F
F->(E)|i
实例输入串为:
i+ii (归约顺序:64264631)
(i+i)*i (归约顺序:64264154632)
第一步:得到拓广文法
第二步:得到LR0项目集
第三步:构造LR0项目集规范族的DFA
(上述步骤基本与LR0相同,在SLR中,根据DFA构造SLR分析表的时候需要用到FOLLOW集)
因此第四步应该是:生成FOLLOW集
第五步:生成分析表
额外功能
1.栈分析
输入串(i+i*i)进行栈分析,归约顺序:64264631
输入串((i+i)*i)进行栈分析,归约顺序:64264154632
2.生成DFA构造图
3.其它文法展示
(1)
S->bAS|bA
A->aSc
(2)
S->BB
B->aB|b
(3)
S->aAcBe
A->b
A ->Ab
B->d
(4)
A->G=E
E->E+T|E-T|T
T->TF|T/F|F
F->(E)|i
G->i
(5)
E->E+E|EE|(E)|i I7和I8的二义性未解决
**
要源码的留邮箱
**