编译原理实验--实验三 预测分析法判断算术表达式的正确性--Python实现

news2025/1/11 23:51:12

目录

一、实验目的和要求

二、实验内容

三、实验环境

四、实验步骤

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

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

3、预测分析表

 4、关键代码

五、实验结果与分析


一、实验目的和要求

  1. 理解自顶向下语法分析方法;
  2. 用预测分析技术实现语法分析器;
  3. 熟练掌握预测分析程序的构造方法。

二、实验内容

        算术表达式的文法是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)文法G’[E],即消除左递归和提取左公因子;

2、设计出文法G’[E]的预测分析表;

3、按算法4.5(P132)编写预测分析程序。(思考:预测分析程序包括四种动作——推导、匹配、接受、出错,详见P131,这些动作如何实现?)

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

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

E→E+T| E-T | T

T→T*F| T/F | F

F→(E)| i

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

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

G’[E]:

E →  TE’

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

T  →  FT’

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

F  → (E)| i

3、预测分析表

 

 4、关键代码

<简要说明程序中所用到的主要变量、数组的作用,主要函数或过程的功能;给出主要功能实现的关键代码,如推导或匹配动作的实现,并加上适当的注释>

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


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/11 9:58
# @Author : LanXiaoFang
# @Site : 
# @File : yufafenxi_LL1.py
# @Software: PyCharm

import cifafenxi_LL1


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

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

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

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

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

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


# 用来存储从txt文件中读取的字符串
aa = " "
# 用来标记字符
pp = 0
# 非终结符表
VN = ["E", "e", "T", "t", "F"]
# 终结符表 l->/ m->% i->id n->num
VT = ['*', 'l', 'm', '+', '-', '(', ')', 'i', 'num', '#']

Fa = ["Te", "+Te", "-Te", "", "Ft", "*Ft", "nFt", "mFt", "", "(E)", "i", "num"]

F = ["E->", "E'->", "E'->", "E'->", "T->", "T'->", "T'->", "T'->", "T'->", "F->", "F->", "F->"]
# 构造预测分析表,-1表示出错,-2表示该行终结符的follow集合,用于错误处理
analysis_table = [
    [-2, -2, -2, -2, -2, 0, -1, 0, 0, -1],
    [-2, -2, -2, 1, 2, -2, 3, -2, -2, 3],
    [-2, -2, -2, -1, -1, 4, -1, 4, 4, -1],
    [5, 6, 7, 8, 8, -2, 8, -2, -2, 8],
    [-2, -2, -1, -1, -1, 9, -1, 10, 11, -2]
]
# 定义栈
stack = Stack()


# 判断字符x是否是终结符
def includevt(x):
    if x in VT:
        return 1
    else:
        return 0


# 查找非终结符,终结符 在预测分析表中的坐标,返回坐标对应信息
def includean(x, a):
    # 非终结符
    for i in range(len(VN)):
        if VN[i] == x:
            break
    # 终结符
    for j in range(len(VT)):
        if VT[j] == a:
            break
    return analysis_table[i][j]


# 错误处理
def destory():
    flag = 0
    flagg = 0
    # 将 "#"和文法开始符依次压入栈中
    stack.push('#')
    # 将第一个非终结符入栈
    stack.push(VN[0])
    # 把第一个输入符号读入a, aa
    global pp
    a = aa[pp]
    x = ""
    while x != '#':
        # print(stack.__dict__.values())
        if flag == 0:
            # 把栈顶符号弹出并放入
            x = stack.pop()
        flag = 0
        #  如果a是终结符
        if includevt(a) == 1:
            if includevt(x) == 1:
                if a == x:
                    if a == '#':
                        flagg = 1
                        print(x, "\t\t", a, "\t\t", " 结束")
                        return 1
                    else:
                        print(x, "\t\t", a, "\t\t", " 匹配终结符", x)
                    pp = pp + 1
                    # 将下一输入符号读入a
                    a = aa[pp]
                else:
                    flag = 1
                    print(x, "\t\t", a, "\t\t", " 出错 ,跳过", a)
                    pp = pp + 1
                    a = aa[pp]

            elif includean(x, a) >= 0:
                h = includean(x, a)
                print(x, "\t\t", a, "\t\t", " 展开非终结符 ", F[h], Fa[h])
                if Fa[h] in aa:
                    stack.push(Fa[h])
                else:
                    aF = Fa[h][::-1]
                    for i in range(len(aF)):
                        stack.push(aF[i])
            elif includean(x, a) == -1:
                flag = 1
                print(x, "\t\t", a, "\t\t", " 出错 ,从栈顶弹出", x)
                x = stack.pop()
            else:
                flag = 1
                print(x, "\t\t", a, "\t\t", " 出错 ,跳过", a)
                pp = pp + 1
                a = aa[pp]
        else:
            flag = 1
            print(x, "\t\t", a, "\t\t", " 出错 ,跳过", a)
            pp = pp + 1
            a = aa[pp]
    if flagg == 0:
        print(x, "\t\t", a, "\t\t", " 结束 \n")


