LR(0)分析表的构建

一、实验要求

构建LR(0)分析表
例:
G[E]:
E->aA
E->bB
A->cA
A->d
B->cB
B->d

二、实验原理

在网上找了好久,发现大多数都是概念和复杂公式,
因此为了使大家能够更好的理解LR(0)分析表的构建,我准备举一个例子进行详细解析,一步步进行分析,直到最后画出分析表。
文法:
E->aA
E->bB
A->cA
A->d
B->cB
B->d

  1. 给初始符再构建一个初始符E’->E(I0)
  2. 为该初始符加上 ...,变成E’->.E
  3. 查看小圆点后字符是否为非终结符,是,将左部为该非终结符的文法加入其中,并为其添加小圆点;反之进行下一步。(I0变成E’->.E E->.aA E->.bB)
  4. 对I项目中的文法进行分析,若小圆点在最后,则不做处理;若小圆点不在最后,则将根据每个文法的小圆点后的字符来开辟出新的I项目,该文法为新项目的首句文法(I0的文法进行开辟,I0不变,生成I1、I2和I3,I1:E’>.E I2:E->.aA I3:E->.bB),然后将新开辟项目的文法小圆点向后移一位(I1:E’>E. I2:E->a.A I3:E->b.B)。注:若开辟的项目和重复,则不进行开辟,再次利用已存在的项目)
  5. 对开辟的项目重复3-4步骤,直到不再有项目开辟
    具体结果请见下图

6.然后根据上图,构建出相应的分析表

三、算法设计

  1. 设计字符串存储文法,采用结构体存储生成的项目以及项目的去向
  2. 为项目0赋予新的初始符,并添加小圆点
  3. 判断小圆点后的字符:
    若为终结符,进行下一步;若为非终结符,将该非终结符为左部的文法加入该项目当中,并为其添加小圆点。
  4. 判断小圆点位置:
    若在该句文法最后,则进行下一步;反之,开辟新的项目,该句文法为新项目的首句文法,并将新项目文法的小圆点后移一位
    注:若开辟的项目中文法和已有的项目重复,则该文法不进行开辟,再次利用已存在的项目。
  5. 重复执行3-4,直到不再有新的空间开辟出来。

四、模块设计

  1. 变量定义
char str[10][10];//存储文法
int n;//存储文法行数
int v=0;//存储生成项的个数
char r[20][10];//存储分析表的float值
int rr[20][10];//存储分析表的int值
struct DFA{int num;//记录生成项的序号 int  c[10];//存储该项目各个文法的去向(子代) int f;//记录该项目的来源(父代) char ss[10][10];//存储该项的文法 int count;//记录该项的文法行数 int l;//存储第一行字符串'.'的位置
}LR[20];
  1. 输入模块
    从键盘输入上下文无关文法,存储到char类型的字符串数组当中
//初始化,输入上下文无关文法,以#号结尾
void Init(){int i,j;cout<<"输入上下文无关文法:"<<endl;for(i=1;;i++){cin>>str[i];if(str[i][0]=='#')break;}strcat(str[0],"Z->");str[0][3]=str[1][0]; n=i+1;
}

我在源代码里还附录了一个输入模块的代码,因为那个是直接将文法输入到字符串中,这样就可减少在进行测试时重复输入文法的时间。
即:

//在一开始编程时可先给定文法,避免每次测试重复输入
void input(){int i,j;strcat(str[0],"Z->E");strcat(str[1],"E->aA");strcat(str[2],"E->bB");strcat(str[3],"A->cA");strcat(str[4],"A->d");strcat(str[5],"B->cB");strcat(str[6],"B->d");n=7;for(i=0;i<n;i++)cout<<str[i]<<endl;cout<<"***************************"<<endl;
}
  1. LR(0)规范集构建模块
    进行算法分析中第2、3、4三步,记住几个关键点,小圆点位置、小圆点后的字符、项目的开辟(递归)
    当中为使测试方便,调用了几个子函数
    以下只附录子函数代码,具体请看最后源代码
    设置小圆点位置

  2. 输出模块
    将生成的项目以分析表的形式得出,因水品有限,采用的是cout结合二维数组进行输出,代码较多,主要是为了使得输出的格式更加的整齐。
    这部分就不重复贴出代码,大家请从源代码里查看。

  • 设置小圆点位置
