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

LR分析简介

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

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

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

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

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

实验内容

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

实验步骤

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

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

具体实现

  见代码。

  1 #include <algorithm>
  2 #include <fstream>
  3 #include <iostream>
  4 #include <map>
  5 #include <set>
  6 #include <stack>
  7 #include <vector>
  8 using namespace std;
  9
 10 using Production = pair<string, vector<string>>;
 11
 12 const int max_state = 110;
 13 const int delay_num = 5e8;
 14
 15 struct ParserLR
 16 {
 17
 18     map<string, int> mp_n; //非终结符映射
 19     map<string, int> mp_t; //终结符映射
 20     vector<Production> P;  //产生式
 21     vector<string> N, T;   //非终结符,终结符
 22     int state_num, operator_num, nonterminal_num, terminal_num, production_num;
 23     vector<string> action[max_state];
 24     vector<int> _goto[max_state];
 25     int init(string filename)
 26     {
 27         N.clear();
 28         T.clear();
 29         P.clear();
 30         mp_n.clear();
 31         mp_t.clear();
 32         for (int i = 0; i < max_state; i++)
 33         {
 34             action[i].clear();
 35             _goto[i].clear();
 36         }
 37         state_num = operator_num = nonterminal_num = terminal_num = production_num = 0;
 38         ifstream in(filename, ios::in);
 39         if (!in.is_open())
 40             return 0;
 41         in >> terminal_num;
 42         for (int i = 0; i < terminal_num; i++)
 43         {
 44             string tmp;
 45             in >> tmp;
 46             T.emplace_back(tmp);
 47             mp_t[tmp] = i;
 48         }
 49         in >> nonterminal_num;
 50         for (int i = 0; i < nonterminal_num; i++)
 51         {
 52             string tmp;
 53             in >> tmp;
 54             N.emplace_back(tmp);
 55             mp_n[tmp] = i;
 56         }
 57         in >> production_num;
 58         for (int i = 0; i < production_num; i++)
 59         {
 60             Production cur;
 61             in >> cur.first;
 62             int sz;
 63             in >> sz;
 64             for (int j = 0; j < sz; j++)
 65             {
 66                 string t;
 67                 in >> t;
 68                 cur.second.emplace_back(t);
 69             }
 70             P.emplace_back(cur);
 71         }
 72         in >> state_num;
 73         for (int i = 0; i <= state_num; i++)
 74             for (int j = 0; j < terminal_num; j++)
 75             {
 76                 string tmp;
 77                 in >> tmp;
 78                 action[i].emplace_back(tmp);
 79             }
 80         for (int i = 0; i <= state_num; i++)
 81             for (int j = 0; j < nonterminal_num; j++)
 82             {
 83                 int tmp;
 84                 in >> tmp;
 85                 _goto[i].emplace_back(tmp);
 86             }
 87         return 1;
 88     }
 89     Production getProduction(int idx)
 90     {
 91         return P[idx - 1];
 92     }
 93     pair<int, vector<Production>> analyze(vector<string> input) //first->出错位置,-1代表无错
 94     {
 95         vector<Production> error;
 96         vector<Production> success;
 97         stack<string> ch; //符号栈
 98         stack<int> st;    //状态栈
 99         ch.emplace("#");
100         st.emplace(0);
101         input.emplace_back("#");
102         int sz = input.size();
103         for (int i = 0; i < sz;)
104         {
105             string now = input[i];
106             if (!mp_t.count(now))
107                 return make_pair(i, success);
108             int ip = mp_t[now];
109             int top = st.top(); //栈顶状态
110             string at = action[top][ip];
111             if (at[0] == ‘r‘) //规约
112             {
113                 string res = at.substr(1, at.size());
114                 int num = stoi(res);
115                 Production trans = getProduction(num);
116                 for (int i = 0; i < trans.second.size(); i++)
117                 {
118                     st.pop();
119                     ch.pop();
120                 }
121                 top = st.top();
122                 string cur = trans.first;
123                 ch.emplace(cur);
124                 st.emplace(_goto[top][mp_n[cur]]);
125                 success.emplace_back(trans);
126             }
127             else if (at[0] == ‘s‘) //移进
128             {
129                 string res = at.substr(1, at.size());
130                 int to_state = stoi(res);
131                 st.emplace(to_state);
132                 ch.emplace(now);
133                 i++;
134             }
135             else if (at == "acc") //接受
136                 return make_pair(-1, success);
137             else //error
138             {
139                 if (now == "#")
140                     return make_pair(i - 1, success);
141                 return make_pair(i, success);
142             }
143         }
144         return make_pair(1, error);
145     }
146 };
147 inline void delay()
148 {
149     for (int i = 0; i < delay_num; i++)
150         ;
151 }
152 inline void display(const pair<int, vector<Production>> &out)
153 {
154     if (out.first == -1)
155     {
156         for (int i = 0; i < out.second.size(); i++)
157         {
158             cout << out.second[i].first << "->";
159             for (int j = 0; j < out.second[i].second.size(); j++)
160                 cout << out.second[i].second[j];
161             cout << "\n";
162         }
163     }
164     else
165         cout << "在第" << out.first + 1 << "个终结符出错.\n";
166 }
167 int main(int argc, char const *argv[])
168 {
169     ParserLR app;
170     string filename = "prj3_8_in.txt";
171     if (app.init(filename))
172     {
173
174         cout << "构建分析器中";
175         delay();
176         cout << ".";
177         delay();
178         cout << ".";
179         delay();
180         cout << ".\n";
181         delay();
182         cout << "构建成功.\n";
183         cout << "请输入终结符个数:";
184         int sz;
185         cin >> sz;
186         cout << "请输入包含" << sz << "个终结符的待分析序列, 终结符间需用空格分离:";
187         vector<string> al;
188         for (int i = 0; i < sz; i++)
189         {
190             string tmp;
191             cin >> tmp;
192             al.emplace_back(tmp);
193         }
194         cout << "开始分析";
195         delay();
196         cout << ".";
197         delay();
198         cout << ".";
199         delay();
200         cout << ".\n";
201         delay();
202         cout << "分析结束.\n";
203         pair<int, vector<Production>> out = app.analyze(al);
204         cout << "分析成功,结果如下:\n";
205         display(out);
206     }
207     return 0;
208 }

