编译原理-python实现LL(1)分析器(GUI界面tkinter,自动分析文法)

原理

  • 本次实验是设计一个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)分析器界面:

图片.png

点击打开文件,选择文法的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实例中,进行转换。
    """

    
  • 7
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值