天天看點

Python實作LL(1) 文法分析器(帶界面)

LL(1)分析法實驗設計思想及算法

子產品結構:

(1)定義部分:定義常量、變量、資料結構。

(2)初始化:設立 LL(1)分析表、初始化變量空間(包括堆棧、結構體、數組、

臨時變量等);

(3)控制部分:從鍵盤輸入一個表達式符号串;

(4)利用 LL(1)分析算法進行表達式處理:根據 LL(1)分析表對表達式符号串進

行堆棧(或其他)操作,輸出分析結果,如果遇到錯誤則顯示錯誤資訊。

Python實作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

Python實作LL(1) 文法分析器(帶界面)
Python實作LL(1) 文法分析器(帶界面)

代碼實作

# -*- 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_())  ## 退出      

實作所用pyqt5庫的安裝

步驟一、pycharm 控制台 pip install pyqt5 或者 File ->Settings ->Project Interpreter 下載下傳添加pyqt5的庫

步驟二、​​環境問題解決​​