编译原理实验-递归下降语法分析

article/2025/10/15 6:12:29

具体代码已放至Github(仅供参考):

qxpBlog/Compiler_UESTC: 电子科技大学编译原理实验 (github.com)

具体实验过程如下:

一、实验目的、原理、内容及步骤:

1目的:通过本实验加深对编译技术中重点算法和编译技术的理解,提高学生的编程能力培养好的程序设计风格。了解和掌握递归下降分析法的基本原理,根据给出的文法能够完成递归下降程序的实现。

2原理:递归下降分析器编译思想是简单的,从识别符号开始,在语法规则支配下进行语法分析,它逐个扫视源程序中的所有字符,根据文法和当前输入字符预测到下一个语法成份U时,便确定U为目标并调用分析和识别U的子程序,在分析U的过程中,又有可能确立其它(或自身)子目标并调用相应子程序,如此继续下去。

3内容:

1、学习所提供的“表达式文法”的递归下降处理

理解 lex.l、rdparser.c 的内容

在 vscode/Clion 中建立工程并调试运行

2、学习所提供的文法

与词法分析所提供的文法作比较

3、编写 rdgram 所提供文法的递归下降程序

(1) 编写不生成“语法树”的递归下降程序 rdcheck.c

(2) 将 rdcheck.c 改造为生成语法树的递归下降程序 rdparser.c

(3) 改进词法分析程序、showAst 函数、main 函数等,使递归下降程序 rdparser最终从命令行读取要分析的程序 test.c,分析后调用 showAst 打印该程序的结构。

4实验步骤:

1)编写不生成“语法树”的递归下降程序 rdcheck.c

主函数如图1-1所示,主要采取一个无限循环结构来实现多次对所输入的文法进行递归下降语法分析,并将分析结果打印出来。

图 1-1 rdcheck文件main函数

编写两个常用的函数:match匹配函数、advance移进函数,如图1-2所示。

图 1-2 match函数与advance函数定义

用这两个函数来实现将所输入的字符串与sysy文法进行逐一匹配。match函数主要用来检查sysy文法中的终结符是否与输入串中的当前字符匹配,如果匹配那么就是用调用advance,将下一个待分析的字符设置为当前字符,之后再调用文法中剩余字符对应的函数进行匹配;如果不匹配,则返回-1,表示文法匹配出错。

对于开始符号CompUnit的文法规则,如图1-3所示。

图 1-3 CompUnit的文法规则

由于其包含公共左因子,所以我们首先要消除公共左因子,改造后的语法规则如图1-4所示。

图 1-4 改造后的CompUnit的文法规则

其对应的语法分析函数CompUnit, CompUnit_,如图1-5所示。

图 1-5 CompUnit、CompUnit_函数定义

sysy文法程序开始可能包含两个部分变量或常量声明(定义)和函数定义。如果输入串是变量或常量声明,那么就进入其对应语法规则左侧非终结符函数Decl(),进行下一步的匹配;如果是函数定义,那么就进入对应的函数FuncDef,进行下一步的匹配。如果下一步也匹配成功,则说明输入串符合sysy文法,并返回1,否则,则表明输入串不符合sysy文法,返回0。

对于sysy文法中的语句Stmt的文法规则,如图1-6所示。

图 1-6 Stmt的语法规则

由于该文法规则中含有公共左因子,所以需要消除其公共左因子,改造后的文法规则如图1-7所示。

图 1-7 改造后的Stmt的语法规则

对应的Stmt函数如图1-8(a)、图1-8(b)、图1-8(c)、图1-8(d)所示。

图 1-8(a) Stmt函数定义

图 1-8(b) Stmt函数定义

图 1-8(c) Stmt函数定义

图 1-8(d) Stmt函数定义

假设输入串是一个while语句,那么在Stmt函数中,match函数会首先匹配while语句中前缀部分的终结符:‘while’、‘(’,之后需要匹配一个由非终结符LorExp产生的循环条件表达式单词序列,因此进入对应的函数LorExp,识别由该非终结符生成的单词序列。之后继续使用match函数匹配非终结符‘)’,最后进入函数Stmt,匹配由非终结符Stmt产生的单词序列。如图1-9所示

 

图 1-9 while语句处理

对于sysy文法中的表达式Exp的语法规则,如图1-10所示。

 

图 1-10 Exp表达式语法规则

对应的Exp函数如图1-11所示。

图 1-11 Exp函数定义

 

在sysy文法中,表达式就是加减表达式,因此在对表达式进行递归下降语法分析时,会进入AddExp函数,识别由非终结符AddExp产生的单词序列。

该递归下降分析程序对输入串“int main(){while(1==2){return 0;}}”的识别结果如图1-12所示。

 

图 1-12 识别结果

2)将 rdcheck.c 改造为生成语法树的递归下降程序 rdparser.c

将rdcheck.c改造后,生成Stmt语句结点的函数astStmt如图2-1(a)、图2-1(b)、图2-1(c)、图2-1(d)、图2-1(e)所示。

 

图 2-1(a) 生成Stmt结点函数astStmt定义

 

图 2-1(b) 生成Stmt结点函数astStmt定义

 

图 2-1(c) 生成Stmt结点函数astStmt定义

 

图 2-1(d) 生成Stmt结点函数astStmt定义

 

图 2-1(e) 生成Stmt结点函数astStmt定义

在语句结点中,关于if语句结点生成的代码部分如图2-2所示。

 

