编译原理 算法3.8 LR分析 c++11实现

时间:2021-10-07 01:46:58

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 - - -
- - -
- - -
- - -
- -
-
- -
- - -
- - -
- - -

输入文件

效果展示

编译原理 算法3.8 LR分析 c++11实现

编译原理 算法3.8 LR分析 c++11实现

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

欢迎下方留言。

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

  1. 《编译原理》构造 LL&lpar;1&rpar; 分析表的步骤 - 例题解析

    <编译原理>构造 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&lpar;1&rpar;分析法的FIRST和FOLLOW集

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

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

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

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

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

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

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

  8. 编译原理根据项目集规范族构造LR&lpar;0&rpar;分析表

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

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

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

随机推荐

  1. JavaScript基本数据类型和引用数据类型

    ECMAScript包含两种不同数据类型的值:基本类型值和引用类型值.基本类型值指的是简单的数据段,而引用类型值那些可能有多个值构成的对象. 在进行变量赋值时,解析器必须确定这个值是基本类型值还是引用 ...

  2. jQuery ajax 返回的数据类型

    请求数据的接口信息如下 当我们打印出返回的数据与数据节点时,我们发现数据节点显示为undefind 查看一下我们的代码 $.ajax({ type: "Post", url: &q ...

  3. oracle-分页查询方案

    一.使用rownum做三层包装查询(常用方案) SELECT * FROM ( SELECT A.*, ROWNUM RN FROM (SELECT * FROM TABLE_NAME) A ) 其中 ...

  4. Android如何让真机显示debug log的调试信息

    真机默认是不开启debug log调试功能的,以前我一直用模拟器,模拟器默认是开启debug log调试功能的,那么如何让真机开启呢? 我用华为Ascend P6为例: 1.进入拨号界面,输入*#*# ...

  5. 配置Nginx 1&period;8支持PHP 5&period;6

    启动PHP和Nginx 修改Nginx配置文件/usr/local/nginx/conf/nginx.conf server { listen ; server_name localhost; loc ...

  6. 自解压的方式创建VC&plus;&plus;程序的打包

    Walkthrough: Deploying a Visual C++ Application By Using the Visual C++ Redistributable Package Visu ...

  7. SSRS&lpar;rdl报表&rpar;分页显示表头和冻结表头

    <TablixRowHierarchy>          <TablixMembers>            <TablixMember>            ...

  8. VC&sol;MFC 当鼠标移到控件上时显示提示信息

    VC/MFC 当鼠标移到控件上时显示提示信息 ToolTip是Win32中一个通用控件,MFC中为其生成了一个类CToolTipCtrl,总的说来其使用方法是较简单的,下面讲一下它的一般用法和高级用法 ...

  9. MySQL&lpar;3&rpar;-索引

    一.索引类型 在MySQL中,存储引擎使用索引,首先在索引中找到对应值,然后根据匹配的索引记录中找到对应的行. 无论是多么复杂的ORM工具,在精妙和复杂的索引面前都是"浮云".这里 ...

  10. XML实体解析器的作用

    XML实体解析器的作用 什么是实体解析器 如果一个sax解析器需要实现对外部实体的自定义处理,那么必须实现一个EntityResolver接口并且注册到SAX驱动上. 从这段文字可以看出来,实体解析器 ...