python 递归下降分析法的设计与实验原理 编译原理

news2024/11/20 4:28:59

本文内容:

本文章实现的文法:

E->T|E+T;
T->F|T*F;
F->i|(E);

利用上一篇文章:python 预备实验2 LL(1)文法构造转化后的输出:

E->TE';
T->FT';
F->i|(E);
E'->+TE'|;
T'->*FT'|;

手工测试,是LL(1)文法下面第二列是首符集,第三列是后继符集

# 经过处理后的文法如下:
# E->TE';       i,(     ),;
# T->FT';       i,(     +,),;
# F->i|(E);     i,(     *,+,),;
# E'->+TE'|;    +,e     ),;
# T'->*FT'|;    *,e     +,),;
# 由于函数名不能有‘所以里面的’由1代替

1)手工将测试的表达式写入文本文件,每个表达式写一行,用“;”表示结束;

2)读入文本文件中的表达式;

3)调用实验一中的词法分析程序搜索单词;

4)把单词送入递归下降分析程序,判断表达式是否正确,若错误,应给出错误信息;

样例:

相关程序:       

        python 预备实验2 LL(1)文法构造                # 将文法消除左递归,提取左因子

        python 词法分析程序的设计 编译原理        # 从文件中提取各个元素,代码中scan部分

 代码如下:(直接从107行开始看,嵌满的是词法分析部分)

# scan 部分=============================================================================
# =====================================================================================
# 输出结果
def output(str, a, b, type):
    global program
    program.append([type, str[a:b + 1]])


# 判断字符串一部分是否属于关键字
# 是返回1不是返回2
def iskeywords(str, a, b):
    # 关键字
    keywords = {"if", "int", "for", "while", "do", "return", "break", "continue"}
    s = str[a:a + b + 1]  # 拷贝字符
    if s in keywords:  # 判断是否存在,存在返回1,否则返回2
        return 1
    else:
        return 2


# 判断字符是否属于运算符或分隔符的一部分。
# 不是返回0,是返回1,是且后面能跟=号返回2
def belong_to(str, type):
    if type == 4:  # 选择运算符
        library = "+-*/=><!"  # 运算符
    else:  # 选择分隔符
        library = ",;{}()"  # 分隔符
    if str in library:  # 存在
        # 是可能后面跟=号的几个符号
        if type == 4 and library.index(str) >= 4:
            return 2
        else:
            return 1
    return 0


# 递归的词法分析函数,读入一行str字符串,初始位置 n = 0
# 分离+判断,打印输出类型
# 由之前的c语言版本改写而成
def scan(str, n):
    # 7 种类型(最后输出1 - 5)
    # -1
    # 0: 初始
    # 1: 关键字, 在keywords中
    # 2: 标识符
    # 3: 常数(无符号整型)
    # 4: 运算符和界符:+ - * / = > < >= <= !=
    # 5: 分隔符:, ; {}()
    i = n
    type = 0
    while i < len(str):
        if type == 0:  # 初始态
            if str[i] == ' ':  # 空格跳过
                n += 1
                i += 1
                continue
            elif str[i] == '\0' or str[i] == '\n':  # 是结束
                return
            elif ('a' <= str[i] <= 'z') or ('A' <= str[i] <= 'Z'):
                type = 1  # 是字母,
            elif '0' <= str[i] <= '9':
                type = 3  # 是数字,常数
            else:
                type = belong_to(str[i], 4)
                if type > 0:  # 是运算符
                    # 是能跟=号的运算符,后面是=号
                    if type == 2 and str[i + 1] == '=':
                        i = i + 1  # 结束位置后移
                    output(str, n, i, 4)  # 输出 + 递归 + 结束
                    scan(str, i + 1)
                    return
                elif belong_to(str[i], 5):  # 是分隔符
                    output(str, n, i, 5)  # 输出 + 递归 + 结束
                    scan(str, i + 1)
                    return
                else:
                    print("失败:", str[i])
                    return
        elif type == 1:  # 关键字或标识符
            if not (('a' <= str[i] <= 'z') or ('A' <= str[i] <= 'Z')):  # 不是字母了
                if '0' <= str[i] <= '9':  # 是数字,只能是标识符
                    type = 2
                else:  # 非字母数字
                    type = iskeywords(str, n, i - 1)
                    output(str, n, i - 1, type)  # 输出 + 递归 + 结束
                    scan(str, i)
                    return
        elif type == 2:  # 标识符
            if not (('a' <= str[i] <= 'z') or ('A' <= str[i] <= 'Z')):
                # 不是字母了
                if not ('0' <= str[i] <= '9'):
                    # 不是数字
                    output(str, n, i - 1, type)  # 输出 + 递归 + 结束
                    scan(str, i)
                    return
        elif type == 3:
            if not ('0' <= str[i] <= '9'):
                # 不是数字
                output(str, n, i - 1, type)  # 输出 + 递归 + 结束
                scan(str, i)
                return
        else:
            print("%d失败" % type)
        i += 1


