清晰記得本次實驗在推了兩次項目集規范簇之后,發現文檔中給出的文法有錯誤,聯系老師得到改正后,遂順利完成。簡單記錄一下本次實驗的經歷,留作以后備用,若有錯誤之處,還請路過的博友不吝賜教。
實驗設計目標
構造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\n", 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; }