编译原理实验--实验二 递归下降法判断算术表达式的正确性--Python实现

news2024/11/23 12:34:01

目录

一、实验目的和要求

二、实验内容

三、实验环境

四、实验步骤

1、语法分析所依据的文法;

2、给出消除左递归及提取左公因子的文法;

五、测试要求

六、实验步骤

1、语法分析所依据的文法

2、给出消除左递归及提取左公因子的文法;

3、关键代码

七、实验结果与分析

 


一、实验目的和要求

  1. 理解自顶向下语法分析方法;
  2. 用递归下降技术实现语法分析器;

二、实验内容

        算术表达式的文法是G[E]:

E→E+T| E-T| T

T→T*F| T/F| F

F→(E)| i

        用递归下降分析法按文法G[E]对算术表达式(包括+、-、*、/、()的算术表达式)进行语法分析,判断该表达式是否正确。

三、实验环境

处理器     AMD Ryzen 7 5800H with Radeon Graphics3.20 GHz

机带RAM   16.0 GB(13.9 GB可用)

Win10家庭版20H2 X64  

PyCharm 2012.2

Python 3.10

四、实验步骤

1、准备:阅读课本有关章节,将上述算术表达式的文法改造成LL(1)文法(即消除左递归和提取左公因子);

2、参考课件P52编写递归下降分析程序。

1、语法分析所依据的文法;

     算术表达式的文法是G[E]:

E→E+T| E-T| T

T→T*F| T/F| F

F→(E)| i

2、给出消除左递归及提取左公因子的文法;

将文法G[E]改造为LL(1)文法如下:

G’[E]:

E →  TE’

E’ → +TE’| -TE’|ε

T  →  FT’

T’→  *FT’|/FT’|ε

F  → (E)| i

五、测试要求

1、为降低难度,表达式中不含变量,只含单个无符号整数或i;

2、如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好);

3、测试用的表达式建议事先放在文本文件中,一行存放一个表达式,以分号结束。而语法分析程序的输出结果写在另一个文本文件中;

4、程序输入/输出示例:

输入如下表达式(以分号为结束)和输出结果:

(a)i;  或  1;

输出:正确

(b)i+i; 或 1+2;

输出:正确

(c)(i+i)*i+i-(i+i*i);  或 (1+2)*3+4-(5+6*7);

输出:正确

