一.实验概述

1.1编译器概况

本次实验,我实现了SysY文法的编译器前端,在windows环境下使用mingw32-make进行编译,具体细节如下:

词法分析使用flex自动生成;

语法分析使用bison自动生成,在语法分析过程中生成抽象语法树AST;

IR生成手动实现,递归遍历抽象语法树,生成老师提供的IR,测例通过率99.03%,仅一个测例未通过;

minic编译器前端支持的功能:

minic -i -o xxx.ir xxx.c                          : 生成ir文件

minic -a -o xxx xxx.c                             : 生成ast树到控制台

minic -s -o xxx.txt xxx.c                        : 生成符号表到xxx.txt

minic -c funcname -o xxx.txt xxx.c       : 生成基本块划分结果到xxx.txt

1.2其他特点:

1.多维数组支持无限维度.

多维数组分析采用递归向下计算地址的方式,每个维度都详细计算,而不是将维度写入文法中,故支持无限维度.

2.在文法处支持const变量及常量的识别.

一开使用SysY文法写,const写了一部分,索性就写完了.

二. 测试结果

test.ir:                                                           ast树(数字是节点类型的enum)

  

符号表:                                                                基本块划分:

 

详细功能及设计细节见 详细设计.

三. 总体架构

1.minic 编译器前端总体架构:

如下图所示,  .l 是词法分析文件 ,  .y 是语法分析文件,在这里生成ast树 ,

c_ast.cpp 是 .y  文件调用的生成ast的后端函数文件  ,  d_create_ir.cpp 是生成ir的核心代码文件.

四. 详细设计

4.1词法分析设计

1.词法分析中识别各进制整数

先写出正则识别规则,然后调用atoi函数进行转换,最后返回token类型.

2.识别ident

编写识别规则, 然后直接返回读取到的string.

3.其他终结符

其他终结符直接返回对应的token类型.

4.注释消除

识别到 “/*”后进入COMMENT, 再次识别到”*/”后退出. “//”同理.

4.2语法分析详细设计

1.在语法分析部分,先定义了词法分析交付给bison 的token类型和需要的具体参数.

如果是token则直接返回token类型, 如果是number 要返回数字, ident 要返回具体的ident.

2.定义文法开始符号, 运算符优先级, 文法符号类型等.

3.编写文法识别规则.

按照SysY文法,详细编写文法识别规则.对于文法中的[](可选项),在当前文法符号中分开识别; 对于{}(0项或多项), 新明明一个文法识别符号,递归地识别.

比如,在数组定义时,数组维度是可有可无的, 将其以及各个维度区分开来写在vardef下,文法识别规则将会过于庞杂,因此将其单写出来.定义为:ArryList ->’[‘ ConstExp ‘]’ |ArryList ‘[‘ ConstExp ‘]’ ;

在每个文法符号中,新建一个ast节点, 并将文法识别的右部接入文法识别中.

当遇到number , ident 这样的终结符结点时,创建一个叶子节点,并将节点类型,数据存入叶子节点.

4.3IR生成部分详细设计

1.设计概要

在ir生成部分, 大体上按照一个文法符号设计一个函数,在函数中判断所识别的文法类型,如果识别失败,则报告错误.

比如,再识别CompItem时, 判断识别的节点类型, 如果是FuncDef 则调用Funcdef 分析函数去分析子节点, 如果识别错误则报告错误.

在接下来我会详细挑选几条较为重要的识别方法做详细解释.

2.程序初始化部分

首先要设置作用域为global, 然后将stdio的函数压入符号表中,如下图所示.

3.函数定义分析子程序

如图所示,在函数调用分析子函数中,先判断错误, 然后再核对节点类型,根据建树时的节点类型分别进行识别, 如果都不是则报告错误.

如果判断是无参函数定义,首先拿出ident ,returnType, 并赋给局部变量待用.然后再将变量名,变量类型,作用域等信息写入符号表.

然后再创建一个新的作用域,之后开始翻译部分.

