当前位置: 首页 > news >正文

编译原理实验五:LR语法分析器的控制程序

文章目录

  • 实验五 LR语法分析器的控制程序
    • 一、实验内容
    • 二、实验目的
    • 三、实验要求
    • 四、相关说明
      • (一)SLR(1)分析表构造示例
      • (二)LR语法分析器分析过程示例
      • (三)C语言实现LR语法分析器总控程序
      • (四)另一个文法分析示例
    • 五、参考代码
    • 六、操作步骤
    • 七,运行结果
      • (一)lex_r.txt文件
      • (二)par_r.txt文件


实验五 LR语法分析器的控制程序

一、实验内容

编程实现LR语法分析器的控制程序。

二、实验目的

1.掌握SLR(1)分析表的构造。
2.掌握LR语法分析器的分析过程,具体来讲就是控制程序是如何根据LR分析表来工作的。

三、实验要求

  1. 写好实验预习报告。
  2. 编写上机源程序和测试程序。
  3. 写出实验结果。
  4. 实验完后要上交实验报告 。

四、相关说明

(一)SLR(1)分析表构造示例

已知文法G[E]:
E→E+T|T
T→T*F|F
F→(E)|i

构造其SLR(1)分析表,步骤如下:

  1. 预备工作
    • 引入产生式S’→E,将文法拓广成G’[S’]。
    • 为产生式编号:
      S’→E
      E→E+T
      E→T
      T→T*F
      T→F
      F→(E)
      F→i
    • 构造文法的状态转换函数GO(I,X)和项目集规范族。
    • 计算非终结符号的follow集:
      follow(S’)={#}
      follow(E)={#,+, )}
      follow(T)={#,+, ),}
      follow(F)={#,+, ),
      }
  2. 构造分析表:得到的SLR(1)分析表如下:
+*i#ETF
000s40s50123
1s60000Acc000
2r2s70r20r2000
3r4r40r40r4000
400s40s50823
5r6r60r60r6000
600s40s50093
700s40s500010
8s6s60s1100000
9r1r1s7r10r1000
10r3r3r3r30r3000
11r5r5r5r50r5000

分析表不含多重定义,该分析表是SLR(1)分析表,相应文法为SLR(1)文法。SLR(1)分析法虽理论上不严格,但很实用且容易实现,能解决大部分语言的识别问题。若要更严格精确地向前看一个输入符号,需使用规范LR分析法(LR(1)分析法)。

(二)LR语法分析器分析过程示例

假设源程序为“ab+c”,经词法分析用单词种别表示为“ii+i#” 。利用上述SLR(1)分析表,手工模拟语法分析器的计算过程如下:

step状态栈符号栈输入符号动作
0)0#i*i+i#初始
1)05#i*i+i#移进
2)03#F*i+i#归约
3)02#T*i+i#归约
4)027#T*i+i#移进
5)0275#T*i+i#移进
6)02710#T*F+i#归约
7)02#T+i#归约
8)01#E+i#归约
9)016#E+i#移进
10)0165#E+i#移进
11)0163#E+F#归约
12)0169#E+T#归约
13)01#E#归约
Acc(接受)

分析过程中,栈中元素构成规范句型的活前缀,加上输入串余下部分为规范句型。一旦栈顶出现句柄,马上归约成产生式左部符号。控制程序算法如下:

  1. 移进:若M[Stop][t.code]=sj,说明句柄尚未形成,执行移进操作。将j移入状态栈,t.code移入符号栈,j成为新的栈顶状态Stop,读下一个单词。
  2. 归约:若M[Stop][t.code]=rk,说明句柄已出现在栈顶,用编号为K的产生式A→β进行归约。假设LEN(β)=r,M[Stop-r][A]=j。先将栈顶r个元素出栈,再将j和A分别移入状态栈和符号栈,j成为新的栈顶状态Stop,当前处理单词不变。
  3. 接受:M[Stop][t.code]=Acc,表示输入串是合法句子,程序终止运行。
  4. 出错:M[Stop][t.code]=空白,表示出错,最简单处理为程序终止运行。

