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

2023-11-03


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


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;
            }
        }
        else
            cout << "==>\t 输入串不符合该文法 " << endl;
    }
    
    return 0;
}

3.5 运行结果

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

在这里插入图片描述

图6-语法分析正确结果

在这里插入图片描述

图7-语法分析错误结果

4、遇到问题

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • Android Launcher应用进程启动流程 基于Android-12

    最近研究了一下Launcher应用进程的创建流程 记录一下 以下只记录一些关键点 因为整体流程特别繁琐 1 ActivityManagerService 应用进程的启动 切换和调度 四大组件的启动和管理 gt systemReady 系统服

随机推荐

  • 百度翻译js渗透

    项目 python请求百度翻译API接口 流程 1 目标网站 www fanyi baidu com 2 抓包找到正确的动态请求 3 分析请求参数 找出会变化的参数sign token 4 JS渗透 找出参数js生产过程 5 模拟js生产参
  • 使用Recyclerview实现多布局效果

    Android应用开发中Recyclerview的使用频率非常高 多布局是使用Recyclerview经常实现效果之一 这里简单介绍一下多布局的基本开发流程 实现一个简单的多布局效果 实现如下效果图 一 创建数据类 创建数据类 用以模拟多布
  • 线性代数之矩阵与坐标系的转换

    空间中的点是可以用向量来描绘的 这些点的描绘是基于我们自建的欧式空间坐标系下 我们可以用一个行向量来表示一个空间的点 那我们的要进行空间坐标的转换的时候怎么办呢 一个行向量 B 我可以理解成IB B的三个值既为三个行向量 1 0 0 0 1
  • ffmpegMP4编码转换为h264

    ffmpeg i input mp4 vcodec h264 output mp4
  • 让phpstorm智能提示laravel代码

    简介 PhpStorm 默认情况下是没有对Laravel框架的代码提示功能的 下面给出Laravel 5 在PhpStorm 2019 1版本下的安装过程 1 开一个laravel项目 2 在根目录运行如下命令 进行安装 copycompo
  • 创建applicationContext.xml

    1 创建applicationContext xml并导入头文件
  • Qt三维图形添加纹理,并使图形旋转。立体图形添加纹理。

    一重山 两重山 山远天高烟水寒 相思枫叶丹 菊花开 菊花残 塞雁高飞人未还 一帘风月闲 pro中添加 QT core gui opengl win32 LIBS lOpengl32 lglu32 lglut mainwindow h ifn
  • 跟我一起写 Makefile(十)

    跟我一起写 Makefile 十 本文来自于CSDN 陈皓博主 网址http blog csdn net haoel article details 2895 详细内容请参考其经典文章 跟我一起写makefile 陈皓
  • 树莓派VLC获取实时视频流

    一 安装VLC之前 升级安装程序apt get sudo apt get update sudo apt get upgrade 二 在摄像头已激活的情况下 安装VLC 输入命令 sudo apt get install vlc VLC安装
  • 【全解析】屏幕尺寸,分辨率,像素,PPI之间到底什么关系?

    全解析 屏幕尺寸 分辨率 像素 PPI之间到底什么关系 2015 09 02 12 22 稿源 产品100 5条评论 撤稿纠错 今天我给大家来讲讲这几个咱们经常打交道的词到底啥意思 以及他们之间到底有什么关系 这篇文章是我花了一个下午从N多
  • vue+nodejs实现文件上传

    前端
  • java 运算符

    运算符介绍 运算符是一种特殊的符号 用以表示数据的运算 赋值和比较等 算术运算符 算术运算符是对数值类型的变量进行运算的 常用的运算符有以下几个 注意 的本质 看一个公式 a b a a b b 注意先乘除后加减 当a和b都是小数或其中一个
  • python中如何使用正则表达匹配\本身?(文末赠书)

    点击上方 Python爬虫与数据挖掘 进行关注 回复 书籍 即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 将军向宠 性行淑均 大家好 我是皮皮 一 前言 前几天在Python钻石群 空 问了一个Python正则表达式的问题
  • String类练习题

    1 编写一个方法 将一段文本中的各个单词的字母顺序翻转 例如 To be or not to be 将变成 oT eb ro ton ot eb 2 String s name zhangsan age 18 classNo 090728
  • [人工智能-深度学习-47]:卷积神经网CNN+循环神经网络RNN与组合电路+时序电路的比较

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 121367263 目录 第1章 计算机
  • 二维条码PDF417译码技术

    摘要 对二维条友PDF417的基本概念 用途 优势做了系统的介绍 着重分析了PDF417条码的具体译码过程 并给出该条码作为多进制码 进行R S纠错译码时所要注意的有关域运算及模运算 关键词 PDF417条码 有限域 错误纠正容量 错误位置
  • Mongo可视化工具studio 3t无限试用

    文章目录 前言 一 下载 二 使用步骤 1 下载后 无脑下一步安装好 2 开始无限试用 总结 前言 mongodb可以说是比较流行的nosql数据库了 它灵活多变的存储 为项目中后续可能的变更提供了极大的便利性 工欲善其事必先利其器 今天推
  • teamviewer安装商业不能链接的解决办法重装()

    安装商业不能链接的解决办法重装 解决办法如下 1 卸载teamviewer 可以通过控制面板或者第三方管理软件卸载 2 Windows R后输入regedit打开注册表 通过Ctrl F调出搜索框 搜索所有的teamviewer关键字 把搜
  • TypeError: write() argument must be str, not bytes报错原因及解决方法

    img data requests get url url content fill Name str i png with open fill Name w as fp fp write img data 问题描述 想用write写入图片
  • 【编译原理】【C语言】实验三:递归下降分析法

    C语言 实验环境 Visual Studio 2019 author zoxiii 递归下降分析法 1 实验内容 2 前期准备 2 1 递归下降分析法原理 2 2 要实现的文法 2 3 需要的函数 3 分析过程 3 1 递归下降分析法设计思