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

article/2025/10/15 6:10:36

目录

一、实验目的和要求

二、实验内容

三、实验环境

四、实验步骤

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 = 0if (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 += 1breakelse:continueelse:index += 1return index# E→TE'
def E():global Errif Err == 0:T()E1()# E'→+TE'| -TE' |ε
def E1():global Err, Indexif Err == 0 and Index != len(Result_Lex[0]):if Result_Lex[0][Index] in [11, 12]:Index += 1if 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 Errif Err == 0:F()T1()# T'→*FT'| /FT' |ε
def T1():global Err, Indexif Err == 0 and Index != len(Result_Lex[0]):if Result_Lex[0][Index] in [13, 14]:Index += 1if 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, Indexif Err == 0:if Result_Lex[0][Index] in [0, 28]:Index += 1elif Result_Lex[0][Index] == 23:Index += 1E()if Result_Lex[0][Index] != 24:Err = 1Index += 1else:Err = 1# 分析主程序
def Analysis_Gs():global Err, Indexif 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 = 1if 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 Trueelse:return False# 判断字符是否为算符或者界符
def Is_marks(string):if string in Punctuation_marks:return Trueelse:return False# 判断是不是空格或者换行
def Is_blank(string):if string in Blank:return Trueelse: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 += 1if End(id): break# 判断是否未保留字if Is_static(temporary):num = Static_word[temporary]Option(num, temporary, Explanation[0])# 判断是否为特殊运算符oddelif 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 += 1if End(id): breakelif strAll[id] == "." and ('0' <= strAll[id + 1] <= '9'):temporary += strAll[id]id += 1if End(id): breakelse:breakOption("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 += 2num = Punctuation_marks[temporary]Option(num, temporary, Explanation[num - 10])else:id += 1num = Punctuation_marks[temporary]Option(num, temporary, Explanation[num - 10])# 判断大于号两种情况:大于、大于等于elif strAll[id] == ">":if strAll[id + 1] == "=":temporary += strAll[id + 1]id += 2num = Punctuation_marks[temporary]Option(num, temporary, Explanation[num - 10])else:id += 1num = Punctuation_marks[temporary]Option(num, temporary, Explanation[num - 10])# 判断赋值符号特殊情况elif strAll[id] == ":":if strAll[id + 1] == "=":temporary += strAll[id + 1]id += 2num = Punctuation_marks[temporary]Option(num, temporary, Explanation[num - 10])# 单独的冒号不是运算符或界符,当非法字符处理else:id += 1Option("30", temporary, "非法字符")# 其他运算法或界符else:id += 1num = Punctuation_marks[temporary]Option(num, temporary, Explanation[num - 10])# 对空格、换行过滤elif Is_blank(strAll[id]):id += 1continue# 对非法字符的处理else:temporary += strAll[id]id += 1Option("30", temporary, "非法字符")def Lex_main():# 获取代码文件print("请输入要分析的代码文件(.txt)路径及完整名称:", end='')global pathR, pathWpathR = 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://chatgpt.dhexx.cn/article/LGHT8kcK.shtml

相关文章

递归下降语法分析

一、实验目的 递归下降语法分析 二、实验题目 三、分析与设计 四、源代码 #include <iostream> #include <fstream> #include <cstring> #include <string> #include <conio.h> #define digit 1 // 1数字 #define op 2 // -*/()# #define Hh …

Java递归下降分析器_递归下降语法分析器

用java语言编写的递归下降语法分析器&#xff0c;是一种适合手写语法编译器的方法&#xff0c;且非常简单。递归下降法对语言所用的文法有一些限制&#xff0c;但递归下降是现阶段主流的语法分析方法&#xff0c;因为它可以由开发人员高度控制&#xff0c;在提供错误信息方面也…

递归下降算法

递归下降算法 算法模型&#xff1a; Term Term Expr ExprExprFactor Factor 单个元素。最小单位。 实现原理&#xff1a; 一个程式进入算法及被看作是一个项&#xff0c;分解成项加表达式的形式&#xff0c;表达式被分解成 表达式加因子的形式&#xff0c;因子是这个算法…

实验二:递归下降语法分析

文章目录 一、实验目的二、实验原理与要求 1、原理 2、要求 三、实验设备四、实验内容五、实验步骤 1. 单词内码表 2. 定义语言文法 3. 语法分析器的实现&#xff08;编码&#xff09; 4. 测试 六、配套资源 一、实验目的 理解自顶向下语法分析的基本模式&#xff0c;熟悉…

编译原理递归下降语法分析器C++实现

编译原理递归下降语法分析器C简单实现 1.递归下降分析法的功能 语法分析器的功能是利用函数之间的递归调用模拟语法树自上而下的构造过程。 2.递归下降分析法的前提 改造文法&#xff1a;消除二义性、消除左递归、提取左因子&#xff0c;判断是否为LL&#xff08;1&#xff0…

编译原理(九)——递归下降法

背景&#xff1a; 自定向下的语法分析方法&#xff0c;LL(1)是一种非常直观的方法&#xff0c;它的分析过程是按照句子的定义来进行的&#xff0c;也就是说从开始符出发对要分析的串进行推导&#xff0c;如果推导成功就证明这个被分析的串是一个合法的句子&#xff0c;否则的话…

【编译原理】【C语言】实验三:递归下降分析法

C语言 实验环境&#xff1a;Visual Studio 2019 author&#xff1a;zoxiii 递归下降分析法 1、实验内容2、前期准备2.1 递归下降分析法原理2.2 要实现的文法2.3 需要的函数 3、分析过程3.1 递归下降分析法设计思想及算法3.2 分析栈的分析过程3.3 流程图3.4 源代码3.5 运行结果 …

JAVA游戏开发-超炫酷贪吃蛇游戏源码及教程

一&#xff0e;前言 某日&#xff0c;看见隔壁家的小朋友在玩一款网络爆款贪吃蛇游戏&#xff0c;感觉很好玩。自己刚好正在学习JAVA编程&#xff0c;也想实现一个类似功能的游戏Demo练手&#xff0c;在网上查看了不少源码案例&#xff0c;全都是很古老的方块式贪吃蛇游戏案例…

Java实现贪吃蛇游戏【代码】

Java实现贪吃蛇游戏【代码】 花了两个下午写了一个贪吃蛇小游戏&#xff0c;本人想写这游戏很长时间了。作为以前诺基亚手机上的经典游戏&#xff0c;贪吃蛇和俄罗斯方块一样&#xff0c;都曾经在我们的童年给我们带来了很多乐趣。世间万物斗转星移&#xff0c;诺基亚曾经作为手…

JavaSE项目 | 纯Java实现贪吃蛇小游戏

目录 一&#xff1a;贪吃蛇游戏的实现步骤 1. 画出窗口 2. 在窗口上添加画布 3. 在画布上添加黑色游戏区 4. 放静态蛇 5. 定义蛇的数据结构 6. 控制蛇头方向 7. 放上开始提示信息 8. 按空格键开始游戏 9. 让蛇动起来 10. 实现暂停 11. 实现转向功能 12. 添加食物 …

java 贪吃蛇 源码+图片

本人也是个初学者&#xff0c;有什么不对的地方&#xff0c;请大佬指点&#xff01;&#xff01;&#xff01; 一、涉及到的知识点如下&#xff1a; 循环&#xff0c;分支方法的抽取数组的使用面向对象继承&#xff0c;子类方法的重写接口&#xff0c;接口的实现 二、游戏图形…

JAVA贪吃蛇代码(带注释)

贪吃蛇 这是游戏效果图片是代码里面的素材游戏数据类 package com.tang.retor_snaker;import javax.swing.*; import java.net.URL;public class Data {private static URL bodyURL Data.class.getResource("/com/tang/retor_snaker/statics/body.png");private st…

JAVA贪吃蛇小游戏源代码系列

欢迎关注公众号&#xff1a; 获取贪吃蛇小游戏的源代码。 贪吃蛇小游戏运行结果如下&#xff1a; 启动界面&#xff1a; 运行界面&#xff1a; 重启界面&#xff1a; 源代码框架如下&#xff1a; 注&#xff1a;在运行程序的时候&#xff0c;得重新设计窗体的大小&#x…

JAVA 实现《贪吃蛇大作战》游戏|CSDN创作打卡

前言 贪吃蛇&#xff08;也叫做贪食蛇&#xff09;游戏是一款休闲益智类游戏&#xff0c;有PC和手机等多平台版本。既简单又耐玩。该游戏通过控制蛇头方向吃东西&#xff0c;从而使得蛇变得越来越长。 本程序是通过java的swing来实现《贪吃蛇大作战》这款游戏。 主要需求 1…

java贪吃蛇源码

欢迎访问我的个人博客 https://jialaner.cn/​​​​​​​ java是一种面向对象的语言&#xff0c;有着其中不用质疑的优点。学习java将近三个月了&#xff0c;一直在琢磨着“万物皆对象”的意义&#xff0c;却总是只知其表不知其意&#xff0c;做完这个java贪吃蛇后才有了那么…

贪吃蛇 java实现超简单的贪吃蛇(附源代码)

贪吃蛇游戏 贪吃蛇是个非常经典的游戏&#xff0c;希望对初学Java的小伙伴有一定帮助。希望大家喜欢&#xff0c;因为写得简单&#xff0c;希望大家都能看得懂。 游戏界面&#xff08;游戏背景素材不喜欢的话可以自己换&#xff0c;就别在乎我选的素材&#xff08;&#x1f9…

java实现贪吃蛇小游戏(源码+注释)

一.工程文件 二.Main.java package com.company;import javax.swing.*;public class Main {public static void main(String[] args) {//创建窗体对象JFrame frame new JFrame();//创建窗体参数&#xff08;&#xff09;frame.setBounds(10,10,900,720);//设置不允许更改大小…

使用Java实现一个简单的贪吃蛇小游戏

基于java实现贪吃蛇小游戏&#xff0c;主要通过绘制不同的图片并以一定速度一帧一帧地在窗体上进行展示。 开发工具&#xff1a;eclipse java工具包&#xff1a;jdk1.8 一、创建新项目 创建一个新的项目&#xff0c;并命名。创建一个名为images的文件夹用来存放游戏相关图片…

Java贪吃蛇全代码

用Java编写精典小游戏——贪吃蛇&#xff01; 前言 我想贪吃蛇应该是不少90后和00后的童年&#xff08;我本人是01年的&#xff09;&#xff0c;回想起从前偷偷拿着我爹的诺基亚在被窝里玩贪吃蛇&#xff0c;不禁感慨万分&#xff0c;时间飞逝&#xff0c;没想到10年后的我也可…

JAVA小项目(四)—— 贪吃蛇【轻松入门,附源码】

目录 &#xff08;一&#xff09;效果图 &#xff08;二&#xff09;代码实现 &#xff08;1&#xff09;将图片加载到程序中 &#xff08;2&#xff09;创建窗体 &#xff08;3&#xff09;创建面板 &#xff08;4&#xff09;绘制静态的小蛇 &#xff08;5&#xff09; 加入监…