清晰记得本次实验在推了两次项目集规范簇之后,发现文档中给出的文法有错误,联系老师得到改正后,遂顺利完成。简单记录一下本次实验的经历,留作以后备用,若有错误之处,还请路过的博友不吝赐教。
实验设计目标
构造LR(1)分析程序,利用它进行语法分析,判断给出的符号串是否为该文法识别的句子。
实验原理
整体思路:在总控程序的控制下,从左到右扫描输入符号串,根据状态栈中的栈顶状态、符号栈中的栈顶字符和文法及当前输入符号,按分析表完成相应的分析工作。
LR分析器由三个部分组成:
- 总控程序,也可以称为驱动程序。对所有的LR分析器总控程序都是相同的。
- 分析表或分析函数,不同的文法分析表将不同,同一个文法采用的LR分析器不同时,分析表将不同,分析表又可以分为动作表(ACTION)和状态转换(GOTO)表两个部分,它们都可用二维数组表示。
- 分析栈,包括文法符号栈和相应的状态栈,它们均是先进后出栈。
-
分析器的动作就是由栈顶状态和当前输入符号所决定。
GOTO[i,X]=j表示,规定当栈顶状态为i,遇到当前文法符号为X时应转向状态j,X为非终结符。
ACTION[i,a]规定了栈顶状态为i时,遇到输入符号a应执行的动作有四种可能:
-
-
移进:
action[i,a]= Sj:状态 j 移入到状态栈,把 a 移入到文法符号栈,其中 i , j 表示状态号。
- 归约:
action[i,a]=Rk:当在栈顶形成句柄时,则归约为相应的非终结符A,即文法中有A->B的产生式,若B的长度为R(即|B|=R),则从状态栈和文法符号栈中自顶向下去掉R个符号,即栈指针SP减去R,并把A移入文法符号栈内,j=GOTO[i,A]移进状态栈,其中i为修改指针后的栈顶状态。
- 接收Acc:
当归约到文法符号栈中只剩文法的开始符号S时,并且输入符号串已结束即当前输入符是'#',则为分析成功。
- 报错:
当遇到状态栈顶为某一状态下出现不该遇到的文法符号时,则报错,说明输入端不是该文法能接受的符号串。
-
实验文法
(0)S->E
(1)E->E+T
(2)E->E-T
(3)E->T
(4)T->F
(5)T->T*F
(6)T->T/F
(7)F->(E)
(8)F->i
实验输入、输出
- 输入数据:
case1: i+i*i#
case2: i+i*#
- 输出结果:
实验实现过程
- 首先就是秃头三次的项目集规范簇(一开始打算手写来着,画满一张A4纸之后, Visio好香啊……)
这里我是采用类似DFS深度遍历的思想来画的,对于一个输入就按着他一个输入分支往下画,直到不能再往下扩展。便回溯将前边的分支填充,所以画出来的最后的图片就出现了这样一个特点,靠前的很多分支都是直接写编号就可以了。当然也可以采用像BFS遍历的思想来画,个人感觉效果应该是一样的。
2. 那有了项目集规范簇,接下来就要构建相应的Action表和Goto表了,这里照着项目集规范簇来并不难,麻烦的是将其转换为代码中对应的表(建表时,眼都看花了……)
-
- Action表
-
- Goto表:
3. 有了Action表和Goto表,剩下的就好办了,为了能更好的理清思路,我又画了LR(1)控制器的流程图。
代码实现部分
(待实验统计完毕之后,在统一上传一下XD)
2020/6/29
LR.h
#include <iostream> #include <string> #include <vector> #include <iomanip> #include "LRTable.h" #ifndef _LR_H #define _LR_H using namespace std; class Analyser{ private: string str; //被分析的字符串 int step; //步骤 int idx; //当前分析符号的下标 vector<char> sign; //符号栈 vector<int> status; //状态栈 public: Analyser(); ~Analyser(); bool startAnalyse(string str); string transformStackToStr(int opt); }; Analyser::Analyser() { this->step = 1; this->idx = 0; } Analyser::~Analyser(){} string Analyser::transformStackToStr(int opt) { string str = ""; //返回状态栈 if(opt == 0) { vector<int>::iterator iter = status.begin(); bool isfirst = true; for(;iter != status.end();iter++) { if(isfirst) { isfirst = false; str += to_string(*iter); } else { str += "_"+to_string(*iter); } } } else//返回符号栈 { vector<char>::iterator iter = sign.begin(); for(; iter != sign.end(); iter++) { str += *iter; } } return str; } bool Analyser::startAnalyse(string nowStr) { this->str = nowStr; //将初始状态输入到栈中 sign.push_back('#'); status.push_back(0); LRAnalyseTable table; //当前的状态 int nowStatus = 0; //初始说明 cout<<setw(10)<<"步骤"<<setw(15)<<"状态栈"<<setw(10)<<"符号栈"<<setw(15)<<"当前符号"<<setw(15)<<"剩余字符串"<<setw(20)<<"动作说明"<<endl; while(table.getAction(nowStatus, str[idx]) != 0) { int result = table.getAction(nowStatus, str[idx]); //输出错误的情况信息 if(result == -1) { printf("error! 在输入符号: %c 处出现错误,符号坐标为: %d ", str[idx], idx); return false; } if(result > 0)//移进操作 { cout<<setw(10)<<step<<setw(15)<<transformStackToStr(0)<<setw(10)<<transformStackToStr(1)<<setw(15)<<str[idx]<<setw(15)<<str.substr(idx)<<setw(20)<<"Action["<<status.back()<<"]["<<str[idx]<<"]=S"<<result<<" 移进状态"<<endl; status.push_back(result); sign.push_back(str[idx]); idx++; } else if(result <= -10)//规约操作 { //还原归约所用产生式的下标 result += 10; result = -result; string rule = table.getGrammer(result); cout<<setw(10)<<step<<setw(15)<<transformStackToStr(0)<<setw(10)<<transformStackToStr(1)<<setw(15)<<str[idx]<<setw(15)<<str.substr(idx)<<setw(20)<<"R"<<result<<":"+rule+"归约"<<endl; //弹出产生式右端的符号和状态 step++; for(int i=rule.size()-1; rule[i]!='>' && i>=0; i--) { status.pop_back(); sign.pop_back(); } nowStatus = status.back(); result = table.getGoto(nowStatus, rule[0]); cout<<setw(10)<<step<<setw(15)<<transformStackToStr(0)<<setw(10)<<transformStackToStr(1)<<setw(15)<<rule[0]<<setw(15)<<str.substr(idx)<<setw(20)<<"Goto["<<nowStatus<<"]["<<rule[0]<<"]="<<result<<" 状态转移"<<endl; status.push_back(result); sign.push_back(rule[0]); } //获取当前状态栈的状态 nowStatus = status.back(); step++; } cout<<setw(10)<<step<<setw(15)<<transformStackToStr(0)<<setw(10)<<transformStackToStr(1)<<setw(15)<<str[idx]<<setw(15)<<str.substr(idx)<<setw(20)<<" 分析完成"<<endl; return true; } #endif
LRTable.h
#include <string> #ifndef _LRTABLE_H #define _LRTABLE_H using namespace std; class LRAnalyseTable { private: //产生式 string grammer[15] = {"S->E", "E->E+T", "E->E-T", "E->T", "T->F", "T->T*F", "T->T/F","F->(E)", "F->i"}; //终结符 char terminalChar[10] = {'+','-','*', '/', '(', ')','i','#'}; //非终结符 char nonTerminalChar[10] = {'E','F','T'}; //终结符的个数 int numTerminalChar = 8; //非终结符的个数 int numNonTerminalChar = 3; //初始化LR(1)分析表 //action表 int Action[50][8] = { {-1, -1, -1, -1, 6, -1, 23, -1},{2, 28, -1, -1, -1, -1, -1, 0},// 0 1 {-1, -1, -1, -1, 6, -1, 23, -1},{-11, -11, 4, 24, -1, -1, -1, -11},//2 3 {-1, -1, -1, -1, 6, -1, 23, -1},{-15, -15, -15, -15, -1, -1, -1 ,-15},//4 5 {-1, -1, -1, -1, 13, -1, 20, -1},{9, 16, -1, -1, -1, 8, -1, -1},//6 7 {-17, -17, -17, -17, -1, -1, -1, 7},{-1, -1, -1, -1, 13, -1, 20, -1},//8 9 {-11, -11, -11, 18, -1, 1 ,-1, -1},{-1, -1, -1, -1, 13, -1, 20, -1},//10 11 {-15, -15, -15, -15, -1, -15, -1, -1},{-1, -1, -1, -1, 13, -1, 20, -1},//12 13 {9, 16, -1, -1, -1, 15, -1, -1},{-17, -17, -17, -17, -1, -17, -1, -1},//14 15 {-1, -1, -1, -1, 13, -1, 20, -1},{-12, -12, 11, 18, -1, -12, -1, -1},//16 17 {-1, -1, -1, -1, 13, -1, 20,-1},{-16, -16, -16, -16, -1, -16, -1, -1},//18 19 {-18, -18, -18, -18, -1, -18, -1, -1},{-14, -14, -14, -14, -1, -14, -1, -1},//20 21 {-13, -13, 11, 18, -1, -13, -1, -1},{-18, -18, -18, -18, -1, -1, -1, -18},//22 23 {-1, -1, -1, -1, 6, 23, -1, -1},{-16, -16, -16, -16, -1, -1, -1, -16},//24 25 {-14, -14, -14, -14, -1, -1, -1, -14},{-13, -13, 4, 24, -1, -1, -1, -13},//26 27 {-1, -1, -1, -1, 6, -1, 23, -1}//28 }; //goto表 int Goto[50][4] = { {1, 26, 27},{-1, -1, -1},//0 1 {-1, 26, 3},{-1, -1, -1},//2 3 {-1, 5, -1},{-1, -1, -1},//4 5 {7, 21, 22},{-1, -1, -1},//6 7 {-1, -1, -1},{-1, 21, 10},//8 9 {-1, -1, -1},{-1, 12, -1},//10 11 {-1, -1, -1},{14, 21, 22},//12 13 {-1, -1, -1},{-1, -1, -1},//14 15 {-1, 21, 17},{-1, -1, -1},//16 17 {-1, 19, -1},{-1, -1, -1},//18 19 {-1, -1, -1},{-1, -1, -1},//20 21 {-1, -1, -1},{-1, -1, -1},//22 23 {-1, 5, -1},{-1, -1, -1},//24 25 {-1, -1, -1},{-1, -1, -1},//26 27 {-1, 26, 3}//28 }; public: LRAnalyseTable(); ~LRAnalyseTable(); int getTerminalIndex(char ch); int getNonTerminalIndex(char ch); int getAction(int status, char ch); int getGoto(int status, char ch); string getGrammer(int idx); }; LRAnalyseTable::LRAnalyseTable(/* args */) { } LRAnalyseTable::~LRAnalyseTable() { } int LRAnalyseTable::getAction(int status, char ch) { return Action[status][getTerminalIndex(ch)]; } int LRAnalyseTable::getGoto(int status, char ch) { return Goto[status][getNonTerminalIndex(ch)]; } string LRAnalyseTable::getGrammer(int idx) { return grammer[idx]; } //获取终结符的下标 int LRAnalyseTable::getTerminalIndex(char ch){ for(int i=0; i<numTerminalChar; i++) { if(ch == terminalChar[i]) { return i; } } return -1; } //获得非终结符的下标 int LRAnalyseTable::getNonTerminalIndex(char ch){ for(int i=0; i<numNonTerminalChar; i++) { if(ch == nonTerminalChar[i]) { return i; } } return -1; } #endif
test.cpp
#include <iostream> #include <string> #include <stack> #include <vector> #include <iomanip> #include "LR.h" using namespace std; int main(){ freopen("in.txt","r", stdin); freopen("out.txt", "w", stdout); string str=""; int cnt = 1; while(getline(cin, str)) { str = str.substr(0, str.size()-1); cout<<"Case"<<cnt++<<": "<<str<<endl; Analyser analyse; if(analyse.startAnalyse(str)){ cout<<"输入符号串"<<str<<"为合法符号串"<<endl; }else{ cout<<"输入符号串"<<str<<"为非法符号串"<<endl; } } return 0; }