1. 实验目的

构造 LR(1)分析程序,利用它进行语法分析,判断给出的符号串是否为该文法识别的句子,了解 LR(K)分析方法是严格的从左向右扫描,和自底向上的语法分析方法。

2. 实验内容

对下列文法,用 LR(1)分析法对任意输入的符号串进行分析:

(1)E-> E+T

(2)E->T

(3)T-> T*F

(4)T->F

(5)F-> (E)

(6)F-> i

3. LR(1)分析法设计思想及算法

本LR(1)分析器能够实现完整的LR(1)分析,在GUI界面中显示读取的当前文法、分析得到的First集、所有的项目集规范族、规范LR分析表以及分析过程步骤表。首先通过GUI中的“打开文件”按钮读取文法文件,“显示文法”按钮将文法显示在GUI上,并将文法保存在相应的数据结构中,最后一键“开始LR(1)分析”按钮自动执行相应,完成分析。本分析器分析过程如下:

  1. 根据文法第一个识别到的非终结符(视为开始符号,假设为 S),增加产生式项目“S’->·S,#”;
  2. 利用“S’->·S,#”为I0,求Closure(I0),获得I0;
  3. 利用Closure(I0),计算I0 中所有非终结符的展望符,遍历所有的X(X指所有I0中“·”后的文法符号),利用GOTO(I0,X),产生新的状态集Ix;随后计算新产生的状态
  4. 遍历每一个 Ix,利用 GOTO(Ix,X),并计算非终结符的展望符,完成完整的 LR(1)项目集族构建;
  5. 根据完整的LR(1)项目集族,构建项目分析表;
  6. 获取分析表后,启动分析程序,结合分析表对读入的输入串进行语句分析,并以表格的形式将分析表、分析过程显示到GUI相应位置;

图1 LR分析器结构

图2 LR(1)算 法流程图

3.1 主程序设计基本思路

     1.LR(1)分析法的主要思想

  1. 进行最左归约(识别句柄并归约它)
  2. 将识别句柄的过程划分为由若干状态控制,每个状态控制识别出句柄的一个符号
  3. 分析栈:存放已识别的文法符号和状态,描述分析过程中的历史和展望信息
  4. 由一个总控程序来控制整个识别过程

2.LR(1)分析法的构造方法

    (1) 构造LR(1)项目集I的闭包函数 CLOSURE(I)

            a)I的任何项目都属于 CLOSURE(I);

b) 若项目(A•B,a)属于CLOSURE(I),B是一个产生式,则对于F IRST(a)中的每个终结符b,如果(B•,b)原来不在CLOSURE(I)中,则把它加进去;

c) 重复步骤 b)直到CLOSURE(I)不再扩大为止。

    (2) 构造GO函数

令I是一个LR(1)项目集,X是一个文法符号,函数GO(I,X)定义为: GOTO(I,X)=CLOSURE(J),其中J={(AX•,a)|(A•X,a)I}。(在执行GOTO时,搜索符并不改变)

    (3) 构造项目集族(伪代码表示)