(d)((i+i)*i+i;    或 ((1+2)*3+4;  

输出:错误,缺少右括号

(e)i+i+i+(*i/i);   或   1+2+3+(*4/5)

输出:错误

5、选作:对学有余力的同学,可增加功能:当判断一个表达式正确时,输出计算结果。

六、实验步骤

1、语法分析所依据的文法

     算术表达式的文法是G[E]:

E→E+T| E-T| T

T→T*F| T/F| F

F→(E)| i

2、给出消除左递归及提取左公因子的文法;

将文法G[E]改造为LL(1)文法如下:

G’[E]:

E →  TE’

E’ → +TE’| -TE’|ε

T  →  FT’

T’→  *FT’|/FT’|ε

F  → (E)| i

3、关键代码

<!--BianYiYuanLiShiYan/yufafenxi_rtda.py-->

'''
在实验一的基础上进行语法分析
递归下降分析法
E→TE'
E'→+TE'| -TE' |ε
T→FT'
T'→*FT'| /FT' |ε
F→(E) | id |num

保留关键字及种别编码
Static_word = {"begin": 1, "end": 2, "if": 3, "then": 4, "while": 5,
               "do": 6, "const": 7, "var": 8, "call": 9, "procedure": 10}

算符和界符及种别编码
Punctuation_marks = {"+": 11, "-": 12, "*": 13, "/": 14, "odd": 15, "=": 16, "<>": 17, "<": 18, ">": 19,
                     "<=": 20, ">=": 21, ":=": 22, "(": 23, ")": 24, ".": 25, ",": 26, ";": 27}

常数的种别编码为28,标识符的种别编码为29,非法字符的种别编码为30
'''
# 导入词法分析的程序
from cifafenxi_rtda import *

# 由于实验二的特殊性,单独把i(id)当做保留字处理
Static_word["i"] = 0

# i + - * 、 ( ) num的种别编码
Lab2_word = [0, 11, 12, 13, 14, 23, 24, 28]

# 出错标志
Err = 0

# 位置标志
Index = 0

# 算术表达式是否符合文法
Correct = "***************正确!"
Error = "*************错误!"


# 语法分析结果写入文件
def WriteFile(string):
    fW = open(pathW, 'a')
    fW.write(string + "\n")
    fW.close()


# 开始正式语法分析前首先判断该算术表达式中的各个字符以及首尾字符是否符合要求
def First():
    index = 0
    if (Result_Lex[0][0] in [0, 23, 28]) and (Result_Lex[0][-1] in [0, 24, 28]):
        for i in Result_Lex[:-2]:
            if i not in Lab2_word:
                index += 1
                break
            else:
                continue
    else:
        index += 1
    return index


# E→TE'
def E():
    global Err
    if Err == 0:
        T()
        E1()


# E'→+TE'| -TE' |ε
def E1():
    global Err, Index
    if Err == 0 and Index != len(Result_Lex[0]):
        if Result_Lex[0][Index] in [11, 12]:
            Index += 1
            if Index != len(Result_Lex[0]) - 1:
                T()
                E1()
            else:
                Index = len(Result_Lex[0])
        elif Result_Lex[0][Index] != 24:
            Err = 1


# T→FT'
def T():
    global Err
    if Err == 0:
        F()
        T1()


# T'→*FT'| /FT' |ε
def T1():
    global Err, Index
    if Err == 0 and Index != len(Result_Lex[0]):
        if Result_Lex[0][Index] in [13, 14]:
            Index += 1
            if Index != len(Result_Lex[0]) - 1:
                F()
                T1()
            else:
                Index = len(Result_Lex[0])
        elif Result_Lex[0][Index] not in [11, 12, 24]:
            Err = 1


# F→(E) | id |num
def F():
    global Err, Index
    if Err == 0:
        if Result_Lex[0][Index] in [0, 28]:
            Index += 1
        elif Result_Lex[0][Index] == 23:
            Index += 1
            E()
            if Result_Lex[0][Index] != 24:
                Err = 1

            Index += 1
        else:
            Err = 1


# 分析主程序
def Analysis_Gs():
    global Err, Index
    if First() == 0:
        F()
        while Index < len(Result_Lex[0]):
            if Result_Lex[0][Index] in [11, 12]:
                E1()
            elif Result_Lex[0][Index] in [13, 14]:
                T1()
            else:
                Index = len(Result_Lex[0])
                Err = 1

        if Err == 0:
            print("语法分析结果:" + Correct)
        else:
            print("语法分析结果:" + Error)
    else:
        print("语法分析结果:" + Error)


if __name__ == '__main__':
    # 首先运行词法分析程序
    Lex_main()
    # 运行语法分析程序
    Analysis_Gs()

<!--BianYiYuanLiShiYan/cifafenxi_rtda.py-->

"""
PL/0程序大纲:
1、PL/0语言的单词结构
关键字(10个):begin, end ,if ,then, while, do, const, var,call,procedure
标识符:字母序列,最大长度10
常数:整型常数
算符和界符(17个):+, -, *,/,odd,=,<>,<,>,<=,>=,:=,(,) ,, ,.,;
2、单词的种别划分
    标识符 作为一种
    常数 作为一种
    算符和界符每个单词作为一个单独种别
3、PL/0的语言的词法分析器将要完成以下工作:
(1)跳过分隔符(如空格,回车,制表符);
(2)识别诸如begin,end,if,while等保留字;
(3)识别非保留字的一般标识符。
(4)识别数字序列。
(5)识别:=,<=,>=之类的特殊符号。
"""

# 输入文件路径
pathR = ""

# 输出文件路径
pathW = ""

# 保存所有字符
strAll = []

# 保存词法分析后的字符及种别编码
Result_Lex = [[], []]

# 保留关键字及种别编码
Static_word = {"begin": 1, "end": 2, "if": 3, "then": 4, "while": 5,
               "do": 6, "const": 7, "var": 8, "call": 9, "procedure": 10}

# 算符和界符及种别编码
Punctuation_marks = {"+": 11, "-": 12, "*": 13, "/": 14, "odd": 15, "=": 16, "<>": 17, "<": 18, ">": 19,
                     "<=": 20, ">=": 21, ":=": 22, "(": 23, ")": 24, ".": 25, ",": 26, ";": 27}

# 常数的种别编码为28,标识符的种别编码为29,非法字符的种别编码为30

# 空格或换行
Blank = {" ", "\n"}

# 中文说明
Explanation = ["保留字", "加号", "减号", "乘号", "除号", "odd运算符", "等于号", "不等于号", "小于号",
               "大于号", "小于等于号", "大于等于", "赋值符号", "左括号", "右括号", "点号", "逗号", "分号", "常数", "标识符"]


# 判断字符数否为保留关键字
def Is_static(string):
    if string in Static_word:
        return True
    else:
        return False


# 判断字符是否为算符或者界符
def Is_marks(string):
    if string in Punctuation_marks:
        return True
    else:
        return False


# 判断是不是空格或者换行
def Is_blank(string):
    if string in Blank:
        return True
    else:
        return False


# 读取文件所有字符并保存在一个列表中
def Scanner():
    fR = open(pathR)  # 返回一个文件对象
    lines = fR.readlines()  # 调用文件的 readline()方法
    for line in lines:
        for i in line:
            strAll.append(i)
    fR.close()
    # strAll.pop()


# 多次重复的操作语句
def Option(num, temp, explanation):
    fW = open(pathW, 'a')
    txt = '%-20s%s' % ("(" + str(num) + "," + temp + ")", explanation + ":" + temp)
    # txt = "({0},{1})\t{2}:{3}".format(num, temp, explanation, temp)
    print(txt)
    fW.write(txt + "\n")
    fW.close()
    Result_Lex[0].append(int(num))
    Result_Lex[1].append(temp)


# 结束
def End(id):
    if id >= len(strAll):
        return True


# 词法分析主方法
def Analysis_Lex():
    """
        共分为四大块,分别是标识符、常数、算符或界符、空格或换行、非法字符,对应下面的 if, elif, elif, elif和 else
    """
    # 索引值
    id = 0

    # 忽略代码段开头的空格或换行
    while Is_blank(strAll[id]):
        id += 1

    # 从第一个有意义的字符开始循环识别直至最后一个字符
    while id < len(strAll):
        # 保存临时结果
        temporary = ""

        # 判断是否为保留字或者标识符
        if ('a' <= strAll[id] <= 'z') or ('A' <= strAll[id] <= 'Z'):
            while ('0' <= strAll[id] <= '9') or ('a' <= strAll[id] <= 'z') or (
                    'A' <= strAll[id] <= 'Z'):
                temporary += strAll[id]
                id += 1
                if End(id): break
            # 判断是否未保留字
            if Is_static(temporary):
                num = Static_word[temporary]
                Option(num, temporary, Explanation[0])

            # 判断是否为特殊运算符odd
            elif temporary == "odd":
                num = Punctuation_marks[temporary]
                Option(num, temporary, Explanation[num - 10])
            # 否则为非保留字标识符
            else:
                Option("29", temporary, Explanation[-1])
        # 判断是否为常数(正数或小数)
        elif '0' <= strAll[id] <= '9':
            while ('0' <= strAll[id] <= '9') or strAll[id] == ".":
                if strAll[id] != ".":
                    temporary += strAll[id]
                    id += 1
                    if End(id): break
                elif strAll[id] == "." and ('0' <= strAll[id + 1] <= '9'):
                    temporary += strAll[id]
                    id += 1
                    if End(id): break
                else:
                    break
            Option("28", temporary, Explanation[-2])
        # 判断是否为运算符或界符
        elif Is_marks(strAll[id]) or strAll[id] == ":":
            temporary += strAll[id]
            # 判断小于号三种情况:小于、小于等于、不等于
            if strAll[id] == "<":
                if strAll[id + 1] == ">" or strAll[id + 1] == "=":
                    temporary += strAll[id + 1]
                    id += 2
                    num = Punctuation_marks[temporary]
                    Option(num, temporary, Explanation[num - 10])
                else:
                    id += 1
                    num = Punctuation_marks[temporary]
                    Option(num, temporary, Explanation[num - 10])
            # 判断大于号两种情况:大于、大于等于
            elif strAll[id] == ">":
                if strAll[id + 1] == "=":
                    temporary += strAll[id + 1]
                    id += 2
                    num = Punctuation_marks[temporary]
                    Option(num, temporary, Explanation[num - 10])
                else:
                    id += 1
                    num = Punctuation_marks[temporary]
                    Option(num, temporary, Explanation[num - 10])
            # 判断赋值符号特殊情况
            elif strAll[id] == ":":
                if strAll[id + 1] == "=":
                    temporary += strAll[id + 1]
                    id += 2
                    num = Punctuation_marks[temporary]
                    Option(num, temporary, Explanation[num - 10])
                # 单独的冒号不是运算符或界符,当非法字符处理
                else:
                    id += 1
                    Option("30", temporary, "非法字符")
            # 其他运算法或界符
            else:
                id += 1
                num = Punctuation_marks[temporary]
                Option(num, temporary, Explanation[num - 10])
        # 对空格、换行过滤
        elif Is_blank(strAll[id]):
            id += 1
            continue
        # 对非法字符的处理
        else:
            temporary += strAll[id]
            id += 1
            Option("30", temporary, "非法字符")


def Lex_main():
    # 获取代码文件
    print("请输入要分析的代码文件(.txt)路径及完整名称:", end='')
    global pathR, pathW

    pathR = input()

    # 读入代码文件
    Scanner()

    # 读入保存结果文件
    print("请输入保存结果的文件(.txt)路径及完整名称:", end='')
    pathW = input()

    # 开始分析代码文件及结果写入
    # print(strAll)
    print('%-16s%s' % ("(种别编码,字符)", "中文介绍:字符"))
    print("-------------词法分析结果为-------------\n")
    # 打开需要写入结果的文档(不存在则创建)并开始写入!
    f = open(pathW, 'a')
    f.write('%-16s%s' % ("(种别编码,字符)", "中文介绍:字符\n"))
    f.write("-------------词法分析结果为-------------\n")
    f.close()

    # 程序实现主方法
    Analysis_Lex()


# 程序运行点
if __name__ == '__main__':
    Lex_main()

七、实验结果与分析

 

 

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

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

相关文章

iNFTnews|国内数藏平台大撤退,寒冬之下海外市场是否有出路?

腾讯旗下继腾讯新闻、幻核之后&#xff0c;仅存的数藏平台也关停了。 11月16日&#xff0c;据界面新闻报道&#xff0c;腾讯TME旗下QQ音乐已经叫停“TME数字藏品”业务&#xff0c;原团队部分成员已内部活水。 一接近腾讯集团的知情人士称&#xff0c;腾讯方面曾对数字藏品业…

【Linux】生产者消费者模型

文章目录1.生产者消费者模型1.1生产者消费者模型的特点1.2生产者消费者模型的原则1.3生产者消费者模型的优点2.基于阻塞队列的生产者消费者模型2.1如何理解生产者消费者模型的并发&#xff1f;3.信号量3.1信号量接口3.2基于环形队列的生产者消费者模型3.3信号量和条件变量的区别…

Git_GitHub——基本操作、创建远程库、远程库操作、团队协作、SSH免密登录

网址:GitHub: Let’s build from here GitHub 目录 一、创建远程仓库 二、远程库操作 2.1 查看远程库别名 2.2 创建远程仓库别名 2.3 推送本地分支到远程仓库 2.4 拉取远程库到本地库 2.5 克隆远程库到本地 三、 跨团队协作 3.1 团队内协作 3.2 跨团队协作 四、SSH免密码登…

Go : golang发布三方包流程简介

文章目录一、创建项目仓库二、拉去仓库&#xff0c;编辑代码三、推送与发布代码四、使用发布的第三方包小结一、创建项目仓库 1.输入仓库的名字&#xff0c;我这里输入simpleExample&#xff0c;用来做演示 2.选择public&#xff0c;公开。要不并不好拉 3.选择需要添加的文件(…

用PyPy加速Python程序

用PyPy加速Python程序 在《Python性能优化指南–让你的Python代码快x3倍的秘诀》中有提到&#xff0c;我们可以用更好的Python运行环境或运行时优化来提升Python的速度&#xff0c;其中最成熟、使用最简单的当属PyPy。用PyPy&#xff0c;可以在不改变源代码的情况下&#xff…

二叉树相关OJ - C++

文章目录&#xff1a;根据二叉树创建字符串二叉树的层序遍历二叉树的最近公共祖先二叉搜索树与双向链表从前序与中序遍历序列构造二叉树从中序与后序遍历序列构造二叉树二叉树的前序遍历&#xff08;非递归&#xff09;二叉树的中序遍历&#xff08;非递归&#xff09;二叉树的…

【LeetCode与《代码随想录》】数组篇:做题笔记与总结-Java版

代码随想录地址 是学习过程中的笔记&#xff01;图来自代码随想录。 文章目录理论题目704. 二分查找35. 搜索插入位置34. 在排序数组中查找元素的第一个和最后一个位置69. x 的平方根367.有效的完全平方数理论 数组是存放在连续内存空间上的相同类型数据的集合。 数组下标都是…

[附源码]java毕业设计新能源汽车租赁管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

基于armv8的kvm实现分析(一)虚拟化介绍

本文基于以下软硬件假定&#xff1a; 架构&#xff1a;AARCH64 内核版本&#xff1a;5.14.0-rc5 1 什么是虚拟化 虚拟化就是把一台物理计算机虚拟成多台逻辑计算机&#xff0c;每台逻辑计算机里面可以运行不同操作系统&#xff0c;而相互之间不受影响&#xff0c;其典型架构…

面试了个 985 毕业的同学,回答“性能调优”题时表情令我毕生难忘

又逢“金九银十”&#xff0c;年轻的毕业生们满怀希望与忐忑&#xff0c;去寻找、竞争一个工作机会。已经在职的开发同学&#xff0c;也想通过社会招聘或者内推的时机争取到更好的待遇、更大的平台。 然而&#xff0c;面试人群众多&#xff0c;技术市场却相对冷淡&#xff0c;面…

JavaIO流:概述

在接触 IO 流前&#xff0c;无论是 变量的声明、数组的创建&#xff0c;又或者是复杂的并发设计还是 Jvm 的性能调优&#xff0c;我们更多的还是和内存打交道。但我们知道计算机组成包括运算器&#xff0c;控制器&#xff0c;存储器&#xff0c;输入设备&#xff0c;输出设备。…

springcloud4:服务注册中心Eureka

直接调用即可&#xff0c;为什么用Eureka什么是服务治理&#xff1f; 多个服务调用&#xff0c;需要有依赖中心管理什么是服务注册&#xff1f; 有一个注册中心&#xff0c;当服务器启动时&#xff0c;会把自己的信息注册到注册中心上什么是服务发现&#xff1f; Client通过注册…

electron打包ffi-napi报错 npm ERR! gyp reason: read ECONNRESET

问题描述 这个问题用了我两天的时间&#xff0c;所以记录一下。 我们项目是使用electronvue&#xff0c;做支付功能的时候需要使用到ffi-napi依赖包。 最后打包的时候ffi-napi报错了&#xff0c;在package.json中去掉ffi-napi就可以打包&#xff0c;但是打包运行后提示缺少ff…

re:Invent 2022,探秘亚马逊云科技的重量级计算创新——Nitro

诞生于16年前的亚马逊云科技&#xff0c;开创了一个全新的云计算领域。秉持着创新与探索精神&#xff0c;自2012年开始&#xff0c;在每年一度的re:Invent全球大会上&#xff0c;亚马逊云科技都会发布最新的云计算技术。对IT产业演进产生了革命性的影响&#xff0c;Nitro系统就…

Java笔记(工厂模式、动态代理、XML)

一、工厂模式 软件设计模式&#xff08;Design pattern&#xff09;&#xff0c;又称设计模式&#xff0c;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的复用性。 什么…

代码随想录算法训练营第四十三天| LeetCode1049. 最后一块石头的重量 II、LeetCode494. 目标和、LeetCode474. 一和零

一、LeetCode1049. 最后一块石头的重量 II 1&#xff1a;题目描述&#xff08;1049. 最后一块石头的重量 II&#xff09; 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将…

让你快速高效的掌握linux内核编译过程

Linux内核编译 一、linux内核的配置与编译&#xff1a; 1.配置内核 1)导入默认配置&#xff1a; make xxxx_defconfig 注1&#xff1a;xxxx表示内核支持的芯片的名称 比如make exynos_defconfig 注2&#xff1a;内核源码中对每个支持的芯片都有默认的配置&#xff0c;默认配置很…

【博学谷学习记录】超强总结,用心分享|架构师-RabbitMQ消息可靠性保障

文章目录一、生产者保证1.1 失败通知1.2 发送方确认1.3 Broker丢失消息二、消费方消息可靠性2.1 消费者手动确认消息依靠三个对象&#xff1a;生产者、消费者、broker一、生产者保证 生产者发送消息到broker时&#xff0c;要保证消息的可靠性&#xff0c;主要的方案有&#xf…

5.28 综合案例2.0-简易起夜灯

HaaS506 - 简易起夜灯简介准备硬件连接图功能实现1.继电器使用说明2. 5.8G雷达感应传感器模块说明3.简易代码3.1测试log简介 案例为了解决晚上起床找不到灯的问题。当你从床上起来时&#xff0c;雷达感应传感器检测到你的活动后自动打开电灯。省去了寻找电灯开关的麻烦。 准备…

java学习笔记 day07-Java基础-综合练习

练习一&#xff1a;飞机票 需求: ​ 机票价格按照淡季旺季、头等舱和经济舱收费、输入机票原价、月份和头等舱或经济舱。 ​ 按照如下规则计算机票价格&#xff1a;旺季&#xff08;5-10月&#xff09;头等舱9折&#xff0c;经济舱8.5折&#xff0c;淡季&#xff08;11月到来…