//为文法添加小数点
void AddD(int a,int b){//为LR[a]的ss第b行字符串添加'.' int i,j=0;char c[10];for(i=0;i<3;i++)c[j++]=LR[a].ss[b][i];c[3]='.';j++;for(i=3;i<strlen(LR[a].ss[b]);i++)c[j++]=LR[a].ss[b][i];memset(LR[a].ss[b],0,sizeof(LR[a].ss[b]));for(i=0;i<j;i++)LR[a].ss[b][i]=c[i];
}
  • 添加移进项目的文法
//添加移进项目的文法
void AddG(int a,char s){//把str文法中左部非终结符与s相同的文法,添加进LR[a]中 int i,j,k;for(i=0;i<n;i++){if(str[i][0]==s){for(j=0;j<strlen(str[i]);j++)LR[a].ss[LR[a].count][j]=str[i][j];AddD(a,LR[a].count); LR[a].count++;}}
}
  • 小圆点后移一位
//将文法小圆点向后移一位
void add(int a,int j){int i,k;char temp;for(i=0;i<strlen(LR[a].ss[j]);i++){LR[v].ss[0][i]=LR[a].ss[j][i];if(LR[a].ss[j][i]=='.')k=i;//记录小圆点的位置 }LR[a].c[j]=v;//记录该条文法的跳转(子)项数 LR[v].f=a;//记录跳转项数的来源(父代) LR[v].count++;temp=LR[v].ss[0][k+1];//小圆点向后移一位 LR[v].ss[0][k+1]=LR[v].ss[0][k];LR[v].ss[0][k]=temp;LR[v].l=k+1;//记录小圆点的位置
}
  • 更新项目的上一项(父代)和下一项(子代)
//更新某一项的来源(父代)和每一句文法的跳转项(子代)
void fix(int a,int b){int i,j,k;k=LR[b].f;//父代 for(i=0;i<LR[k].count;i++)if(LR[k].c[i]==b)LR[k].c[i]=a;//子代
}
  • 删除相同的项目
//比较,删除相同的内容
void Compare(int a){int i,j,k;for(i=0;i<a;i++){for(j=i+1;j<=a;j++){if(strcmp(LR[i].ss[0],LR[j].ss[0])==0){//若相同 fix(i,j);memset(LR[j].ss,0,sizeof(LR[j].ss));//靠后的文法内容清空 LR[j].count=0;}}}
}
  1. 主函数
//主函数
int main(){Init();;//输入上下文无关文法 //input();//输入上下文无关文法 gfj();//规范集的构建 Output();//输出分析表
}

5、结果展示

  1. 首先编译运行
  2. 然后输入上下文无关文法,以#键结尾

3.然后回车获得结果


6、源代码

源代码已经过编译,可直接使用。代码都有详细的注释,大家碰到任何问题都可以留言,一起讨论。

#include<iostream>
#include<iomanip>
#include<string.h>
using namespace std;char str[10][10];//存储文法
int n;//存储文法行数
int v=0;//存储生成项的个数
char r[20][10];//存储分析表的float值
int rr[20][10];//存储分析表的int值
struct DFA{int num;//记录生成项的序号 int  c[10];//存储该项的上一项位置 int f;//记录 char ss[10][10];//存储该项的文法 int count;//记录该项的文法行数 int l;//存储第一行字符串'.'的位置
}LR[20];//初始化,输入上下文无关文法,以#号结尾
void Init(){int i,j;cout<<"输入上下文无关文法:"<<endl;for(i=1;;i++){cin>>str[i];if(str[i][0]=='#')break;}strcat(str[0],"Z->");str[0][3]=str[1][0]; n=i+1;
}//在一开始编程时可先给定文法,避免每次测试重复输入
void input(){int i,j;strcat(str[0],"Z->E");strcat(str[1],"E->aA");strcat(str[2],"E->bB");strcat(str[3],"A->cA");strcat(str[4],"A->d");strcat(str[5],"B->cB");strcat(str[6],"B->d");n=7;for(i=0;i<n;i++)cout<<str[i]<<endl;cout<<"***************************"<<endl;
}//为文法添加小数点
void AddD(int a,int b){//为LR[a]的ss第b行字符串添加'.' int i,j=0;char c[10];for(i=0;i<3;i++)c[j++]=LR[a].ss[b][i];c[3]='.';j++;for(i=3;i<strlen(LR[a].ss[b]);i++)c[j++]=LR[a].ss[b][i];memset(LR[a].ss[b],0,sizeof(LR[a].ss[b]));for(i=0;i<j;i++)LR[a].ss[b][i]=c[i];
}//添加移进项目的文法
void AddG(int a,char s){//把str文法中左部非终结符与s相同的文法,添加进LR[a]中 int i,j,k;for(i=0;i<n;i++){if(str[i][0]==s){for(j=0;j<strlen(str[i]);j++)LR[a].ss[LR[a].count][j]=str[i][j];AddD(a,LR[a].count); LR[a].count++;}}
}//将文法小圆点向后移一位
void add(int a,int j){int i,k;char temp;for(i=0;i<strlen(LR[a].ss[j]);i++){LR[v].ss[0][i]=LR[a].ss[j][i];if(LR[a].ss[j][i]=='.')k=i;//记录小圆点的位置 }LR[a].c[j]=v;//记录该条文法的跳转(子)项数 LR[v].f=a;//记录跳转项数的来源(父代) LR[v].count++;temp=LR[v].ss[0][k+1];//小圆点向后移一位 LR[v].ss[0][k+1]=LR[v].ss[0][k];LR[v].ss[0][k]=temp;LR[v].l=k+1;//记录小圆点的位置
}//更新某一项的来源(父代)和每一句文法的跳转项(子代)
void fix(int a,int b){int i,j,k;k=LR[b].f;//父代 for(i=0;i<LR[k].count;i++)if(LR[k].c[i]==b)LR[k].c[i]=a;//子代
}//比较,删除相同的内容
void Compare(int a){int i,j,k;for(i=0;i<a;i++){for(j=i+1;j<=a;j++){if(strcmp(LR[i].ss[0],LR[j].ss[0])==0){//若相同 fix(i,j);memset(LR[j].ss,0,sizeof(LR[j].ss));//靠后的文法内容清空 LR[j].count=0;}}}
}//LR(0)规范集的构建
void gfj(){int i,j,k,p;for(i=0;i<20;i++){LR[i].num=i;//Iiif(i==0){//第一个,赋予文法的初始符Z->E for(k=0;k<strlen(str[0]);k++)LR[0].ss[0][k]=str[0][k];AddD(0,LR[0].count);//添加小圆点 Z->.E LR[0].l=3;//小圆点的位置 LR[0].count++;//文法数目+1     if(LR[i].ss[0][4]>='A'&&LR[i].ss[0][4]<'Z') AddG(i,LR[i].ss[0][4]);//添加移进项目的文法E->aA E->bBv++;//为每一个文法进行向外扩散 I1、I2、I3 for(j=0;j<LR[0].count;j++){p=strlen(LR[0].ss[j]);if(LR[0].ss[j][p-1]!='.'){add(0,j);//将向外扩散的文法小圆点向后移一位 v++;}}}else{//除I0以外的生成项 p=strlen(LR[i].ss[0]);//Ii第一句文法的长度 if(LR[i].count==1&&LR[i].ss[0][p-1]=='.'){//若小圆点再最后则不进行处理 }else{p=LR[i].l;//小圆点的位置 if(LR[i].ss[0][p+1]>='A'&&LR[i].ss[0][p+1]<'Z')AddG(i,LR[i].ss[0][p+1]);//添加移进项目的文法//为每一个文法进行向外扩散 for(j=0;j<LR[i].count;j++){add(i,j);//将待向外扩散的文法小圆点向后移一位 v++;}}Compare(i);//对所有已生成的项进行比较,若有重复的则删除i值靠后的 }}//将生成的项进行整理,补齐有缺的部分(删除重复的之后可能会有内容为空,从后面补齐) for(i=0;i<20;i++){for(j=i+1;j<20;j++){if(LR[i].count==0&&LR[j].count!=0&&LR[i].num<LR[j].num){fix(i,j);LR[i]=LR[j];LR[j].count=0;               }}}//对当前的生成项进行编号 for(i=0;LR[i].count!=0;)i++;v=i;//输出当前生成项及各项的文法 cout<<"各项的序号和文法内容:"<<endl;for(i=0;i<v;i++){cout<<i<<":"<<endl;for(j=0;j<LR[i].count;j++)cout<<LR[i].ss[j]<<endl;cout<<endl;}
} //分析表根据第一行进行匹配处理
void match(int a,int b){int i,j;char k;if(b==0&&LR[a].count==1&&strlen(LR[a].ss[b])-1==LR[a].l){//若是终结文法 (即小圆点在最后) if(LR[a].ss[b][0]=='Z')//且是初始符终结 strcpy(r[a+1],"acc");elsefor(i=0;i<5;i++)//Action都输出r r[a+1][i]='r';}else{//不是终结文法,k储存小圆点后一个字符,进行比较判断 if(b==0)k=LR[a].ss[b][LR[a].l+1];elsek=LR[a].ss[b][4];for(i=0;i<strlen(r[0]);i++){if(r[0][i]==k)rr[a+1][i]=LR[a].c[b];}}
}//进行分析表的构建和输出
void Output(){int i,j,k,temp;strcpy(r[0],"abcd#EAB");//分析表的第一行 cout<<"LR0分析表:"<<endl;cout<<setw(20)<<"Action"<<setw(18)<<"Goto"<<endl; //分析表根据第一行进行匹配 for(i=0;i<v;i++){for(j=0;j<LR[i].count;j++)match(i,j);//进行匹配处理 }//进行分析表输出形式的处理 for(i=0;i<v+1;i++){temp=0;//保证输出对其 if(i==0)cout<<"   ";if(i>0&&i<=10)cout<<" "<<i-1<<":";if(i>10)cout<<i-1<<":";if(strcmp(r[i],"acc")==0)cout<<setw(24);//先输出Action部分 for(j=0;j<5;j++){if(r[i][j]=='r'){//输出终结文法 cout<<setw(4)<<r[i][j]<<k;temp=1;}else if(strcmp(r[i],"acc")==0)//输出起始符的终结欸文法 cout<<r[i][j];else{ if(i==0)cout<<setw(5)<<r[i][j];else if(rr[i][j]==0)cout<<setw(5)<<" ";elsecout<<setw(4)<<"S"<<rr[i][j];}}if(temp==1)k++;//进行对r1、r2、r3等下标的处理 //再输出Goto部分 for(j=5;j<strlen(r[0]);j++){if(i==0)cout<<setw(5)<<r[i][j];else if(rr[i][j]==0)cout<<setw(5)<<" ";elsecout<<setw(5)<<rr[i][j];}cout<<endl;}
}//主函数
int main(){int i,j;Init();//input();//输入上下文无关文法 gfj();//规范集的构建 //Excels();Output();//输出分析表
}

LR(0)分析表的构建相关推荐

  1. 【编译原理笔记06】语法分析,移入-归约分析:自底向上的分析,LR(0)分析法,LR(0)分析表的构建(基于自动机)

    本次笔记内容: 4-8 自底向上的分析概述 4-9 LR分析法概述 4-10 LR0分析 4-11 LR0分析表构造 本节课幻灯片,见于我的 GitHub 仓库:第6讲 语法分析_3.pdf 本节课介 ...

  2. LR(0)项目集规范族的构造及LR(0)分析表的构造

    求出文法的所有项目,按一定规则构造识别活前缀的NFA, 再确定化为DFA确定化的工作量较大,而且容易出错,实际应用中并不使用,这里介绍的目的仅仅是为了便于理解.具体见识别活前缀的有限自动机构建方法_用 ...

  3. 【编译原理】 LR(0) 分析表

    LR(0) 步骤 1. 写成 拓广文法 (S'→ S) 2. 活前缀的 DFA(靠近.后面的非终结符) 看状态Ii里面,是不是只存在一个规约项目.如果有两个规约项目,就是规约--规约冲突:如果同时有规 ...

  4. 编译原理 实验四 LR(0)分析法(LR0分析表的自动生成)

    写在前面 由于代码较长,csdn对文章总长度有字数限制,想只看完整代码的请移步另一篇博客. https://blog.csdn.net/qq_46640863/article/details/1257 ...

  5. LR(0)分析和SLR

    ①栈顶出现句柄时规约,否则移入. ②移入项目.代约项目.归约项目 1.引入开始符,得到增广文法,并且给每个式子编号.(即如果文法开头不是一个非终结符到另一个非终结符的产生式则需要加入,如加入 S'-& ...

  6. 【编译原理】LR(0)分析方法(c++实现)

    基本流程 Created with Raphaël 2.2.0 输入文法 拓广文法 求项目集规范族 GO[I,a]转移函数 构造DFA (识别活前缀的自动机) LR(0)分析表 LR(0)分析输入串 ...

  7. 编译原理学习笔记(七)~LR(0)分析

    LR(0) 项目 解释:右部某位置标有圆点的产生式称为相应文法的一个LR(0)项目(简称为项目) 举例说明:S–>bBB则可以推导出4个项目 注: 项目描述了句柄识别的状态 产生式A→ε 只生成 ...

  8. 【LR(0)分析】LR(0)算法的分析与设计

    LR(0)算法的模拟实现 一.实验目的 通过设计.编写和构造LR(0)项目集规范簇(识别文法全部活前缀的DFA)和LR 分析表.对给定的符号串进行LR 分析的程序,了解构造LR(0)分析表的步骤,对文 ...

  9. LR(1)分析表-语法树-四元式

    这学期的编译原理终于学完了,不愧是号称最难的科目.要用C++从头到尾实现一下小型编译器,还真不容易啊.不过总算是做完了. 首先上文法,这个文法是根据上一篇博客简化的,但还是有一点问题的,暂时发现有一个 ...

最新文章

  1. apollo mqtt linux qt,MQTT第5版更新,以及如何应用到Qt MQTT模块中
  2. 并发编程-17AQS同步组件之 Semaphore 控制并发线程数的信号量
  3. 移动前端开发基础与优化
  4. Tiny之Web工程构建
  5. 在Arcmap中加载互联网地图资源的4种方法
  6. MyBatis自学(1):MyBatis概述
  7. hbase入门综合概要介绍
  8. zabbix分布式监控部署proxy安装
  9. SQL必知必会知识点总结
  10. matlab-高数 反、双曲、正、余弦、正切函数
  11. SVN重定向svn switch
  12. 数据传输完整性_数据集成:什么是数据完整性?
  13. 服务器主板双cpu性能好吗,双CPU的电脑用起来,性能和功耗都是原来的两倍?
  14. ubuntu 14.04.5 firefox 浏览器flash插件安装
  15. openssl 命令行加密解密
  16. 如何在VI中使用小键盘上的数字键
  17. AS问题解决 Not recognizing known sRGB profile
  18. Web项目实现前端锁屏功能
  19. serial.serialutil.SerialException: could not open port 'COM1': PermissionError(13, '拒绝访问。', None, 5)
  20. ie visio 打开_Visio viewer 不能从IE打开vsd文件(转) | 学步园

热门文章

  1. 解决Charles 30分钟关闭的问题 - 软件使用篇
  2. 头条广告算法实习生面试总结
  3. 流程圣经:流程绩效管理
  4. 一文看懂TVS二极管SM8S30A
  5. python 图片识别服装_Python与服装图像3
  6. 淘宝内容营销的时代到来,卖家们该如何决策?
  7. linux eclipse cuda,CUDA Linux eclipse intel Mosix
  8. 从内存中加载并运行exe
  9. 游戏回归娱乐本质 中型休闲游戏可能崛起
  10. 树莓派:迅雷远程下载