LR分析简介

  LR分析是应用最广泛的一类分析方法,它是实用的编译器功能中最强的分析器,其特点是:

  1,采用最一般的无回溯移进-规约方法。

  2,可分析的文法是LL文法的真超集。

  3,能够及时发现错误,及时从左扫描输入序列的最大可能。

  4,分析表较为复杂,难以手工构造。

实验内容

  根据LR分析表action和goto实现LR分析。

实验步骤

  输入 序列$\omega$和文法$G$的LR分析表action与goto。

  输出 若$\omega \in L(G)$,得到$\omega$的规范规约,否则指出一个错误。

具体实现

  见代码。

 #include <algorithm>
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <stack>
#include <vector>
using namespace std; using Production = pair<string, vector<string>>; const int max_state = ;
const int delay_num = 5e8; struct ParserLR
{ map<string, int> mp_n; //非终结符映射
map<string, int> mp_t; //终结符映射
vector<Production> P; //产生式
vector<string> N, T; //非终结符,终结符
int state_num, operator_num, nonterminal_num, terminal_num, production_num;
vector<string> action[max_state];
vector<int> _goto[max_state];
int init(string filename)
{
N.clear();
T.clear();
P.clear();
mp_n.clear();
mp_t.clear();
for (int i = ; i < max_state; i++)
{
action[i].clear();
_goto[i].clear();
}
state_num = operator_num = nonterminal_num = terminal_num = production_num = ;
ifstream in(filename, ios::in);
if (!in.is_open())
return ;
in >> terminal_num;
for (int i = ; i < terminal_num; i++)
{
string tmp;
in >> tmp;
T.emplace_back(tmp);
mp_t[tmp] = i;
}
in >> nonterminal_num;
for (int i = ; i < nonterminal_num; i++)
{
string tmp;
in >> tmp;
N.emplace_back(tmp);
mp_n[tmp] = i;
}
in >> production_num;
for (int i = ; i < production_num; i++)
{
Production cur;
in >> cur.first;
int sz;
in >> sz;
for (int j = ; j < sz; j++)
{
string t;
in >> t;
cur.second.emplace_back(t);
}
P.emplace_back(cur);
}
in >> state_num;
for (int i = ; i <= state_num; i++)
for (int j = ; j < terminal_num; j++)
{
string tmp;
in >> tmp;
action[i].emplace_back(tmp);
}
for (int i = ; i <= state_num; i++)
for (int j = ; j < nonterminal_num; j++)
{
int tmp;
in >> tmp;
_goto[i].emplace_back(tmp);
}
return ;
}
Production getProduction(int idx)
{
return P[idx - ];
}
pair<int, vector<Production>> analyze(vector<string> input) //first->出错位置,-1代表无错
{
vector<Production> error;
vector<Production> success;
stack<string> ch; //符号栈
stack<int> st; //状态栈
ch.emplace("#");
st.emplace();
input.emplace_back("#");
int sz = input.size();
for (int i = ; i < sz;)
{
string now = input[i];
if (!mp_t.count(now))
return make_pair(i, success);
int ip = mp_t[now];
int top = st.top(); //栈顶状态
string at = action[top][ip];
if (at[] == 'r') //规约
{
string res = at.substr(, at.size());
int num = stoi(res);
Production trans = getProduction(num);
for (int i = ; i < trans.second.size(); i++)
{
st.pop();
ch.pop();
}
top = st.top();
string cur = trans.first;
ch.emplace(cur);
st.emplace(_goto[top][mp_n[cur]]);
success.emplace_back(trans);
}
else if (at[] == 's') //移进
{
string res = at.substr(, at.size());
int to_state = stoi(res);
st.emplace(to_state);
ch.emplace(now);
i++;
}
else if (at == "acc") //接受
return make_pair(-, success);
else //error
{
if (now == "#")
return make_pair(i - , success);
return make_pair(i, success);
}
}
return make_pair(, error);
}
};
inline void delay()
{
for (int i = ; i < delay_num; i++)
;
}
inline void display(const pair<int, vector<Production>> &out)
{
if (out.first == -)
{
for (int i = ; i < out.second.size(); i++)
{
cout << out.second[i].first << "->";
for (int j = ; j < out.second[i].second.size(); j++)
cout << out.second[i].second[j];
cout << "\n";
}
}
else
cout << "在第" << out.first + << "个终结符出错.\n";
}
int main(int argc, char const *argv[])
{
ParserLR app;
string filename = "prj3_8_in.txt";
if (app.init(filename))
{ cout << "构建分析器中";
delay();
cout << ".";
delay();
cout << ".";
delay();
cout << ".\n";
delay();
cout << "构建成功.\n";
cout << "请输入终结符个数:";
int sz;
cin >> sz;
cout << "请输入包含" << sz << "个终结符的待分析序列, 终结符间需用空格分离:";
vector<string> al;
for (int i = ; i < sz; i++)
{
string tmp;
cin >> tmp;
al.emplace_back(tmp);
}
cout << "开始分析";
delay();
cout << ".";
delay();
cout << ".";
delay();
cout << ".\n";
delay();
cout << "分析结束.\n";
pair<int, vector<Production>> out = app.analyze(al);
cout << "分析成功,结果如下:\n";
display(out);
}
return ;
}

源代码

id - * #

E T F

E  E - T
E T
T T * F
T F
F - F
F id s4 s5 null null
null s6 null acc
null r2 s7 r2
null r4 r4 r4
null r6 r6 r6
s4 s5 null null
s4 s5 null null
s4 s5 null null
null r5 r5 r5
null r1 s7 r1
null r3 r3 r3 - - -
- - -
- - -
- - -
- -
-
- -
- - -
- - -
- - -

