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. 欧拉路&&欧拉回路

    T1是欧拉路板子,但我不会,直接爆炸.. 这玩意就是个dfs,但我以前一直以为欧拉路只能$O(nm)$求 今天才知道可以$O(n+m)$ 欧拉路判定: 无向:起点终点为奇度点,其余偶度 有向:起点终点 ...

  2. Mybatis精讲(一)---环境配置及架构梳理

    目录 简介 ORM模型 Hibernate Ibatis 环境搭建 jar 配置 xml方式配置 代码方式配置 两种方式对比 Mybatis结构 源码解读xml环境加载 映射器解读 Ibatis # ...

  3. Project Euler 57: Square root convergents

    五十七.平方根收敛(Square root convergents) 二的平方根可以表示为以下这个无穷连分数: \[ \sqrt 2 =1+ \frac 1 {2+ \frac 1 {2 +\frac ...

  4. Wycieczki 线性代数

    B. Wycieczki 题目描述 给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种.将所有可能的路径按路径长度排序,请输出第k小的路径的长度,注意路径不一定是简单路径,即可以 ...

  5. Docker 学习 | 基础命令

    基本概念定义 基本组成 客户端/守护进程 C/S架构 本地/服务器 镜像 容器基石 只读文件系统 联合加载(union mount) 容器 通过镜像启动 执行 写时复制 仓库 公有 docker hu ...

  6. 你知道如何优化Join语句吗?

    join语句的两种算法,分别是:NLJ和BNL 测试数据: create table t1(id int primary key, a int, b int, index(a)); create ta ...

  7. js数组方法大全(上)

    # js数组方法大全(上) 记录一下整理的js数组方法,免得每次要找方法都找不到.图片有点多,注意流量,嘻嘻! 本期分享 join() reverse() sort() concat() slice( ...

  8. Unity中用Mesh画一个圆环(二)

    中目标-生成完整面 在之前的内容中我们已经成功生成了一个面,接下来我们要生成剩下的面就很容易了. 我们把之前生成的面当作顶面,接着我们来生成底面. 还记得前面说过\(\color{#1E90FF}{D ...

  9. 领扣(LeetCode)单调数列 个人题解

    如果数组是单调递增或单调递减的,那么它是单调的. 如果对于所有 i <= j,A[i] <= A[j],那么数组 A 是单调递增的. 如果对于所有 i <= j,A[i]> = ...

  10. ZeroC ICE的远程调用框架 ThreadPool

    ThreadPool提供Reactor/Proactor服务,并且强偶合了Reactor(反应器)/Proactor(前摄器).不同于Reactor/Proactor使用线程池 进行事件处理的设计.如 ...