一、实验名称
LL(1)文法的判断及转换
二、实验目的
输入:任意一个文法
输出:(1)是否为LL(1)文法
(2)若是,给出每条产生式的select集
(3)若不是,看看是否含有左公共因子或者含有左递归,并用相应的方法将非 LL(1)文法变成LL(1)文法,并输出新文法中每条产生式的select集。
三、实验原理
1、First集定义
令X为一个文法符号(终止符或非终止符)或ε,则集合First(X)有终止符组成,此外可能还有ε,它的定义如下:
\1. 若X是终止符或ε,则First(X)= {X}。
\2. 若X是非终结符,则对于每个产生式X—>X1X2…Xn,First(X)包含了First(X1)-{ε}。 若对于某个i < n,所有的集合First(X1),... ,First(Xi)都包含了ε,则First(X)也包 括了First(Xi+1)- {ε}。若所有集合First(X1),...,First(Xn)都包括了ε,则First(X)也包括了ε。
2、Follow集定义
给出一个非终结符A,那么集合Follow(A)则是由终结符组成,此外可能还含有#(#是题目约定的字符串结束符)。集合Follow(A)的定义如下:
\1. 若A是开始符号,则#在Follow(A)中。
\2. 若存在产生式B—>αAγ,则First(γ)- {ε}在Follow(A)中。
\3. 若存在产生式B—>αAγ,且ε在First(γ)中,则Follow(A)包括Follow(B)。
3、Select集定义
对于产生式A—>α。集合select(A—>α)定义如下:
\1. 若α不能推出ε,则select(A—>α) = first(α)。
\2. 若α能推出ε,则select(A—>α)= first(α)∪ follow(A)。
4、含左递归文法
一个文法G,若存在P经过一次或多次推导得到Pa(即能推导出以P开头的式子), 则称G是左递归的。
左递归分为直接左递归和间接左递归。
直接左递归经过一次推导就可以看出文法存在左递归,如P→Pa|b。
间接左递归侧需多次推导才可以看出文法存在左递归,如文法:S→Qc|c,Q→Rb|b,R→Sa|a有S =>Qc =>Rbc =>Sabc
四、实验思路
本次实验采用python完成。
1、求非终结符是否能导出空
a. 第一轮扫描。当前的产生式还没被删除,非终结符lp可以导出空,将以该非终结符为左部的产生式标记为要删除的。产生式右部分解,若该产生式右部包含终结符,删除该产生式因为由它不会导出空。判断没有被删除的产生式中是否还有以该非终结符为左部的产生式。
b. 第二轮扫描。逐一扫描每一条产生右部的每一个符号,循化直至每个非终结符的状态都确定下来。
2、求First集算法
存储每一个非终结符对应的First集,扫描每一条产生式,记录每一轮扫描是每个非终结符First集是否增大过。全部初始化为没有增大的状态,对于课本的五种类型依次求解,每次将结果加入对应的集合中,若一次扫描First集没有增大,则说明循环结束。
3、求Follow集算法
存储每一个非终结符对应的Follow集,将'#'加入文法的开始符号的Follow集合中,记录每一轮扫描是每个非终结符Follow集合是否增大过,全部初始化为没有增大的状态,扫描每一条产生式的右部,扫描到非终结符,判断在该非终结符之后的子串能否推导空,若该符号串可以推导出空,还要将Follow(lp)加入到里面。
4、求Select集算法
初始化每条产生式对应的Select集合为空,若产生式右部不能推导出空,则将右部的First集加入Select集,如果可以推出空,则需要同时将左部的Follow集合右部的First集去掉空的部分加入Select集。
五、实验小结
通过本次实验,知道了如何判断一个文法是不是LL(1)文法,同时对于First、Follow以及Select集的求解原理变得更加熟悉,并且知道了如何用计算机语言求解First,Follow以及Select集。不足之处是,没有完成判断文法是否为左递归文法以及左递归文法的转换部分。
六、附件
1、源代码
class Gw:
def __init__(self):
with open('D:\\test\\Gw.txt') as f:
content = f.readlines()
content = [line.strip() for line in content]
self.Vn = content[0].split(' ')
self.Vt = content[1].split(' ')
self.start = content[2]
self.produce = []
self.left = []
self.right = []
for i in range(3,len(content)):
self.produce.append(content[i])
self.left.append(content[i].split('->')[0])
self.right.append(content[i].split('->')[1])
def showGw(self):
print('非终结符:',self.Vn)
print('终 结 符:',self.Vt)
print('开始符号:',self.start)
print('产生式如下:')
for l,r in zip(self.left,self.right):
print(l+'->'+r)
def canEmpty(self):
self.isEmpty = dict()
for i in range(len(self.Vn)):
self.isEmpty[self.Vn[i]] = -1
print(self.isEmpty)
temp = self.produce[::]
deleteIndex=[]
pointer = 0
while pointer')[0]
rp = temp[pointer].split('->')[1]
if rp=='!':
self.isEmpty[lp] = 1
for i in range(len(temp)):
if temp[i].split('->')[0]==lp and i not in deleteIndex:
deleteIndex.append(i)
l = list(rp)
isContainVt = [i in self.Vt for i in l]
if True in isContainVt:
deleteIndex.append(pointer)
for k in range(len(temp)):
if k not in deleteIndex:
if temp[k].split('->')[0]==lp:
break
else:
self.isEmpty[lp] = 0
pointer = pointer+1
while -1 in self.isEmpty.values():
for i in range(len(temp)):
if i not in deleteIndex:
lp = temp[i].split('->')[0]
rp = temp[i].split('->')[1]
rlsit = list(rp)
for j in range(len(rlsit)):
if self.isEmpty[rlsit[j]]==1:
if j==len(rlsit)-1:
self.isEmpty[lp]=1
elif self.isEmpty[rlsit[j]]==0:
deleteIndex.append(i)
for k in range(len(temp)):
if k not in deleteIndex:
if temp[k].split('->')[0]==lp:
break
else:
self.isEmpty[lp] = 0
else:
continue
def show(self):
print('非终结符能否推导出空的信息:')
for v in self.Vn:
if self.isEmpty[v]==1:
yon = '是'
else:
yon = '否'
print('%s:%s'%(v,yon))
def getFirst(self):
self.First = dict()
for i in self.Vn:
self.First[i] = list()
isChange = dict()
while True:
for k in self.Vn:
isChange[k] = 0
for i in range(len(self.produce)):
lp = self.produce[i].split('->')[0]
rp = self.produce[i].split('->')[1]
rlist = list(rp)
if rlist[0]=='!' or rlist[0] in self.Vt:
if rlist[0] not in self.First[lp]:
self.First[lp].append(rlist[0])
isChange[lp]=1
else:
for j in rlist:
if j in self.Vn:
if self.isEmpty[j]==1:
oldsize = len(self.First[lp])
templist = self.First[j][::]
if '!' in templist:
templist.remove('!')
for x in templist:
if x not in self.First[lp]:
self.First[lp].append(x)
if rp.endswith(j) and '!' not in self.First[lp]:
self.First[lp].append('!')
newsize = len(self.First[lp])
if oldsize!=newsize:
isChange[lp]=1
else:
oldsize = len(self.First[lp])
if j in self.Vn:
templist = self.First[j][::]
for x in templist:
if x not in self.First[lp]:
self.First[lp].append(x)
else:
if j not in self.First[lp]:
self.First[lp].append(x)
newsize = len(self.First[lp])
if oldsize!=newsize:
isChange[lp]=1
break
if 1 not in isChange.values():
print('First集合不再增大!')
break
else:
print('First集合有增大!')
pass
def showFirst(self):
print('First集合信息:')
for v in self.Vn:
print(v,self.First[v])
def canCauseEmpty(self,plist):
first = list()
if len(plist)==0:
first.append('!')
else:
for i in plist:
if i in self.Vn:
if self.isEmpty[i]==1:
t = self.First[i][::]
if '!' in t:
t.remove('!')
for k in t:
if k not in first:
first.append(k)
if ''.join(plist).endswith(i) and '!' not in first:
first.append('!')
else:
for k in self.First[i]:
if k not in first:
first.append(k)
break
else:
if i not in first:
first.append(i)
break
return first
def getFollow(self):
self.Follow = dict()
for i in self.Vn:
self.Follow[i] = list()
self.Follow[self.start].append('#')
isChange = dict()
while True:
for k in self.Vn:
isChange[k] = 0
for i in range(len(self.produce)):
lp = self.produce[i].split('->')[0]
rp = self.produce[i].split('->')[1]
rlist = list(rp)
for j in range(len(rlist)):
if rlist[j] in self.Vn:
reslist = self.canCauseEmpty(rlist[j+1::])
if '!' in reslist:
oldsize = len(self.Follow[rlist[j]])
for y in self.Follow[lp]:
if y not in self.Follow[rlist[j]]:
self.Follow[rlist[j]].append(y)
newsize = len(self.Follow[rlist[j]])
if oldsize!=newsize:
isChange[rlist[j]] = 1
else:
pass
oldsize = len(self.Follow[rlist[j]])
for x in reslist:
if x!='!' and x not in self.Follow[rlist[j]]:
self.Follow[rlist[j]].append(x)
newsize = len(self.Follow[rlist[j]])
if oldsize!=newsize:
isChange[rlist[j]] = 1
if 1 not in isChange.values():
break
def showFollow(self):
print('Follow集合信息:')
for key in self.Vn:
print(key,self.Follow[key])
def getSelect(self):
self.Select = dict()
for i in self.produce:
self.Select[i] = list()
for i in range(len(self.produce)):
lp = self.produce[i].split('->')[0]
rp = self.produce[i].split('->')[1]
rlist = list(rp)
if rlist[0]=='!':
for v in self.Follow[lp]:
if v not in self.Select[self.produce[i]]:
self.Select[self.produce[i]].append(v)
elif rlist[0] in self.Vt:
self.Select[self.produce[i]].append(rlist[0])
else:
res = self.canCauseEmpty(rlist)
if '!' not in res:
for v in res:
if v not in self.Select[self.produce[i]]:
self.Select[self.produce[i]].append(v)
else:
for v in res:
if v not in self.Select[self.produce[i]] and v!='!':
self.Select[self.produce[i]].append(v)
for v in self.Follow[lp]:
if v not in self.Select[self.produce[i]]:
self.Select[self.produce[i]].append(v)
def showSelect(self):
print('Select集合信息:')
for key in self.produce:
print(key,self.Select[key])
def isLLone(self):
isright = []
for k in self.Vn:
tset = set()
tset.add('#')
tset = tset | set(self.Vt)
for l,r in zip(self.left,self.right):
if k==l:
p = l+'->'+r
tset = tset & set(self.Select[p])
if len(tset)==0:
isright.append(1)
else:
isright.append(0)
if 0 in isright:
print('不是LL(1)文法!')
self.isll1 = False
else:
print('是LL(1)文法!')
self.isll1 = True
print(isright)
if __name__=='__main__':
w = Gw()
#w.showGw() #文件中读到的内容
w.canEmpty()
w.show()
w.getFirst()
w.showFirst()
w.getFollow()
#res = w.canCauseEmpty(['A','D'])
#print('res=',res)
w.showFollow()
w.getSelect()
w.showSelect()
w.isLLone()
2、运行结果截图
输入:
文法的判断及转换/2.png)
输出:
文法的判断及转换/1.png)