编译原理(4)LR(0)语法分析程序设计(Python实现)

2023-11-18

1. 实验要求

(1)已知文法G[S']

(0) S'→E 

(1) E→aA

(2) E→bB

(3) A→cA 

(4) A→d

(5) B→cB 

(6) B→d

手工建立文法G[S']的LR(0)的项目集规范族DFA和LR(0)分析表。

(2) 根据清华大学版《编译原理(第3版)》教材上LR(0)语法分析的算法思想及算法流程,构造LR(0)语法分析程序。

(3)用该LR(0)语法分析程序对任意给定的键盘输入串进行语法分析,并根据栈的变化状态输出给定串的具体分析过程。如果分析成功,则输入输出串是给定文法的合法句子的结论;如果分析不成功,则输入输出串不是给定文法的句子的结论。

详细分析过程可以看看这篇:

LR(0)分析法 - ISGuXing - 博客园 (cnblogs.com)

2. 分析结果输出

 3. Python代码实现

import copy

class Stack():  # 定义类
    def __init__(self):  # 产生一个空的容器
        self.__list = []

    def size(self):  # 返回栈中元素个数
        return len(self.__list)

    def push(self, item):  # 入栈
        self.__list.append(item)

    def push_list(self, list):  # 末尾添加多个元素
        for i in range(len(list)):
            self.push(list[i])

    def pop(self):  # 出栈
        return self.__list.pop()

    def pop_list(self, n):  # 出栈n个元素
        for i in range(n):
            self.pop()

    def peek(self):  # 返回栈顶元素
        return self.__list[-1]

    def is_empty(self):  # 判断是否已为空
        return not self.__list

    def deep_copy(self):  # 深拷贝
        new_stack = Stack()
        for i in range(self.size()):
            new_stack.push(self.__list[i])
        return new_stack

    def bottom_output(self):  # 从栈底开始排列输出
        string = ""
        for i in range(self.size()):
            string += str(self.__list[i])
        return string

    def top_output(self):  # 从栈顶开始排列输出
        string = ""
        for i in range(self.size() - 1, -1, -1):
            string += str(self.__list[i])
        return string


# LR(0)分析表
LR0_table = {
    0: {'a': 'S2', 'b': 'S3', 'c': None, 'd': None, '#': None, 'E': 1, 'A': None, 'B': None},
    1: {'a': None, 'b': None, 'c': None, 'd': None, '#': 'acc', 'E': None, 'A': None, 'B': None},
    2: {'a': None, 'b': None, 'c': 'S4', 'd': 'S10', '#': None, 'E': None, 'A': 6, 'B': None},
    3: {'a': None, 'b': None, 'c': 'S5', 'd': 'S11', '#': None, 'E': None, 'A': None, 'B': 7},
    4: {'a': None, 'b': None, 'c': 'S4', 'd': 'S10', '#': None, 'E': None, 'A': 8, 'B': None},
    5: {'a': None, 'b': None, 'c': 'S5', 'd': 'S11', '#': None, 'E': None, 'A': None, 'B': 9},
    6: {'a': 'r1', 'b': 'r1', 'c': 'r1', 'd': 'r1', '#': 'r1', 'E': None, 'A': None, 'B': None},
    7: {'a': 'r2', 'b': 'r2', 'c': 'r2', 'd': 'r2', '#': 'r2', 'E': None, 'A': None, 'B': None},
    8: {'a': 'r3', 'b': 'r3', 'c': 'r3', 'd': 'r3', '#': 'r3', 'E': None, 'A': None, 'B': None},
    9: {'a': 'r5', 'b': 'r5', 'c': 'r5', 'd': 'r5', '#': 'r5', 'E': None, 'A': None, 'B': None},
    10: {'a': 'r4', 'b': 'r4', 'c': 'r4', 'd': 'r4', '#': 'r4', 'E': None, 'A': None, 'B': None},
    11: {'a': 'r6', 'b': 'r6', 'c': 'r6', 'd': 'r6', '#': 'r6', 'E': None, 'A': None, 'B': None},
}
# 产生式
Product = {
    0: {'S': 'E'},
    1: {'E': 'aA'},
    2: {'E': 'bB'},
    3: {'A': 'cA'},
    4: {'A': 'd'},
    5: {'B': 'cB'},
    6: {'B': 'd'},
}
# 终结符
VT = ['a', 'b', 'c', 'd', '#']
# 非终结符
VN = ['S', 'A', 'B', 'E']

# 记录分析结果
analyzeResult = False
# 记录分析步骤
analyzeStep = 0
# 状态栈、符号栈、字符串栈
stateStack = Stack()
symbolStack = Stack()
stringStack = Stack()


# 字符串翻转
def reverseString(string):
    return string[::-1]

# 检查输入串
def checkString(string):
    stringLegal = True
    if (len(string) == 0):
        print("输入出错:输入串不能为空!")
        stringLegal = False
    if (string[-1] != "#"):
        print("输入出错:输入串缺少结束符'#'!")
        stringLegal = False
    for i in range(len(string)):
        if (string[i] not in VT):
            print("输入出错:存在不是终结符的字符!")
            stringLegal = False
            break
    return stringLegal


# 初始化三个栈
def initStack(string):
    # 状态栈,入栈0
    stateStack.push(0)
    # 符号栈,入栈#
    symbolStack.push('#')
    # 输入串入栈,即string逆序入栈
    stringStack.push_list(reverseString(string))
    # 调用分析函数
    toAnalyze()

# 根据栈中内容进行分析
def toAnalyze():
    global analyzeResult
    global analyzeStep
    analyzeStep += 1
    stateStack_top = stateStack.peek()
    stringStack_top = stringStack.peek()
    curren = LR0_table[stateStack_top][stringStack_top]

    # LR(0)分析表中查到None
    if (curren == None):
        print(
            "|{:^5}|{:^20}|{:^20}|{:^20}|{:^10}|{:^10}|".format(analyzeStep, stateStack.bottom_output(),
                                                               symbolStack.bottom_output(), stringStack.top_output(),
                                                               'None', ''))
        print("错误:Action值为None!")
        analyzeResult = False
        return

    # LR(0)分析表中查到acc
    if (curren == 'acc'):
        print(
            "|{:^5}|{:^20}|{:^20}|{:^20}|{:^10}|{:^10}|".format(analyzeStep, stateStack.bottom_output(),
                                                               symbolStack.bottom_output(), stringStack.top_output(),
                                                               'acc', ''))
        analyzeResult = True
        return

    # LR(0)分析表中查到S
    elif (curren[0] == 'S' ):
        print(
            "|{:^5}|{:^20}|{:^20}|{:^20}|{:^10}|{:^10}|".format(analyzeStep, stateStack.bottom_output(),
                                                               symbolStack.bottom_output(), stringStack.top_output(),
                                                               curren, ''))
        # 状态栈进S下标
        curren = curren.replace('S', '')
        stateStack.push(int(curren))
        # 字符栈弹栈顶部元素进符号栈
        symbolStack.push(stringStack.pop())
        # 接着分析
        toAnalyze()

    # LR(0)分析表中查到r
    elif (curren[0] == 'r' ):
        # 保存一下curren, stateStack和symbolStack,,以便后续打印出来
        save_curren = curren
        save_stateStack = stateStack.deep_copy()
        save_symbolStack = symbolStack.deep_copy()
        # 根据r下标,寻找归约所用的产生式的key和value
        curren = curren.replace('r', '')
        key = list(Product[int(curren)].keys())[0]
        value = list(Product[int(curren)].values())[0]
        # 根据产生式左部value的长度,对状态站和符号栈进行退栈操作
        val_len = len(value)
        stateStack.pop_list(val_len)
        symbolStack.pop_list(val_len)
        # 根据状态站当前栈顶元素和产生式左部key,寻找GOTO值
        state_top = stateStack.peek()
        tmp_GOTO = LR0_table[state_top][key]
        # GOTO值为None,打印错误信息
        if (tmp_GOTO == None):
            print(
                "|{:^5}|{:^20}|{:^20}|{:^20}|{:^10}|{:^10}|".format(analyzeStep, save_stateStack.bottom_output(),
                                                                   save_symbolStack.bottom_output(),
                                                                   stringStack.top_output(),
                                                                   save_curren, 'None'))
            print("错误:GOTO值为None!")
            analyzeResult = False
            return
        # GOTO值不为None,将GOTO值入状态栈,产生式左部key入符号栈
        else:
            print(
                "|{:^5}|{:^20}|{:^20}|{:^20}|{:^10}|{:^10}|".format(analyzeStep, save_stateStack.bottom_output(),
                                                                   save_symbolStack.bottom_output(),
                                                                   stringStack.top_output(),
                                                                   save_curren, tmp_GOTO))
            stateStack.push(tmp_GOTO)
            symbolStack.push(key)
            toAnalyze()

if __name__ == '__main__':
    print("请输入待分析的字符串:")
    string = input()
    # 滤空格
    string = string.replace(" ", "")
    if (checkString(string)):
        print(
            "|{:^4}|{:^18}|{:^18}|{:^18}|{:^10}|{:^10}|".format('步骤', '状态栈', '符号栈', '输入串', 'Action', 'GOTO'))
        initStack(string)
        if (analyzeResult):
            print("由以上分析可知,该串是文法的合法句子。\n")
        else:
            print("由以上分析可知,该串不是文法的合法句子。\n")

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编译原理(4)LR(0)语法分析程序设计(Python实现) 的相关文章

随机推荐

  • 做编程题没有思路怎么办

    来信 老师您好 我是一名计算机专业大二的学生 我现在在做一系列c语言竞赛一些容易的题目 可是我发现我碰到的问题会很没有思路 不知道方向 看见网上的很多同学都能够解决 而我却不能 我不知道自己差到哪了 我不知道怎么办才好 都有很多中学生的水平
  • macOS 12 Monterey:一次全新的跨设备协作体验

    macOS 12 Monterey是苹果公司的一次重大突破 它打破了设备间的壁垒 将不同设备无缝地连接在一起 极大地提升了用户的工作效率和娱乐体验 Monterey带来了通用控制 AirPlay 捷径等新功能 以及一些实用的新小功能 安装
  • 本地项目放到服务器启动不了,spring boot在本地能启动,放服务器上就不行,

    写完代码 放到服务器上去发现启动不了 服务器的tomcat没有问题 因为上面已经有两个项目在跑 报错内容 百度过了 百度上都说是server api的问题 但是我没有引入这个包 被逼无奈才来这里请教的 java util concurren
  • 25_pre_content 阶段 mirror 模块

    文章目录 precontent 阶段的 mirror 模块 示例配置 precontent 阶段的 mirror 模块 作用 实时拷贝流量 处理请求时 生成子请求访问其他服务 对子请求的返回值不做处理 ngx http mirror mod
  • 调用接口登录禅道_Java调用禅道api接口查询以及创建任务(傻瓜式复制粘贴--旗舰版禅道页面调用)

    背景 系统需要调用禅道的接口进行工单的创建 并对工单进行附件上传等信息的操作 禅道接口为http接口 每次请求都需要带上zentaosid进行请求 由于专业版禅道升级为旗舰版禅道版本变更 请求的URL有所变化 变化最大为常量类配置 如下 实
  • Set集合

    目录 一 Set集合的特点 二 Set集合取值 三 常用实现类HashSet和TreeSet 四 Comparable和Comparator的使用 4 1 Comparable 4 2 Comparator 五 LinkedHashSet
  • HTML5本地存储

    1 背景 在HTML4 01中 想要在浏览器中存储用户的数据时 我们一般只能用Cookie来实现 不过Cookie有很多限制 大小限制 最大4KB 数量限制 每个站点只允许存储20个Cookie 如果想要存储更多Cookie 则要把旧的Co
  • 【数组中数字出现的次数-有限状态自动机】

    数组中数字出现的次数 一 有限状态自动机解法 二 一般解法 想必大家对数组中数字出现的次数的这种题并不少见 主要有三种 1 找出数组中只出现一次的数字 其他数字出现两次 2 找出数组中仅有的两个仅出现一次的数字 其他数字出现两次 3 找出数
  • 互联网摸鱼日报(2022-12-07)

    互联网摸鱼日报 2022 12 07 InfoQ 热门话题 Solo推出完全集成的云原生应用程序网络平台 经历过亿级DAU的打磨检验 抖音同款RTC到底有何魔力 性能提升 2 5 倍 字节开源高性能 C JSON 库 sonic cpp 最
  • 讲一下进程地址空间

    进程地址空间是指每个进程在计算机内存中所占用的地址空间 地址空间是指能被访问的内存地址范围 它由若干个连续的内存块组成 每个进程都有自己的地址空间 这意味着每个进程都有自己的内存地址范围 不会与其他进程冲突 进程地址空间通常被划分为几个部分
  • creator图片循环显示_CocosCreator 多块地图无限无缝滚动 组件

    地图滚动出现的原因 export enum DIRECTION LEFT 1 RIGHT 2 UP 3 DOWN 4 多块地图滚动组件 const ccclass property cc decorator ccclass export d
  • 数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)

    原文 http blog csdn net sup heaven article details 39313731 数据结构中常见的树 BST二叉搜索树 AVL平衡二叉树 RBT红黑树 B 树 B 树 B 树 转载 2014年09月16日
  • ERROR: transport error 202: gethostbyname: unknown host

    今天碰到了一个很奇怪的问题 当我启动tomcat的时候 报了如下的错误 ERROR transport error 202 gethostbyname unknown host ERROR JDWP Transport dt socket
  • 作为运维你还在想要不要学Python,听听运维老司机怎么说!

    现阶段 掌握一门开发语言已经成为高级运维工程师的必备计能 不会开发 你就不能充分理解你们系统的业务流程 你就不能帮助调试 优化开发人开发的程序 开发人员有的时候很少关注性能的问题 这些问题就得运维人员来做 一个业务上线了 导致CPU使用过高
  • Python数据可视化入门教程(非常详细)

    什么是数据可视化 数据可视化是为了使得数据更高效地反应数据情况 便于让读者更高效阅读 通过数据可视化突出数据背后的规律 以此突出数据中的重要因素 如果使用Python做数据可视化 建议学好如下这四个Python数据分析包 分别是 Panda
  • springboot整合ueditor(jsp)踩过的坑(富文本上传本地视频)(亲身经历)

    有一天老板突然找我让我改富文本 一脸懵逼 不过也不能推啊默默地接下了 大家都知道现在的富文本视频功能都是只有上传链接的没有从本地上传这一说 就连现在的csdn的也是 于是我找了好多个 最终发现百度的ueditor可以 经过几天的日夜 甚至牺
  • vue框架如何将侧边栏完全隐藏

    vue框架如何将侧边栏完全隐藏 如何将vue的左侧边栏在缩进的时候完全隐藏呢 效果图如下 找到目录src style sidebar scss 然后搜索 hideSidebar可以搜出两个 不要慌 下面的时手机端的 我们拉到上面的 hide
  • 【算法日志】哈希表应用:set和map容器,哈希表的使用(day5)

    代码随想录60day 链表 day4 链表 day3 目录 前言 一 三种哈希结构 数组 散列技术 哈希思想 哈希碰撞 set 集合 map 映射 二 哈希表的一些应用 总结 前言 哈希表 也叫散列表 是一种较为常用的数据结构 我们常用的数
  • 1486. XOR Operation in an Array

    class Solution public int xorOperation int n int start int nret start for int m 1 m lt n m nret nret start 2 m return nr
  • 编译原理(4)LR(0)语法分析程序设计(Python实现)

    1 实验要求 1 已知文法G S 0 S E 1 E aA 2 E bB 3 A cA 4 A d 5 B cB 6 B d 手工建立文法G S 的LR 0 的项目集规范族DFA和LR 0 分析表 2 根据清华大学版 编译原理 第3版 教材