图 2-2 生成if语句结点代码

在构建抽象语法树(AST)的过程中,我们将舍弃一些无用的界符,例如‘;’、‘(’、‘)’等,方便编译后续阶段的进行。因此在构建if语句结点时,用match匹配终结符‘if’、‘(’并将其舍弃,如果匹配成功则之后进入函数astLorExp,生成条件判断表达式结点l;反之匹配失败,返回NULL。之后匹配终结符‘)’并将其舍弃,如果匹配成功,那么就进入函数astStmt,生成复合语句结点CompoundStmt,反之则匹配失败,返回NULL。之后,如果能够匹配字符‘else’,那么就继续进入astStmt函数,生成复合语句结点CompoundStmt,最后返回生成的if语句结点;反之,则输入串中无else语句,那么直接返回不含else语句的if语句。

对于生成加减表达式结点的函数astAddExp定义如图2-3所示。

 

图 2-3 生成加减表达式结点函数astStmAddExp定义

在生成加减表达式结点的时候,根据AddExp文法可知,加减表达式包含右递归,其每个操作数都可能是由若干个其他表达式构成,因此采用while循环结构来生成加减表达式结点。

3)该进词法分析程序、showAst 函数、main 函数等,使递归下降程序 rdparser最终从命令行读取要分析的程序 test.c,分析后调用 showAst 打印该程序的结构。

改进后的main函数如图3-1所示。

 

图 3-1 改进后的main函数

从文法开始符号CompUnit对应的函数astCompUnit开始构建输入串的抽象语法树(AST),并将抽象语法树(AST)的根节点返回给node,之后调用函数showAst打印生成的抽象语法树(AST)。

改进后的showAst函数如图3-2(a)、图3-2(b)所示。

 

图 3-2(a) 改进后的showAst函数

 

图 3-2(b) 改进后的showAst函数

在showAst函数中,对与不同的结点类型,我们将打印不同的信息。

对于函数形参结点,我们调用showParaDecl函数对其所包含的结点进行打印,如图3-3所示。由于构建函数形参结点的时候我们是逆序构建,因此需采用非递归中序遍历的算法对其结点进行打印,以便保证函数形参顺序的正确。

 

图 3-3 showParaDecl函数定义

对于复合语句,我们调用showCompoundStmt函数对其所包含的结点进行打印,如图3-4所示。由于构建函数形参结点的时候我们是顺序序构建,并且假定只有left结点才是真正的语句结点,因此我们只需逐一访问每一层复合语句结点的左子结点并打印其所包含的信息即可。

   

 

图 3-4 showCompoundStmt函数定义               

对于函数调用结点,我们调用showCallExp函数对其所包含的结点进行打印,如图3-5所示。由于构建函数形参结点的时候我们是顺序序构建,并且假定只有left结点才是真正的语句结点,因此我们只需逐一访问每一层复合语句结点的左子结点并打印其所包含的信息即可。

   

 

图 3-5 showCallExp函数定义    

           

对于编译单元,我们调用showTranstion函数对其所包含的结点进行打印,如图3-6所示。由于构建函数形参结点的时候我们是顺序序构建,并且只有left结点才是变量结点或常量结点或者函数定义节点,因此我们只需逐一访问每一层编译单元结点的左子结点并打印其所包含的信息即可。

 

 图 3-6 showTrasntion函数定义

二、实验运行结果:

(1)测试用例1:在终端输入字符串“int main(int m, int n){l = 1 + 2; while(1 == 2){ l = l + 1;} }” ,程序运行结果如图4-1所示。

 

图 4-1 测试用例1运行结果

(2)测试用例2:在终端输入字符串“int main(int m, int n){l = 1 + 2; if(1 > 2){return 1;} } ” ,程序运行结果如图4-2所示。

 

图 4-2 测试用例2运行结果

(3)测试用例3:在终端输入字符串“int main(int m, int n){l = 1; if(6 < 2){return 1;}else {return 5;} }” ,程序运行结果如图4-3所示。

 

图 4-3 测试用例3运行结果

(4)测试用例4:在终端输入字符串“int func(){return 1;} int main(){int l = func();}”,程序运行结果如图4-4所示。

 

图 4-4 测试用例4运行结果

三、实验结论与总结:

        本次实验所实现的递归下降分析程序,能够完成对sysy文法中函数定义、函数形参、语句块、语句、表达式等部分的语法分析,并能够生成相应的抽象语法树(AST)。较好的完成了本次实验的要求。

        通过本次实验,了解和掌握了递归下降分析法的基本原理,并且能够根据给出的文法完成递归下降程序的实现。同时也对编译过程中词法分析这一环节有了一个更加深刻的认知。同时,在实验的过程中也逐渐地的意识到递归下降分析法的缺陷所在:对含有公共左因子和左递归的文法词法分析效果较差。


http://chatgpt.dhexx.cn/article/Y3w5Zsu9.shtml

相关文章

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

目录 一、实验目的和要求 二、实验内容 三、实验环境 四、实验步骤 1、语法分析所依据的文法&#xff1b; 2、给出消除左递归及提取左公因子的文法&#xff1b; 五、测试要求 六、实验步骤 1、语法分析所依据的文法 2、给出消除左递归及提取左公因子的文法&#xff1…

递归下降语法分析

一、实验目的 递归下降语法分析 二、实验题目 三、分析与设计 四、源代码 #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年后的我也可…