# 递归下降分析程序部分=====================================================================
# =====================================================================================
# 经过处理后的文法如下:
# E->TE';       i,(     ),;
# T->FT';       i,(     +,),;
# F->i|(E);     i,(     *,+,),;
# E'->+TE'|;    +,e     ),;
# T'->*FT'|;    *,e     +,),;
# 由于函数名不能有‘所以里面的’由1代替
def Parse():
    def ParseE():  # E的分析子程序 E->TE';    i,(     ),;
        global lookahead, parseerror
        if parseerror:
            return
        elif lookahead[0] == 2 or lookahead[0] == 3 or lookahead[1] == '(':
            ParseT()
            ParseE1()
        else:
            print("E 错误")
            parseerror = 1
            # exit(0)

    def ParseT():  # T的分析子程序 T->FT';    i,(     +,),;
        global lookahead, parseerror
        if parseerror:
            return
        elif lookahead[0] == 2 or lookahead[0] == 3 or lookahead[1] == '(':
            ParseF()
            ParseT1()
        else:
            print("T 错误")
            parseerror = 2
            # exit(0)

    def ParseF():  # F的分析子程序 F->i|(E);  i,(     *,+,),;
        global lookahead, parseerror
        if parseerror:
            return
        elif lookahead[0] == 2 or lookahead[0] == 3:
            MatchToken('i')
        elif lookahead[1] == '(':
            MatchToken('(')
            ParseE()
            MatchToken(')')
        else:
            print("F 错误")
            parseerror = 3
            # exit(0)

    def ParseE1():  # E'的分析子程序 E'->+TE'|;   +,e     ),;
        global lookahead, parseerror
        if parseerror:
            return
        elif lookahead[1] == '+':
            MatchToken('+')
            ParseT()
            ParseE1()
        elif lookahead[1] == ')' or lookahead[1] == ';':
            pass
        else:
            print("E' 错误")
            parseerror = 4
            # exit(0)

    def ParseT1():  # T'的分析子程序 T'->*FT'|;   *,e     +,),;
        global lookahead, parseerror
        if parseerror:
            return
        elif lookahead[1] == '*':
            MatchToken('*')
            ParseF()
            ParseT1()
        elif lookahead[1] == '+' or lookahead[1] == ')' or lookahead[1] == ';':
            pass
        else:
            print("T' 错误")
            parseerror = 5
            # exit(0)

    def MatchToken(type):
        global lookahead, parseerror
        mate = 0
        if parseerror:
            return
        elif type == "i":  # 匹配常数或表达式
            if lookahead[0] == 2 or lookahead[0] == 3:
                mate = 1
        else:  # 匹配符号
            if lookahead[1] == type:
                mate = 1
        if mate:
            lookahead = GetToken()  # 读入下一个
        else:
            print("需要",type, "实际", lookahead, "匹配错误")
            parseerror = 6
            # exit(0)

    def GetToken():
        global program, lookahead
        return program.pop(0)

    global program, lookahead,parseerror
    parseerror = 0  # 错误标记
    lookahead = program.pop(0)
    ParseE()
    if parseerror==0:
        print("正确")


file = "program.txt"
file = open(file)  # 读取文件
while i := file.readline():
    program = []  # 记录读到的句子
    scan(i, 0)
    print(i[:-1])
    Parse()
file.close()

program.txt文件部分:

