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

article/2025/10/15 6:40:27

C语言
实验环境:Visual Studio 2019
author:zoxiii


递归下降分析法

  • 1、实验内容
  • 2、前期准备
    • 2.1 递归下降分析法原理
    • 2.2 要实现的文法
    • 2.3 需要的函数
  • 3、分析过程
    • 3.1 递归下降分析法设计思想及算法
    • 3.2 分析栈的分析过程
    • 3.3 流程图
    • 3.4 源代码
    • 3.5 运行结果
  • 4、遇到问题

1、实验内容

  用高级语言实现递归下降分析程序。使用输入串i*(i+i),输出分析栈中所有内容,并给出分析结果。

2、前期准备

2.1 递归下降分析法原理

  自顶向下分析就是从文法的开始符触发并寻找出这样一个推导序列:推导出的句子恰好为输入符号串;或者说,能否从根节点出发向下生长出一颗语法树,其叶节点组成的句子恰好为输入符号串。显然,语法树的每一步生长(每一步推导)都以能否与输入符号串匹配为准,如果最终句子得到识别,则证明输入符号串为该文发的一个句子;否则,输入符号串不是该文法的句子。
  递归下降分析法是一种自顶向下的分析方法,文法的每个非终结符对应一个递归过程(函数)。分析过程就是从文法开始符触发执行一组递归过程(函数),这样向下推到直到推出句子;或者说从根节点出发,自顶向下为输入串寻找一个最左匹配序列,建立一颗语法树。

2.2 要实现的文法

  已知要实现的文法如下,可以观察到该文法G[E]中包含直接左递归:

G[E]:	E->E+T|T
T->T*F|F
F->(E)|i

  为了实现确定的自顶向下分析,要求文法满足下述两个条件:
(1) 文法不含左递归,即不存在这样的非终结符A:有A->A……存在;
(2) 无回溯,对文法的任一非终结符号,当其产生式右部有多个候选式可供选择时,各候选式所推出的终结符号串的首字符集合要两两不相交。
  因为文法如果包含左递归和回溯在文法分析的时候就可能会做大量无用的工作,导致分析效率降低。
  所以首先需要对该文法消除左递归和回溯,得到如下文法G’[E]:

G’[E]:	E->TE’
E’->TE’|ε
T->FT’
T’->*FT’|ε
F->(E)|i

  另外为了方便编写代码,所以将文法表示符替换成便于书写的单个大写字母,得到新的G[E]文法如下:

G[E]:	E->TG
G->+TG|ε
T->FS
S->*FS|ε
F->(E)|i

2.3 需要的函数

  在不含左递归和回溯的条件下,我们就能够构造一个不带回溯的自顶向下的分析程序,这个分析程序是由一组递归过程组成的,每个过程对应文法的一个非终结符。这样的一个分析程序称为递归下降分析器。分析书上给出的伪代码首先可以确定的函数有以下几个:
(1)void E(); //E–>TG
(2)void G(); //G–>+TG|ε
(3)void T(); //T–>FS
(4)void S(); //S–>*FS|ε
(5)void F(); //F–>(E)|i
(6)void err(); //提示错误信息
  然后对于实验要求对于分析过程中的分析栈进行输出,所以我们使用一个字符串类型的变量stackStr来模拟分析栈,并添加vector类型的链表记录每一步的分析结果,以供输出。在此基础上就需要添加压栈等相关函数如下:
(1)int check();//验证是否已经到栈底
(2)void push(string pre, string value);//将字符串存入输出栈

3、分析过程

3.1 递归下降分析法设计思想及算法

  为文法G[E]的每个非终结符号E构造一个递归过程,不妨命名为E。E的产生式的右部指出这个过程的代码结构,在匹配时:
(1)若是终结符a,则继续对照,若匹配则输入串向前进一个符号;否则出错。
(2)若是非终结符号A,则调用与此非终结符对应的过程。当A的右部有多个产生式时,可用选择结构实现。
  具体为:
  1)对于每个非终结符号 E->A|B|,|Z 处理的方法如下:

E( )
{
ch=s[i];
if(ch 可能是 A 的首字符 ) 处理 A 的程序部分 ;
else if(ch 可能是 B 的首字符 ) 处理 B 的程序部分 ;
,
else error()
}

  2)对于每个右部 A->x1x2,xn的处理架构如下:

处理 x1 的程序;
,
处理 xn 的程序;

  3)如果右部为空,则不处理。
  4)对于右部中的每个符号 xi