源代码

4
id - * #
3
E T F
6
E 3 E - T
E 1 T
T 3 T * F
T 1 F
F 2 - F
F 1 id
10
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
1 2 3
-1 -1 -1
-1 -1 -1
-1 -1 -1
-1 -1 -1
-1 -1 8
-1 9 3
-1 -1 10
-1 -1 -1
-1 -1 -1
-1 -1 -1

输入文件

效果展示

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

欢迎下方留言。

原文地址:https://www.cnblogs.com/mooleetzi/p/12019489.html

时间: 2024-08-30 13:25:42

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

编译原理课后作业【自顶向下分析法】语法分析

实验二: 题目:语法分析 目的:通过该实验掌握描述语法的文法和自顶向下分析法中的预测分析法. 要求:对给定的文法建立预测分析表:利用预测分析法对实验一的结果进行语法分析,对不符合给定文法的表达式给出出错位置信息. 内容:给定描述语法的文法为: E->E+T|T T->T*F|F F->i|(E) 题目如上描述. 用了STL里的MAP写了个程序,写的比较简单也可能有BUG,欢迎大家指出修正 Source code: 1 //Jeremy Wu {Wushuaiyi} CS1301 2 //

9月九日作业:对编译原理的个人理解

1.编译原理是计算机专业的一门重要专业课,旨在介绍编译程序构造的一般原理和基本方法.内容包括语言和文法.词法分析.语法分析.语法制导翻译.中间代码生成.存储管理.代码优化和目标代码生成.2.学习编译原理可以用语法分析来分析出一段用户上传是否含有代码,从而让你的网站更健壮.分析和分解用户输入的SQL语句,理解是否有害和是否有SQL注入.在业务软件中结算方面允许用户输入条件表达式和四则运算,允许用户自定义结算公式或条件,使软件上一个档次.3.如何学习编译原理:反复看书: 这个办法看起来最笨,却是基本

