📅  最后修改于: 2023-12-03 15:41:05.957000             🧑  作者: Mango
在编译原理中,给定一种语法,通常需要通过计算其第一套和第二套来构建语法分析器。本文将介绍如何编写一个程序来计算给定语法的第一套和第二套。
第一套指的是文法G的每一个非终结符A能推出的首符集合,记作FIRST(A)。其中,首符指的是一个产生式右部可能的第一个符号。
我们可以通过递归求解式子的方式来计算每个非终结符的FIRST集合。
def compute_first(grammar, non_terminals):
# 初始化FIRST集合
first = {}
for nt in non_terminals:
first[nt] = set()
while True:
# 标记是否FIRST集合有改变
changed = False
for nt in non_terminals:
for production in grammar[nt]:
# 如果产生式右部第一个符号是终结符,则将其加入FIRST集合
if production[0] not in non_terminals:
if production[0] not in first[nt]:
changed = True
first[nt].add(production[0])
# 否则,将该产生式右部的非终结符的FIRST集合加入FIRST集合
else:
index = 0
add_epsilon = True
while index < len(production) and add_epsilon:
if production[index] in non_terminals:
add_epsilon = False
for symbol in first[production[index]]:
if symbol != "epsilon" and symbol not in first[nt]:
first[nt].add(symbol)
changed = True
if "epsilon" not in first[production[index]]:
add_epsilon = False
else:
if production[index] not in first[nt]:
first[nt].add(production[index])
changed = True
add_epsilon = False
index += 1
if add_epsilon:
if "epsilon" not in first[nt]:
first[nt].add("epsilon")
changed = True
# 如果FIRST集合没有改变,则计算结束
if not changed:
break
return first
假设文法G如下:
E -> T E'
E' -> + T E' | epsilon
T -> F T'
T' -> * F T' | epsilon
F -> ( E ) | id
其中,非终结符为E、E'、T、T'、F,终结符为+、*、(、)、id、epsilon。
我们可以通过调用compute_first函数,计算每个非终结符的FIRST集合:
grammar = {
"E": [["T", "E'"]],
"E'": [["+", "T", "E'"], ["epsilon"]],
"T": [["F", "T'"]],
"T'": [["*", "F", "T'"], ["epsilon"]],
"F": [["(", "E", ")"], ["id"]]
}
non_terminals = ["E", "E'", "T", "T'", "F"]
first_set = compute_first(grammar, non_terminals)
print(first_set)
输出结果为:
{
'E': {'(', 'id'},
"E'": {'+', 'epsilon'},
'T': {'(', 'id'},
"T'": {'*', 'epsilon'},
'F': {'(', 'id'}
}
第二套指的是文法G的每一个非终结符A所能推出的所有产生式右部的符号串的FIRST集合的并集,即FOLLOW(A)。其中,产生式右部的FIRST集合的并集为从该符号串开始可能的第一个终结符,以及可能的epsilon。
同样地,我们可以通过递归求解式子的方式来计算每个非终结符的FOLLOW集合。
def compute_follow(grammar, start_symbol, non_terminals, first_set):
# 初始化FOLLOW集合
follow = {}
for nt in non_terminals:
follow[nt] = set()
follow[start_symbol] = {"$"}
while True:
# 标记FOLLOW集合是否有改变
changed = False
for nt in non_terminals:
for production in grammar[nt]:
for i in range(len(production)):
symbol = production[i]
if symbol in non_terminals:
# 如果该非终结符不在产生式最后一个符号,则将其之后的符号加入其FOLLOW集合,即FIRST(其后一个符号)
if i < len(production) - 1:
next_symbol = production[i + 1]
if next_symbol in non_terminals:
for s in first_set[next_symbol]:
if s != "epsilon" and s not in follow[symbol]:
follow[symbol].add(s)
changed = True
if "epsilon" in first_set[next_symbol]:
for s in follow[nt]:
if s not in follow[symbol]:
follow[symbol].add(s)
changed = True
else:
if next_symbol not in follow[symbol]:
follow[symbol].add(next_symbol)
changed = True
# 否则,将该产生式所在非终结符的FOLLOW集合加入它的FOLLOW集合
elif nt != symbol:
for s in follow[nt]:
if s not in follow[symbol]:
follow[symbol].add(s)
changed = True
# 如果FOLLOW集合没有改变,则计算结束
if not changed:
break
return follow
假设文法G如下:
S -> A a
S -> b B
A -> c A
A -> epsilon
B -> d B
B -> epsilon
其中,非终结符为S、A、B,终结符为a、b、c、d、epsilon。
我们可以通过调用compute_follow函数,计算每个非终结符的FOLLOW集合:
grammar = {
"S": [["A", "a"], ["b", "B"]],
"A": [["c", "A"], ["epsilon"]],
"B": [["d", "B"], ["epsilon"]]
}
non_terminals = ["S", "A", "B"]
start_symbol = "S"
first_set = compute_first(grammar, non_terminals)
follow_set = compute_follow(grammar, start_symbol, non_terminals, first_set)
print(follow_set)
输出结果为:
{
'S': {'$', 'd'},
'A': {'a', 'b', 'd'},
'B': {'a', 'b', 'd'}
}
通过以上程序的实现,我们可以方便地计算给定语法的第一套和第二套。在实际应用中,这些集合有助于构建语法分析器,帮助解析程序语言。