① 如果 xi 为终结符号:
if(xi == 当前的符号 )
{
NextChar() ; //NextChar 为前进一个字符函数
return;
}
else 出错处理2;
② 如果 xi 为非终结符号,直接调用相应的过程 xi()

3.2 分析栈的分析过程

  我们将对符合要实现的文法的、正确的输入串“i*(i+i)#”进行分析,其中,“#”为输入串的分隔符。进行语法分析时,首先将“#”和文法开始符E压入栈中,当语法分析到栈中仅剩“#”而扫描输入串指针已经指向输入串尾部的“#”时,则语法分析成功。
  首先我们按照已给的文法和输入串画出该语法树如下图所示:

在这里插入图片描述

图1-语法分析树
  对于分析文法的过程中,对输入串的每一个字符都需要调用函数分析,根据当前扫描到的字符ch进行匹配,将匹配到的过程字母压栈,在执行完之后,再将对应的字母出栈,对该输入串分析后得到每一步分析栈的情况如下:

在这里插入图片描述

图2-语法分析栈

3.3 流程图

在这里插入图片描述

图3-主程序流程图

  对于文法的每一个表达式都要编写对应的函数来匹配字符,下面为其中一个过程的流程图。

在这里插入图片描述

图4-函数F()流程图

  另外,压栈函数在每一次匹配字符的过程中都需要压栈,其中使用到了一些C++自带的函数:
(1) substr(a,b):a:起始位置、b:字符串长度;
(2) str.find_first_of(ch):从str开始查找ch的位置;
(3) erase(idx, n):删除从位置idx开始的n个字符;
(4) v.push_back():将字符串压入向量v中;

在这里插入图片描述

图5-压栈函数流程图

3.4 源代码

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
using namespace std;
//变量定义
string s, str, stackStr;//s:输入串、str:中间变量、stackStr : 模拟栈
int i;
string ch;//当前分析字符
vector<string> v;//字符串类型的向量(文法+分析栈)
//函数声明
void E();                   //E-->TG
void G();                   //G-->+TG|ε
void T();                   //T-->FS
void S();                   //S-->*FS|ε
void F();                   //F-->(E)|i
void err();					//提示错误信息
int check();//验证是否已经到栈底
void push(string pre, string value);//将字符串存入输出栈
/*** 函数功能:提示错误信息*/
void err()
{cout << "ERROR" << endl;exit(0);
}
/*** 函数功能:将字符串存入输出栈*/
void push(string pre, string value)
{ch += s[i];int idx = stackStr.find_first_of(pre[0], 0);//从头开始找到pre开始的位置if (value != "ε")//不是空字时{string value1;value1 = value;if (value[0] == '+')value1 = "TG";if (value[0] == '*')value1 = "FS";if (value[0] == '(')value1 = "E";if (value[0] == 'i')value1 = "";stackStr.replace(idx, 1, value1);//将第一个pre的位置替换为value}else{stackStr.erase(idx, 1);//删除从该位置开始的1个字符}v.push_back((pre + value + "," + stackStr));//将对应的表达式和栈的内容存加入在向量v尾部
}
/*** 函数功能:验证是否已经到栈底*/
int check()
{if (i >= s.size()) {return 1;}else if (s[i] == '#'){ch += '#';return 1;}return 0;
}
/*** 函数功能:E-->TG*/
void E()
{push("E-->", "TG");T();G();
}
/*** 函数功能:G-->+TG|ε*/
void G() {if (s[i] == '+'){str = s[i];str += "TG";i++;push("G-->", str);T();G();}else{push("G-->", "ε");}
}
/*** 函数功能:T-->FS*/
void T()
{push("T-->", "FS");F();S();
}
/*** 函数功能:S-->*FS|ε*/
void S() {if (s[i] == '*'){str = s[i];str += "FS";i++;push("S-->", str);F();S();}else{push("S-->", "ε");}
}
/*** 函数功能:F-->(E)|i*/
void F() {if (s[i] == '('){i++;push("F-->", "(E)");E();if (s[i] == ')'){i++;}else{err();}}else if (s[i] == 'i'){i++;push("F-->", "i");}else{err();}
}
/*** 函数功能:主函数*/
int main() {cout << "===================================================" << endl;cout << "=== 递归下降分析 ===" << endl;cout << "===================================================" << endl;cout << "===请输入字符串 (以#号结束)===" << endl;while (cin >> s) //输入要分析的字符串{v.clear();i = 0;stackStr = "E#";//初始化栈E();if (check()){cout << "=====>\t\t 输入串分析正确! " << endl;cout << "推导过程如下: " << endl;cout << "文法\t\t分析栈\t\t当前分析字符\n";cout << "E      \t\tE#\t\t" << s[0] << endl;//初始栈的内容int i;for (i = 0; i < v.size(); i++){cout << v[i].substr(0, v[i].find_first_of(",", 0)) << "\t";cout << setiosflags(ios::right) << setw(10) << v[i].substr(v[i].find_first_of(",", 0) + 1) << "\t\t";cout << ch[i] << endl;}}elsecout << "==>\t 输入串不符合该文法 " << endl;}return 0;
}

