编译原理求first和follow代码实现并打印预测分析表
filename=input("请输入要打开的文件名:")
with open(filename,'r',encoding='utf-8')as file:
lines=file.readlines()
Predictions = [line.strip() for line in lines]
print(f'从文件中读取的规则:\n{Predictions}\n')
#找出终结符和非终结符
Vt=[]
Vn=[]
begin = Predictions[0]
BEGIN = begin[0]
for rule in Predictions:
left,right = rule.split('->')
#左边加入非终结符
for lefts in left:
if lefts.isupper() and lefts not in Vn:
Vn.append(lefts)
#右边加入终结符
for rights in right:
#if rights.islower() and lefts != '->' and lefts !='ε' and rights not in Vt:
if not rights.isupper() and rights != 'ε' and rights != '->' and rights not in Vt:
Vt.append(rights)
print("非终结符列表:",Vn)
print("终结符列表",Vt)
def checkEmpty(v):
global Predictions
for i in Predictions:
production=i.split('->')
# print(production) ['E', 'TA']
if production[0] == v and production[1]=='ε':
return True
return False
def addInsert(arr,v):
flag=False
if type(v)==str:
if v not in arr:
arr.append(v)
flag = True
else:
for a in v:
if a not in arr:
flag=True
arr.append(a)
return flag
def getFirst():
global Predictions, Vt, Vn
first = {}
for i in Predictions:
first[i.split('->')[0]] = []#如first[S]=[]
while True:
flag = False
for i in Predictions:
key = i.split('->')[0]
temp = i.split('->')
# print(temp) ['S','bBZ'] temp[0]为左部,temp[1]为右部 ,
r1 = temp[1][0]
if r1 in Vt or r1 == 'ε':
if addInsert(first[key], r1) == True:
flag = True
if r1 in Vn:
empty = 0
for t in i.split('->')[1]:
if t in Vn:
if addInsert(first[key], [_ for _ in first[t] if _ != 'ε']):
flag = True
if checkEmpty(t) == False:
break
else:
empty += 1
else:
break
if empty == len(i.split('->')[1]):
if addInsert(first[key], 'ε') == True:
flag = True
if flag == False:
break
#print(first)
return first
def getFollow(first):
follow = {}
global Predictions,begin
for v in Vn:
if v == BEGIN:
follow[v]=set('#')
else:
follow[v]=set()
while True:
flag1=False
for i in Predictions:#遍历所有的规则,然后对规则进行处理
left = i.split('->')[0]
right = i.split('->')[1]
for index in range(len(right)):#遍历规则有部分的符号
if right[index] in Vt or 'ε' in right[index]:
continue
if index == (len(right) - 1):
for i in follow[left]:
lg = len(follow[right[index]])
follow[right[index]].add(i)
if lg < len(follow[right[index]]):
flag1 = True
else:
if right[index + 1] in Vt: #如果有部分的符号为终结符
#将终结符添加到当前符号的FOLLOW集中
follow[right[index]].add(right[index + 1])
lg = len(follow[right[index]])
if lg < len(follow[right[index]]):
flag1 = True
else:
#如果右部的当前符号后面紧跟着一个非终结符,则将该非终结符的FIRST集中不包含空(ε)的符号添加到当前符号的FOLLOW集中
#遍历该非终结符的FIRST集中的每个符号,如果符号不是空(ε),则将其添加到当前符号的FOLLOW集中
for i in first[right[index + 1]]:
if i != 'ε':
follow[right[index]].add(i)
lg = len(follow[right[index]])
if lg < len(follow[right[index]]):
flag1 = True
# 设置一个标志flag变量用于判断当前符号的后续符号是否都可以添加到FOLLOW集中
flag = False
#如果右部的当前符号后面的所有符号的FIRST集都包含空(ε)则将左部非终结符的FOLLOW集添加到当前符号的FOLLOW集中。
for _ in right[index + 1:]:
if (_ in Vt) or ('ε' not in first[_]):
flag = True
if flag == False:
for _ in follow[left]:
follow[right[index]].add(_)
lg = len(follow[right[index]])
if lg < len(follow[right[index]]):
flag1 = True
if flag1 == False:
break
print(follow)
return follow
def getSELECT(production, first, follow):
select = set()
left, right = production.split('->')
#第一种情况如果右部包含空(ε),则将左部的 FOLLOW 集合中不包含空(ε)的符号添加到 SELECT 集合中。
if 'ε' in right:
select |= follow[left] - set(['ε'])
for symbol in right:
#第二种情况
if symbol in first:
select =select.union(set([_ for _ in first[symbol] if _ != 'ε']))
if 'ε' not in first[symbol]:
break
else:
#如果不在first中,就说明不能退出空
select =select.union(set([symbol]))
break
select-=set('ε')
return select
if __name__=='__main__':
First=getFirst()
Follow=getFollow(First)
ru = Predictions[0]
s = ru[0]
print("FIRST集:")
for k in Vn:
print(' FIRST[', k, ']: ', First[k])
print("FOLLOW集:")
for k in Vn:
print(' FOLLOW[', k, ']: ', Follow[k])
print("SELECT集:")
for production in Predictions:
select = getSELECT(production, First, Follow)
print(' SELECT[', production, ']: ', select)
#print(select)
# 将 '#' 添加到终结符列表 Vt 中
Vt.append('#')
# 构造预测分析表
predict_table = {}
for non_terminal in Vn:
predict_table[non_terminal] = {}
for terminal in Vt:
predict_table[non_terminal][terminal] = set()#为每一个非终结符添加集合
print('222',predict_table)
for production in Predictions:
left, right = production.split('->')
select = getSELECT(production, First, Follow)
for terminal in select:
if terminal != 'ε':
predict_table[left][terminal].add(production)
#print('333',predict_table)
if 'ε' in select:
for terminal in Follow[left]:
predict_table[left][terminal].add(production)
# 打印预测分析表
print("预测分析表:")
print(" ", end="")
for terminal in Vt:
print(f"{terminal:^10}", end="")
print()
for non_terminal in Vn:
print(f"{non_terminal:<2}", end="")
for terminal in Vt:
if predict_table[non_terminal][terminal]:
print(f"{str(predict_table[non_terminal][terminal]):^10}", end="")
else:
print(f"{'':^10}", end="")
print()
#写入文件
with open("follows.txt", "w+") as wp:
for k in Follow:
wp.write("follow(%s): \t" % k)
wp.write("%s\n" % Follow[k])
#写入文件
with open("first.txt", "w+") as wp:
for k in Follow:
wp.write("follow(%s): \t" % k)
wp.write("%s\n" % First[k])