背景

期末考试免考,冲!

实验名称

消除一切左递归

实验时间

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的推导,也不含有以ε为右部的产生式,那么就可以采用下述算法消除文法的所有左递归。

for (i=1;i<=n;i++)for (j=1;j<=i-1;j++){ 把形如Ai→Ajγ的产生式改写成Ai→δ1γ /δ2γ /…/δkγ 其中Aj→δ1 /δ2 /…/δk是关于的Aj全部规则;消除Ai规则中的直接左递归;}

实验结果

源代码

#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++实现】编译原理 免考小队 消除一切左递归相关推荐

  1. 【C++实现】编译原理 免考小队 FIRSTVT集生成算法

    背景 期末考试免考,冲! 实验名称 FIRSTVT集生成算法 实验时间 2020年6月3日 到 2020年6月9日 院系 信息科学与工程学院 组员姓名 Chocolate.kry2025.钟先生.le ...

  2. 【C++实现】编译原理 免考小队 NFA转换为等价的DFA

    背景 期末考试免考,冲! 实验名称 对任意给定的NFA M进行确定化操作 实验时间 2020年5月21日 到 2020年5月24日 院系 信息科学与工程学院 组员姓名 Chocolate.kry202 ...

  3. 消除文法左递归-编译原理

    下面是消除左递归公式: 下面是对a,B的解释 下面举个例子: 1.把2式带入1式 然后消除左递归: 注意:把1式带入2式也可以.

  4. 大连理工大学软件学院编译原理第四次上机-----非递归语法分析

    题目描述 要求: 使用的文法如下: E->TE' E'->+TE'|ε T->FT' T'->*FT'|ε F->(E)|id 对于任意给定的输入串(词法记号流)进行语法 ...

  5. 编译原理 --- 语法分析概念,自上而下分析面临的问题以及如何消除左递归问题

    第一部分 --- 语法分析基本概念 1.上面这个箭头 --> 符号表示的意思是P被 α 定义 A是一个非终止符,γ是一个和α,β属于同一个集合的元素 1.一个双箭头符号表示的是直接推出,而一个双 ...

  6. 编译原理-提取左公因子---消除左递归(自己看)

    参考: https://blog.csdn.net/liyun123gx/article/details/19924993 https://blog.csdn.net/hxfghgh/article/ ...

  7. 《编译原理》实验报告——递归下降语法分析器的构建

    一.实验要求 运用递归下降法,针对给定的上下文无关文法,给出实验方案.预估实验中可能出现的问题. 二.实验方案 1.构造LL(1),通过设计.编制.调试递归下降语法分析程序,对输入的符号串进行分析匹配 ...

  8. 大学编译原理试卷考试题

    <编译原理>期末试题(一) 一.是非题(请在括号内,正确的划√,错误的划×)(每个2分,共20分) 1.编译程序是对高级语言程序的解释执行.(× ) 2.一个有限状态自动机中,有且仅有一个 ...

  9. 编译原理_P1004

    龙书相关知识点总结 //*************************引论***********************************// 1. 编译器(compiler):从一中语言( ...

最新文章

  1. 电源纹波分析及测试方法
  2. 【springboot+easypoi】一行代码搞定excel导入导出
  3. 路由复用器--gorilla/mux
  4. SkyEye图形化界面使用技巧篇(一)
  5. 单线程无阻塞IO模型在Node.js中的工作方式
  6. Spring事务-1
  7. 公文中的六角括号怎么打?
  8. 智慧农业项目建设体系之质量追溯平台建设体系
  9. Ubuntu离线安装curl
  10. iOS 仿微信发送语音消息按钮 - 语音录音机(二)
  11. 微信小程序 收起键盘 wx.hideKeyboard()
  12. 笔记本键盘两个ctrl键同时失灵怎么解决?
  13. 秋枫学习笔记-原创文章整理
  14. Django项目连接MongoDB的三种方法
  15. java模拟网易邮箱登录_使用服务端和客户端两种方法 模拟网易邮箱实现全选,全不选的功能...
  16. i5-10200h怎么样
  17. Android模仿今日头条搜索页
  18. WebSocket快速入门及基本使用
  19. Java国际化——ResourceBundle基本使用
  20. excel被删除的文件到哪里找?电脑excel删除了怎么恢复?

热门文章

  1. 谴责那些没有良知的人
  2. 神策杯 2018高校算法大师赛(个人、top2、top6)方案总结
  3. Explain执行计划key_len详解
  4. 深度学习寿命预测技术路线
  5. 异常问题解决方案经验总结
  6. 如何从一名“普通码农”成长为技术Leader?
  7. Outlook Express日常错误代码解析
  8. Unity集成穿山甲后打包报错android:networkSecurityConfig , Picked up JAVA_TOOL_OPTIONS:-Dfile.encoding=UTF-8
  9. java.lang.String cannot be cast to com.rock.bpo.agent.base.LoginUser
  10. 经济学day01 微观经济学和宏观经济学