LL(1)分析法實驗設計思想及算法
子產品結構:
(1)定義部分:定義常量、變量、資料結構。
(2)初始化:設立 LL(1)分析表、初始化變量空間(包括堆棧、結構體、數組、
臨時變量等);
(3)控制部分:從鍵盤輸入一個表達式符号串;
(4)利用 LL(1)分析算法進行表達式處理:根據 LL(1)分析表對表達式符号串進
行堆棧(或其他)操作,輸出分析結果,如果遇到錯誤則顯示錯誤資訊。

測試截圖:
測試所用到文法:
文法 一、:
E->TG
G->+TG|—TG
G->ε
T->FS
S->*FS|/FS
S->ε
F->(E)
F->i
文法 二、:
E->TQ
Q->+TQ|ε
T->FW
W->*FW|ε
F->(E)|i
代碼實作
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'FF_1_form.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost!
#
# created by leechoy 2019.9.18
#
# 僅供學習參考 , 提示 : 該代碼 沒有實作消除左遞歸,僅測試了部分文法,個别文法可能測試不成功
#
# 界面GUI由 pyqt5 寫 代碼基本都已經注釋完畢
#
# 由于 pyqt5 控件 适應視窗大小改變,會發生改變,是以初始運作彈出界面時,棧分析表部分沒有全部漏出來得放大視窗
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
FIRST = {} ##first集
FOLLOW = {} ## follow 集
VT = [] ##終結符
VC = [] ## 非終結符
STACK = ['#'] ## 輸入棧
STACK_INPUT = [] ## 剩餘輸入串
# 建構二維字典(類似于矩陣,可以像矩陣一樣通路)存儲分析表
SELECT = {"": {"": ""}}
sentences = []
# 界面UI類
class Ui_Form(object):
# 初始化界面
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(1033, 837)
self.gridLayout = QtWidgets.QGridLayout(Form)
self.gridLayout.setObjectName("gridLayout")
self.textEdit = QtWidgets.QTextEdit(Form)
self.textEdit.setGeometry(QtCore.QRect(510, 0, 521, 191))
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(28)
font.setItalic(True)
self.textEdit.setFont(font)
self.textEdit.setObjectName("textEdit")
self.gridLayout.addWidget(self.textEdit, 0, 2, 1, 1)
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setEnabled(True)
self.pushButton.setGeometry(QtCore.QRect(510, 190, 521, 51))
font = QtGui.QFont()
font.setFamily("仿宋")
font.setPointSize(18)
font.setBold(False)
font.setItalic(False)
font.setUnderline(False)
font.setWeight(50)
self.pushButton.setFont(font)
self.pushButton.setObjectName("pushButton")
self.gridLayout.addWidget(self.pushButton, 1, 2, 1, 1)
self.lineEdit = QtWidgets.QLineEdit(Form)
self.lineEdit.setGeometry(QtCore.QRect(0, 790, 1031, 44))
self.lineEdit.setReadOnly(True)
self.lineEdit.setObjectName("lineEdit")
self.gridLayout.addWidget(self.lineEdit, 4, 0, 1, 3)
self.tableStack = QtWidgets.QTableWidget(Form)
self.tableStack.setGeometry(QtCore.QRect(510, 240, 521, 550))
self.tableStack.setObjectName("tableStack")
self.gridLayout.addWidget(self.tableStack, 2, 2, 2, 1)
self.tableStack.setColumnCount(4)
# 設定tablewidget 棧分析表的表頭
self.tableStack.setHorizontalHeaderLabels(["輸入棧", "剩餘輸入串", "所用表達式", "動作"])
self.textEdit_2 = QtWidgets.QTextEdit(Form)
self.textEdit_2.setGeometry(QtCore.QRect(0, 0, 511, 191))
font = QtGui.QFont()
font.setPointSize(7)
self.textEdit_2.setFont(font)
self.textEdit_2.setObjectName("textEdit_2")
self.gridLayout.addWidget(self.textEdit_2, 0, 0, 1, 2)
self.textFirst_set = QtWidgets.QTextBrowser(Form)
self.textFirst_set.setGeometry(QtCore.QRect(0, 240, 256, 192))
font = QtGui.QFont()
font.setPointSize(13)
self.textFirst_set.setFont(font)
self.textFirst_set.setObjectName("textFirst_set")
self.gridLayout.addWidget(self.textFirst_set, 2, 0, 1, 1)
self.textFollow_set = QtWidgets.QTextBrowser(Form)
self.textFollow_set.setGeometry(QtCore.QRect(255, 240, 256, 192))
font = QtGui.QFont()
font.setPointSize(13)
self.textFollow_set.setFont(font)
self.textFollow_set.setObjectName("textFollow_set")
self.gridLayout.addWidget(self.textFollow_set, 2, 1, 1, 1)
self.pushButton_2 = QtWidgets.QPushButton(Form)
self.pushButton_2.setEnabled(True)
self.pushButton_2.setGeometry(QtCore.QRect(0, 190, 511, 51))
font = QtGui.QFont()
font.setFamily("仿宋")
font.setPointSize(18)
font.setBold(False)
font.setItalic(False)
font.setUnderline(False)
font.setWeight(50)
self.pushButton_2.setFont(font)
self.pushButton_2.setObjectName("pushButton_2")
self.gridLayout.addWidget(self.pushButton_2, 1, 0, 1, 2)
self.tableAnalyze = QtWidgets.QTableWidget(Form)
self.tableAnalyze.setGeometry(QtCore.QRect(0, 430, 511, 361))
self.tableAnalyze.setObjectName("tableAnalyze")
self.gridLayout.addWidget(self.tableAnalyze, 3, 0, 1, 2)
# 隐藏分析表的橫縱表頭
self.tableAnalyze.verticalHeader().setVisible(False) # 隐藏垂直表頭
self.tableAnalyze.horizontalHeader().setVisible(False) # 隐藏水準表頭
# self.tableAnalyze.setColumnCount(0)
# self.tableAnalyze.setRowCount(0)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
# 設定響應槽(信号源 conect 槽)
self.pushButton.clicked.connect(self.onClick_analyze_stack)
self.pushButton_2.clicked.connect(self.onClick_create_first_follow_analyze_set)
# 按下分析棧鍵 生成棧分析表
def onClick_analyze_stack(self):
# 擷取輸入串并加入‘#’結束标志
layer_stack = 1
# 先設定 table層數為1 後 動态增加
self.tableStack.setRowCount(layer_stack)
# 擷取 輸入串 輸入框中的句子 并且儲存在 STACK_INPUT 清單中
for word in self.textEdit.toPlainText():
STACK_INPUT.append(word)
# 輸入串清單尾端插入‘#’ 代表句子結束
STACK_INPUT.append('#')
# 輸入棧初始化含 ‘E’
STACK.append('E')
# 列印分析棧表 的第一行 ,初始化的預設行
#列印輸入串
str_w = ''
for word in STACK_INPUT:
str_w += word
item1 = QtWidgets.QTableWidgetItem(str_w)
self.tableStack.setItem(0,1 , item1)
str_w = ''
# 列印輸入棧
for word in STACK:
str_w += word
item1 = QtWidgets.QTableWidgetItem(str_w)
# 動作 初始化
self.tableStack.setItem(0, 0, item1)
item1 = QtWidgets.QTableWidgetItem("初始化")
self.tableStack.setItem(0, 3, item1)
# 後續LL(1)文法分析棧表的實作 分兩個部分①輸入串頂不為‘#’(還沒到輸入串結束符) ②輸入串為‘#’(到輸入串結束符)
while STACK_INPUT[0] != '#':
# 層數增加一層
layer_stack = layer_stack+1
self.tableStack.setRowCount(layer_stack)
# 如果符号棧的棧頂為大寫字元(即非終結符)則執行下面的操作 符号出棧入棧 并列印相關表資訊
if STACK[-1].isupper():
# 獲得預測分析表中,的資料,利用 M[S][VT]矩陣調用操作類型
str = SELECT[STACK[-1]][STACK_INPUT[0]].split("->")[1]
item1 = QtWidgets.QTableWidgetItem(SELECT[STACK[-1]][STACK_INPUT[0]])
self.tableStack.setItem(layer_stack - 1, 2, item1)
# print(SELECT[STACK[-1]][STACK_INPUT[0]])
# 如果 符号棧頂的動作不是是 S -> ε 則符号棧 出棧 入棧(逆序)
if str != 'ε':
##逆置
STACK.remove(STACK[-1])
for word in str[::-1]:
STACK.append(word)
#print(STACK)
# 在表中顯示動作
item1 = QtWidgets.QTableWidgetItem("POP,PUSH("+str+")")
self.tableStack.setItem(layer_stack - 1, 3, item1)
# 如果 符号棧頂的動作是 S -> ε 則符号棧 則直接出棧
else:
STACK.remove(STACK[-1])
# 在表中顯示動作
item1 = QtWidgets.QTableWidgetItem("POP")
self.tableStack.setItem(layer_stack - 1, 3, item1)
# 在表中顯示符号棧,剩餘輸入串
str_w = ''
for word in STACK:
str_w += word
item1 = QtWidgets.QTableWidgetItem(str_w)
self.tableStack.setItem(layer_stack-1, 0, item1)
str_w = ''
for word in STACK_INPUT:
str_w += word
item1 = QtWidgets.QTableWidgetItem(str_w)
self.tableStack.setItem(layer_stack-1, 1, item1)
# 如果符号棧棧頂是 終結符(非大寫) ,則比較符号棧棧頂 和 剩餘輸入串的第一個元素是否相同相同則都出棧
else:
if STACK[-1] == STACK_INPUT[0]:
STACK.remove(STACK[-1])
STACK_INPUT.remove(STACK_INPUT[0])
#print(STACK)
#顯示 資訊
str_w = ''
for word in STACK:
str_w += word
item1 = QtWidgets.QTableWidgetItem(str_w)
self.tableStack.setItem(layer_stack - 1, 0, item1)
str_w = ''
for word in STACK_INPUT:
str_w += word
item1 = QtWidgets.QTableWidgetItem(str_w)
self.tableStack.setItem(layer_stack - 1, 1, item1)
item1 = QtWidgets.QTableWidgetItem("GETNEXT(i)")
self.tableStack.setItem(layer_stack - 1, 3, item1)
# 剩餘輸入串已經到‘# ’
while STACK[-1] != '#':
layer_stack = layer_stack + 1
self.tableStack.setRowCount(layer_stack)
# 如果分析表中的動作是 S->ε 則 列印 該表達式
if SELECT[STACK[-1]][STACK_INPUT[0]][-1] == 'ε':
item1 = QtWidgets.QTableWidgetItem(SELECT[STACK[-1]][STACK_INPUT[0]])
self.tableStack.setItem(layer_stack - 1, 2, item1)
STACK.remove(STACK[-1])
# print(STACK)
#顯示資訊
str_w = ''
for word in STACK:
str_w += word
item1 = QtWidgets.QTableWidgetItem(str_w)
self.tableStack.setItem(layer_stack - 1, 0, item1)
str_w = ''
for word in STACK_INPUT:
str_w += word
item1 = QtWidgets.QTableWidgetItem(str_w)
self.tableStack.setItem(layer_stack - 1, 1, item1)
item1 = QtWidgets.QTableWidgetItem("POP")
self.tableStack.setItem(layer_stack - 1, 3, item1)
print(1)
print("按下分析棧鍵")
# 按下生成各類幾個集合,first,follow,analyze set
def onClick_create_first_follow_analyze_set(self):
print("按下生成各類幾個鍵,first,follow,analyze set")
sentences.clear()
# 獲得所輸入框輸入的文法
for sentence in self.textEdit_2.toPlainText().split():
# 将每行的含有‘|’ 的句型分成兩句,擷取文法,并儲存在sentence
if len(sentence.split('|')) <2:
sentences.append(sentence)
else:
part_head = sentence.split('|')[0]
sentences.append(part_head)
part_foot = sentence.split('|')[1]
sentences.append(part_head[0]+"->"+part_foot)
# 初始化 First Follow 集合
initail()
#擷取First 集合
getFirst()
getFisrt_3()
getFisrt_3()
# print( FIRST )
# 擷取Follow 集合
getFOLLOW_3()
getFOLLOW_3()
## 擷取 終結符 非終結符
getVT_VC()
## 擷取 預測分析表
getSelect()
# 界面顯示 first集
for i, j in FIRST.items():
str = j[0]
for temp in j[1:]:
str = str + ',' + temp
self.textFirst_set.setText(
"FIRST(" + i + ")" + " = {" + str + "}" + "\n" + self.textFirst_set.toPlainText())
# 界面顯示follow 集合
for i, j in FOLLOW.items():
str = j[0]
for temp in j[1:]:
str = str + ',' + temp
self.textFollow_set.setText(
"FOLLOW(" + i + ")" + " = {" + str + "}""\n" + self.textFollow_set.toPlainText())
## 設定預測分析表的列數 len(VT)+2 終結符的數量加2,2 為第一列非終結符占一列,最後#占一列
self.tableAnalyze.setColumnCount(len(VT)+2)
layer_analyze = 1
## 設定預測分析表的層數
self.tableAnalyze.setRowCount(layer_analyze)
VT_1 = VT [:]
VT_1.append('#')
for size in range(len(VT_1)):
self.tableAnalyze.setColumnWidth(size, 70)
item1 = QtWidgets.QTableWidgetItem(VT_1[size])
self.tableAnalyze.setItem(0, size+1, item1)
for i in range(len(VC)):
# 沒周遊一次增加一層
layer_analyze = layer_analyze + 1
self.tableAnalyze.setRowCount(layer_analyze)
# 界面顯示 預測分析表
#顯示預測分析表的第一列 非終結符
item1 = QtWidgets.QTableWidgetItem(VC[i])
self.tableAnalyze.setItem(i+1,0, item1)
# 顯示預測分析表的每一行 動作
for size in range(len(VT_1)):
if(VT_1[size] in SELECT[VC[i]]):
item1 = QtWidgets.QTableWidgetItem(SELECT[VC[i]][VT_1[size]])
self.tableAnalyze.setItem(i+1, size + 1, item1)
#
# item1 = QtWidgets.QTableWidgetItem(VT_1[size])
# self.tableAnalyze.setItem(0, size, item1)
##部分 界面 初始化資料
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "FF(1)文法"))
self.textEdit.setHtml(_translate("Form",
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Arial\'; font-size:28pt; font-weight:400; font-style:italic;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>"))
self.pushButton.setText(_translate("Form", "一鍵分析"))
self.lineEdit.setText(
_translate("Form", " ©️2019 Leechoy 李阡殇"))
self.textEdit_2.setHtml(_translate("Form",
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:7pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">E->TG</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">G->+TG|</span><span style=\" font-family:\'宋體\'; font-size:8pt;\">—</span><span style=\" font-size:8pt;\">TG </span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">G-></span><span style=\" font-family:\'宋體\'; font-size:8pt;\">ε</span><span style=\" font-size:8pt;\"> </span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">T->FS</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">S->*FS|/FS</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">S-></span><span style=\" font-family:\'宋體\'; font-size:8pt;\">ε</span><span style=\" font-size:8pt;\"> </span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">F->(E)</span></p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-family:\'Times New Roman\'; font-size:8pt;\">F->i </span></p></body></html>"))
self.textFirst_set.setHtml(_translate("Form",
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:15pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>"))
self.pushButton_2.setText(_translate("Form", "一鍵生成"))
# 初始化 first 集 和follow集合字典的鍵值對中的 值 為空
def initail():
for str in sentences:
part_begin = str.split("->")[0]
part_end = str.split("->")[1]
FIRST[part_begin] = ""
FOLLOW[part_begin] = "#"
###求first集 中第第一部分針對 -> 直接推出第一個字元為終結符 部分
def getFirst():
for str in sentences:
part_begin = str.split("->")[0]
part_end = str.split("->")[1]
if not part_end[0].isupper():
FIRST[part_begin] = FIRST.get(part_begin) + part_end[0]
# 求first第二部分 針對 A -> B型 把B的first集加到A 的first集合中
def getFirst_2():
for str in sentences:
part_begin = ''
part_end = ''
part_begin += str.split('->')[0]
part_end += str.split('->')[1]
# 如果型如A ->B 則把B的first集加到A 的first集中去
if part_end[0].isupper():
FIRST[part_begin] = FIRST.get(part_begin) + FIRST.get(part_end[0])
# 求first第三部分,不斷調用前面的求first集的方法使,得first不在增加為止
def getFisrt_3():
while (1):
test = FIRST
getFirst_2()
# 去除重複項
for i, j in FIRST.items():
temp = ""
for word in list(set(j)):
temp += word
FIRST[i] = temp
if test == FIRST:
break
# 計算follow集的第一部分,先計算 S -> A b 類型的
def getFollow():
for str in sentences:
part_begin = str.split("->")[0]
part_end = str.split("->")[1]
##如果是 S->a 直接推出終結符 則 continue
if len(part_end) == 1:
continue
##否則執行下面的操作
else:
# 将->後面的分開再倒序
temp = []
for i in part_end:
temp.append(i)
temp.reverse()
# 如果非終結符在句型的末端則把"#" 加入進去
if temp[0].isupper():
FOLLOW[temp[0]] = FOLLOW.get(temp[0]) + FOLLOW.get(part_begin)
temp1 = temp[0]
for i in temp[1:]:
if not i.isupper():
temp1 = i
else:
if temp1.isupper():
FOLLOW[i] = FOLLOW.get(i) + FIRST.get(temp1).replace("ε", "")
if ('ε' in FIRST.get(temp1)):
FOLLOW[i] = FOLLOW.get(i) + FOLLOW.get(part_begin)
else:
FOLLOW[i] = FOLLOW.get(i) + temp1
temp1 = i
# 如果終結符在句型的末端
else:
temp1 = temp[0]
for i in temp[1:]:
if not i.isupper():
temp1 = i
else:
if temp1.isupper():
FOLLOW[i] = FOLLOW.get(i) + FIRST.get(temp1)
else:
FOLLOW[i] = FOLLOW.get(i) + temp1
temp1 = i
def getFOLLOW_3():
while (1):
test = FOLLOW
getFollow()
# 去除重複項
for i, j in FOLLOW.items():
temp = ""
for word in list(set(j)):
temp += word
FOLLOW[i] = temp
if test == FOLLOW:
break
# 獲得終結符
def getVT_VC():
temp = []
for sentence in sentences:
for word in sentence:
if not word.isupper():
temp.append(word)
for i in temp:
if not i in VT:
VT.append(i)
VT.remove('ε')
##求 非終結符
VT.sort()
temp = []
for sentence in sentences:
temp.append(sentence[0])
for i in temp:
if not i in VC:
VC.append(i)
VC.sort()
##獲得文法預測分析表 利用select = first(s) 如果 first集中包含空字 'ε' 則select = first + follow
##二維字典添加 鍵值函數
##建構預測分析表
#二維字典,插入(建立)資料函數
def addDict_2D(thedict, key_a, key_b, val):
if key_a in thedict:
thedict[key_a].update({key_b: val})
else:
thedict.update({key_a: {key_b: val}})
# 獲得分析表
def getSelect(): # 如果first(s)中有空字的 在矩陣 M(S,follow(S))填 S - > ε
for i, j in FIRST.items():
if 'ε' in j:
for follow in FOLLOW.get(i):
addDict_2D(SELECT, i, follow, i + "->ε")
for str in sentences:
part_begin = str.split("->")[0]
part_end = str.split("->")[1]
############################
##需要改進
###############################
if not part_end[0].isupper():
if part_end[0] != 'ε':
addDict_2D(SELECT, part_begin, part_end[0], str)
else:
for i in FIRST.get(part_begin):
addDict_2D(SELECT, part_begin, i, str)
# print(VT)
# print(FIRST)
# print(VC)
# print(FOLLOW)
##列印 first集 和follow 集合
# for i ,j in FIRST.items() :
# str = j[0]
# for temp in j[1:]:
# str = str+ ',' +temp
# print("FIRST("+ i + ")" + " = {"+str+"}")
#
# for i ,j in FOLLOW.items():
# str = j[0]
# for temp in j[1:]:
# str = str + ',' + temp
# print("FOLLOW("+ i + ")" + " = {"+str+"}")
##列印 分析表
# print(FIRST)
# print(FOLLOW)
# for i in SELECT:
# print(SELECT[i])
# # # for j in SELECT[i]:
# # # print(SELECT[i][j])
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
widget = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(widget)
widget.show()
sys.exit(app.exec_()) ## 退出