前面花了两篇文章来介绍词法分析和语法分析,接下来才是比较有意思的部分——一个小型类C编译器的设计和实现(其实是编译原理的课程设计啦!~)我用的是python2.7.13+PyQt来做的。。。事实上,正是从编译原理课程设计开始,我开始转战python,这也算是用python写的第一个程序吧^_^局限性肯定是存在的,只要大家从中能get到一点点于自己而言有用的东西我就灰常开森了O(∩_∩)O哈哈~最后,欢迎大家批评指正!THX~
一、 实验目标
- 了解形式语言与自动机理论在计算机中的应用,掌握文法的构造方法。
- 了解词法分析的原理,并掌握利用语法栈对词法分析的结果进行自上而下的语法分析的方法。
- 了解中间代码的构成形式,掌握语法制导的翻译方法和跳转回填的方法。
- 了解8086汇编语言的语法,并掌握利用语义分析生成的中间代码进行目标代码生成的方法。
- 了解一遍完成的、简单语言的编译方法。
- 在上述基础上,实现一个类C编译器,要求读入指定格式的C语言代码并生成8086汇编代码。(选择了含过程调用的文法)
二、开发前提
1.开发语言
选取python作为本次类C编译器的开发语言,解释器为python2.7.13。(不同版本语法有一定的差异)与C++相比如下所示:
1.1 优点
- python集成了一些基本的字符串操作,具有强大的字符串处理能力。
- Python中没有变量类型定义的概念,变量实现机制实质是封装了的指针,通过使用变量来区分变量类型,省去了变量类型定义的麻烦
- python中提供了list类型和dictionary类型变量的处理方法,通过使用这两个类,可以实现变量的快速索引和查找。
- 提供了turple元组处理方法,可以用list来实现数据表。
1.2 缺点
- 与相比C++而言,Python代码更简洁,但是执行速度不如C++。
- python没有指针的概念,没有变量引用的概念,在参数传递时需要通过数组来进行地址传递。
- Python对开发环境限制较多。
- 综合上述对比,以及想学习一门新的编程语言的想法,我选择python作为开发语言。
2、开发环境
- pycharm2016 professional+Aconoad2(解释器为python2.7.13)
- UI界面使用PyQt4(引入工具包)
- 其余使用到的库均为python的标准库,主要如下:
import json # 处理格式化数据读入的库
import collections # 定义元组的库
import sys # 系统操作库
import re # 正则表达式处理库
import PyQt # 界面采用库
3. C语言语法规则
该部分将从目标语言的词法规则,语法规则入手,介绍对对文法的预处理过程,为后来代码的编写打好了基础。(PS:这里的目标语言就是课程设计中所要求的简易类C语言)
3.1 词法规则
关键字:int | void | if | else | while | return
标识符: 字母(字母|数字)* (注:不与关键字相同)
数值:数字(数字)*
赋值号: =
算符: + | - | * | / | = | == | > | >= | < | <= | !=
界符: ;
分隔符: ,
注释号: /* */ | //
左括号: (
右括号: )
左大括号: {
右大括号: }
字母:| a |....| z | A |....| Z |
数字:0| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
结束符:#
3.2 语法规则
选择了包含过程调用的文法1来进行本次编译器的开发,文法1的详细内容详见实验要求(PS:为了方便我还是贴过来好了),在此主要通过原始文法与处理后文法的对照,来简要介绍对文法1的处理(详见功能模块中文法处理部分)
【文法1_包含过程调用】 (说明: { }中的项表示可重复若干次)
Program ::= <声明串>
<声明串> ::=<声明>{<声明>}
<声明> ::=int <ID> <声明类型> | void <ID> <函数声明> #记下变量类型
<声明类型>::=<变量声明> | <函数声明> #填入变量表 声明语句处理模块
<变量声明> ::= ;
<函数声明> ::=’ (‘<形参>’) ‘<语句块>
<形参>::= <参数列表> | void #新建一张变量表,存储形参和局部变量
<参数列表> ::= <参数> {, <参数>}
<参数> ::= int <ID> #填入形参和局部变量类型表 定义函数类(名字,形参,内变)
<语句块> ::= ‘{‘<内部声明> <语句串>’}’
<内部声明> ::= 空 | <内部变量声明>{; <内部变量声明>}#内变填入刚刚新生成的函数类
<内部变量声明>::=int <ID>
<语句串> ::= <语句>{ <语句> }
<语句> ::= <if语句> |< while语句> | <return语句> | <赋值语句>
<赋值语句> ::= <ID> =<表达式>;
<return语句> ::= return [ <表达式> ] (注:[ ]中的项表示可选)
<while语句> ::= while ‘( ‘<表达式> ‘)’ <语句块>
<if语句> ::= if ‘(‘<表达式>’)’ <语句块> [ else <语句块> ](注:[ ]中的项表示可选)
<表达式>::=<加法表达式>{ relop <加法表达式> } (注:relop-> <|<=|>|>=|==|!=)
<加法表达式> ::= <项> {+ <项> | -<项>}
<项> ::= <因子> {* <因子> | /<因子>}
<因子> ::=num | ‘(‘<表达式>’)’ |<ID> FTYPE
FTYPE ::= <call> | 空
<call> ::=’(’<实参列表> ’) ’
<实参> ::=<实参列表> | 空
<实参列表> ::= <表达式>{, <表达式>}
<ID>::=字母(字母|d数字)*
【程序实例】
int a;
int b;
int program(int a,int b,int c)
{int i;int j;i=0; if(a>(b+c)){j=a+(b*c+1);}else{j=a;}while(i<=100){i=j*2;}return i;
}int demo(int a)
{a=a+2;return a*2;
}void main(void)
{int a;int b;int c;a=3;b=4;c=2;a=program(a,b,demo(c))return;
}
4、8086汇编语言语法规则
8086汇编语言属于CISC指令集,指令集比较庞大,实现的功能较多,但指令长短不一。该部分将从8086汇编的代码结构和语法简介两方面来介绍目标代码的语法规则。
4.1 代码结构
【数据段】
DATA SEGMENT
//该段需要声明程序中所用到的数据
//严格遵循先定义,后使用,除非采用内存分配方式
DATA ENDS【代码段】
CODE SEGMENTASSUME CS:CODE,DS:DATA // 固定语句,完成代码段和数据段的加载工作PPOCEDURE PROC NEAR //调用子函数的代码段 PPOCEDURE ENDP START: MOV AX,DATA //主函数段MOV DS,AXEND START
CODE ENDS //代码段结束标志
4.2 语法简介
8086语言基本指令解释以及针对本次实验的文法所用到的指令和基本语法解释如下:
(1)内存操作指令
MOV OP1,OP2 PO1<---OP2
寄存器,存储器,累加器<---寄存器,存储器,累加器,立即数
(兼有LOAD和STORE指令的功能)
(2)临时变量存储指令
PUSH 压栈
POP 出栈
(3)加减运算指令
寄存器<---寄存器,存储器
ADD OP1,OP2 OP1<--OP1+OP2
SUB OP1,OP2 OP1<--OP1-OP2
(4)乘除运算指令
MUL OPRD EDX:EAX<--EAX*OPRD
DIV OPRD EDX:EAX/OPRD = 商EAX,余数EDX
(5)条件跳转指令
CMP OP1,OP2 OP1-OP2
JZ 等于0跳转
JNZ 非0跳转
JG 大于跳转
JGE 大于等于0跳转
JL 小于0跳转
JLE 小于等于0跳转
二、总体设计
该部分设计主要描述了编译器的总体架构,首先从编译器的原理和功能模块两部分阐述了整个设计,具体如下所示:
1、实验原理
实验中实现的指定文法的类C编译器,故首先需要了解编译程序的流程,并对整个编译器代码框架作出布局,具体如下所示:
1.1 编译程序
编译程序的流程如下图所示:
1.2 文法处理
整个编译程序执行之前,需要对给定的文法进行处理,转化为便于计算机识别的形式。此外,还需要将转化后的文法变为符合LL(1)的文法,具体处理后的文法如下所示:
- 文法名对照(肯定要先把推导公式里的汉字转变成英文表示嘛~)
Program:Prog 声明串:Dstr 声明:Decl 声明类型:Type
变量声明:Vari 函数声明:Func 形参:Vpam 参数列表:Pist
语句块:Bloc 内部声明:Kdec 内部变量声明:Kdev
语句串:Lstr 赋值语句:Asla 语句:Lang return语句:Rela
表达式:Expr while语句:Whla if语句:Ifla 加法表达式:Aexp
FTYPE:FTYP 因子:Fact 项:Item Call:Call
实参:Rpam 实参列表:Rist ID:ID
中间变量:Stem/Ptem/Ltem/Rtem/Atem/Itmp/Ftem/Rltm
- 处理后的文法
注:左边汉字表示的是未处理的文法,右边带标号的字符串表示编译器处理时采用的文法。
1.3 功能划分
按照编译程序的执行流程,将整个编译器划分为如下几个部分来实现:
- 类的定义:ClassType.py
- 词法分析:LEX.py
- 语法分析:SynAly.py
- 语义分析:SemAly.py
- 目标代码生成:CodeGen.py
- 界面生成:compiler.py
其中语法分析模块调用词法分析的结果,并在执行到相应的语义动作时调用语义分析模块,语义分析模块在基本块完成时调用目标代码生成模块生成目标代码。
2.功能模块
在对整个编译器的原理进行分析并完成了文法的处理后,开始详细分析整个编译器的执行流程,并附加上每个模块的数据流,绘制出系统的功能流程图如下所示(实际上也对系统的开发模块做了规划):
内容略多。。。。所以,功能设计部分就放在下一回再说啦~感谢大家持续关注✿✿ヽ(°▽°)ノ✿