{ C={CLOSURE({(S’• S,#)})};

DO{FOR C 中的每个项目集I和每个文法符号X

IF GO(I,X) 非空且不属于 C

把 GO(I,X) 加入 C 中;

}WHILE C 依然扩大;

}

     (4) LR(1)分析表的构造

假定LR(1)项目集规范族C={I0, I1, …… ,In},令每个项目集Ik的下标 k为分析器的一个状态,G’的LR(1)分析表含有状态0,1,…… ,n。

令那个含有项目[S’→·S,#]的Ik的下标k为状态0(初态),ACTION 表和GOTO表可按如下方法构造:

  1. 若项目[A→α·,b]属于Ik,那么置ACTION[k,b]为“用产生式 A →α进行规约”,简记为rj(假定A→α为文法G’的第j个产生式)。
  2. 若项目[A→α·aβ,b]属于Ik且GO(Ik, a)= Ij,则置ACTION[k,a]为 “把状态j和符号a移进栈”,简记为 sj 。
  3. 若项目[S’→S·,#]属于Ik,则置ACTION[k, #]为“接受”,简记为 acc。
  4. 若GOTO(Ik, A)= Ij,A为非终结符,则置 GOTO(k, A)=j,分析表中凡不能用规则1至5填入信息的空白格均置上“出错标志 ”(空)。

按上述算法构造的含有ACTION和GOTO两部分的分析表,如果每个入口不含多重定义,则称它为文法G的一张规范的LR(1)分析表。

3.2 主要变量和函数定义

使用Eclipse的UML插件AmaterasUML直接列出相关变量以及函数如下:

下面对部分关键函数进行说明:
public void Get_First()  // 求出单个非终结符FIRST集合函数
// 寻找First集合元素的子过程(对产生式右部的所有符号)
public void KeFind_First(char s[], int j, int i)public void First_Finally()//优化当前求得的first集合(去掉重复符号)
public String Get_First_String(String str)//求字符串的First集//返回最小为空的下标函数(针对二维数组)public int index_Null(String temp[][], int m)//求解项目集函数public String Get_Projects(String temp, char c)//获得该产生式FIRST集(展望)函数(针对二维数组)public String Get_RightFir(String temp[][], int a, int b)//获得该产生式FIRST集(展望)函数(针对一维数组)public String Get_RightFir(String str[], int i)//求解CLOSURE函数public void CLOSURE(int a) public void CLOSURE1(String str[])//截取到小圆点后一位符号的字符串函数public String Get_Behind(String temp[][], int i)//求新的项目集public String[] Get_New(String temp[][], int i, char X)public int GOTO(int a, char X) //构建GOTO函数public void main_CloGo()  //求解CLOSURE和GOTO的主函数public void Get_Action()  //构建action表函数

3.3 关键数据结构

private char VN[] = new char[999];  //定义终结符数组
private char VT[] = new char[999];  //定义非终结符数组
private char Total[] = new char[999];  //终结符+非终结符集合
private String temp_Str[] = new String[999];  //临时字符串数组(用来临时存放文法产生式等等)
private String[] First = new String[VN.length];  //单个非终结符的first集
private String Projects[][] = new String[999][999];  //项目集合
private String ACtion[][] = new String[999][VT.length];  //存放ACtion的数组
private int temp_Goto[][] = new int[999][999];  //临时存放GOTO表,便于操作
private int Goto[][] = new int[999][Total.length];  //存放GOTO表
private int Flag = 6666;  // 一个判断字符是否属于某个数组的判断标志
private boolean Visited[] = new boolean[999];  //定义布尔型访问数组

3.4  源程序关键代码(Java实现)

(完整源程序位于见 LR1.java )//求解项目集函数public String Get_Projects(String temp, char c) {StringBuilder strBuf = new StringBuilder();  //实例化一个StringBuilder对象,便于对字符串进行操作int j;for(j = 0; j < 3; j++) {strBuf.append(temp.charAt(j));  //产生式左部和箭头添加进strBuf中}strBuf.append('·');  //给产生式右部添加小圆点//跳过产生式左部及箭头,对右部进行遍历for(j = 3; j < temp.length(); j++) {strBuf.append(temp.charAt(j));  //产生式右部符号添加进strBuf中去}strBuf.append(',');  //逗号隔开项目与展望strBuf.append(c);  //添加上该项目的展望return strBuf.toString();  //返回该项目字符串}//获得该产生式FIRST集(展望)函数(针对二维数组)public String Get_RightFir(String temp[][], int a, int b) {int j = 3;  //从产生式右部开始StringBuilder strB = new StringBuilder();  //实例化StringBuilder对象//跳过小圆点while(temp[a][b].charAt(j) != '·' && j < temp[a][b].length()) {j++;}j++;  //加1,j指向小圆点后的第一个字符//如果下一个字符是非终极符的话if(IsInVn(temp[a][b].charAt(j)) != Flag && j < temp[a][b].length()) {j++;  //看该字符的下一个字符(因为要求该字符的First集)//循环遍历while(temp[a][b].charAt(j) != ',' && j < temp[a][b].length()) {strB.append(temp[a][b].charAt(j));  j++;}j++;  //跳过逗号strB.append(temp[a][b].charAt(j));  //加入展望} else {if(temp[a][b].charAt(j) == ',') {//下一个字符是逗号的话j++;  //指向展望下标strB.append(temp[a][b].charAt(j));  //添加}}return Get_First_String(strB.toString());  //返回}//获得该产生式FIRST集(展望)函数(针对一维数组)public String Get_RightFir(String str[], int i) {int j = 3;  //老样子,跳过产生式左部和箭头符号从产生式右部开始StringBuilder strB = new StringBuilder();  //实例化StringBuilder对象//遍历循环,最终实现j指向当前项目的小圆点位置while(str[i].charAt(j) != '·' && j < str[i].length()) {j++;}j++;j++;while(str[i].charAt(j) != ',' && j < str[i].length()) {strB.append(str[i].charAt(j));j++;}j++;strB.append(str[i].charAt(j));return Get_First_String(strB.toString());}//求解CLOSURE函数public void CLOSURE(int a) {//a为对应CLOSURE闭包序号int i;int j, k, m;int h = 0;/** Projects二维数组中的项目格式形如 :S->A·Bb,展望*/for(i = 0; i < Projects[a].length; i++) {//如果当前对应项目的数组不为空的话if(Projects[a][i] != null) {j = 3;  //跳过产生式左部和箭头符号while(Projects[a][i].charAt(j) != '·' && j < Projects[a][i].length()) {//循环遍历使j指向项目中小圆点的位置j++;}j++;  //指向小圆点的下一个符号if(IsInVn(Projects[a][i].charAt(j)) != Flag) {//如果该符号是非终极符的话for(k = 0; k < temp_Str.length; k++) {//遍历文法中所有产生式if(temp_Str[k] != null) {//如果当前产生式不为空的话if(temp_Str[k].charAt(0) == Projects[a][i].charAt(j)) {//如果当前文法的某个产生式左部String temp = Get_RightFir(Projects, a, i);for(m = 0; m < temp.length(); m++) {if(IsInI(Get_Projects(temp_Str[k], temp.charAt(m)), Projects, a) == false) {h = index_Null(Projects, a);  //定位到Projects[a][h] = Get_Projects(temp_Str[k], temp.charAt(m));}}}}}}}}}//求解CLOSURE函数(针对字符串)public void CLOSURE1(String str[]) {int i;int j, k, m;int h = 0;//同样,str字符串格式形如:S->A·Bb,展望for(i = 0; i < str.length; i++) {if(str[i] != null) {//如果字符串非空j = 3;  //j指向项目左部          while(str[i].charAt(j) != '·' && j < str[i].length()) {//遍历循环,直至j指向小圆点j++;}j++;  //指向小圆点的后一位符号//下面参照教材P115,使用求解LR(1)闭包的步骤进行求解if(j < str[i].length()) {if(IsInVn(str[i].charAt(j)) != Flag) {//当前j指向的符号是非终结符for(k = 0; k < temp_Str.length; k++) {if(temp_Str[k] != null) {//文法中k指向的产生式不为空的话if(temp_Str[k].charAt(0) == str[i].charAt(j)) {//并且该产生式就是j指向的非终结符的话(步骤2)String temp = Get_RightFir(str,i);  //得到当前的First集合for(m = 0; m < temp.length(); m++) {if(IsInI(Get_Projects(temp_Str[k], temp.charAt(m)), str) == false) {h = index_Null1(str);str[h] = Get_Projects(temp_Str[k],temp.charAt(m));}}}}}}}}}}//求新的项目集public String[] Get_New(String temp[][], int i, char X) {String tem[] = new String[999];int j = 0, f = 0;int k;StringBuilder strB = new StringBuilder();  //实例化StringBuilder对象while(j < temp[i].length) {//循环生成每个项目集的所有项目if(temp[i][j] != null) {//如果当前项目不为空k = 0;while(temp[i][j].charAt(k) != '·' && k < temp[i][j].length()) {//循环遍历直到k指向小圆点strB.append(temp[i][j].charAt(k));  //小圆点前的符号通通装进strB中k++;}k++;  //指向小圆点后一个符号if(k < temp[i][j].length() && temp[i][j].charAt(k) == X) {strB.append(temp[i][j].charAt(k));  //将该符号加入进strB中strB.append('·');  //小圆点进入(相当于小圆点后移一位)k++;  //当前k指向小圆点while(k < temp[i][j].length()) {//循环遍历,将原来后面的所有符号(包括展望)全部接在strB后面strB.append(temp[i][j].charAt(k));k++;}tem[f] = strB.toString();  //一个新的项目成功生成f++;  //加1,下一个strB.replace(0, strB.length(), "");  //清空} else {strB.replace(0, strB.length(), "");  //清空}}j++;}return tem;}

3.5  效果图

3.6 完整代码

https://download.csdn.net/download/H17Q0818/12158117

编译原理 实验三 LR(1)分析法 Java相关推荐

  1. 编译原理实验三 LR(1)分析法

    实验三 LR(1)分析法 构造 LR(1)分析程序,利用它进行语法分析,判断给出的符号串是否为该文 法识别的句子,了解 LR(K)分析方法是严格的从左向右扫描,和自底向上的 语法分析方法. 二.实验内 ...

  2. java编程实现算符优先分析法,编译原理实验三-算符优先分析法

    编译原理实验3-算符优先分析法 #include #include #include #include #define SIZE 128 char priority[6][6]; //算符优先关系表数 ...

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

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

  4. 编译原理 实验二 LL(1)分析法程序实现

    源代码仓库:CompilePrincipleLearning/experiment_2 · yusixian/CompilePrincipleLearning (github.com) 一. 实验目的 ...

  5. 合工大 编译原理 实验三

    合工大 编译原理 实验三 LR(1) 分析法 本项目使用c++实现,利用Windows API制作了简易的UI界面. 具体功能如下: 支持查看文法,项目族,LR(1) 分析表,句子归约过程. 可使用包 ...

  6. 编译原理-实验四-LR(0)语法分析程序的设计

    一.实验目的 了解LR(0)语法分析算法的基本思想,掌握LR(0)语法分析程序的构造方法. 二.实验内容 根据LR(0)语法分析算法的基本思想,设计一个对给定文法进行LR(0)语法分析的程序,并用C. ...

  7. 编译原理实验三 语义分析程序设计与实现

    一.实验目的 在实现词法.语法分析程序的基础上,编写相应的语义子程序,进行语义处理,加深对语法制导翻译原理的理解,进一步掌握将语法分析所识别的语法范畴变换为某种中间代码(四元式)的语义分析方法,并完成 ...

  8. 【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)

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

  9. 编译原理之语法分析(预测分析法)

    编译器之语法分析 自顶向下 上下文无关文法 语法树 NFA→CFG 预测分析法 改写CFG 原因 消除二义性 消除左递归 消除左公因子 消除空产生式 消除回路 自顶向下 上下文无关文法 CFG本质上就 ...

最新文章

  1. python一个小程序:猜数字
  2. 25 uname-用于显示系统信息
  3. 趣头条将获得阿里1.71亿美元的可转债,为期三年...
  4. 全球及中国洗发护发市场品牌营销调研与投资竞争策略研究报告2022版
  5. c# 获取excel单元格公式结果_excel公式应用技巧:文字和数字混合的单元格,如何求和?...
  6. Redis 缓存数据库
  7. lzg_ad:XPE操作系统镜像尺寸优化
  8. win8能开发android的sdk么,Win8下Android SDK安装与环境变量配置教程
  9. c语言手游常用代码,c语言源代码【操作流程】
  10. 作为刚開始学习的人应该怎样来学习FPGA
  11. overflow c语言_C语言表结构(一)
  12. Python学习-9.Python函数定义
  13. ijkplayer框架深入剖析
  14. python ssd目标检测_基于自注意力的SSD图像目标检测算法
  15. NSX-T业务转发04—— 多层Tier0Tier1路由
  16. 天呐!java兼职接单
  17. 2014 android 新技术,向友商学习 Android 12新功能前瞻:似曾相识
  18. 基于C++的《元素战争》基于win32框架的电脑游戏设计
  19. 关于DMA,TCM和Cache
  20. GitHub网站的主题设置

热门文章

  1. 严重: Null component Catalina:type=JspMonitor,name=jsp,WebModule=//localhost
  2. Dalvik虚拟机探析
  3. 8.Spring学习笔记_使用外部属性文件(by尚硅谷_佟刚)
  4. 输入关键字生成对联_对联生成器
  5. MySQL增加新的分区
  6. 机器学习数学基础九:回归分析
  7. 遅くまで起きる vs 遅くまで寝る
  8. Visual Studio 2010带来的新机遇、新特性和新动力
  9. SAP-MM知识精解-STO公司内的库存转储订单(01)-不带交货单配置及操作
  10. aix系统挂载nas