在翻译一开始,利用拿出的ident, returnType 信息, 构造中间IR .

在这里,就遇到了新的问题.比如局部变量的定义,全局变量的定义,指针型变量的定义等.由于这些方法在后续识别中,会反复重用,因此将其封装起来,并定义id,每使用一次就++, 防止局部变量或临时变量重复定义.如上图的declare_local_varible();

在函数定义的一开始,就要申请一个局部变量,用来存储返回值.即便void,也要申请,只是不返回(这是没有优化后的识别方法)

做完这些工作后,就可以将 “entry”压入irlist, 然后调用Block()分析器进行分析,最后将临时的localvlist,tempvlist压入irlist中,最后将存储返回值的tempv赋给localv,压入exit语句,返回.

同理,有参函数定义基本结构相同,只是在参数处要调用FuncFParams()分析器来分析参数信息.如果有参数,要创建局部变量和临时变量,并在程序一开始,将参数值赋给局部变量,便于后续使用.同时也要写入符号表,便于后续分析查询.

在函数定义的最后,要重置变量id, 清空当前的变量列表,回退作用域到全局等.

4.函数参数识别子程序

首先判断节点类型,然后递归调用单个参数的分析程序,FuncFParam();最后加上逗号,把他们链接在一起,返回.

其中,对于函数参数的识别,首先申请局部变量和临时变量,然后再调用Ident拿出参数名称,并且根据节点类型判断参数类型,之后写入符号表.

数组型参数,将在下文详细介绍.

5.Stmt 语句分析子程序

Stmt 是整个翻译过程中,语句的主题,也是实现程序结构则主要实现方式.在接下来,首先介绍总体识别概要,之后将详细分语句介绍.

首先Stmt要区分开不同的语句类型,进行分别识别.如果是未定义的,则报告错误.

6.If 条件语句的识别

如果在文法识别时,已经是别了是if(不带else),则只需要两个label,一个用于语句开始的label,一个用于quit的label.然后调用Cond()分析(调价表达式分析子程序),之后调用Stmt()分析中间的语句部分,分析完成后压入quit_label,然后结束.

对于IF-Else, 大同小异,只是在else的时候再申请一个标签,然后再分别调用Stmt去识别.在if-else中,对标签的传入也有所不同.对if部分,传入if_stmt和quit, 在else部分传入else_stmt.

7.while 循环语句识别.

首先申请三个标签,一个是cond 条件表达式进入标签,一个是do部分标签,一个是quit标签.

然后在适当的位置压入标签,并且调用stmt()子程序进行分析.

8.条件表达式识别概述:

在条件表达式中,一级一级递归调用分析.

9.短路求值实现细节

短路求值细节分为 && 和 ||两种.

在&&中,如果第一个Cond 识别后,跳转到第二个Cond的识别.

而在|| 中, 如果第一个Cond正确可直接跳转到语句执行部分.

对应的实现方式就是在传入的两个label处,&&传入第一个Cond的是label_cond2 和label_quit, 而在第二个Cond中传入的是 label_do 和label_quit.

而在 || 中,如果错误则跳转到第二个cond ,正确直接跳转到执行语句处.

10.表达式实现细节

表达式分为两种, 一种是需要直接拿出确定的值,多数出现在数组定义,常量定义等; 另一种是需要执行时运算的,在这里就要申请一个局部变量来存储运算临时结果,然后返回临时变量. 总体上还是采用递归调用的方式进行.

11.数组维度识别细节

在数组维度识别中,已经无法通过简单的传参来确定,这里引入了全局变量的使用.当第一次识别到数组时,去符号表里拿出数组维度信息,然后存入用于存储数组各维度信息的list中,然后每一次识别,都从该lis中pop掉头部的一项数据,这样在识别到最后的时候,列表为空的时候,就是最后一个维度,直接加在地址偏移量上就好.

12.左值实现细节

左值在被识别处,有两种,一种是局部变量,另一种是指针变量.然而这里的难点是,文法识别中的数组在这里并不一定就是指针变量,因此要进行严格的判断.

