编译技术课程设计
一、设计内容
实现一个小型的编译程序,当输入高级语言源程序时,输出四元式程序,此为必做,选做部分为同时输出汇编语言程序。
二、设备与环境
设备:笔记本电脑
语言:C语言
开发环境:Visual Studio 2019
三、相关规范
1、程序语句
选取赋值语句、if语句和while语句作为前三种结构的代表,考虑由下面产生式所定义的程序语句:
S→if B then S else S︱while B do S︱begin L end︱A
L→S;L︱S
A→i:=E
B→B∧B︱B∨B︱¬B︱(B)︱i rop i︱i
E→E+E︱EE︱(E)︱i
其中各非终结符的含义如下表1所示。
各终结符的含义如下表2所示。
表2 终结符的含义
其中,六种关系运算符分别为:
< 小于 <= 小于等于 <> 不等于 > 大于 >= 大于等于 = 等于
2、优先级
(1)表达式的运算 由高到低的优先顺序:算术运算>关系运算>布尔运算,并且服从左结合原则。
(2)算术运算符由高到低的优先顺序:“()”>“”>“+”。
(3)关系运算符6个关系运算符优先级相同。
(4)布尔运算符由高到低的优先顺序:“¬”>“∧”>“∨”。
3、单词的内部定义
词法分析器输出的单词符号格式为二元式:(单词种别,单词自身的值)。具体定义如下表3所示:
表3 关于单词的内部定义
其中6种关系运算符,单词种别编码相同,规定其单词自身的值如下表4。
表4 关系运算符与其单词值
注:(1)单词自身的值对变量而言是指其在变量表中的位置
(2)若单个种别编码已足以区分该单词是何种类,那么其自身值被定义为0
4、算术表达式的LR分析表
算术表达式文法G[E]如下:
E->E+E︱EE︱(E)︱i
拓广文法为G’[E]:
(0) S′→E
(1) E→E+E
(2) E→EE
(3) E→(E)
(4) E→i 由此得到算术表达式的SLR(1)分析表如表5所示。
表5 算术表达式的SLR(1)分析表
5、布尔表达式的LR分析表
拓广文法G[S’]:
(0) S′→E
(1) B→i
(2) B→i rop i
(3) B→(B)
(4) B→not B
(5) A→B and
(6) B→AB
(7) O→B or
(8) B→OB
由此得到布尔表达式的SLR(1)分析表如表6所示。
表6 布尔表达式的SLR分析表
6、程序语句的LR分析表
拓广文法 G[S’]:
(0) S′→S
(1) S→if e then S else S
(2) S→while e do S
(3) S→begin L end
(4) S→a
(5) L→S
(6) L→S;L
SLR(1)分析表如下表7所示。
表7 程序语句的SLR分析表
四、具体实现
1、宏定义单词符号的种别编码
根据上表3的内容依次定义相关单词符号对应的编码,如下图1所示。
图1 定义种别编码
2、使用的数据结构
2.1 结构体rwords
结构体rwords存放保留字、逻辑运算符单词名称及对应的种别编码。设置reswords[10]存放系统保留字和逻辑运算符,是为了词法分析时的查表操作。因为无论是保留字还是逻辑运算符,它们的单词构造都与标识符相同,但却不能将其视为变量。因此,在分析出单词为标识符后,通过查reswords,判断这个单词是变量还是保留字还是逻辑运算符与或非。
当查表成功后,返回保留字或逻辑运算符的种别编码;当查表失败,返回-1,从而对标识符进行后续操作。
2.2 结构体aa
结构体aa即为词法分析所规定输出的单词符号格式二元式:(单词种别编码,单词自身的值)。定义aa类型的数组buf[1000],顺序存放词法分析后的二元式结果。
2.3 结构体fourexp
结构体fourexp即最终需要的四元式数据结构:(op,arg1,arg2,result),其中,op用于存储运算符和转移指令标识,由于转移指令标识例如“j”、“j>”等没有对应的种别编码,因此直接将其定义为char类型,便于显示。定义操作数arg1和arg2为aa类型的变量是考虑到区分变量、常数和临时变量。定义result为int类型的变量是基于如下考虑:
(1)result只可能是三种情况之一:地址量;临时变量;变量。
(2)可基于对op位的考虑,当op位为转移指令标识时,result为必为地址量,因此可以有效区分地址量和其他两者。
(3)关于临时变量和变量的区分,也是基于对运算符的考虑,当op为“:=”进行赋值操作时,result位必为变量;而当op为算术运算符时,result位必为产生的临时变量保存运算的中间结果。
3、词法分析器
词法分析的任务是输入源程序,对构成源程序的字符串进行扫描和分解,识别出一个个单词符号,并形成二元式,以便后续的语法分析阶段的操作。
词法分析流程如下图2所示。
4、语法分析和语义分析
在进行语法分析的同时进行语义分析,根据已有的SLR分析表进行相应的分析程序构造。根据当前状态和输出串的当前分析字符决定分析过程,主要执行两个动作:
(1) 移进s。将当前状态移入状态栈,当前分析字符移入符号栈。
(2) 归约r。根据对应的产生式进行归约。若产生式右部有n个字符,状态栈和符号栈就出栈n个字符,并将符号栈的这n个归约为产生式左部的非终结符入符号栈。根据当前状态栈的栈顶状态和符号栈栈顶的非终结符,决定下一个状态是什么,并将这个状态移入状态栈。
4.1算术表达式和赋值语句分析
在对表达式进行算术表达式和赋值语句的分析时,最需要注意的是中间变量的产生。不单独设置中间变量数组进行中间变量的保存,而是通过单词自身的值来反映这是第几个中间变量,通过种别编码来标明其中间变量的身份。
例如产生式: ,其对应的语义子程序为
E.place = newtemp(); //生成中间变量
emit(+,E.place,E.place,E.place) //产生一条四元式
4.2 布尔表达式分析
在对表达式进行布尔表达式分析时,最需要注意的是真假出口的地址。详细的产生式翻译程序说明见编程。
为了防止逻辑运算符对真假口的更新从而干扰条件转移指令的判断,因此在进入布尔表达式分析之前,需要保存此条产生式最开始的真口地址,例如对x and y分析时,需要对x的真出口进行保存,否则这整条表达式的真出口将更新为y的真出口。
由于程序的顺序执行,当布尔表达式分析完成后,真口肯定是当前nxq所指向的四元式地址,而假口需要在程序语句分析过程中才能知道它需要跳转到的地址所在,因此在程序语句分析过程中需要对假口所在的四元式地址进行保存,以便后续回填。
4.3 程序语句分析
程序语句分析流程图如下图3所示。
3.4 显示四元式结果并保存四元式到 pas.med
在语义分析时,已生成中间代码——四元式了。因此,这个阶段对语义分析所产生四元式进行显示和保存,将四元式结果保存在pas.med中。
4、结果分析
(1)词法分析结果
词法分析结果即生成的二元式如下图4所示,
(2)变量名表
(3)程序语句分析结果
(4)四元式分析结果
五、课程设计总结
在此次编译技术的课程设计中,实现了一个小型的编译程序,对输入的高级语言源程序进行词法分析、语法分析和语义分析生成四元式程序。由于时间原因没能去思考完选做部分的汇编语言的翻译程序。在课设中,通过阅读实验指导书,以及上网查找相关资料,我了解了关于如何将一个高级语言程序翻译成四元式输出。这其中贯穿了编译程序的总体结构,通过源程序的输入,对其进行词法分析得出该程序的二元式,然后在此基础上进行语法分析,采取LR分析法,这是一种“移进—归约”的自底向上语法分析方法,选取SLR(1)分析表进行分析;最后在通过语义分析将此程序翻译成四元式的形式。