aa = cifafenxi_LL1.main()
print("语法分析工程如下 :")
print('-' * 20, '文法如下', '-' * 20)
print("E->TE'")
print("E'->+TE'|-TE'|~")
print("T->FT'")
print("T'->*FT'|/FT'|%FT'|~")
print("F->(E)|id|num")
print("要分析的语句是 :", aa)
print("语法分析工程如下 :")
print("栈顶元素 \t\t 当前单词记号 \t\t 动作 ")
print("-" * 50)
destory()

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

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/11 9:57
# @Author : LanXiaoFang
# @Site : 
# @File : cifafenxi_LL1.py
# @Software: PyCharm

import re


class Token(object):

    # 初始化
    def __init__(this):
        # 存储分词的列表
        this.results = []

        # 行号
        this.lineno = 1

        # 关键字
        this.keywords = ['auto', 'struct', 'if', 'else', 'for', 'do', 'while', 'const',
                         'int', 'double', 'float', 'long', 'char', 'short', 'unsigned',
                         'switch', 'break', 'defalut', 'continue', 'return', 'void', 'static',
                         'auto', 'enum', 'register', 'typeof', 'volatile', 'union', 'extern']
        '''
    regex中:*表示从0-, +表示1-, ?表示0-1。对应的需要转义
    { 表示限定符表达式开始的地方 \{
    () 标记一个子表达式的开始和结束位置。子表达式可以获取共以后使用:\( \)
    r表示原生字符串。
    '''

        Keyword = r'(?P<Keyword>(auto){1}|(double){1}|(int){1}|(if){1}|' \
                  r'(#include){1}|(return){1}|(char){1}|(stdio\.h){1}|(const){1})'
        # 运算符
        Operator = r'(?P<Operator>\+\+|\+=|\+|--|-=|-|\*|#|\*=|/=|/|%=|%)'

        # 分隔符/界符
        Separator = r'(?P<Separator>[,:\{}:)(<>])'

        # 数字: 例如:1 1.9
        Number = r'(?P<Number>\d+[.]?\d+)'

        # 变量名 不能使用关键字命名
        ID = r'(?P<ID>[a-zA-Z_][a-zA-Z_0-9]*)'

        # 方法名 {1} 重复n次
        Method = r'(?P<Method>(main){1}|(printf){1})'

        # 错误  \S 匹配任意不是空白符的字符
        # Error = r'(?P<Error>.*\S+)'
        Error = r'\"(?P<Error>.*)\"'

        # 注释  ^匹配行的开始 .匹配换行符以外的任意字符 \r回车符 \n换行符
        Annotation = r'(?P<Annotation>/\*(.|[\r\n])*/|//[^\n]*)'

        # 进行组装,将上述正则表达式以逻辑的方式进行拼接, 按照一定的逻辑顺序
        # compile函数用于编译正则表达式,生成一个正则表达式对象
        this.patterns = re.compile('|'.join([Annotation, Keyword, Method, ID, Number, Separator, Operator, Error]))

    # 读文件
    def read_file(this, filename):
        with open(filename, "r", encoding="UTF-8") as f_input:
            return [line.strip() for line in f_input]

    # 结果写入文件
    def write_file(this, lines, filename='results.txt'):
        with open(filename, "a") as f_output:
            for line in lines:
                if line:
                    f_output.write(line)
                else:
                    continue

    def get_token(this, line):

        # finditer : 在字符串中找到正则表达式所匹配的所有字串, 并把他们作为一个迭代器返回
        for match in re.finditer(this.patterns, line):
            # group():匹配的整个表达式的字符 # yield 关键字:类似return ,返回的是一个生成器,generator
            yield (match.lastgroup, match.group())

    def run(this, line, flag=True):
        tokens = []
        for token in this.get_token(line):
            if flag:
                tokens.append(token)
                print("line %3d :" % this.lineno, token)
            '''
            else:
                yield "line %3d :" % this.lineno + str(token) + "\n"
           '''
        return tokens

    def printrun(this, line, flag=True):
        for token in this.get_token(line):
            if flag:
                print("lines x: ", token)


