一、实验目的
1.学习消除文法左递归算法和提取左公共因子。
2.掌握预测分析法实现语法分析。
3.自动求得“FIRST”、“FOLLOW”集构造预测分析表。
3.能够使用自己编写的分析程序对简单的程序段进行语法翻译。
二、实验内容
用预测分析法编制语法分析程序,实现可以采用任何一种编程工具。
可用如下文法进行测试,见表一。
表1 语法分析单元的测试文法
编号 | 文法 |
A001 | E->aA E->bB A->cA A->d B->cB B->d |
三、实验要求
1*. 定义语言的语法规则。
2. 预测分析表的构造。
3*.对于遇到的语法错误,能够做出简单的错误处理,给出简单的错误提示,保证顺利完成语法分析过程。
四、设计思路和实现方法
算法实现主要包含以下步骤:
求出文法的First集:对于任意一个非终结符号A,求出其所能推导出来的终结符号的集合FIRST(A)。需要逐个考虑A产生的每个推导式子,如果式子的第一个符号是终结符,则将该符号加入FIRST(A);如果是非终结符,则将该非终结符对应的FIRST集与A的FIRST集取并集。需要递归地进行这个操作,直到所有的终结符和非终结符的FIRST集都已求出来。
求出文法的Follow集:对于任意一个非终结符号A,求出其可以出现在哪些符号后面,将这些符号加入到A的FOLLOW集中。对于文法的开始符号S,将$(文法结束符)加入到S的FOLLOW集中。需要逐个考虑每个产生式,将右部每个非终结符的FIRST集加入到其后继符号的FOLLOW集中,如果该非终结符后面还有其他符号,且后继符号的FIRST集中包含ε,则将该非终结符的FOLLOW集加入到后继符号的FOLLOW集中。需要递归地进行这个操作,直到所有的终结符和非终结符的FOLLOW集都已求出来。
构造LL(1)分析表:对于任意一个非终结符号A和终结符号a,如果存在A → α且a∈FIRST(α),则将A → α加入分析表M[A, a]中;如果存在A → α且ε∈FIRST(α),则将A的FOLLOW集中的所有符号加入到分析表M[A, a]中。需要逐一考察所有的非终结符和终结符,生成LL(1)分析表。
五、代码实现和算法分析
5.1定义文法和终结符、非终结符集合
grammar = defaultdict(list) # 字典,key是产生式左端,value是产生式右端
non_terminals = set() # 非终结符
terminals = set() # 终结符
# 读取无左递归文法
with open("C:\\Users\\M\\Desktop\\PycharmProjects\\BY2\\test.txt") as f:
for line in f:
left, right = line.strip().split("->")
non_terminals.add(left) # 将产生式左侧加入非终结符集
productions = right.split("|")
for p in productions:
grammar[left].append(p)#产生式右侧加入字典
for q in p:
if q!='@':
terminals.add(q)#将产生式右侧里的所以有字符除了@加入终结符
terminals -= non_terminals#将非终结符取出得到终结符集
first = {}
follow = {}
5.2获取每个非终结符的First集合
for non_term in non_terminals:
first[non_term] = set()
def get_first(symbol):
for prod in grammar[symbol]:
if prod[0] in terminals:
first[symbol].add(prod[0])#若X->a…,则将终结符a加入FIRST(X)中;
elif prod[0]=='@':
first[symbol].add('@')#若X->ε ,则将ε加入FIRST(X)中;
#若 X->BCD…E,则将First(B)所有元素(除了空集)加入 First(X),然后检测First(B),若First(B)中不存在空集,
# 即ε,则停止,若存在则向B的后面查看,将First(C)中所有元素(除了空集)加入First(X),然后再检测First(C)中是否有ε…直到最后,
# 若E之前的所有非终结符的First集中都含有ε,则检测到E时,将First(E)也加入First(X),若First(E)中含有ε,则将ε加入First(X)。
elif prod[0] in non_terminals:
for p in prod:
for q in get_first(p):
if q != '@':
first[symbol].add(q)
if '@' not in get_first(p):
break
if p==prod[len(prod)-1]:
if '@' in p:
first[symbol].add('@')
return first[symbol]
for non_term in non_terminals:
get_first(non_term)
5.3获取每个非终结符的Follow集合
for non_term in non_terminals:
follow[non_term] = set()
follow['E'].add("#")#向开始符的follow集加入#
def get_follow(symbol):
for left, prods in grammar.items():
for prod in prods:
if symbol in prod:
idx = prod.index(symbol)#求非终止符symbol在产生式中的位置
if idx != len(prod) - 1: # symbol后还有其他符号情况,一个个遍历
for i in range(idx + 1, len(prod)):
if prod[i] in terminals:#若为终结符直接加入并跳出循环
follow[symbol].add(prod[i])
break
else:#若为非终结符
for p in first[prod[i]]:
if p!='@':
follow[symbol].add(p)#将去除@的first集加入
if '@' not in first[prod[i]]:
break#@不在first集中则跳出循环
if i==len(prod)-1:
follow[symbol]|=get_follow(left)#若@存在于first集中且已经遍历到最后则将左侧的follow集赋给右侧follow集
else: # symbol在产生式右端末尾
if left!=symbol:
follow[symbol]|=get_follow(left)#将左侧的follow集赋给右侧follow集
return follow[symbol]
for non_term in non_terminals:
get_follow(non_term)
5.4构造LL(1)分析表
table = defaultdict(dict)
for non_term in non_terminals:
for term in terminals.union(set("#")):
table[non_term][term] = ""
for left, prods in grammar.items():
for prod in prods:
first_set = set()
if prod != '@':
for i, symbol in enumerate(prod) :
if symbol in terminals:
first_set.add(symbol)
break#若为终结符直接加入并跳出
elif '@' in first[symbol]:
first_set |= first[symbol] - set("@")#将除了@的first集合加入
elif i==len(symbol):
first_set |= first[symbol]#当遍历到最后则可以直接将first集加入
else:
first_set |= first[symbol]
break#如果first集没有@则直接加入并跳出循环
else:
first_set |= set("@")
for term in first_set:#能推导出表中的终结符则直接将产生式填充
if term in table[left]:
table[left][term] = prod
if "@" in first_set: # 产生式能够推导出空串,则用Follow集合填充
follow_set = follow[left]
for term in follow_set:
if term in table[left]:
table[left][term] = prod
# 输出LL(1)分析表
terminals.add('#')
print(' '+f"{'':^1}{'|':^4}".join(terminals))
print(' -------------------------------------')
for non_term in non_terminals:
row = f"{non_term:^10}|"
for term in terminals:
if term in table[non_term]:
prod_str = " ".join(table[non_term][term])
row += f"{prod_str:^5}|"
else:
row += " " * 7 + "|"
print(row)
六、案例运行结果
运行文法
构造结果
运行文法
构造结果