3.5 运行结果

下图为递归下降分析法的推导过程、输出内容为:
(1) 文法表达式;
(2) 分析栈;
(3) 当前分析字符;

在这里插入图片描述

图6-语法分析正确结果

在这里插入图片描述

图7-语法分析错误结果

4、遇到问题

  对于文法的最后一个表达式中的F->(E)在判断“)”时出错,检查发现原因是因为前面识别到“(”时已经对输入串的索引值加1了,所以直接比较就可以,但在编写时由进行了一次加1操作,导致出错。

参考文献:《编译原理教程 (第4版)》 胡元义 2016


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

相关文章

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; 加入监…

Java贪吃蛇大作战

作为Java新手小白&#xff0c;渴望学习一些好玩有趣的java程序 废话不多说&#xff0c;接下来我会一步一步实现java小程序&#xff1a;贪吃蛇大作战哦&#xff01; 实现 Java贪吃蛇一共分四个步骤&#xff1a; 1、画出窗体对象 2、绘制静态ui 3、使用鼠标监听器事件和定时器事…

Java简易小游戏贪吃蛇(Java实战)

这个版本的贪吃蛇我是跟着“黑马程序员”写的。小伙伴们可以跟着视频试着做一下&#xff0c;同时视频也会更详细。 B站学习链接&#xff1a;【黑马】两个小时带你用Java语言写一个贪吃蛇游戏【配套源码笔记】_哔哩哔哩_bilibili 相对于新手而言&#xff0c;贪吃蛇应该算是一个…

JAVA实现贪吃蛇游戏

本文实现的功能有: 1.绘制静态窗口 2.绘制游戏面板 3.绘制静态小蛇 4.通过键盘控制小蛇移动 5.吃食物 6.积分系统和失败判定 最近在学GUI&#xff0c;然后又有读者希望我写一下相关的实战。刚好博主在b站漫无目的的寻找着题材的时候看到了一个写贪吃蛇游戏的视频&#xff0c;于…

Java实现贪吃蛇大作战小游戏(完整版)

大家好&#xff0c;今天尝试用swing技术写一个贪吃蛇大作战小游戏&#xff0c;供大家参考。 效果展示 目录 效果展示 一、游戏界面 二、得分情况 项目介绍 项目背景 总体需求 实现过程 代码展示 主类 &#xff1a;Demo类 MyPanel类 ①构造方法 ②初始化方法 ③绘制方法…

用java写一个贪吃蛇小游戏(源码在最后)

一、引入 涉及技能&#xff1a; 循环、分支方法的抽取数组的使用面向对象继承&#xff0c;子类方法的重写接口&#xff0c;接口的实现GUI&#xff08;图像化界面编程&#xff09; GUI中的组件&#xff1a; 7.1 窗口 7.2 弹窗 7.3 面板 7.4 文本框 7.5 列表框 7.6 按钮 7.7 图…

安卓蓝牙开发总结(一)—蓝牙开启与关闭

蓝牙开启与关闭 1、引言2、布局文件3、蓝牙打开与关闭  3.1 通过对按钮监听方法  3.2 通过设置点击事件 4、结果展示及总结5、参考链接 1、引言 最近在学习如何在安卓手机上对蓝牙进行操控&#xff0c;作为初学者发现大多数博客对安卓开发的初学者极为不友好&#xff0c;特…

【Bluetooth|蓝牙开发】二、蓝牙开发入门

个人主页&#xff1a;董哥聊技术 我是董哥&#xff0c;嵌入式领域新星创作者 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; 【所有文章汇总】 1、蓝牙基础概念 蓝牙&#xff0c;是一种利用低功率无线电&#xff0c;支持设备短距离…