def main():
    token = Token()
    filepath = input("请输入文件路径:")
    print('-' * 20, '词法分析', '-' * 20)
    lines = token.read_file(filepath)
    tokenss = []
    for line in lines:
        tokens = token.run(line, True)
        for i in tokens:
            tokenss.append(i[1])
    return tokenss

五、实验结果与分析

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

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

相关文章

线程是什么?线程的相关概念以及基本的使用方法说明【内附可执行源码注释完整】

文章目录❓线程是什么&#x1f680;为什么要在程序中使用线程&#x1f34e;线程的优点、缺点&#x1f382;线程的应用场合&#x1f330;线程的基本使用⭐创建线程⭐线程的终止⭐等待指定线程结束⭐线程程序的编译命令&#x1f3e0;线程使用案例❓线程是什么 首先我们要知道进程…

3.1 首页功能的开发-跳转到首页

第三章. 业务功能开发 项目结构如下&#xff1a; 3.1 首页功能的开发 用户访问项目首页&#xff0c;首先进入登录页面。 用户登录流程图&#xff1a; 我们先写跳转到登录界面&#xff0c;在请求准发到index中如下图所示&#xff1a; 由于配置了视图解析器&#xff0c;查看ind…

GaussDB修改表空间占用磁盘使用率阈值

GaussDB数据库表空间占用磁盘使用率达到了你所设定的值后数据库会进入只读模式 执行非只读SQL时报错如下&#xff1a; ERROR: cannot execute %s in a read-only transaction. 或者运行中部分非只读SQL&#xff08;insert、update、create table as、create index、alter tab…

MCE | 促炎症的 PCD——细胞焦亡

程序性细胞死亡 (Programmed cell death, PCD) 是多细胞生物中&#xff0c;由基因调控的细胞自杀过程&#xff0c;对多细胞生物的发育、体内稳态和完整性至关重要。PCD 的研究涉及多个领域&#xff0c;如免疫、神经系统发育、癌症、感染等。常见的 PCD 有细胞凋亡 (Apoptosis)、…

贪吃蛇-第12届蓝桥杯Scratch选拔赛真题精选

[导读]&#xff1a;超平老师计划推出Scratch蓝桥杯真题解析100讲&#xff0c;这是超平老师解读Scratch蓝桥真题系列的第83讲。 蓝桥杯选拔赛每一届都要举行4~5次&#xff0c;和省赛、国赛相比&#xff0c;题目要简单不少&#xff0c;再加上篇幅有限&#xff0c;因此我精挑细选…

通信基石Socket结合OOP实现程序间的通信

学习目录前言一.Socket是用来干什么的二.如何用代码建立通信连接三.Java实现TCP发收消息四.UDP的Socket编程前言 先分享一下最近看到的几篇面经&#xff1a; 看了一下关于Socket的知识点好像面试十分高频&#xff0c;它作为通信的基石许多组件&#xff0c;框架都是在他的基础…

oracle数据库id字段自增长

mysql数据库中建表的时候可以设置id字段自增长&#xff0c;oracle数据库中要实现id字段自增长需要借助于sequence&#xff08;序列&#xff09;和trigger&#xff08;触发器&#xff09;。 下面通过一个简单的示范说明。 --1.新建一个账户表 account create table accout( …

机械转码日记【25】多态

目录 前言 1.多态的概念 2. 多态的定义及实现 2.1多态的构成条件 2.2 虚函数 2.3虚函数的重写 2.4虚函数重写的两个例外 2.4 C11 override 和 final 2.5 重载、覆盖(重写)、隐藏(重定义)的对比 3.抽象类 3.1概念 3.2 接口继承和实现继承 4.多态的原理 4.1虚函…

个人微信api

我们是一家专业服务企业数字化微信管理服务的技术服务团队&#xff0c;服务于需求SCRM、机器人、营销系统、社群小助手等具有研发能力的企业,同时我们也接收因使用Xp方案、ipad方案、PC方案导致被批量封号的企业&#xff0c;我们合作伙伴目前包含&#xff1a;金融服务行业Top10…

无协同资源创新打法,这几个品牌在双11「品牌嘉年华」实现品效双收