输入文件

效果展示

代码使用部分c++11特性,如有本地编译需要,请确认环境。

欢迎下方留言。

编译原理 算法3.8 LR分析 c++11实现的更多相关文章

  1. 《编译原理》构造 LL(1) 分析表的步骤 - 例题解析

    <编译原理>构造 LL(1) 分析表的步骤 - 例题解析 易错点及扩展: 1.求每个产生式的 SELECT 集 2.注意区分是对谁 FIRST 集 FOLLOW 集 3.开始符号的 FOL ...

  2. 编译原理实验之SLR1文法分析

    ---内容开始--- 这是一份编译原理实验报告,分析表是手动造的,可以作为借鉴. 基于  SLR(1) 分析法的语法制导翻译及中间代码生成程序设计原理与实现1 .理论传授语法制导的基本概念,目标代码结 ...

  3. 编译原理(六)自底向上分析之LR分析法

    自底向上分析之LR分析法 说明:以老师PPT为标准,借鉴部分教材内容,AlvinZH学习笔记. 基本概念 1. LR分析:从左到右扫描(L)自底向上进行规约(R),是规范规约,也即最右推导(规范推导) ...

  4. 【编译原理】语法分析LL(1)分析法的FIRST和FOLLOW集

    近来复习编译原理,语法分析中的自上而下LL(1)分析法,需要构造求出一个文法的FIRST和FOLLOW集,然后构造分析表,利用分析表+一个栈来做自上而下的语法分析(递归下降/预测分析),可是这个FIR ...

  5. 编译原理实习(应用预测分析法LL(1)实现语法分析)

    #include<iostream> #include<fstream> #include<iomanip> #include<cstdio> #inc ...

  6. 编译原理 #02# 简易递归下降分析程序(js实现)

    // 实验存档 截图: 代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"&g ...

  7. 【编译原理】c++实现自下而上语法分析器

    写在前面:本博客为本人原创,严禁任何形式的转载!本博客只允许放在博客园(.cnblogs.com),如果您在其他网站看到这篇博文,请通过下面这个唯一的合法链接转到原文! 本博客全网唯一合法URL:ht ...

  8. 编译原理根据项目集规范族构造LR(0)分析表

    转载于https://blog.csdn.net/Johan_Joe_King/article/details/79058597?utm_medium=distribute.pc_relevant.n ...

  9. 《编译原理》LR 分析法与构造 LR(1) 分析表的步骤 - 例题解析

    <编译原理>LR 分析法与构造 LR(1) 分析表的步骤 - 例题解析 笔记 直接做题是有一些特定步骤,有技巧.但也必须先了解一些基本概念,本篇会通过例题形式解释概念,会容易理解和记忆,以 ...

随机推荐

  1. NOIP模拟测试12

    T1 斐波那契 一道找规律题,被我做成了贼难的题. 观察图片可知x=f[i-1]+j.(j为x的父亲)且j<=f[i-1],然后就二分找父亲没了. #include<bits/stdc++ ...

  2. Docker从入门到掉坑(二):基于Docker构建SpringBoot微服务

    本篇为Docker从入门到掉坑第二篇:基于Docker构建SpringBoot微服务,没有看过上一篇的最好读过 Docker 从入门到掉坑 之后,阅读本篇. 在之前的文章里面介绍了如何基于docker ...

  3. [转载]2.3 UiPath循环活动For Each的介绍和使用

    一.For Each的介绍 For Each:循环迭代一个列表.数组.或其他类型的集合, 可以遍历并分别处理每条信息 二.For Each在UiPath中的使用 1.打开设计器,在设计库中新建一个Fl ...

  4. Android音频开发(1):基础知识

    Android音频开发(1):基础知识 导读 人的说话频率基本上为300Hz~3400Hz,但是人耳朵听觉频率基本上为20Hz~20000Hz. 对于人类的语音信号而言,实际处理一般经过以下步骤: 人 ...

  5. mvn上传dubbo jar到nexus

    第一种方式: mvn deploy:deploy-file -DgroupId=com.alibaba -DartifactId=dubbo -Dversion=2.8.4 -Dpackaging=j ...

  6. python面向对象<三>

    类属性.实例属性: class Tool(object): #属性(类属性)类对象(Tool) num = 0 #方法 def __init__(self,new_name): self.name = ...

  7. dubbo中出现can not be invoked any more

    具体错误示例如下 从错误看,是客户方发起调用时,dubbo会去检查本地的invoker instance,如果发现invoker已经是destroy status,则直接抛出上面的异常,下面先来说下平 ...

  8. 别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】

    目录 1.什么是类的加载(类初始化) 2.类的生命周期 3.接口的加载过程 4.解开开篇的面试题 5.理解首次主动使用 6.类加载器 7.关于命名空间 8.JVM类加载机制 9.双亲委派模型 10.C ...

  9. lqb 基础练习 回文数

    基础练习 回文数 时间限制:1.0s   内存限制:512.0MB     问题描述 1221是一个非常特殊的数,它从左边读和从右边读是一样的,编程求所有这样的四位十进制数. 输出格式 按从小到大的顺 ...

  10. 力扣(LeetCode)4的幂 个人题解

    给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方. 示例 1: 输入: 16 输出: true 示例 2: 输入: 5 输出: false 进阶:你能不使用循环或者递归 ...