原理
-
本次實驗是設計一個LL(1)分析器,給定任意文法,自動消除一切左遞歸(前提是文法中不含迴路,且產生式右部不含有空字),自動回溯(提取最左公因子),自動生成FIRST集,自動生成FOLLOW集,自動生成LL(1)分析表,給定一個輸入串,輸出該輸入串的分析過程。
-
本次實驗需要的原理知識在此不過多闡述,可以參考《編譯原理教程》第四版(胡元義主編)
-
需要注意的是,書本上自動消除一切左遞歸的算法中,我認爲會出現第一個產生式的直接左遞歸沒有消除(不知道對不對,看兩層循環中中第一個產生式沒有消除直接左遞歸),所以消除完所有產生式的左遞歸後,在進行處理,判斷是否含有直接左遞歸,有則消除;其他算法都是根據書上的原理進行實現的。
-
設計一個類LL1,初始化傳入參數:終結符(Tset),非終結符(NTset),文法開始符(S)(需要注意的是,本次實驗必須要求文法開始符是字符S), 產生式(Production), FIRST集(firstset),FOLLOW集(followset), LL(1)分析表(Table)
class LL1: def __init__(self, Tset, NTset, S, Production, firstset, followset, Table): self.Tset = Tset self.NTset = NTset self.S = S self.Production = Production self.firstset = firstset self.followset = followset self.Table = Table
各個參數的數據存放形式: Tset:[]列表 NTset:[]列表 S: 'S' 字符串 Production:{},例如:A->aA|b, 就是表示爲{'A': ["aA","b"]} firstset: {}, 例如:fisrt(A):(a,b),就是表示爲{‘A’:["a","b"]} followset:{},同firstset一樣 Table:{}嵌套字典,例如:M[A,a] = (A->a),那麼就是表示爲{'A':{'a':['A->a']}}
-
LL1類的各個方法:
- removeLeftRecursion(self):消除一切左遞歸
- removeLeftCommonFactor(self):消除最左公因子
- createFirst(self),createFirstSet(self): 生成FIRST集
- createFollow(self),createFollowSet(self):生成FOLLOW集
- createTable(self):生成LL1分析表
-
其他函數是跟界面各個按鈕綁定的函數,跟實驗主旨沒啥大的關係,就不闡述了
-
本次實驗GUI是用Python tkinter
實驗效果
LL(1)分析器界面:
點擊打開文件,選擇文法的txt文件,就是把文件的內容讀到文本框中
點擊消除左遞歸,消除左因子,生成first集,生成follow集按鈕,就是生成對應的結果。
點擊生成Table表,就會生成一個新窗口,顯示結果
然後在輸入框輸入判定的字符串
點擊判斷棧情況,輸入該字符串的分析過程
源代碼(main.py)
import tkinter
import tkinter.filedialog #對話框
import tkinter.ttk # 表格
"""
Production產生式存放:
A->a | b
{A:['a','b']}
firstset集存在,follow集同理
first(A) = (a,b),則{'A':[a,b]}
table表(M表)例如:M[A,(] = "A->(abc"
{'A':{'(':["A->(abc"]}}
"""
class LL1:
def __init__(self, Tset, NTset, S, Production, firstset, followset, Table):
self.Tset = Tset
self.NTset = NTset
self.S = S
self.Production = Production
self.firstset = firstset
self.followset = followset
self.Table = Table
# 消除一切左遞歸
def removeLeftRecursion(self):
i = 1
while i <= len(self.NTset):
j = 1
while j <= i-1:
# set轉換爲list,才能進行下標取值操作
Ai = self.NTset[i-1] # Ai非終結符
Aj = self.NTset[j-1] # Aj非終結符
rmList = [x for x in self.Production[Ai] if x.startswith(Aj)]
if rmList:
addList = [y+x.replace(Aj, "", 1) if y != 'ε' else x.replace(Aj, "", 1)
for x in rmList for y in self.Production[Aj]]
# 本來不需要替換的,保留
self.Production[Ai] = list(
filter((lambda x: x not in rmList), self.Production[Ai]))
# 追加新替換的式子進來
# extend,append函數沒有返回值的,所以不能list().extend(),否則返回None
self.Production[Ai].extend(addList)
# print("去除間接左遞歸", self.Production)
# 消除直接左遞歸
LeftRecursionProduction = [
x for x in self.Production[Ai] if x.startswith(Ai)] # 左遞歸的產生式
notLeftRecursionProduction = [
x for x in self.Production[Ai] if not x.startswith(Ai)] # 不是左遞歸的產生式
if LeftRecursionProduction: # 含有左遞歸的產生式
newNT = Ai+"'" # 新終結符,如 A,A'
if newNT not in self.NTset:
self.NTset.append(newNT)
if notLeftRecursionProduction:
self.Production[Ai] = [
x+newNT for x in notLeftRecursionProduction]
else:
self.Production[Ai] = [newNT]
newList = [
x.replace(x[0], '', 1)+newNT for x in LeftRecursionProduction]
newList.append('ε') # 追加空字,啞f西no
self.Production[newNT] = newList # 加進產生式的字典中
j = j+1
i = i+1
"""
特殊判斷,第一個非終結符對應的產生式本身是直接左遞歸,
注意是第一個非終結符,因爲上面的兩層循環判斷了除第一個非終結符的情況了
"""
LeftRecursionProduction = [
x for x in self.Production[self.NTset[0]] if x.startswith(self.NTset[0])]
notLeftRecursionProduction = [
x for x in self.Production[self.NTset[0]] if not x.startswith(self.NTset[0])]
if LeftRecursionProduction:
newNT = 'S'+"'"
if newNT not in self.NTset: # 新的終結符判斷是否存在,不存在插入
self.NTset.append(newNT)
if notLeftRecursionProduction:
self.Production['S'] = [
x+newNT for x in notLeftRecursionProduction]
else:
self.Production['S'] = [newNT]
newList = [x.replace(x[0], '', 1) +
newNT for x in LeftRecursionProduction]
newList.append('ε') # 追加空字,啞f西no
self.Production[newNT] = newList # 加進產生式的字典中
# 去除多餘的生成式,dfs
"""
fromkeys方法注意:
如果每個key的value都爲同一個對象,
則操作該key的value時,所有的key的value都會改變
"""
judge = dict.fromkeys(self.NTset, 0) # 字典,非終結符做key,0做valu
stack = []
stack.append(self.S)
judge[self.S] = 1 # 標誌爲已訪問
while(len(stack) > 0 and (0 in judge.values())):
top = stack.pop()
rightPro = self.Production[top] # 產生式右部
for item in rightPro:
i = 0
while i < len(item):
ch = ""
if item[i] in self.NTset:
ch += item[i]
if i != len(item)-1:
if item[i+1] == "'":
ch += "'"
if judge[ch] == 0:
stack.append(ch)
judge[ch] = 1 # 標誌爲訪問過
i = i+1
for key, values in judge.items():
if values == 0:
self.Production.pop(key) # 去除產生式
self.NTset.remove(key) # 去除非終結符
# 消除最左公因子
"""
算法思想:將每個非終結符的產生式右部,也就是多個字符串,按照首個字符分類,
然後進行轉換,新轉換生成的新非終結符的產生式更新到self實例中,進行轉換。
"""
def removeLeftCommonFactor(self):
for item in self.NTset: # 遍歷每個非終結符的產生式
diff = {} # 將相同首字符的分爲一類
for i in self.Production[item]: # 遍歷該非終結符的每個產生式
if i[0] not in diff.keys():
diff[i[0]] = [i]
else:
diff[i[0]].append(i)
oldList = [] # 原來非終結符產生式的右部
newNt = item # 新產生的非終結符
for key, value in diff.items():
newList = [] # 新產生的非終結符產生式的右部
if len(value) > 1: # 可以提取公因子
newNt += "'" # 新的終結符
oldList.append(key+newNt)
for v in value:
if len(v) == 1: # 提取公因子後爲ε
newList.append('ε')
else:
# 提取非公因子的部分,replace函數需要指定替換的次數,bug error
newList.append(v.replace(v[0], '', 1))
if len(newList) != 0:
self.Production[newNt] = newList # 添加新的產生式右部
self.NTset.append(newNt) # 添加新的非終結符
else:
# 注意這裏value爲列表,所以value[0]爲所求
oldList.append(value[0])
self.Production[item] = oldList
def createFirst(self):
for nt in self.NTset:
if nt not in self.firstset.keys():
self.firstset[nt] = []
for eachPro in self.Production[nt]:
if eachPro[0] in self.Tset or eachPro[0] == 'ε': # A->a
self.firstset[nt].append(eachPro[0])
self.firstset[nt] = list(set(self.firstset[nt]))
else: # A->B...
if eachPro[0] in self.firstset.keys():
# 將Y1集除空字追加到X
R_addto_L = [
x for x in self.firstset[eachPro[0]] if x != 'ε']
self.firstset[nt].extend(R_addto_L)
self.firstset[nt] = list(
set(self.firstset[nt])) # 去重
if 'ε' in self.Production[eachPro[0]]: # 第一個Y1存在ε
after_Y1_str = eachPro[1:]
if not after_Y1_str: # 爲空,即是A->B 這種情況,且B含有空字,需要追加空字
self.firstset[nt].append('ε')
self.firstset[nt] = list(
set(self.firstset[nt]))
else:
index = 0
for ch in after_Y1_str:
if 'ε' in self.Production[ch]:
index = index+1
else:
break
existNULL_Y = after_Y1_str[0:index]
if len(existNULL_Y) == len(after_Y1_str): # Y1後面的Y都有空字,加
self.firstset[nt].append('ε')
self.firstset[nt] = list(
set(self.firstset[nt]))
for each_Y in existNULL_Y:
Y_addto_X = [
x for x in self.firstset[each_Y] if x != 'ε']
self.firstset[nt].extend(Y_addto_X)
self.firstset[nt] = list(
set(self.firstset[nt]))
def createFirstSet(self):
while True:
size1 = 0
for item in self.firstset.values():
size1 += len(item)
# print(size1)
self.createFirst()
size2 = 0
for item in self.firstset.values():
size2 += len(item)
if size1 == size2:
break
def createFollow(self):
for nt in self.NTset:
for key, values in self.Production.items():
for item in values:
index = item.find(nt)
flag = 1
newnt = nt+"'"
if newnt in item:
flag = 0
if flag and index >= 0:
newindex = index + len(nt)-1
if newindex == len(item)-1: # 非終結符剛剛好在末尾,規則3
self.followset[nt].extend(self.followset[key])
self.followset[nt] = list(set(self.followset[nt]))
# 非終結符後面有字符,item[newindex+1]表示所求follow集的非終結符後面的第一個字符
else:
nextNT = item[newindex+1] # 後面的符號
if nextNT in self.Tset: # 終結符
self.followset[nt].append(nextNT)
self.followset[nt] = list(
set(self.followset[nt]))
else:
i = newindex + 2
while i < len(item):
if item[i] == "'":
nextNT += "'"
else:
break
i = i+1
# 後面是非終結符
# first集去除空字加到follow
add_list = [
x for x in self.firstset[nextNT] if x != 'ε']
self.followset[nt].extend(add_list)
self.followset[nt] = list(
set(self.followset[nt]))
if 'ε' in self.firstset[nextNT]:
self.followset[nt].extend(
self.followset[key])
self.followset[nt] = list(
set(self.followset[nt]))
# print("{}->{},非終結符爲:{}".format(key,item,nt))
# print("規則爲:",self.followset)
# print("==========", self.followset['S'])
def createFollowSet(self):
for nt in self.NTset:
self.followset[nt] = []
self.followset["S"].append('#')
while True:
"""
# 判斷坑了我5小時。。。
# 不能f1 = self.followset 執行函數 f2 = self.followset,
# 然後判斷f1 == f2?
# f1隨着self.followset變化而變化??未解決!
"""
size1 = 0
for item in self.followset.values():
size1 += len(item)
# print(size1)
self.createFollow()
size2 = 0
for item in self.followset.values():
size2 += len(item)
if size1 == size2:
break
def createTable(self):
ckey = self.Tset
ckey.append("#")
value = list()
for k in self.NTset: # 初始化一個嵌套字典
for ck in ckey:
if k in self.Table:
self.Table[k].update({ck: value})
else:
self.Table.update({k: {ck: value}})
"""
ERROR fromkeys少用!!
# cdict = dict.fromkeys(ckey, []) # 初始化子字典 一維
# self.Table = dict.fromkeys(self.NTset, cdict) # Table表 二維
"""
for key, values in self.firstset.items():
for value in values:
if value != "ε":
if len(self.Production[key]) == 1: # 該非終結符只有一個產生式
str_pro = "{}->{}".format(key, self.Production[key][0])
if len(self.Table[key][value]) == 0:
self.Table[key][value] = [str_pro]
else:
self.Table[key][value].append(str_pro)
else:
for i in self.Production[key]: # 看首字符
if value[0] == i[0]: # 產生式首字符和first集的每個元素首字符
str_pro = "{}->{}".format(key, i)
if len(self.Table[key][value]) == 0:
self.Table[key][value] = [str_pro]
else:
self.Table[key][value].append(str_pro)
break
# first集存在空字(產生式中有空字)
else:
for k in self.followset[key]:
str_pro = "{}->{}".format(key, 'ε')
if len(self.Table[key][k]) == 0:
self.Table[key][k] = [str_pro]
else:
self.Table[key][k].append(str_pro)
def readFile(filename):
Tset = set() # 終結符集
NTset = set() # 非終結符集
S = 'S' # 文法開始符
Production = dict() # 產生式
with open(filename, encoding='utf-8', mode='r') as f:
lines = [x.strip() for x in f.readlines() if not x.isspace()]
for line in lines:
line = line.split('->', 1) # 分割
line = [x.strip() for x in line] # 去除空白符
line[1] = line[1].split('|') # 分割
line[1] = [x.strip() for x in line[1]]
if line[0] not in Production.keys(): # 判斷非終結符是否存在
addDict = {line[0]: line[1]}
Production.update(addDict)
else:
Production[line[0]].extend(line[1]) # 存在,追加
# 11 -> 11|221 產生式
# {'11': ['11', '221']}
NTset = list(set(Production.keys())) # 字典的key作爲非終結符
NTset.remove('S')
NTset.append('S') # 保證文法開始符在最後,消除左遞歸方便點
# 遍歷字典的values,判斷每個字符如果不是非終結符,那麼就是終結符,使用集合推導式,
Tset = list(set([c for item in Production.values()
for ch in item for c in ch if c not in NTset and c != 'ε' and c != "'"]))
return Tset, NTset, S, Production
# “打開文件”按鍵函數
def btn1(LB_txt1, x):
LB_txt1.delete(1, tkinter.END) # 清空列表框
filename = tkinter.filedialog.askopenfilename()
x.Tset, x.NTset, x.S, x.Production = readFile(filename)
with open(filename, 'r', encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
LB_txt1.insert(tkinter.END, line)
# “消除左遞歸”按鍵函數
def btn2(LB_txt2, x):
LB_txt2.delete(1, tkinter.END)
x.removeLeftRecursion()
for key, value in x.Production.items():
for v in value:
str_pro = "{}->{}".format(key, v)
LB_txt2.insert(tkinter.END, str_pro)
# "消除左公因子"按鍵函數
def btn3(LB_txt3, x):
LB_txt3.delete(1, tkinter.END)
x.removeLeftCommonFactor()
for key, value in x.Production.items():
for v in value:
str_pro = "{}->{}".format(key, v)
LB_txt3.insert(tkinter.END, str_pro)
# "生成first集"的按鍵函數
def btn4(LB_first,x):
LB_first.delete(1,tkinter.END)
x.createFirstSet()
for key,values in x.firstset.items():
if key in x.NTset: # 防止用戶沒進行消除左遞歸操作就生成first集
str_firstset_right = ""
for v in values:
str_firstset_right = str_firstset_right + v + ", "
str_first = "first({}) : ( {} )".format(key,str_firstset_right)
LB_first.insert(tkinter.END,str_first)
# "生成follow集"的按鍵函數
def btn5(LB_follow,x):
LB_follow.delete(1,tkinter.END)
x.createFollowSet()
for key,values in x.followset.items():
if key in x.NTset: # 防止用戶沒進行消除左遞歸,消除左因子操作就生成follow集
str_followset_right = ""
for v in values:
str_followset_right = str_followset_right + v + ", "
str_follow = "follow({}) : ( {} )".format(key,str_followset_right)
LB_follow.insert(tkinter.END,str_follow)
# 給定輸入串,輸出該輸入串的分析過程
def is_legal(input_text,x):
input_str = input_text.get() # input_text爲輸入框對象
table_root = tkinter.Tk()
table_root.title("輸入串的判斷情況")
table_root.geometry('1200x500')
# 創建表格
table_table = tkinter.ttk.Treeview(table_root,height=200)
# 定義列
table_table["columns"] = ["符號棧", "當前輸入符號", "輸入串","說明"]
table_table.pack()
table_table.column("#0",width=0,anchor='center')
# 設置列寬度
table_table.column("符號棧", width=200,anchor='center')
table_table.column("當前輸入符號", width=200,anchor='center')
table_table.column("輸入串", width=200,anchor='center')
table_table.column("說明", width=600,anchor='center')
# 設置列名
table_table.heading("符號棧",text="符號棧")
table_table.heading("當前輸入符號",text="當前輸入符號")
table_table.heading("輸入串",text="輸入串")
table_table.heading("說明",text="說明")
stack = []
stack.append("#")
stack.append("S")
input_str += "#"
i = 0
colindex = 0 # gui 表格的行數
a = input_str[i] # 當前輸入符號
top = stack[len(stack)-1]
"""
str1,str2都是相同的,但是懶得改了...
"""
while stack[len(stack)-1] != "#":
top = stack[len(stack)-1]
cur_stack_str = "".join(stack)
stack.pop() # 彈出棧頂
a = input_str[i]
if a not in x.NTset and a not in x.Tset:
str1 = cur_stack_str
str2 = a
str3 = input_str[i+1:]
str4 = "ERROR"
insert_tablelist = [str1,str2,str3,str4]
table_table.insert('',colindex,values=insert_tablelist)
print("Error!!")
return False
if top in x.Tset:
if top == a:
if a != "#":
i = i+1
insert_tablelist = []
str1 = cur_stack_str
str2 = a
str3 = input_str[i:] # 注意這裏是i,不是i+1
str4 = "匹配,彈出棧頂符號{}並讀出輸入串的下一個輸入符號{}".format(top,input_str[i])
insert_tablelist = [str1,str2,str3,str4]
table_table.insert('',colindex,values=insert_tablelist)
colindex = colindex+1
continue
# else:
# print("succeed")
# return True
else:
table_item = x.Table[top][a]
# print("查表爲",table_item)
if table_item: # []或者['A->i']
table_item = x.Table[top][a][0]
table_item = table_item.split('->')[1]
if table_item != 'ε': # T'S 分離成 T' S
j = 0
table_item += '#'
insert_stack = []
while j < len(table_item)-1:
ch = table_item[j] # 表示單個字符
if ch in x.Tset:
insert_stack.append(ch)
j = j+1
else:
while table_item[j+1] == "'" and j < len(table_item):
ch += "'"
j = j+1
j = j+1
insert_stack.append(ch)
insert_stack.reverse() # 翻轉,逆序入棧
stack.extend(insert_stack)
# gui 打印
str1 = cur_stack_str
str2 = a
str3 = input_str[i+1:]
str4 = "彈出棧頂符號{},將M[{},{}]中{}的{}壓入棧中".format(top,top,a,x.Table[top][a][0],"".join(insert_stack))
insert_tablelist = [str1,str2,str3,str4]
table_table.insert('',colindex,values=insert_tablelist)
# ######
else: # 空字不壓入
str1 = cur_stack_str
str2 = a
str3 = input_str[i+1:]
str4 = "彈出棧頂符號{},因爲M[{},{}]中爲{}->'ε',故不壓入棧中".format(top,top,a,top)
insert_tablelist = [str1,str2,str3,str4]
table_table.insert('',colindex,values=insert_tablelist)
else: # 表中無此項
return False
colindex = colindex+1
str1 = cur_stack_str
str2 = a
str3 = input_str[i+1:]
str4 = "匹配,分析成功!"
insert_tablelist = [str1,str2,str3,str4]
table_table.insert('',colindex,values=insert_tablelist)
print("成功!!!")
# "生成table表"的按鍵函數
def btn6(x):
x.createTable()
table_root = tkinter.Tk()
table_root.title("table表")
table_root.geometry('710x170')
# 創建表格
table_table = tkinter.ttk.Treeview(table_root)
# 定義列
column_list = x.Tset
# print("非終結符:",column_list)
table_table["columns"] = column_list
table_table.pack()
table_table.column("#0",width=100,anchor='center')
# 設置列寬度,列名
for i in column_list:
table_table.column(i, width=100,anchor='center')
table_table.heading(i, text=i)
# 給表格添加數據
i = 0
for key,values in x.Table.items():
column_tup = list()
for v in values.values():
if v:
column_tup.append(v)
else:
column_tup.append("")
table_table.insert('',i,text=key,values=column_tup)
i = i+1
# gui界面
def main():
Tset = []
NTset = []
S = "S"
Production = dict()
firstset = {}
followset = {}
Table = {}
x = LL1(Tset, NTset, S, Production, firstset,
followset, Table) # 一個窗體,對應一個LL1類
root = tkinter.Tk()
root.title("LL(1)分析器")
root.geometry('600x500')
root.resizable(0, 0) # 防止用戶調整尺寸
# 詞法文件框
LB_txt1 = tkinter.Listbox(root, height=19, width=23, bd=2.5)
LB_txt1.insert(0, "文法產生式:")
# 消除左遞歸框
LB_txt2 = tkinter.Listbox(root, bd=2.5, height=12, width=28)
LB_txt2.insert(0, "消除左遞歸後的文法產生式:")
# 消除左因子框
LB_txt3 = tkinter.Listbox(root, bd=2.5, height=12, width=28)
LB_txt3.insert(0, "消除左因子後的文法產生式:")
# first集合框
LB_first = tkinter.Listbox(root, bd=2.5, height=12, width=28)
LB_first.insert(0, "FIRST集:")
# follow集合框
LB_follow = tkinter.Listbox(root, bd=2.5, height=12, width=28)
LB_follow.insert(0, "FOLLOW集:")
# LB_follow.insert(1,"FOLLOW集:")
# LB_follow.delete(1, tkinter.END) # 刪除列表的內容
# 輸入框
input_text = tkinter.Entry(root, bd=2, width=50,fg='red')
# btn width 85, height 35
Btn_1 = tkinter.Button(root, command=lambda: btn1(
LB_txt1, x), text="打開文件", activebackground="green", activeforeground="white", bd=4, width=10)
Btn_2 = tkinter.Button(root, command=lambda: btn2(
LB_txt2, x), text="消除左遞歸", activebackground="green", activeforeground="white", bd=4, width=10)
Btn_3 = tkinter.Button(root, command=lambda: btn3(
LB_txt3, x), text="消除左因子", activebackground="green",activeforeground="white", bd=4, width=10)
Btn_4 = tkinter.Button(root, command=lambda: btn4(
LB_first,x),text="生成first集", activebackground="green",activeforeground="white", bd=4, width=10)
Btn_5 = tkinter.Button(root, command=lambda: btn5(
LB_follow,x),text="生成follow集", activebackground="green",
activeforeground="white", bd=4, width=10)
Btn_6 = tkinter.Button(root, text="生成Table表", activebackground="green",
command=lambda:btn6(x),activeforeground="white", bd=4, width=10)
Btn_7 = tkinter.Button(root, command=lambda: is_legal(
input_text,x),text="判定棧情況", activebackground="green",activeforeground="white", bd=4, width=10)
# 定位
input_text.place(x=200, y=460,height=30)
LB_txt1.place(x=5, y=0)
LB_txt2.place(x=172, y=0)
LB_txt3.place(x=377, y=0)
LB_first.place(x=172, y=230)
LB_follow.place(x=377, y=230)
Btn_1.place(x=5, y=350)
Btn_2.place(x=5, y=385)
Btn_3.place(x=90, y=385)
Btn_4.place(x=5, y=420)
Btn_5.place(x=90, y=420)
Btn_6.place(x=5, y=455)
Btn_7.place(x=90, y=455)
root.mainloop()
print(x.Table)
if __name__ == "__main__":
main()
# dict不支持下標索引
"""
測試樣例
S->Qc|c
Q->Rb|b
R->Sa|a
S->S+S|S-S|S*S|S/S| (S) | I
S->TS'
S'->+TS'|ε
T->FT'
T'->*FT'|ε
F->(S)|i
A->ab1 | ab2 | Dc | Ac |Acc ==>消除左遞歸新生成的生成式,還有可能存在最左公因子
D->ad #消除所以消除左遞歸新的終結符爲A',最左公因子用A''
A->aB|abD|abDE ==》這種情況公因子怎麼提取。。
S->ac|abd|cc|cce
S->LA
L->i:|ε
A->i=e
注意!!!:
文法開始符一定要以S開頭!!!!!!
"""
測試文件(.txt)
注意,一定文法開始符一定是要S,不能是其他字符。忘記優化了
S->TS'
S'->+TS'|ε
T->FT'
T'->*FT'|ε
F->(S)|i
總結
實驗原理還易理解,但是後期突然加上界面化,就導致設計的函數不太合理,存在冗餘,所以還是要好好思考項目的架構,各個函數模塊的功能。