求first集,follow集,打印預測分析表,最后實現總控程序。
注釋寫的很清楚啦就不寫實驗思路了qwq(主要是懶
源代碼:
import sys
import operator
import copy
#終結符
terSymbol = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
'+','-','*','/','(',')','^']
#非終結符
non_ter = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
firstSET = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
followSET = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#求first集
def first(gra_line):
indexSET = []
x = gra_line[0] #x是開頭的非終結符
ind = non_ter.index(x) #firstSET[ind]是當前非終結符的first集
#找到所有產生式右部的第一個字符,下標值返回列表indexSET
i = 0
while i < len(gra_line):
if gra_line[i] == '>' or gra_line[i] == '|':
indexSET.append(i + 1)
i += 1
#判斷這些字符是否為終結符、空、非終結符,i是下標啊!gra_line[i]才是遍歷的那個字符
for i in indexSET:
if gra_line[i] in terSymbol or gra_line[i] == '@':
if gra_line[i] not in firstSET[ind]:
firstSET[ind].append(gra_line[i])
elif gra_line[i] in non_ter:
#先把gra_line[i]的first集的非空元素加進去
if len(firstSET[non_ter.index(gra_line[i])]) == 0:
for gr in grammer:
if gr.startswith(gra_line[i]):
first(gr)
break
for g in firstSET[non_ter.index(gra_line[i])]:
if g != '@' and g not in firstSET[ind]:
firstSET[ind].append(g)
#再判斷一堆空的那個情況
j = i #i是indexSET遍歷用的,不能改,j用來遍歷gra_line好了
while '@' in firstSET[non_ter.index(gra_line[j])]:
j += 1
#全包含空就把空加進去
if gra_line[j] == '|' or gra_line[j] == '\n':
firstSET[ind].append('@')
break
#把gra_line[j]的first集的非空元素加進去
if len(firstSET[non_ter.index(gra_line[j])]) == 0:
for gr in grammer:
if gr.startswith(gra_line[j]):
first(gr)
break
for g in firstSET[non_ter.index(gra_line[j])]:
if g != '@' and g not in firstSET[ind]:
firstSET[ind].append(g)
return firstSET
#求字符串的first集
def firststr(str):
firsts = []
if str == '@':
firsts.append('@')
return firsts
else:
for s in str:
if s in terSymbol:
firsts.append(s)
return firsts
elif '@' not in firstSET[non_ter.index(s)]:
for f in firstSET[non_ter.index(s)]:
if f not in firsts:
firsts.append(f)
return firsts
else:
for f in firstSET[non_ter.index(s)]:
if f != '@' and f not in firsts:
firsts.append(f)
if str.endswith(s):
firsts.append('@')
return firsts
#求follow集
def follow(gra_line):
#如果是開始符號則把#加入它的follow集
if gra_line[0] == grammer[0][0] and '#' not in followSET[non_ter.index(gra_line[0])]:
followSET[non_ter.index(gra_line[0])].append('#')
#查找產生式右部的非終結符並把下標加到nonIndex中,注意不能用index是因為可能會有重復的字符
nonIndex = []
g = 0
while g < len(gra_line):
if gra_line[g] in non_ter:
nonIndex.append(g)
g += 1
#從第二個非終結符開始判斷它的位置,注意n是該非終結符在gra_line中的下標,gra_line[n]才是遍歷的那個非終結符
for n in nonIndex[1:]:
#如果該終結符在產生式最右,則把follow(nonIndex[0])加入follow(n)中
if gra_line[n + 1] == '|' or gra_line[n + 1] == '\n':
for i in followSET[non_ter.index(gra_line[0])]:
if i not in followSET[non_ter.index(gra_line[n])]:
followSET[non_ter.index(gra_line[n])].append(i)
#如果該終結符不在最后
else:
#先把該終結符后的字符串截取出來
str = ''
for s in gra_line[n + 1:]:
if s == '|' or s == '\n':
break
else:
str = str + s
#如果字符串的first集中含有空
if '@' in firststr(str):
for i in followSET[non_ter.index(gra_line[0])]:
if i not in followSET[non_ter.index(gra_line[n])]:
followSET[non_ter.index(gra_line[n])].append(i)
for i in firststr(str):
if i != '@' and i not in followSET[non_ter.index(gra_line[n])]:
followSET[non_ter.index(gra_line[n])].append(i)
#如果字符串的first集中不含有空
else:
for i in firststr(str):
if i != '@' and i not in followSET[non_ter.index(gra_line[n])]:
followSET[non_ter.index(gra_line[n])].append(i)
return followSET
def cmp(SET1, SET2):
i = 0
while i < 26:
if (operator.eq(SET1[i], SET2[i])) == False:
return False
else:
i += 1
return True
#構造預測分析表
def table(grammer, column, row):
col = len(column) + 1
ro = len(row) + 1
#創建一個c列r行的二維列表
gra = [['\t' for col in range(col)] for row in range(ro)]
gra[0][0] = ' '
i = 1
for c in column:
gra[0][i] = c
i += 1
j = 1
for r in row:
gra[j][0] = r
j += 1
for gra_line in grammer:
# 先看每一行有幾個產生式,把每一個產生式的開始索引加入pro_index
pro_index = []
i = 0
while i < len(gra_line):
if gra_line[i] == '>' or gra_line[i] == '|':
pro_index.append(i + 1)
i += 1
for p in pro_index:
str = ''
for s in gra_line[p:]:
if s == '|' or s == '\n':
break
else:
str = str + s
#找到了單獨的產生式后,看這個產生式的first集,注意產生式是單獨一個空的情況
if str == '@':
for f in followSET[non_ter.index(gra_line[0])]:
r = row.index(gra_line[0]) + 1
c = column.index(f) + 1
pro = gra_line[0] + '->' + str
gra[r][c] = pro
else:
for n in firststr(str):
if n == '@':
continue
r = row.index(gra_line[0]) + 1
c = column.index(n) + 1
pro = gra_line[0] + '->' + str
gra[r][c] = pro
if '@' in firststr(str):
for f in followSET[non_ter.index(gra_line[0])]:
r = row.index(gra_line[0]) + 1
c = column.index(f) + 1
pro = gra_line[0] + '->' + str
gra[r][c] = pro
return gra
#總控程序
def master(ana_str, column, row):
flag = 0
ana_str += '#'
stack = ['#']
stack.append(grammer[0][0])
while flag == 0:
print("當前棧中元素為:")
print(stack)
print("當前字符串為:")
print(ana_str)
if stack[-1] == ana_str[0] and stack[-1] == '#':
print("匹配成功,分析結束")
flag = 1
return True
elif stack[-1] == ana_str[0] and stack[-1] != '#':
stack.pop()
ana_str = ana_str[1:]
print("非終結符匹配成功")
elif stack[-1] in non_ter:
if ana_str[0] not in column:
print("匹配失敗")
flag = 1
return False
elif gra[row.index(stack[-1]) + 1][column.index(ana_str[0]) + 1] != '\t':
str = gra[row.index(stack[-1]) + 1][column.index(ana_str[0]) + 1]
print("當前所用產生式為:")
print(str)
stack.pop()
right_str = str[3:]
right_str = right_str[::-1]
if right_str == '@':
continue
else:
for s in right_str:
stack.append(s)
else:
print("匹配失敗")
flag = 1
return False
else:
print("匹配失敗")
flag = 1
return False
if __name__ == '__main__':
ana_str = input("請輸入一個待分析的串:")
print("請輸入一個文法:")
grammer = sys.stdin.readlines()
for gr in grammer:
first(gr)
print("該文法的非終結符的first集為:")
i = 0
for f in firstSET:
if len(f) != 0:
print('first(' + non_ter[i] + '):',f)
i += 1
print()
#求follow集用的循環……因為我沒想到該怎么遞歸
#關於遞歸:如何在需要把follow(A)加入follow(B)的時候准確求出follow(A)???????
#一個易錯點:直接把followSET賦給follow1,會被引用。用切片可以避免普通列表的引用,但是對於嵌套列表好像沒什么用
follow1 = copy.deepcopy(followSET)
for gr in grammer:
follow(gr)
follow2 = copy.deepcopy(followSET)
while not cmp(follow1, follow2):
follow1 = copy.deepcopy(followSET)
for gr in grammer:
follow(gr)
follow2 = copy.deepcopy(followSET)
print("該文法的非終結符的follow集為:")
j = 0
for f in followSET:
if len(f) != 0:
print('follow(' + non_ter[j] + '):', f)
j += 1
print()
column = []
row = []
for gra_line in grammer:
for gr in gra_line:
if gr == '-' and gra_line.index(gr) == 1:
continue
if gr in terSymbol and gr not in column:
column.append(gr)
if gr in non_ter and gr not in row:
row.append(gr)
column.append('#')
print("該文法的預測分析表為:")
gra = table(grammer, column, row)
for i in range(len(gra)): # 控制行
if i == 0:
for j in range(len(gra[i])): # 控制列
if j == 0:
print(gra[i][j], end='\t')
else:
print(gra[i][j], end='\t\t')
else:
for j in range(len(gra[i])): # 控制列
print(gra[i][j], end='\t')
print()
print()
#總控程序
print("分析過程如下:")
master(ana_str, column, row)