具体代码已放至Github(仅供参考):
qxpBlog/Compiler_UESTC: 电子科技大学编译原理实验 (github.com)
具体实验过程如下:
一、实验内容及步骤:
1.实验内容:
在实验3及示例程序的基础上完成算术表达式、逻辑表达式、赋值语句、条件语句、循环语句对应的代码生成,函数命名分别为:genArithmeticExpr, genLogicExpr,genAssignStmt, genIfStmt, genWhileStmt,这五个函数的函数参数都为: (past node, char* result),其中node为相关类型的结点,result 为用来保存三地址代码/LLVM虚拟指令的存储空间并且每行只放一条指令。
2.实验步骤:
(1)编写threeaddress.l文件
threeaddress.l源程序中声明及定义部分如图1-1所示。
图 1-1 threeaddress.l源程序中声明及定义部分
threeaddress.l源程序中规则部分(识别符号的正则表达式及其对应的相关的动作)如图1-2(a)、1-2(b)所示。
图 1-2(a) threeaddress.l源程序中规则部分
图 1-2(b) threeaddress.l源程序中规则部分
threeaddress.l源程序中辅助函数(yywrap函数)部分如图1-3所示。
图 1-3 threeaddress.l源程序中辅助函数部分
threeaddress.y源程序的定义部分中,包含的头文件、函数声明、全局变量声明以及联合类型全局变量yylval的成员变量声明如图2-1所示。
图 2-1 threeaddress.y源程序的定义部分
定义部分中,使用%token定义的终结符以及使用%type定义的非终结符,如图2-2所示。
图 2-2 终结符、非终结符定义
threeaddress.y源程序的翻译规则(文法及对应的处理)部分中,sysy文法开始部分及其对应的处理如图2-3所示。处理部分中$$ 指的该产生式归约后数值栈栈顶元素,$n 指产生式右侧从左到右第n个符号在数值栈所对应的值。其中astRoot是链表头结点(空结点),指向链表总结点,astRoot的next为链表的第一个结点。
图 2-3 sysy文法开始部分
最后,threeaddress.y源程序中的函数定义部分为空。
ast.h文件中,全局变量定义及抽象语法树结点定义如图3-1所示。
图 3-1 全局变量定义及抽象语法树结点定义
建立ast结点函数的声明如图4-2所示。
图 3-2 建立ast结点函数的声明
ast.c文件中,showAst函数如图4-1(a)、图4-2(b)所示。
图 4-1(a) showAst函数
图 4-1(b) showAst函数
在showAst函数中,对与不同的结点类型,我们将打印不同的信息。
创建新链表结点newList函数如图4-2所示,其中结点的ivalue为链表结点数,left为链表的第一个具有实际意义的结点,right为尾结点。
图 4-2 newList函数
关于算术表达式、逻辑表达式、赋值语句、条件语句、循环语句对应的中间代码生成函数声明如图5-1所示。
图 5-1 genthreeaddress.h文件代码
genthreeaddress.c文件中每一条三地址代码存储在codearry数组中,通过函数addCodes添加到数组中,该函数代码如图6-1所示。
图 6-1 三地址代码存储函数
基本表达式三地址代码生成函数genPrimaryExpr如图6-2(a)、图6-2(b)、图6-2(c)所示,该函数主要是对整数、浮点数、表达式、复合语句以及变量调用所涉及到的三地址代码生成进行处理。
图 6-2(a) 基本表达式三地址代码生成函数
图 6-2(b) 基本表达式三地址代码生成函数
图 6-2(c) 基本表达式三地址代码生成函数
算术表达式三地址代码生成函数如图6-3(a)、图6-3(b)所示,该函数对算术表达式结点node进行三地址代码化,并将result字符类型指针指向生成的算术表达式三地址代码。其中,也对一元算术表达式进行了特殊化的处理。
图 6-3(a) 算数表达式三地址代码生成函数
图 6-3(b) 算数表达式三地址代码生成函数
逻辑表达式三地址代码生成函数如图6-4(a)、图6-4(b)、图6-4(c)所示,该函数对逻辑表达式结点node进行三地址代码化,并将result字符类型指针指向生成的逻辑表达式三地址代码。其中,也对一元算术表达式进行了特殊化的处理。
图 6-4(a) 逻辑表达式三地址代码生成函数
图 6-4(b) 逻辑表达式三地址代码生成函数
图 6-4(c) 逻辑表达式三地址代码生成函数
赋值表达式三地址代码生成函数如图6-5所示,该函数对赋值表达式结点node进行三地址代码化,并将result字符类型指针指向生成的赋值表达式三地址代码。
图 6-5 赋值表达式三地址代码生成函数
条件语句三地址代码生成函数如图6-6(a)、图6-6(b)、图6-6(c)所示,该函数对条件语句结点node进行三地址代码化,并将result字符类型指针指向生成的条件语句三地址代码。同时,对于条件语句有无else语句的两种情况,分别进行了相应的处理。
图 6-6(a) 条件语句三地址代码生成函数
图 6-6(b) 条件语句三地址代码生成函数
图 6-6(c) 条件语句三地址代码生成函数
语句表达式三地址代码生成函数如图6-7(a)、图6-7(b)所示,该函数对循环语句结点node进行三地址代码化,并将result字符类型指针指向生成的循环语句三地址代码。
图 6-7(a) 循环语句三地址代码生成函数
图 6-7(b) 循环语句三地址代码生成函数
main函数如图7-1所示,主要是打印输入字符串对应于sysy文法所形成的抽象语法树,以及其生成的三地址代码。
图 7-1 main函数代码部分
二、实验运行结果及测试:
(1)输入测试用例“1+5-9*3/2;”,程序输出结果如图8-1所示。
图 8-1 测试用例1运行结果
图 8-2 测试用例2运行结果
图 8-3 测试用例3运行结果
图 8-4 测试用例4运行结果
图 8-5 测试用例5运行结果
三、实验结论与总结:
本次实验所实现的三地址代码生成程序,基本上能够完成对sysy文法中算术表达式、逻辑表达式、赋值语句、条件语句、循环语句对应的代码生成,并能够生成并打印相应的抽象语法树(AST)。较好的完成了本次实验的基本要求。
通过本次实验,了解并掌握了三地址中间代码生成的基本原理,对Bison(Yacc的GNU版本)和Flex这两个工具有了更加深刻的认识与理解,能够利用Flex和Bison这两个工具自动生成识别相应文法的词法分析器、语法分析器。同时也对编译过程中中间代码分析这一环节有了一个更加深刻的认知。同时在运行本次实验提供的事例,对llvm代码生成也有了一定的了解。