在数组中,如果是别到时数组,先去符号表里拿出维度,然后看调用它的引用了几个维度,如果调用的维度小于数组总维度,那么他一定是指针类型,如果等于那么就要当作变量类型去处理.

变量类型直接去符号表里拿出局部变量id ,然后申请一个tempv, 临时变量,把数值付给他,然后将临时变量返回.

13.基本快划分

如果是条件跳转语句,则他所跳转到的label 和该语句的下一条语句都要区分基本快,如果是无条件跳转,只需要将他的label处划分即可.

五. 调试和Bug修改

5.1 调试和bug修改总结

1. 如果严格按照文法去写,一定会报移入规约冲突.其中一条是if, if-else的,目前还无法解决.

还有一种是变量定义的 int ident 和函数定义的 int ident () block ,在识别到int后面时会出现移入规约冲突,解决办法是将int写入函数识别符号里,不单独写成vartype.

2.解决label重复的问题:如果是if, while的嵌套调用,则会多申请label,解决办法是,如果是嵌套的识别,则将label传下去,子程序识别到有label后,便不再重复定义.

3.数组全局变量返回不对

最后找到问题,是查询符号表后,返回不完善,要判断是全局还是局部,全局返回标识符,局部返回局部变量id.

4.解决第18个测例

//原来是函数调用表达式不对,void不需要临时变量返回,i32需要

5.第29个测例

//putint 调用函数,,原来是,无参函数调用中,没有将void 和i32分开写,return tempvariable,

还没调出来

原来是label后面冒号没了,找到错误,修改后过了

6.第33个测例:

直接生成IR ,跑,:

原来是函数定义时候,参数间的逗号没了

修好后

//这个是途中自定义的错误,直接跑,报这个错误

调试到这里exp 返回了错误

最后发现在这里调用的时候把儿子传错了

7. 继续调bug, unary_op有问题,突然想起来 ! 非还没处理

最后发现一元运算符 !  要用eq 0 来判断

8. 继续修,估计数组传参又不对了

错误的:

正确的:

要在找到变量类型后就判断返回啥,如果是数组名字要加 *

还是有bug

老师的ir 声明处 没有维度也要加零?

后来在传参数时,存储array 的temp 也必须是 i32*的

9. 改完这个bug , 就剩四个了

//.067. ge丢了

10. 最后一个bug,由于时间原因还未解决, 后续放假会继续尝试.

下图是正确的,而我都识别为了指针类型.

5.2 测试和结果

1.全部测例通过结果:

六. 实验总结

通过本次实验,学习了编译技术以及编译器实现细节,锻炼了自己的代码能力,为后续学习生涯打下坚实基础.

本次实验全部是本人独立实现,没有抄袭/调用他人文件,也没有修改测例脚本sh文件, 遇到问题会与同学一起探讨,最终想清楚识别原理.

七. 程序源码

TODO

