序言
因为课程实验需要,使用python+pyqt编写了一个简易的显示LL(1)分析表的程序
大体思路都是按照书上给的步骤来的,其中提取公共左因子这一块没有针对性测试,可能会出现一些毛病
如有错误望指正
运行截图
代码
首先是methods.py,里面包含除了求预测分析表的其他方法
"""
编译原理实验6
构造G[E]的LL(1)预测分析表
SDUWH 章逸民 2021-6-10
首先消除左递归
然后使所有候选首符集两两不相交
求出FIRST集和FOLLOW集
最后生成预测分析表
"""
import string
# 非终结字符, 假设均为大写字母
Vn = []
for upletter in string.ascii_uppercase:
Vn.append(upletter)
Vn_unused = Vn[:]
# Vn.extend(['α', 'β', 'γ', 'δ', 'ε', 'ζ', 'ν', 'ξ', 'ο', 'π', 'ρ', 'σ', 'η', 'θ'])
"""
输入示例
S -> a | ^ | (T)
T -> T,S | S
"""
# s = 'S -> a | ^ | (T)\n' \
# 'T -> T,S | S'
#
# s = 'E -> E+T | T\n' \
# 'T -> T*F | F\n' \
# 'F -> (E) | i'
# s = 'R -> Sa | a\n' \
# 'Q -> Rb | b\n' \
# 'S -> Qc | c'
def reset():
global Vn
Vn = []
for up_letter in string.ascii_uppercase:
Vn.append(up_letter)
global Vn_unused
Vn_unused = Vn[:]
# 预处理
def pre_deal(s):
product = s.split('\n')
t = {}
not_end_c = [] # 非终结符集合
end_c = [] # 终结符集合
for p in product:
left = p.split('->')[0].replace(' ', '')
t[left] = []
if left not in not_end_c:
not_end_c.append(left) # 生成式左侧必为非终结符
if Vn_unused.count(left) != 0:
Vn_unused.remove(left)
all_right = p.split('->')[1].split('|')
for right in all_right:
right = right.replace(' ', '')
t[left].append(right)
for r in range(len(right)):
if right[r] in Vn:
# 大写字母
if right[r] not in not_end_c:
not_end_c.append(right[r])
Vn_unused.remove(right[r])
elif right[r] != 'ε' and right[r] not in end_c and right[r] != ' ':
# 除了空串的终结字符
end_c.append(right[r])
return t, not_end_c, end_c
# 消除左递归
def clear_left_loop(s):
# 按照消除左递归方法进行操作
t, not_end_c, end_c = pre_deal(s)
for i in range(len(not_end_c)):
for j in range(i):
# 如果not_end_c[i]的产生式右端的开头字符为not_end_c[j],则用t[not_end_c[j]]替代
for k in t[not_end_c[i]]:
if k[0] == not_end_c[j]:
for m in t[not_end_c[j]]:
t[not_end_c[i]].append(m+k[1:])
t[not_end_c[i]].remove(k)
# 消除not_end_c[i]的直接左递归
# 先将开头为not_end_c[i]的移至前端,并设置flag为True,代表需要消除直接左递归
flag = False
for k in t[not_end_c[i]]:
if k[0] == not_end_c[i]:
t[not_end_c[i]].remove(k)
t[not_end_c[i]].insert(0, k)
flag = True
if flag:
# 需要消除直接左递归
# 先从Vn_unused里挑选一个非终结符
V = Vn_unused[0]
Vn_unused.remove(V)
not_end_c.append(V)
t[V] = []
temp = []
for k in t[not_end_c[i]]:
if k[0] == not_end_c[i]:
t[V].append(k[1:] + V)
else:
temp.append(k + V)
t[not_end_c[i]] = temp[:]
t[V].append('ε')
temp_t = {}
for key, value in t.items():
flag, pub, pub_len = select_pub_elem(value)
temp = {
key: value
}
while flag:
V = Vn_unused[0]
Vn_unused.remove(V)
not_end_c.append(V)
temp[key] = [pub + V]
temp[V] = []
for v in value:
if v[:pub_len] != pub:
temp[key].append(v)
else:
temp[V].append(v[pub_len:])
value = temp[key]
flag, pub, pub_len = select_pub_elem(temp[key])
temp_t.update(temp)
t = temp_t
print(t)
print("非终结符", not_end_c)
print("终结符", end_c)
return t, not_end_c, end_c
# 提取公共左因子
def select_pub_elem(L):
temp = []
pub = ''
pub_len = 0
flag = False
for l in L:
if l[0] not in temp:
temp.append(l[0])
else:
# 需要消除公共左因子
flag = True
pub = l[0]
pub_len = 1
break
if flag:
temp = []
for l in L:
if l[0] == pub:
temp.append(l[1:])
_, m, n = select_pub_elem(temp)
pub += m
pub_len += n
return flag, pub, pub_len
# 生成FIRST集
def first_set(key, t, not_end_c, end_c):
result = []
if key in end_c:
# 如果X是终结符,那么FIRST(X) = {X}
result.append(key)
elif key in not_end_c:
for value in t[key]:
if value[0] in end_c:
result.append(value[0])
else:
# 向FIRST(X)中加入FIRST(Y1)的所有非空符号
for m in first_set(value[0], t, not_end_c, end_c):
if m not in result and m != 'ε':
result.append(m)
# 如果ε在FIRST(Y1)中,再加入FIRST(Y2)中所有非ε符号,以此类推
temp = 0
while temp < len(value) - 1 and 'ε' in first_set(value[temp], t, not_end_c, end_c):
temp += 1
for m in first_set(value[temp], t, not_end_c, end_c):
if m not in result and m != 'ε':
result.append(m)
# 如果X -> ε,则将 ε 加入FIRST(X)中
if 'ε' in t[key]:
result.append('ε')
return result
def all_first_set(s):
t, not_end_c, end_c = clear_left_loop(s)
FIRST = {}
for c in not_end_c + end_c:
FIRST[c] = first_set(c, t, not_end_c, end_c)
print("FIRST集", FIRST)
return FIRST, t, not_end_c, end_c
# 生成FOLLOW集
def all_follow_set(s, start_c):
FIRST, t, not_end_c, end_c = all_first_set(s)
FOLLOW = {}
# 初始化
for c in not_end_c:
FOLLOW[c] = []
FOLLOW[start_c].append('#')
# 循环至每个FOLLOW不再增大
# 使用L代表长度
L = 1
temp = 0
while(L != temp):
temp = L
# 对所有产生式操作
for key, value in t.items():
for v in value:
# 此时 key -> v
for i in range(len(v)-1):
if v[i] in not_end_c:
for m in FIRST[v[i+1]]:
if m not in FOLLOW[v[i]] and m != 'ε':
FOLLOW[v[i]].append(m)
L += 1
# 如果v[i]后的FIRST[β]包含ε
flag = True
for j in range(i+1, len(v)):
if 'ε' not in FIRST[v[j]]:
flag = False
if flag:
# 将FOLLOW(key)加至FOLLOW(v[i])中
for f in FOLLOW[key]:
if f not in FOLLOW[v[i]]:
FOLLOW[v[i]].append(f)
L += 1
if v[len(v)-1] in not_end_c:
# 将FOLLOW(key)加至FOLLOW(v[len(v)-1])中
for f in FOLLOW[key]:
if f not in FOLLOW[v[len(v)-1]]:
FOLLOW[v[len(v)-1]].append(f)
L += 1
print('FOLLOW集', FOLLOW)
return FIRST, FOLLOW, t, not_end_c, end_c
下面是pyqt文件,我命名为interface.py,其中的set函数包含了将FIRST集和FOLLOW集显示以及生成预测分析表的功能
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'interface.ui'
#
# Created by: SDUWH 章逸民 2021-6-10
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
import methods
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(951, 866)
self.tE = QtWidgets.QTextEdit(Form)
self.tE.setGeometry(QtCore.QRect(10, 30, 401, 371))
self.tE.setObjectName("tE")
self.tB_first = QtWidgets.QTextBrowser(Form)
self.tB_first.setGeometry(QtCore.QRect(480, 50, 201, 351))
self.tB_first.setObjectName("tB_first")
self.tB_follow = QtWidgets.QTextBrowser(Form)
self.tB_follow.setGeometry(QtCore.QRect(740, 90, 201, 311))
self.tB_follow.setObjectName("tB_follow")
self.btn_table = QtWidgets.QPushButton(Form)
self.btn_table.setGeometry(QtCore.QRect(10, 410, 931, 28))
self.btn_table.setObjectName("btn_table")
self.btn_table.clicked.connect(self.set)
self.tW = QtWidgets.QTableWidget(Form)
self.tW.setGeometry(QtCore.QRect(10, 450, 931, 401))
self.tW.setObjectName("tW")
self.tW.setColumnCount(0)
self.tW.setRowCount(0)
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(10, 10, 72, 15))
self.label.setObjectName("label")
self.tE2 = QtWidgets.QTextEdit(Form)
self.tE2.setGeometry(QtCore.QRect(740, 30, 201, 31))
self.tE2.setObjectName("tE2")
self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setGeometry(QtCore.QRect(740, 10, 72, 15))
self.label_2.setObjectName("label_2")
self.label_3 = QtWidgets.QLabel(Form)
self.label_3.setGeometry(QtCore.QRect(480, 30, 72, 15))
self.label_3.setObjectName("label_3")
self.label_4 = QtWidgets.QLabel(Form)
self.label_4.setGeometry(QtCore.QRect(740, 70, 72, 15))
self.label_4.setObjectName("label_4")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.btn_table.setText(_translate("Form", "生成"))
self.label.setText(_translate("Form", "输入文法:"))
self.label_2.setText(_translate("Form", "开始符号:"))
self.label_3.setText(_translate("Form", "FIRST集:"))
self.label_4.setText(_translate("Form", "FOLLOW集:"))
def set(self):
methods.reset()
self.tW.clear()
self.tB_first.clear()
self.tB_follow.clear()
s = self.tE.toPlainText().strip()
start_c = self.tE2.toPlainText().strip()
FIRST, FOLLOW, t, not_end_c, end_c = methods.all_follow_set(s, start_c)
s = ""
for key, value in FIRST.items():
s += "FIRST( {} ) = ".format(key)
for v in value:
s += v + " "
s += '\n'
self.tB_first.setText(s)
s = ""
for key, value in FOLLOW.items():
s += "FOLLOW( {} ) = ".format(key)
for v in value:
s += v + " "
s += "\n"
self.tB_follow.setText(s)
self.tW.setRowCount(len(not_end_c))
self.tW.setColumnCount(len(end_c)+1)
self.tW.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
self.tW.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
self.tW.setHorizontalHeaderLabels(end_c+['#'])
self.tW.setVerticalHeaderLabels(not_end_c)
end_c.append('#')
# 对每个产生式
for key, value in t.items():
for v in value:
# 此时为 key -> v
s = '{} -> {}'.format(key, v)
temp = s
if v[0] != 'ε':
for a in FIRST[v[0]]:
if a in end_c:
if self.tW.item(not_end_c.index(key), end_c.index(a)) is not None:
temp = self.tW.item(not_end_c.index(key), end_c.index(a)).text() + ', ' + s
new_item = QtWidgets.QTableWidgetItem(temp)
self.tW.setItem(not_end_c.index(key), end_c.index(a), new_item)
# 判断ε是否属于FIRST(v)
flag = True
for p in v:
if 'ε' not in FIRST[p]:
flag = False
break
if flag:
for b in FOLLOW[key]:
if self.tW.item(not_end_c.index(key), end_c.index(b)) is not None:
temp = self.tW.item(not_end_c.index(key), end_c.index(b)).text() + ', ' + s
new_item = QtWidgets.QTableWidgetItem(temp)
self.tW.setItem(not_end_c.index(key), end_c.index(b), new_item)
else:
# 此时为 key -> ε
for b in FOLLOW[key]:
if self.tW.item(not_end_c.index(key), end_c.index(b)) is not None:
temp = self.tW.item(not_end_c.index(key), end_c.index(b)).text() + ', ' + s
new_item = QtWidgets.QTableWidgetItem(temp)
self.tW.setItem(not_end_c.index(key), end_c.index(b), new_item)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
widget = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(widget)
widget.show()
sys.exit(app.exec_())