语法分析部分的自上而下分析有两个方法,一个是递归分析法,另一个就是预测分析法。预测分析法最重要的一个环节就是构造预测分析表。接下来介绍一下如何用python来构造一个文法的预测分析表。
构造过程会用到求文法符号的FIRST集、FOLLOW集,或者求符号串的FIRST集合的算法,可以参考之前发的一篇文章 点击此处跳转
首先介绍一下预测分析表的结构,简单来说他就是一张表,表的两个属性分别是非终结符和终结符(包括‘#’),形如:
— | a | b | c | ( | # |
---|---|---|---|---|---|
E | |||||
E’ | |||||
F | |||||
F’ | |||||
A |
其中对应的内容是产生式的形式,若是没有产生式则可以写入标记来表示其匹配不到具体的产生式,进入报错处理程序。
根据文法构造预测分析表具体条件:
扫描全部产生式.
1.计算产生式的右部的FIRST集合,如果求出来的FIRST集合中包含终结符,那么就把这条产生式放入对应非终结符和终结符的格子中
2.如果ε在FIRST集合中,则计算该非终结符的FOLLOW集合,如果求出来的FOLLOW集合中包含终结符,那么就把这条产生式放入对应的非终结符和终结符的格子中
具体代码分析
接下来将对代码拆分分析,完整代码下载 点击此处跳转
在这里我定义形参lst,存储所有产生式,嵌套列表第一层为产生式,第二层为具体的每一条产生式,例如:
lst_demo = [["E","→","T","E","'"],["E","'","→","+","T","E","'","|","ε"],["T","→","F","T","'"],\
["T","'","→","*","F","T","'","|","ε"],["F","→","(","E",")","|","q"]]
# 根据文法构造预测分析表
import syntactic_parser_demo
import string
# 形参lst为文法的所有产生式,返回值为预测分析表table,其中最后两个元素分别为非终结符列表和终结符列表
def get_table(lst):
这里定义返回值为预测分析表table,其结构为一个嵌套列表,外层列表按照非终结符的顺序,内存列表按终结符的顺序。因此配合的,就要提前构造非终结符列表和终结符列表,来作为顺序。
s = [] # 非终结符列表
l = [] # 终结符列表
punc = string.punctuation
# 获取非终结符和终结符列表
for i in lst:
s.append("".join(i[:i.index('→')]))
for j in i:
if j.islower() or (j in punc) and (j not in l) and (j != "'") and (j != '|'):
if j == 'ε':
l.append('#')
else:
l.append(j)
l = list(set(l))
max_length = max(len(s),len(l))
# 定义预测分析表,以嵌套列表的形式存储,[[1,2,3],[],[]]外层列表按非终结符的顺序,内层列表按终结符的顺序
table = [['!' for i in range(max_length)] for i in range(max_length)]
有话说: 这里定义了一个空的嵌套列表,并且全部赋值为 ‘!’
获取接下来要用到的FOLLOW集合
follow = syntactic_parser_demo.get_follow(lst)
接下来判定的逻辑代码挺简单:
for p in lst:
temp_s = "".join(p[:p.index('→')])
one = s.index(temp_s) # 获取外层列表索引值
temp_split = [] # 将每一个产生式分割以便求的长度来进行分类讨论
# 获取这一条产生式的所有'|'的索引值
temp_or = []
temp_push = []
for k in range(len(p)):
if p[k] == '|':
temp_or.append(k)
# 获取这一条产生式'→'的索引值
temp_get = p.index('→')
# 转换成列表方便组合
temp_push.append(temp_get)
# 合并查找索引列表
temp_search = temp_push + temp_or
# 把一个产生式以'→'和'|'为分隔符分割,将分割后的数据存储在嵌套列表temp_split里
for m in range(len(temp_search)):
if len(temp_search) == 1:
temp_split = [p[temp_search[m] + 1:]]
else:
if m == (len(temp_search) - 1):
temp_split.append(p[temp_search[m] + 1:])
else:
temp_split.append(p[temp_search[m] + 1:temp_search[m + 1]])
for n in temp_split:
temp_first = syntactic_parser_demo.get_first_bunch("".join(n),lst)
for q in temp_first:
if q != 'ε':
two = l.index(q) # 获取内层列表索引值
table[one][two] = temp_s.split() + ['→'] + n
if 'ε' in temp_first:
temp_follow = follow[temp_s]
for z in temp_follow:
two = l.index(z)
table[one][two] = temp_s.split() + ['→'] + n
table.append(s)
table.append(l)
return table
可供测试的代码:
if __name__ == '__main__':
lst_demo = [["E","→","T","E","'"],["E","'","→","+","T","E","'","|","ε"],["T","→","F","T","'"],\
["T","'","→","*","F","T","'","|","ε"],["F","→","(","E",")","|","q"]]
get_table(lst_demo)