【编译原理】自下而上语法分析(C/C++源码+实验报告)
文章目录
- 1 实验目的和内容
- 1.1 实验目的
- 1.2 实验内容
- 1.3 实验要求
- 2 设计思想
- 2.1 根据BNF描述该文法
- 2.2 根据文法写出LR(0)项目集规范族
- 2.3 根据项目集规范族画出识别活前缀的DFA
- 2.4 判断该文法是否是LR(0)文法
- 2.5 构造LR(0)分析表
- 3 算法流程
- 4 源程序
- 5 调试数据
- 6 思考:词法分析+语法分析
- 6.1 将实验一的词法分析作为函数写入语法分析程序
- 6.2 调试数据
- 6.3 调试结果
- 7 实验调试情况及体会
- 7.1 实验调试情况
- 7.2 实验体会
1 实验目的和内容
1.1 实验目的
(1)给出 PL/0 文法规范,要求编写 PL/0 语言的语法分析程序。
(2)通过设计、编制、调试一个典型的自下而上语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析方法。
(3)选择最有代表性的语法分析方法,如算符优先分析法、LR 分析法;或者调研语法分析器的自动生成工具 YACC 的功能与工作原理,使用YACC 生成一个自底向上的语法分析器。
1.2 实验内容
(1)已给 PL/0 语言文法,构造表达式部分的语法分析器。
(2)分析对象〈算术表达式〉的 BNF 定义如下:
<表达式> ::= [+|-]<项>{<加法运算符> <项>}
<项> ::= <因子>{<乘法运算符> <因子>}
<因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’
<加法运算符> ::= +|-
<乘法运算符> ::= *|/
<关系运算符> ::= =|#|<|<=|>|>=
1.3 实验要求
(1)将实验一“词法分析”的输出结果,作为表达式语法分析器的输入,进行语法解析,对于语法正确的表达式,报告“语法正确”;对于语法错误的表达式,报告“语法错误”, 指出错误原因。
(2)把语法分析器设计成一个独立一遍的过程。
(3)采用算符优先分析法或者 LR 分析法实现语法分析;或者调研语法分析器的自动生成工具 YACC 的功能与工作原理,使用 YACC 生成一个自底向上的语法分析器。
2 设计思想
2.1 根据BNF描述该文法
(1)对BNF中的各对象简称如下
E:表达式
T:项
F:因子
i:ident
n:number
(2)文法如下
2.2 根据文法写出LR(0)项目集规范族
项目集规范族如下所示,一共有16个状态。
2.3 根据项目集规范族画出识别活前缀的DFA
活前缀是指规范句型的一个前缀,这种前缀不含句柄之后的任何符号。在上面的项目集中一共有四种项目,分别是归约项目、接受项目、移进项目和待约项目,根据以下两条规则来构造识别文法所以活前缀的DFA。
(1)若状态i为X->X1…Xi-1Xi…Xn,状态j为X->X1…Xi-1XiXi+1…Xn,则从状态i画一条标志为Xi的有向边到状态j;
(2)若状态i为X->α.Aβ,A为非终结符,则从状态i画一条ε边到所有状态A->.γ。
图1 识别活前缀的DFA
2.4 判断该文法是否是LR(0)文法
(1)对于I1,Follow(S)={#},与+、-不冲突;
(2)对于I2,Follow(E)={+,-,),#},与*、/不冲突;
(3)对于I12,Follow(E)={+,-,),#},与*、/不冲突。
综上,该文法不存在移进归约冲突,所以是LR(0)文法。
2.5 构造LR(0)分析表
状态 | Action | Action | Action | Action | Action | Action | Action | Action | Action | Goto | Goto | Goto |
---|---|---|---|---|---|---|---|---|---|---|---|---|
状态 | + | - | * | / | ( | ) | i | n | # | E | T | F |
0 | S4 | S5 | S6 | 1 | 2 | 3 | ||||||
1 | S7 | S8 | acc | |||||||||
2 | r1 | r2 | S9 | S10 | r1 | r1 | ||||||
3 | r4 | r4 | r4 | r4 | r4 | r4 | ||||||
4 | S4 | S5 | S6 | 11 | 2 | 3 | ||||||
5 | r8 | r8 | r8 | r8 | r8 | r8 | ||||||
6 | r9 | r9 | r9 | r9 | r9 | r9 | ||||||
7 | r4 | r5 | r6 | 12 | 3 | |||||||
8 | r4 | r5 | r6 | 13 | 3 | |||||||
9 | r4 | r5 | r6 | 14 | ||||||||
10 | r4 | r5 | r6 | 15 | ||||||||
11 | S7 | S8 | S16 | |||||||||
12 | r2 | r2 | S9 | S10 | r2 | r2 | ||||||
13 | r3 | r3 | S9 | S10 | r3 | r3 | ||||||
14 | r5 | r5 | r5 | r5 | r5 | r5 | ||||||
15 | r6 | r6 | r6 | r6 | r6 | r6 | ||||||
16 | r7 | r7 | r7 | r7 | r7 | r7 |
3 算法流程
语法分析程序的输入是一个个单词符号的二元组,输出是一个个语法单位,它的任务是在词法分析识别出单词符号串的基础上,分析并判定程序的语法结构是否符合语法规则。
首先逐行扫描单词符号二元组,然后遍历串的每一个字符,判断是否匹配结果,再进行下一步的判断,选择对比分析表,进行归约,把对应的字母和状态压入栈中,并输出相应的语句。如果不符合这几种情况,将会归入出错处理。完整算法流程如下图所示。
图2 算法流程图
4 源程序
#include<bits/stdc++.h>
using namespace std;//构造结构体---二元组
struct Two_tuples
{string type;//词法分析的类型string value;//词法分析的值
};string input;//全局变量定义输入
Two_tuples result[200];//存放词法分析二元组
int k = 0;//输入二元组时访问下标
bool ans = true;//存放判断结果
int fh[101],zt[101];//符号栈,状态栈bool target_judge(string input , string str) //判断是否和目标串匹配
{//首先匹配长度,看长度是否一致if(input.length()!=str.length()) return false;//长度一致,逐个匹配字符else{for(unsigned int i=0;i<str.length();i++){if(input[i]!=str[i]) return false;}return true;}
} void input_cf() //输入词法分析的二元组
{string str;while(cin>>str){//定义指针访问const char *d = "(),";char *p;//临时存放字符串,便于分割处理char buf[101];strcpy(buf,str.c_str()); //字符串转成数组//开始处理每个二元组p = strtok(buf,d);int i = 0;//把输入的二元组存储到结构体中while(p){if(i%2==0) result[k].type = p;else result[k].value = p;i++;p = strtok(NULL,d);}k++;}result[k].type = "#";
}//在LR分析表中判断是哪个目标串(0--8)
int chart(string str)
{if(target_judge("plus",str)) return 0;else if(target_judge("minus",str)) return 1;else if(target_judge("times",str)) return 2;else if(target_judge("slash",str)) return 3;else if(target_judge("lparen",str)) return 4;else if(target_judge("rparen",str)) return 5;else if(target_judge("ident",str)) return 6;else if(target_judge("number",str)) return 7;else if(target_judge("#",str)) return 8;else return -1;
} //ETF归约(9--11)
int gy_1(int num)
{//E---表达式if(num - 100 == 1) return 9;else if(num - 100 == 2) return 9;else if(num - 100 == 3) return 9;//T---项else if(num - 100 == 4) return 10;else if(num - 100 == 5) return 10;else if(num - 100 == 6) return 10;//F---因子else if(num - 100 == 7) return 11;else if(num - 100 == 8) return 11;else if(num - 100 == 9) return 11; else return -1;
} //gy_1中规约后约几项
int gy_2(int num)
{//E---表达式if(num - 100 == 1) return 1;else if(num - 100 == 2) return 3;else if(num - 100 == 3) return 3;//T---项else if(num - 100 == 4) return 1;else if(num - 100 == 5) return 3;else if(num - 100 == 6) return 3;//F---因子else if(num - 100 == 7) return 3;else if(num - 100 == 8) return 1;else if(num - 100 == 9) return 1; else return -1;
} int main()
{input_cf(); //输入词法分析结果//LR分析表,其中大于100为归,小于100为移进int LR_chart[17][12]={{0,0,0,0,4,0,5,6,0,1,2,3},{7,8,0,0,0,0,0,0,100,0,0,0},{101,101,9,10,0,1,0,0,101,0,0,0},{104,104,104,104,0,104,0,0,104,0,0,0},{0,0,0,0,4,0,5,6,0,11,2,3},{108,108,108,108,0,108,0,0,108,0,0,0},{109,109,109,109,0,109,0,0,109,0,0,0},{0,0,0,0,4,0,5,6,0,0,12,3},{0,0,0,0,4,0,5,6,0,0,13,3},{0,0,0,0,4,0,5,6,0,0,0,14},{0,0,0,0,4,0,5,6,0,0,0,15},{7,8,0,0,0,16,0,0,0,0,0,0},{102,102,9,10,0,102,0,0,102,0,0,0},{103,103,9,10,0,103,0,0,103,0,0,0},{105,105,105,105,0,105,0,0,105,0,0,0},{106,106,106,106,0,106,0,0,106,0,0,0},{107,107,107,107,0,107,0,0,107,0,0,0}}; int i = 0 , j = 0; //访问栈下标fh[0] = 8 , zt[0] = 0;//初值while(LR_chart[zt[j]][chart(result[i].type)]!=100){//判断错误if(LR_chart[zt[j]][chart(result[i].type)]==0){ans = false;break;}//移进else if(LR_chart[zt[j]][chart(result[i].type)]<100){j++;zt[j] = LR_chart[zt[j-1]][chart(result[i].type)];fh[j] = chart(result[i].type);i++;}//归约else{int res = LR_chart[zt[j]][chart(result[i].type)];j -= gy_2(res);j++;//移入符号栈,状态栈fh[j] = gy_1(res);zt[j] = LR_chart[zt[j-1]][gy_1(res)];//判断错误if(zt[j]==0){ans = false;break;}}}if(ans == true) cout<<"Yes,it is correct."<<endl;else cout<<"No,it is wrong."<<endl;return 0;
}
5 调试数据
(1)测试样例如下
【样例输入】
(lparen,()
(ident,a)
(plus,+)
(number,15)
(rparen,))
(times,*)
(ident,b)【样例输出】
Yes,it is correct.
(2)测试样例结果如下
图3 样例测试结果
6 思考:词法分析+语法分析
6.1 将实验一的词法分析作为函数写入语法分析程序
int main()
{string st1r;//输入源程序for(int i=0;i<L-1;i++){cin>>str1;//检测输入的结束string str;str = fun_cifa(str1);//调用词法分析子程序
}
input_cf(); //处理词法分析结果//LR分析表,其中大于100为归,小于100为移进int LR_chart[17][12]={{0,0,0,0,4,0,5,6,0,1,2,3},{7,8,0,0,0,0,0,0,100,0,0,0},{101,101,9,10,0,1,0,0,101,0,0,0},{104,104,104,104,0,104,0,0,104,0,0,0},{0,0,0,0,4,0,5,6,0,11,2,3},{108,108,108,108,0,108,0,0,108,0,0,0},{109,109,109,109,0,109,0,0,109,0,0,0},{0,0,0,0,4,0,5,6,0,0,12,3},{0,0,0,0,4,0,5,6,0,0,13,3},{0,0,0,0,4,0,5,6,0,0,0,14},{0,0,0,0,4,0,5,6,0,0,0,15},{7,8,0,0,0,16,0,0,0,0,0,0},{102,102,9,10,0,102,0,0,102,0,0,0},{103,103,9,10,0,103,0,0,103,0,0,0},{105,105,105,105,0,105,0,0,105,0,0,0},{106,106,106,106,0,106,0,0,106,0,0,0},{107,107,107,107,0,107,0,0,107,0,0,0}}; int i = 0 , j = 0; //访问栈下标fh[0] = 8 , zt[0] = 0;//初值while(LR_chart[zt[j]][chart(result[i].type)]!=100){//判断错误if(LR_chart[zt[j]][chart(result[i].type)]==0){ans = false;break;}//移进else if(LR_chart[zt[j]][chart(result[i].type)]<100){j++;zt[j] = LR_chart[zt[j-1]][chart(result[i].type)];fh[j] = chart(result[i].type);i++;}//归约else{int res = LR_chart[zt[j]][chart(result[i].type)];j -= gy_2(res);j++;//移入符号栈,状态栈fh[j] = gy_1(res);zt[j] = LR_chart[zt[j-1]][gy_1(res)];//判断错误if(zt[j]==0){ans = false;break;}}}if(ans == true) cout<<"Yes,it is correct."<<endl;else cout<<"No,it is wrong."<<endl;return 0;
}
6.2 调试数据
【样例输入】
(a+15)*b 【样例输出】
(lparen,()
(ident,a)
(plus,+)
(number,15)
(rparen,))
(times,*)
(ident,b)
Yes,it is correct.
6.3 调试结果
图4 样例测试结果
7 实验调试情况及体会
7.1 实验调试情况
由上两步中的测试样例可以得到,所有的测试样例均得到了相应的输出结果,说明代码编写成功,并且在代码中设置了出错处理,以便解决其他情况。
本次实验同时也实现了词法分析与语法分析合在一起去识别源程序的程序,但依然存在一些问题,比如语句只能一句一句地去识别,而不能进行整体识别,该问题会在后续过程中加以解决。
7.2 实验体会
这次实验采用的方法是自下而上分析中的LR分析法,在做实验之前,一直考虑是用算符优先分析还是LR分析来写,最后还是决定用LR分析来写。LR分析算法比较难算的地方是构造LR分析表,需要首先画出项目集规范族和DFA,需要注意的是要认真写出每个项目集规范族,以防错了一个会影响整个结果。再三检查后,才要开始按照规则构造ACTION和GOTO 分析表。分析表也需要多次核实,如果写错一个,在归约过程中就会出错。
在写代码的时候,由于归约和移进是通过s和r进行区分,于是我想到如果归约的话就加100,而acc就用100来表示,减少判断,给编码带来了方便。在判断结束之前,只有两个动作分别为移进和规约,然后分别对这两个过程进行处理就可以。
因为自己完成代码比较早,在当时上实验课时老师说道分析表是不是可以用代码来构造,算符分析表构造可能相对容易,但LR分析表中构造DFA就比较麻烦,根据DFA构造分析表的过程不算困难。
通过这次的实验,自己回顾了算符分析和LR分析的具体方法,也计算了老师ppt里的几道练习题,对于自下而上的语法分析过程进一步的理解也通过这次实验加强了自己处理字符串和运用栈来解决问题的能力。对编译原理的语法分析过程有了更好的理解,为下一次语义分析奠定基础。
最后,感谢刘善梅老师和其他同学在这次实验中给予我的帮助,不胜感激!
【编译原理】自下而上语法分析(C/C++源码+实验报告)相关推荐
- 【编译原理】自上而下语法分析(CC++源码+实验报告)
文章目录 1 实验目的和内容 1.1 实验目的 1.2 实验内容 1.3 实验要求 2 设计思想 2.1 根据BNF描述该文法 2.2 根据文法画相应的语法图 2.3 判断是否是LL(1)文法-求Fi ...
- 编译原理预测分析法c语言,编译原理预测分析法C语言的实验报告.doc
题目:编写识别由下列文法所定义的表达式的预测分析程序. EàE+T | E-T | T TàT*F | T/F |F Fà(E) | i 输入:每行含一个表达式的文本文件. 输出:分析成功或不成功信息 ...
- 基于SSM的商城项目项目源码+实验报告
基于SSM的商城项目 本项目是今年暑假小学期时完成的,耗时四天时间,在小学期结束项目答辩时,在年级里斩获冠军,在此很感谢我的团队,正是我们各个环节的配合,最终才能够获得如此好的成绩. 目录 基于SSM ...
- c语言五子棋源代码vc6,五子棋源码实验报告及人机对战说明.docx
1. 五子棋对战说明 2. 实验报告 3. 源代码 五 子 棋 作品特点:C 语言程序 五子棋 作品功能:五子棋人机对战,人人对战. 目录:1 五子棋介绍. 五子棋棋型介绍. 人人对战的实现. 电脑下 ...
- java编译使用androidsdk,详解Android源码的编译
本文将为大家介绍的是如何设置Android源码的编译环境,包括Linux下的配置.主要基于Android 1.0环境,希望对大家了解Android开发有所帮助. 本次编译过程主要参考官方文档(http ...
- vs+cmake完美编译RTS游戏,类似魔兽争霸源码
网上的一个RTS游戏,网上的代码比较老,不能直接编译.这个仓库是我整理编译通过的.代码版权归于原作者. 源码下载 vs+cmake完美编译RTS游戏,类似魔兽争霸源码下载-其他文档类资源-CSDN文库 ...
- 实时编译、动态执行C/C++源码函数
实时编译.动态执行C/C++源码函数 语法格式:fileCLASS *pObj = <file.cpp> 该语法获得源代码file.cpp的函数接口对象指针pObj,通过pObj调用fil ...
- ubuntu 14.04.5 编译Android 4.4.4 r1源码(最新)
本文博客链接:http://blog.csdn.net/qq1084283172/article/details/54426189 吐槽:ubuntu系统真是让人又爱又恨,也有可能是VMware Wo ...
- 【2021软件创新实验室暑假集训】SpringMVC框架(设计原理、简单使用、源码探究)
系列文章目录 20级 Java篇 [2021软件创新实验室暑假集训]计算机的起源与大致原理 [2021软件创新实验室暑假集训]Java基础(一) [2021软件创新实验室暑假集训]Java基础(二) ...
- 如何用电脑反编译微信小程序,获得源码(学习用途)
故事背景:上周末,搞前端的发小(老表)找到我,问我微信小程序能否反编译成功,然后他需要源码,看他的接口签名算法 周末在公司研究了一下午 最后成功了,心路历程走一波,总结一下 一.准备工具: 模拟器: ...
最新文章
- JAVA面试题系列:如何解决Redis的并发竞争问题
- 计算机仿真实验用的教学软件是,AR增强现实教学软件,学习效果倍增
- matlab用ezmesh绘制单位球,Matlab------------命令大全2
- 13.transform确保目标空间足够大
- 项目学生:带有Jersey的Web服务服务器
- 【转】ABP源码分析二十一:Feature
- Linux 进程地址空间 进程内存布局
- syslog工具_07 Docker 可视化管理和监控工具
- Java http发送post请求
- java线程视频教程_java线程视频教程
- ​我国首个5G消息平台标准发布;华为拿下支付牌照;工业富联、酷派发布2020年业绩报告...
- 手绘线条一直画不直_板绘线条不流畅怎么办?线稿就得这样练!
- Map的实现类中,哪些是有序的,哪些是无序的
- 宝藏又小众的Digital Tutors视频教程素材网站分享
- 成分句法分析 依存句法分析 Parsing 知识图谱
- Unity Shader学习-高光反射
- Python进阶之路 简单的棋盘游戏
- SLM—仿真过程与数据管理平台
- C语言fopen函数的用法
- operation simDhs or Dhs
热门文章
- Redis的持久化方式
- 基于AT89S52单片机的汽车LED尾灯控制器设计
- 数据中心的三种布线方式(EOR/MOR/TOR)
- 常用的条形码类型以及如何选择条码类型、条形码字体和条形码控件
- 华为管理学案例分析_华为战略管理案例分析.docx
- SQL数据库学习之路(一)
- ShadowGun Billboard Blinking God Rays
- apache连接mysql配置_Apache+PHP配置及连接mysql数据库
- System Repair Engineer (SREng) 2.6 正式发布
- Windows 如何完整备份驱动