【C++实现】编译原理 免考小队 消除一切左递归
生活随笔
收集整理的這篇文章主要介紹了
【C++实现】编译原理 免考小队 消除一切左递归
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
背景
期末考試免考,沖!
實驗名稱
消除一切左遞歸
實驗時間
2020年5月27日 到 2020年5月31日
院系
信息科學與工程學院
組員姓名
Chocolate、kry2025、鐘先生、leo、小光
實驗環境介紹
- windows 10 操作系統
- Eclipse 進行 java 編程
- CodeBlocks 進行 C++ 編程
實驗目的與要求
目的
- 深刻理解左遞歸的算法
- 掌握消除左遞歸的過程
- 加強團隊合作能力
- 提高自身的編程能力和解決問題的能力
要求
- 編程實現消除一切左遞歸
- 算法簡潔,不冗余
解決問題
產生式直接消除左遞歸
形如 P → Pα | β 可以通過直接消除轉化為:
P → βP′ P′ → αP′ | ?產生式間接消除左遞歸
有時候雖然形式上產生式沒有遞歸,但是因為形成了環,所以導致進行閉包運算后出現左遞歸,如下:
S → Qc | c Q → Rb | b R → Sa | a雖不具有左遞歸,但S、Q、R都是左遞歸的,因為經過若干次推導有
- SQcRbcSabc
- QRbSabQcab
- RSaQcaRbca
就顯現出其左遞歸性了,這就是間接左遞歸文法。
消除間接左遞歸的方法是:
把間接左遞歸文法改寫為直接左遞歸文法,然后用消除直接左遞歸的方法改寫文法。
如果一個文法不含有回路,即形如PP的推導,也不含有以ε為右部的產生式,那么就可以采用下述算法消除文法的所有左遞歸。
實驗結果
源代碼
#include<bits/stdc++.h> #define endl '\n' using namespace std; const int maxn=100+5; char buf[maxn]; //輸入產生式 int n; //產生式的數量 class node{ public:string left; //產生式左部set<string> right; //產生式右部node(const string& str){left=str;right.clear();}void push(const string& str){right.insert(str);}void print(){printf("%s->",left.c_str());set<string>::iterator it = right.begin();printf("%s",it->c_str());it++;for (;it!= right.end();it++ )printf("|%s",it->c_str());cout<<endl;} }; map<string,int> mp; //記錄每個node的下標 vector<node> vnode; //每一個產生式 string start; //文法G[s] bool used[maxn]; //用于去掉無用產生式 //初始化工作 void init(){mp.clear();vnode.clear();start="S"; } //消除間接左遞歸 void eliminateIndirectLeftRecursion(){for(int i=0;i<vnode.size();i++){for(int j=0;j<i;j++){vector<string> ans;set<string>& righti=vnode[i].right;set<string>& rightj=vnode[j].right;char ch=vnode[j].left[0]; //取所有Aj產生式的左部的非終結符set<string>::iterator iti,itj;for(iti=righti.begin();iti!=righti.end();iti++){if(iti->at(0)==ch) //如果當前產生式右部的非終結符和Aj相同for(itj=rightj.begin();itj!=rightj.end();itj++)ans.push_back(*itj+iti->substr(1)); //進行替換操作,先存儲起來}while(!righti.empty()){if(righti.begin()->at(0)!=ch) //存儲當前沒有替換的產生式右部ans.push_back(*righti.begin());righti.erase(righti.begin()); //被替換過的產生式右部也刪除掉}for(int k=0;k<ans.size();k++) //將替換過的產生式右部進行更新操作righti.insert(ans[k]);}}cout<<"消除間接左遞歸后的結果:"<<endl;for(int k=0;k<vnode.size();k++)vnode[k].print();cout<<endl; } //消除直接左遞歸 void eliminateDirectLeftRecursion(){for(int i=0;i<vnode.size();i++){char ch=vnode[i].left[0];set<string>& right=vnode[i].right; //拿到當前右部set<string>::iterator it;string tmp=vnode[i].left.substr(0,1)+"\'"; //對非終結符更改bool flag=true;for(it=right.begin();it!=right.end();it++){if(it->at(0)==ch){vnode.push_back(node(tmp));mp[tmp]=vnode.size();flag=false;break;}}int idx=mp[tmp]-1;if(flag) continue; //對于非終結符不相同的產生式我們需要跳過vector<string> ans;set<string>& tmpSet=vnode[idx].right;tmpSet.insert("~"); //添加空字符while(!right.empty()){if(right.begin()->at(0)==ch)tmpSet.insert(right.begin()->substr(1)+tmp);elseans.push_back(right.begin()->substr(0)+tmp);right.erase(right.begin()); //刪除掉原本產生式右部}for(int k=0;k<ans.size();k++)right.insert(ans[k]); //更新加入新的產生式右部}cout<<endl;cout<<"消除直接左遞歸后的結果:"<<endl;for(int k=0;k<vnode.size();k++)vnode[k].print();cout<<endl; } //搜索 void dfs(int x){if(used[x]) return;used[x]=1; //將當前下標記錄set<string>::iterator it=vnode[x].right.begin();for(;it!=vnode[x].right.end();it++){for(int i=0;i<it->length();i++){if(isupper(it->at(i))){ //判斷是否是大寫字母if(i+1<it->length() && it->at(i+1)=='\'') //如果當前是替換的那個字符dfs(mp[it->substr(i,2)]-1);elsedfs(mp[it->substr(i,1)]-1);}}} } //去掉無用產生式 void removeUselessProduction(){memset(used,0,sizeof(used));int idx=mp[start]-1;dfs(idx); //搜索cout<<"最終文法:"<<endl;vector<node> res;for(int i=0;i<vnode.size();i++)if(used[i]) //存儲已經標記過的產生式res.push_back(vnode[i]);vnode.clear();vnode=res; } int main(){cout<<"請輸入文法G[S]的產生式數量:"<<endl;while(cin>>n){init(); //初始化getchar();cout<<"依次輸入文法G[S]的產生式:"<<endl;for(int i=0;i<n;i++){scanf("%s",buf); //輸入產生式int len=strlen(buf),j;for(j=0;j<len;j++)if(buf[j]=='-'){buf[j]=0; //進行左部和右部切分break;}string tmp=buf; //拿到產生式的左部if(!mp[buf]){vnode.push_back(node(tmp));mp[tmp]=vnode.size();}int idx=mp[tmp]-1; //獲取左部的下標tmp=buf+j+2; //拿到產生式的右部vnode[idx].push(tmp);}//確定開始節點int idx=vnode.size()-1;start=vnode[idx].left[0];eliminateIndirectLeftRecursion(); //消除間接左遞歸eliminateDirectLeftRecursion(); //消除直接左遞歸removeUselessProduction(); //去掉無用產生式/**test*//*cout<<"---------test---------"<<endl;for(int k=0;k<vnode.size();k++)vnode[k].print();*/for(int k=0;k<vnode.size();k++)vnode[k].print();}return 0; }輸出結果
測試樣例
6 S->Qc S->c Q->Rb Q->b R->Sa R->a6 R->Sa R->a Q->Rb Q->b S->Qc S->c6 Q->Rb Q->b R->Sa R->a S->Qc S->c6 Q->Rb R->Sa Q->b S->Qc R->a S->c參考文獻
感謝以下博主的文章,本文參考了部分代碼和知識。
馮強計算機考研:編譯原理-消除左遞歸黎辰:編譯原理(三) 消除文法的左遞歸
學如逆水行舟,不進則退總結
以上是生活随笔為你收集整理的【C++实现】编译原理 免考小队 消除一切左递归的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 李宏毅线性代数笔记7 子空间
- 下一篇: 10大Android手机杀毒软件