抖音双11好物节圆满收官&#xff0c;每年双11&#xff0c;我们关注的不仅仅是不断刷新的成绩&#xff0c;也是在更多元的场景、更丰厚的资源和更强劲的平台资源助力之下&#xff0c;商家在营销动作上带来了怎样的“惊喜”。 在「内容场景」&#xff0c;双11期间&#xff0c;抖…

VScode设置pretty-printer无效

文章目录VScode设置pretty-printer无效问题解决尝试正式解决参考VScode设置pretty-printer无效 问题 win10系统下&#xff0c;VScode中即使在launch.json中进行了如下设置&#xff0c;还是无效 {"description": "为 gdb 启用整齐打印","text"…

【设计模式】 - 结构型模式 - 外观模式

目录标题前言外观模式概述结构实现&#xff1a;智能家电控制优缺点前言 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式&#xff0c;前者采用继承机制来组织接口和类&#xff0c;后者釆用组合或聚合来组合对象。 由于组合关系或聚…

基于机器学习的自动音乐生成播放器

目录 详细设计说明书 1 1 引言 1 1.1 编写目的 1 1.2 背景 1 2. 此项目的任务提出者&#xff1a;西电软件工程课程组、西电软件开发小组 1 1.3 定义 2 1.4 参考资料 2 3 程序描述 4 3.1 011 参数调整模块 4 3.1.3 界面设计 4 3.1.4 参数调整的内部逻辑 5 3.2 012 自动谱曲模块 …

webpack 的基本使用(详解)

前言&#xff1a; 你是否也是只会运用框架中集成好的Webpack配置呢&#xff1f;你明白每一项的意义么&#xff1f;你懂多少Webpack的个性化配置项呢&#xff1f;本篇文章为你讲解Webpack中的各种配置项参数及作用&#xff01; 目录前言&#xff1a;一&#xff0c;什么是Webpack…

【算法手札】深入理解宽度遍历(bfs)和深度遍历(dfs)搜索

算法的重要性不言而喻&#xff0c;现在我们的生活也已经离不开各种算法&#xff0c;一个好的算法能大大提高程序的运行效率&#xff0c;是学习编程的一个重要模块&#xff0c;而遍历算法也是算法里的一个大的模块&#xff0c;今天我们一起来学习一下深度遍历算法&#xff08;de…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java电子书店管理系统ya226

做毕业设计一定要选好题目。毕设想简单&#xff0c;其实很简单。这里给几点建议&#xff1a; 1&#xff1a;首先&#xff0c;学会收集整理&#xff0c;年年专业都一样&#xff0c;岁岁毕业人不同。很多人在做毕业设计的时候&#xff0c;都犯了一个错误&#xff0c;那就是不借鉴…

双功能接头试剂知识分享:Alkyne maleimide,Mal-Alkyne,炔烃-马来酰亚胺

炔烃马来酰亚胺是一种双功能接头试剂&#xff0c;可将末端炔烃连接到各种含硫醇分子&#xff0c;例如含有半胱氨酸残基的蛋白质。然后可以通过铜催化的点击化学反应将炔烃部分与各种叠氮化物缀合。 Alkyne maleimide is a bifunctional connector reagent that can connect ter…

AB Test实验设计

1. 版本设计 实验版本的设计要遵循变量的单一性&#xff0c;不能一下子改变多个因素&#xff0c;如同一个按钮不能同时改变按钮颜色和按钮文字&#xff0c;实验设计越简单越容易得出正确的结论。 案例时间&#xff1a; 2. 实验时长 业界的实验时长一般是2-3周&#xff0c;最…

Packet Tracer - 在 OSPFv2 中传播默认路由

地址分配表 设备 接口 IPv4 地址 子网掩码 默认网关 R1 G0/0 172.16.1.1 255.255.255.0 不适用 S0/0/0 172.16.3.1 255.255.255.252 不适用 S0/0/1 192.168.10.5 255.255.255.252 不适用 R2 G0/0 172.16.2.1 255.255.255.0 不适用 S0/0/0 172.16.3.2 …

Redis对象及redisObject源码解析

写在前面 以下内容是基于Redis 6.2.6 版本整理总结 一、对象 前面几篇文章&#xff0c;我们介绍了Redis用到的主要的数据结构&#xff0c;如&#xff1a;sds、list、dict、ziplist、skiplist、inset等。 但是&#xff0c;Redis并没有直接使用这些数据结构来实现key-value数…