(三)C语言实现LR语法分析器总控程序

以例1中的SLR(1)分析表为例,用C语言实现LR语法分析器的总控程序。为直观起见,产生式按原样储存。产生式的左部符号为p[i][0],产生式右部符号串长度为strlen(p[i]) - 3(ɛ产生式右部符号串长度为0单独计算)。将SLR(1)分析表数字化,si改用i表示,rj改用 - j表示,Acc用99表示,出错用0表示。总控程序源代码如下:

#include <fstream>
using namespace std;
#include <iostream>
#include <stdlib.h>
#include <string.h>struct code_val{char code; char val[20];
};// 产生式
const char *p[]={"S’→E", "E→E+T", "E→T", "T→T*F", "T→F", "F→(E)", "F→i"
};const char TNT[]="+*()i#ETF";// LR分析表数字化,列字符+*()i#ETF用数字012345678标识
const int M[][9]={{0, 0, 4, 0, 5, 0, 1, 2, 3},{6, 0, 0, 0, 0, 99},{-2, 7, 0, -2, 0, -2},{-4, -4, 0, -4, 0, -4},{0, 0, 4, 0, 5, 0, 8, 2, 3},{-6, -6, 0, -6, 0, -6},{0, 0, 4, 0, 5, 0, 0, 9, 3},{0, 0, 4, 0, 5, 0, 0, 0, 10},{6, 0, 0, 11},{-1, 7, 0, -1, 0, -1},{-3, -3, 0, -3, 0, -3},{-5, -5, 0, -5, 0, -5}
};int col(char); void main() {// 状态栈初值int state[50]={0}; // 符号栈初值char symbol[50]={'#'}; // 栈顶指针初值int top=0; // 语法分析结果输出至文件par_r.txtofstream coutf("par_r.txt"); // lex_r.txt存放词法分析结果,语法分析器从该文件输入数据ifstream cinf("lex_r.txt"); // 结构变量,存放单词二元式struct code_val t; // 读一个单词cinf >> t.code >> t.val; int action; int i,j=0; // 输出标题(并非必要)coutf << "step\t状态栈\t符号栈\t输入符号" << endl; do{// 输出step(并非必要)coutf << j++ << ") " << '\t'; for(i=0;i<=top;i++) coutf << state[i] << " "; coutf << '\t'; for(i=0;i<=top;i++) coutf << symbol[i]; // 输出当前输入符号(单词种别,并非必要)coutf << '\t' << t.code << endl; action = M[state[top]][col(t.code)]; // 移进if(action > 0 && action != 99){ state[++top]=action; symbol[top]=t.code; // 读下一个单词cinf >> t.code >> t.val; }// 归约else if(action < 0){ // ε产生式的右部符号串长度为0,无需退栈if(strcmp(p[-action]+3,"ε")){ // "→"为汉字,占二字节,故减3top = top-(strlen(p[-action])-3); }state[top + 1]=M[state[top]][col(p[-action][0])]; symbol[++top]=p[-action][0]; }// 接受else if(action == 99){ coutf << "\tAcc" << endl; break;}// 出错else{ coutf << "Err in main()>" << action << endl; break;}}while(1); 
}int col(char c) { for(int i=0;i<(int)strlen(TNT);i++){if(c == TNT[i]) return i; }cout << "Err in col char>" << c << endl; exit(0); 
}

假设源程序“a*b+c”的单词二元式存放于文件lex_r.txt中,控制程序处理结果保存在文件par_r.txt中,与手工计算结果一致。

程序中的常量p(产生式)、TNT(列字符)、M(分析表)与文法有关。当控制程序用于其他场合时,修改这些常量值即可,程序其余部分无需改动。

(四)另一个文法分析示例