SysY文法编译器前端设计实现相关推荐

  1. c语言编译前端,c语言编译器前端的设计与实现.doc

    c语言编译器前端的设计与实现 计 算 机 与 信 息 工 程 学 院 软硬件专业综合课程设计总结报告 题目: C语言编译器前端的设计与实现 姓 名: 学 号: 专 业: 计算机科学与技术 指导教师: ...

  2. 《编译与反编译技术实战 》一2.3 编译器的设计与实现概述

    本节书摘来自华章出版社<编译与反编译技术实战 >一书中的第2章,第2.3节,庞建民 主编 ,刘晓楠 陶红伟 岳 峰 戴超 编著,更多章节内容可以访问云栖社区"华章计算机" ...

  3. 基于虎书实现LALR(1)分析并生成GLSL编译器前端代码(C#)

    基于虎书实现LALR(1)分析并生成GLSL编译器前端代码(C#) 为了完美解析GLSL源码,获取其中的信息(都有哪些in/out/uniform等),我决定做个GLSL编译器的前端(以后简称编译器或 ...

  4. 《硬件仿真(Emulation)EDA工具前端设计流程》

    硬件仿真(Emulation)EDA工具设计流程总结,仅介绍必要设计步骤,具体详细步骤由于公司机密,暂不详细说明. 文章目录 前言 1. Emulation 1.1 什么是Emulation 1.2 ...

  5. c++ 模板类实现堆栈实验报告_编译原理——小型类C编译器的设计和实现(生成8086汇编代码)之1:问题定义以及总体功能...

    前面花了两篇文章来介绍词法分析和语法分析,接下来才是比较有意思的部分--一个小型类C编译器的设计和实现(其实是编译原理的课程设计啦!~)我用的是python2.7.13+PyQt来做的...事实上,正 ...

  6. 编译器前端概述与可信编译器

    作为一名程序员,编译器是我们日常开发中时时刻刻都会用到的工具.但是现在很多程序员往往都忽略了编译器的重要性和背后的原理,他们会说:「我不可能去写一门新的语言,还有必要学习编译技术吗?」这种想法是把编译 ...

  7. 【简历投递】数字IC前端设计工程师-北京

    目录 1.兆易创新 (社招)数字前端实现资深/主管工程师 2.海尔集成电路 (X招)IC前端设计工程师 3.龙芯中科 (校招)SoC结构设计工程师 (校招)多核结构设计工程师 4.紫光同芯 (校招)数 ...

  8. 大巧不工-WEB前端设计修炼之道pdf

    下载地址:网盘下载 大巧不工:Web前端设计修炼之道:全面探讨Web前端设计的方法.原则.技巧和最佳实践5大专业社区一致鼎力推荐!前端开发工程师.前端设计师.前端架构师和用户体验设计师等新兴职业的出现 ...

  9. web前端网页设计作业_网页前端设计快速入门技巧

    我最近收到部分在校学生的邮件,很多在临近毕业实习这段时间,面临着找工作,表示压力非常大,也不知道如何去选择,大多数都是处于迷茫期,问我怎么办?也有想学前端的,问我怎么才能够快速的入门? 先说我自己吧, ...

最新文章

  1. 开始学习python
  2. html网页访问WebAPI中的方法遇到的问题
  3. 百天搞懂Java(一) - JDK环境配置
  4. PHP $_SERVER 变量
  5. STM32 - 定时器高级应用说明 - 多触波的实现 (N-pulse waveform generation using timer synchronization)- 02
  6. 嵌入网站的挖矿代码——Webmine
  7. iPhone 13与12对比图曝光:更厚、更大了
  8. Ubuntu18.04构建Go语言项目
  9. 【c++基础】菱形继承问题
  10. discuz mysql语句_discuz 数据库插入
  11. SaaSBase:最受欢迎的跨境电商软件有哪些(上篇)
  12. bios设置raid启动模式Linux,[BIOS设置]BIOS中RAID模式怎么设置?
  13. shell笔记(C语言中文网)
  14. 钉钉视频下载方法地瓜网络钉钉视频下载器
  15. 推荐:(链接)幽默男女--绝对搞笑!!(每日更新)
  16. Android动画内置插值器
  17. 洛谷P1433 吃奶酪--Java解法(货郎担问题)
  18. 用python搭建一个服务器
  19. GPS卫星位置的计算
  20. Linux 命令之 7za -- 文件压缩命令

热门文章

  1. 软件工程应用与实践(2)——application.properties配置文件分析
  2. 一键批量替换文本工具
  3. 卡塔尔世界杯进入倒计时,万人观赛的直播体验如何保障?
  4. 微信小程序-音视频剪辑
  5. [生存志] 第79节 国语述诸国
  6. 婴儿奶瓶温度计的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  7. python最强脚本工具_python脚本工具最百里自瞄
  8. 恕我直言,Java四大名著并不一定适合你!
  9. 2021年数学建模国赛C题思路
  10. 带你读懂Pinbar交易信号