10;
1+2;
(1+2)*3+(5+6*7);
((1+2)*3+4;
1+2+3+(*4+5);
(a+b)*(c+d);
((ab3+de4)**5)+1;

 结果:

10;
正确
1+2;
正确
(1+2)*3+(5+6*7);
正确
((1+2)*3+4;
需要 ) 实际 [5, ';'] 匹配错误
1+2+3+(*4+5);
E 错误
(a+b)*(c+d);
正确
((ab3+de4)**5)+1
F 错误

 小计:关于递归下降LL(1)分析程序的写法:

        在LL(1)递归下降分析程序中,每个非终结符都有一个独属的分析子程序,比如这篇文章的文法,就有多达5个非终结分析子程序,1个终结符分析程序,因为每一个非终结符的程序都是独立写出来的,所以这种方法对于每一种新的文法都要重写。(相比之下,表驱动的LL(1)文法分析程序就友好很多,但这里不说)

        首先我们要求要写文法的select集(选择集),判别是不是LL(1)文法,若是,就可以进行下一步(不是LL(1)文法的话,消除左递归,提取左因子,还不行就没法用这个方法。)

        开始,有一个指针(变量,这里是lookahead),指向(保存着)当前要分析的词汇,在这篇文章中,它是[type,data]形式的,type是类型,我们只需知道2是标识符,3是常量,data保存着具体的字符,比如“(”,“15”

        针对所有终结符,有一个统一的判断函数(MatchToken),输入要判断的符号类型,然后判断当前指针指向的变量是不是,如果不是,就抛出错误,结束分析,否则就指针右移。

        针对每一个非终结符,有自己专属的分析程序,这篇文章的命名统一为Parse+非终结符,假如,有一个产生式:A   ->    Ba | b | ε        #这里ε代表空

       select(A->Ba)=  a,d        select(A->b)= b        select(A->ε)= e      

 (select(A->Ba)和 select(A->b),select(A->ε)不可能有交集,否则就不是LL(1)文法了)

那么,他的分析程序就长这样:

def ParseA():
    if lookahead == 'a' or lookahead == 'd':
        ParseB()
        MatchToken('d')
    elif lookahead == 'b':
        MatchToken('b')
    elif lookahead == 'e':
        pass
    else:
        (报错)

        每次判断lookahead是不是这个产生式的选择集,是的话,就按它的产生式,有非终结符就调用非终结符的分析程序,有终结符就调用终结符的判断程序(MatchToken),空的话没有,就直接pass就行。如果没找到的话,抛出错误。特别要注意,算选择集别把的句子终结符(#)忘了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/541260.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flink+Pulsar、Kafka问题分析及方案 -- 事务阻塞

Pulsar、Kafka的事务设计 Pulsar跟Kafka在设计事务功能时&#xff0c;在消费者读取消息的顺序方面&#xff0c;都采用了类似的设计。 比如说&#xff0c;先创建txn1&#xff0c;然后创建txn2&#xff0c;这两个事务生产消息到同一个topic/partition里&#xff0c;但是txn2比tx…

【前端知识】常见的加密算法介绍

【前端知识】常见的加密算法介绍 1 常见的加密算法&#xff08;1&#xff09;哈希函数&#xff08;2&#xff09;对称加密&#xff08;3&#xff09;非对称加密&#xff08;4&#xff09;消息认证码&#xff08;MAC&#xff09; 2.总结 1 常见的加密算法 略微介绍一下前端中常…

Kerberos

序言 kerberos 除了说帮我们验证Java程序是否具有权限来请求Hadoop的服务,也可以来帮助我们检查新增的节点是是否是真实的节点,还是黑客为了套取数据的节点. 比如为HDFS新增一个DataNode节点,如果没有Kerberos验证, 随便一个节点只要连接上NameNode就会存储数据,黑客就可以获…

LeetCode:23. 合并 K 个升序链表

23. 合并 K 个升序链表 1&#xff09;题目2&#xff09;过程3&#xff09;代码1. 最开始2.初步优化 4&#xff09;结果1. 最开始2. 初步优化 1&#xff09;题目 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合…

机器学习基础认识(一)

机器学习应用 机器学习的应用&#xff0c;主要分为两类&#xff1a;预测、分类 预测&#xff0c;一般是指&#xff1a;根据数据&#xff0c;预测数值 分类&#xff0c;一般是指&#xff1a;根据数据&#xff0c;进行分类 预测与分类的关系【个人理解】 分类&#xff0c;本质…

零基础怎么入门网络安全?看这篇就够啦!

由于我之前写了不少网络安全技术相关的故事文章&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人在微信里问我&#xff1a; 我刚入门网络安全&#xff0c;该怎么学&#xff1f;要学哪些东西&#xff1f;有哪些方向&#xff1f;怎么选&#xff…

Centos7.6部署postgresql15主从

目录 安装pg15&#xff08;master和standby&#xff09;主数据库配置(master)初始化数据库创建归档日志目录设置数据库访问权限修改数据库配置文件开启数据库 从数据库配置(standby)同步主库的数据文件创建文件standby.signal启动从数据库 主从状态验证master上验证standby上验…

H5性能测试怎么做?这些关键指标你得搞清楚

目录 01、Http相关 02、组件是否压缩 03、图片格式和大小是否合适 04、CSS放在顶部 05、JS放在底部 06、JS &CSS压缩 07、是否添加缓存 08、避免非200返回值 09、使用CDN 03、WebView相关 学习资源分享 软件测试面试小程序 01、Http相关 01、Http请求个数 有…

新星计划 Electron+vue2 桌面应用 1 基础

/(ㄒoㄒ)/~~报名了两个新星计划&#xff0c;工作之余写博客…… 另外一个是uniapp的属于个人兴趣&#xff0c;这个桌面应用正好符合工作需要。 活动地址&#xff1a;https://marketing.csdn.net/p/1738cda78d47b2ebb920916aab7c3584 教程地址&#xff1a; 2023新星导师活动…

Java实现PDF导出/预览

网上有很多关于PDF导出的文章&#xff0c;但是个人感觉实现的过于复杂&#xff0c;又是模板又是html的&#xff0c;有的还需要字体模板的支持&#xff0c;本片文章只是实现简单的PDF表格导出&#xff0c;可以实现PDF动态表格导出/预览&#xff0c;这类文章网上很少&#xff0c;…

实践「容器镜像扫描」,Get 云原生应用的正确打开方式

&#x1f31f; 容器技术的兴起&#xff0c;让应用程序更加轻量化和可移植&#xff0c;大大提高了应用交付效率。但容器中的应用也面临各种安全威胁&#xff0c;容器及其镜像安全不可小觑。 近日&#xff0c;在「DevSecOps 软件安全开发实践」课程上&#xff0c;极狐(GitLab) 高…

Linux设置系统时间(上海时区、硬件时间、重启有效)

#查看时间 date#删除当前时区 rm -rf /etc/localtime #修改默认时区为上海 ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime #设置硬件时间 月/日/年 时:分:秒 hwclock --set --date"05/18/2023 17:11:15"#设置系统时间和硬件时间同步 hwclock --hctosys#保…

大数据发展前沿复习

对抗学习 生成对抗网络&#xff08;GAN&#xff09;是非监督式学习的一种方法&#xff0c;透过两个神经网络相互博弈的方式进行学习。生成对抗网络由一个生成网络与一个判别网络组成。生成网络以随机取样作为输入&#xff0c;其输出结果需要尽量模仿训练集中的真实样本。判别网…

vmware17pro安装激活ubuntu22版本最新教程无废话

第一步&#xff1a;下载 下载很方便 官方一键下载链接 第二步 安装 点下一步&#xff0c;一键安装即可&#xff0c;有可能会重启电脑&#xff0c;没关系的&#xff0c;是安全的 第三步&#xff1a;ji活 懂得都懂这是什么 JU090-6039P-08409-8J0QH-2YR7F 4A4RR-813DK-M81A9…

C语言算法--快速排序法

C语言算法–快速排序法 1-什么是快速排序法 快速排序&#xff08;Quicksort&#xff09;是一种常用的排序算法&#xff0c;它基于分治的思想。它的核心思想是选择一个基准元素&#xff0c;将数组划分为两个子数组&#xff0c;使得左边的子数组中的所有元素都小于等于基准元素…

【Flutter开发】Navigator2.0介绍及使用

目录 Navigator1.0Navigator2.0APPRouteInformationParserRouterDelegate 问题The Navigator.pages must not be empty to use the Navigator.pages API浏览器的回退按钮 总结 Navigator1.0 我们学习flutter一开始接触的路由管理就是Navigator1.0&#xff0c;它非常方便&#…

JAVA-Activiti 7与达梦、人大金仓兼容-nacos、服务pom文件配置(2)

目录 第一步,修改nacos服务配置 >需注意< 第二步,pom.xml依赖包配置 Activiti的源码包解决之后,接下来就好做很多了 第一步,修改nacos服务配置 spring:datasource:url: jdbc:kingbase8://127.0.0.1:54321/progress?currentSchemaprogress,productNamePostgreSQL,SYS…

保密+完整+可用+安全,规避代码安全「马奇诺防线」,构建软件供应链整体安全

近日&#xff0c;在「江狐会」广州站上&#xff0c;极狐(GitLab) 高级解决方案架构师武让分享了如何通过三大阶段 四大要点&#xff0c;规避代码安全「马奇诺防线」&#xff0c;真正确保软件供应链安全。以下内容整理自本次演讲。Enjoy&#xff5e; 先跟大家分享一个故事 一战…

计算机体系结构|MIT6.175和MIT6.375学习笔记

在2023年初&#xff0c;达坦科技发起成立硬件设计学习社区&#xff0c;邀请所有有志于从事数字芯片设计的同学加入我们的学习互助自学小组&#xff0c;以理解数字芯片设计的精髓&#xff0c;强化理论知识的同时提升实操技能&#xff0c;继而整体提升设计能力。现在&#xff0c;…

Vmware虚拟机安装MacOS13-Ventura详细教程

小编亲测 前提准备 功能强大的 Windows 电脑&#xff08;不能太差&#xff0c;不然会卡&#xff09;至少8GB内存默认是80GB的存储空间VMWare Workstation&#xff08;版本应该没什么需求&#xff0c;我装的是VMware Workstation 17 Pro&#xff09;Unlocker解锁软件MacOS Ventu…