考虑文法G[S’]:
① S’→S
② S→(S)
③ S→ε

分析步骤如下:

  1. 构造识别该文法的所有规范句型的活前缀的DFA。
  2. 对归约项目左部符号求FOLLOW集,FOLLOW(S)={),# }。
  3. 根据SLR(1)分析表的构造方法,由它的DFA得到分析表如下:
状态#S
0S2r2r21
1Acc
2S2r2r23
3S4
4r1r1

五、参考代码

#include <fstream>
using namespace std;
#include <iostream>
#include <stdlib.h>
#include <string.h>// 定义存储单词二元式的结构体
struct code_val {char code;char val[20];
};// 产生式
const char* p[] = {"S’→E", "E→E+T", "E→T", "T→T*F", "T→F", "F→(E)", "F→i"
};// LR分析表列的字符
const char TNT[] = "+*()i#ETF";// LR分析表数字化,列字符+*()i#ETF用数字012345678标识
const int M[][9] = {{0, 0, 4, 0, 5, 0, 1, 2, 3},{6, 0, 0, 0, 0, 99},{-2, 7, 0, -2, 0, -2},{-4, -4, 0, -4, 0, -4},{0, 0, 4, 0, 5, 0, 8, 2, 3},{-6, -6, 0, -6, 0, -6},{0, 0, 4, 0, 5, 0, 0, 9, 3},{0, 0, 4, 0, 5, 0, 0, 0, 10},{6, 0, 0, 11},{-1, 7, 0, -1, 0, -1},{-3, -3, 0, -3, 0, -3},{-5, -5, 0, -5, 0, -5}
};// 列定位函数,将字符+*()i#ETF分别转换为数字012345678
int col(char c) {for (int i = 0; i < (int)strlen(TNT); i++) {if (c == TNT[i]) return i;}cout << "Err in col char>" << c << endl;exit(0); // 终止程序运行
}int main() {// 状态栈初值int state[50] = { 0 };// 符号栈初值char symbol[50] = { '#' };// 栈顶指针初值int top = 0;// 语法分析结果输出至文件par_r.txtofstream coutf("par_r.txt");// lex_r.txt存放词法分析结果,语法分析器从该文件输入数据ifstream cinf("lex_r.txt");// 结构变量,存放单词二元式struct code_val t;// 读一个单词cinf >> t.code >> t.val;int action;// 输出时使用的计数器,并非必要int i, j = 0;// 输出标题,并非必要coutf << "step\t状态栈\t符号栈\t输入符号" << endl;do {// 输出step,并非必要coutf << j++ << ") " << '\t';for (i = 0; i <= top; i++) coutf << state[i] << " ";coutf << '\t';for (i = 0; i <= top; i++) coutf << symbol[i];coutf << '\t' << t.code << endl;action = M[state[top]][col(t.code)];// 移进if (action > 0 && action != 99) {state[++top] = action;symbol[top] = t.code;// 读下一个单词cinf >> t.code >> t.val;}// 归约else if (action < 0) {// ε产生式的右部符号串长度为0,无需退栈if (strcmp(p[-action] + 3, "ε")) {// "→"为汉字,占二字节,故减3top = top - (strlen(p[-action]) - 3);}state[top + 1] = M[state[top]][col(p[-action][0])];symbol[++top] = p[-action][0];}// 接受else if (action == 99) {coutf << "\tAcc" << endl;break;}// 出错else {coutf << "Err in main()>" << action << endl;break;}} while (1);return 0;
}

六、操作步骤

1,双击程序快捷图标启动程序(可使用其他c++集中环境的程序)。
在这里插入图片描述
2,单击【新建项目】。
在这里插入图片描述
3,选择【空项目】——输入项目名称【shiyan5】——单击【确认】按钮。
在这里插入图片描述
4,右击【源文件】——选择【添加】——单击【新建项】。
在这里插入图片描述
5,选择【C++文件】——输入项目文件名称【shiyan5.cpp】——单击【添加】按钮。
在这里插入图片描述
6,将上述的参考代码粘贴到shiyan5.cpp文件的内容中。
在这里插入图片描述