编译原理——算符优先分析文法(附源代码)

算符优先分析文法 一.写在前面 算符优先分析文法是一种工具,在编译的过程中,隶属于语法分析环节,却又与中间代码的生成息息相关,编译可以分为五个阶段:词法分析.语法分析.语义分析(中间代码的生成).代码优化.目标代码生成.语法分析是指:在词法分析基础上,将单词符号串转化为语法单位(语法范畴)(短语.子句.句子.程序段.程序),并确定整个输入串是否构成语法上正确的程序.也就是说语法分析是检验输入串的语法是否正确,注意这里的语法正确,只是简单地符合自己定义的规范,而不能检测出运行时错误,比如"X/0&

编译原理——正规式转DFA算法概述

一.概念概述 给定一个单词,判断该单词是否满足我们给定的单词描述规则,需要用到编译原理中词法分析的相关知识,其中涉及到的两个很重要的概念就是正规式(Regular Expression)和有穷自动机(Finite Automata).正规式是描述单词规则的工具,首先要明确的一点是所有单词组成的是一个无穷的集合,而正规式正是描述这种无穷集合的一个工具:有穷自动机则是识别正规式的一个有效的工具,它分为确定的有穷自动机(Deterministic Finite Automata,DFA)和不确定的有穷

linux下编译原理分析

linux下编译hello.c 程序,使用gcc hello.c,然后./a.out就可以运行:在这个简单的命令后面隐藏了许多复杂的过程,这个过程包括了下面的步骤: ====================================================================================== 预处理: 宏定义展开,所有的#define 在这个阶段都会被展开 预编译命令的处理,包括#if #ifdef 一类的命令 展开#include 的文件,像上面h

编译原理(四)语法分析之自顶向下分析

语法分析之自顶向下分析 说明:以老师PPT为标准,借鉴部分教材内容,AlvinZH学习笔记. 基本过程分析 1. 一般方法:对任一字符串,试图用一切可能的方法,从树根节点(开始符号)出发,根据文法自上而下地为输入符号串建立一棵语法树.直观理解为从开始符号出发,依据规则建立推导序列,最后推至目标字符串. 2. 特点:分析过程是带有预测的,是一种试探过程.试探失败就会出现回溯问题,降低了分析的效率. 3. 存在问题:左递归问题.回溯问题. 问题一:左递归问题 1. 左递归文法:文法规则中有形如 \(

编译原理简单词法分析器(first,follow,分析表)源码下载

编译原理(简单词法分析器下载) http://files.cnblogs.com/files/hujunzheng/%E7%AE%80%E5%8D%95%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90%E5%99%A8.zip

编译原理简单介绍

编译原理学习导论 大学课程为什么要开设编译原理呢?这门课程关注的是编译器方面的产生原理和技术问题,似乎和计算机的基础领域不沾边,可是编译原理却一直作为大学本科的必修课程,同时也成为了研究生入学考试的必考内容.编译原理及技术从本质上来讲就是一个算法问题而已,当然由于这个问题十分复杂,其解决算法也相对复杂.我们学的数据结构与算法分析也是讲算法的,不过讲的基础算法,换句话说讲的是算法导论,而编译原理这门课程讲的就是比较专注解决一种的算法了.在20世纪50年代,编译器的编写一直被认为是十分困难的事情,第

编译原理学习导论

编译原理学习导论 大学课程为什么要开设编译原理呢?这门课程关注的是编译器方面的产生原理和技术问题,似乎和计算机的基础领域不沾边,但是编译原理却一直作为大学本科的必修课程,同一时候也成为了研究生入学考试的必考内容.编译原理及技术从本质上来讲就是一个算法问题而已,当然因为这个问题十分复杂,其解决算法也相对复杂.我们学的数据结构与算法分析也是讲算法的,只是讲的基础算法,换句话说讲的是算法导论,而编译原理这门课程讲的就是比較专注解决一种的算法了.在20世纪50年代,编译器的编写一直被觉得是十分困难的事情