7,创建测试文件lex_r.txt,右击【资源文件】——选择【添加】——单击【新建项】。
在这里插入图片描述
8,这里不选择文件格式,直接输入完整的文件名【lex_r.txt】——单击【添加】按钮。
在这里插入图片描述
9,将下面的测试内容添加到文件lex_r.txt中。

i a
* nul
i b
+ nul
i c
# nul

10,添加完成后,保持文件。
在这里插入图片描述
11,创建输出结果的文件par_r.txt,右击【资源文件】——选择【添加】——单击【新建项】。
在这里插入图片描述
12,输入文件名【par_r.txt】——单击【添加】按钮。
在这里插入图片描述
13,创建完成即可,不要添加任何内容。
在这里插入图片描述
14,右击【shiyan5.cpp】文件——单击【编译】。
在这里插入图片描述
15,如下图编译成功。
在这里插入图片描述
16,单击绿色的【运行按钮】,运行项目。
在这里插入图片描述
17,par_r.txt文件输出的运行结果。
在这里插入图片描述

七,运行结果

(一)lex_r.txt文件

i a
* nul
i b
+ nul
i c
# nul

(二)par_r.txt文件

step	状态栈	符号栈	输入符号
0) 	0 	#	i
1) 	0 5 	#i	*
2) 	0 3 	#F	*
3) 	0 2 	#T	*
4) 	0 2 7 	#T*	i
5) 	0 2 7 5 	#T*i	+
6) 	0 2 7 10 	#T*F	+
7) 	0 2 	#T	+
8) 	0 1 	#E	+
9) 	0 1 6 	#E+	i
10) 	0 1 6 5 	#E+i	#
11) 	0 1 6 3 	#E+F	#
12) 	0 1 6 9 	#E+T	#
13) 	0 1 	#E	#Acc

相关文章:

  • BrepGen中的几何特征组装与文件保存详解 deepwiki occwl OCC包装库
  • 亲测有效!OGG 创建抽取进程报错 OGG-08241,如何解决?
  • gRPC开发指南:Visual Studio 2022 + Vcpkg + Windows全流程配置
  • 深入理解 Java 字节码操作码
  • Rust 数据结构:HashMap
  • 【沉浸式求职学习day42】【算法题:滑动窗口】
  • NVC++ 介绍与使用指南
  • LeetCode 33. 搜索旋转排序数组:二分查找的边界艺术
  • 计算机视觉与深度学习 | Matlab实现EMD-LSTM和LSTM时间序列预测对比(完整源码和数据)
  • PIC16F18877 ADC 代码
  • Reactor (epoll实现基础)
  • 木材价格动态定价实战指南:多算法模型与行业案例深度解析
  • 机器学习-人与机器生数据的区分模型测试 -数据筛选
  • hyper-v 虚拟机怎么克隆一台一样的虚拟机?
  • Python 在黎曼几何中的应用
  • 手机打电话时如何将通话对方的声音在手机上识别成文字
  • markdown 文档编辑软件 MarkText 使用教程
  • 板凳-------Mysql cookbook学习 (二)
  • spring cache使用指南
  • 西门子 Teamcenter13 Eclipse RCP 开发 1.3 工具栏 单选按钮
  • 广东缉捕1名象牙走私潜逃非洲“红通”逃犯
  • 101岁陕西省军区原司令员冀廷璧逝世,曾参加百团大战
  • 体坛联播|热刺追平单赛季输球纪录,世俱杯或创收20亿美元
  • 南昌上饶领导干部任前公示:2人拟提名为县(市、区)长候选人
  • 贵州省委军民融合发展委员会办公室副主任李刚接受审查调查
  • 长三角首次,在铁三赛事中感受竞技与生态的共鸣