编译原理
- 编译原理
- 程序设计语言分类
- 翻译
- 编译
- 解释
- 编译的转换过程
- 两阶段的转换
- 三阶段的转换
- 编译程序的工作
- 词法分析
- 语法分析
- 中间代码生成
- 优化
- 目标代码生成
- 表格与表格管理
- 出错处理
- 语句翻译实例过程
- 编写编译程序方式
编译原理
是介绍高级程序设计语言变换成计算机硬件所能识别的机器语言,以便计算机进行处理。
程序设计语言分类
- 高级语言
- 汇编语言
- 机器语言
在计算机上如何执行一个高级语言程序?
- 把高级语言程序翻译成机器语言程序
- 运行所得的机器语言程序求得计算结果
翻译
是指能把某种语言的源程序,在不改变语义的条件下,转换成另一种语言程序—目标语言程序。
编译
专指由高级语言转换为低级语言(汇编语言或机器语言),相当于现实中的笔译。
解释
接受某高级语言的一个语句输入,进行解释并控制计算机执行,马上得到这句的执行结果,然后再接受下一句。
以源程序作为输入,不产生目标程序,一边解释一边执行。
优点:直观易懂,结构简单,易于实现人机对话。
缺点:效率低,因为不产生目标程序,所以下次要用的话还是要再解释一遍。
编译的转换过程
两阶段的转换
编译(compile) — 运行(run)
比如一个Pascal语言源程序a.pas,经过编译程序进行编译,转换成包括机器语言(0和1)的目标代码的可执行程序a.exe。
三阶段的转换
编译 — 汇编 — 运行
比如一个C语言源程序a.c,经过编译程序转换成汇编语言,再经过汇编程序转换成目标代码a.obj或a.exe,此处因为是C语言,所以转换成的目标代码为a.obj文件(也是由机器码构成的)。
接下来就是连接(link)过程,该过程不属于编译过程,就是将有关的目标文件彼此相连接,经过连接(link)才将a.obj转换成a.exe可执行文件。
编译程序的工作
先看自然语言的翻译
- 识别出句子中的一个个单词
- 分析句子的语法结构
- 根据句子的含义进行初步翻译
- 对译文进行修饰
- 写出最后译文
编译程序的工作与自然语言的翻译极为类似。
编译程序的工作一般分为五或六个步骤(语义分析阶段可以再中间代码生成阶段完成):
词法分析:分析源程序当中的词写得对不对。
语法分析:分析句子是什么样的功能。
语义分析和中间代码生成:语义分析根据要完成的功能看看这句话的意思对不对,中间代码生成方便句子转换成0,1的中间过程,句子到中间代码很容易,中间代码到二进制也很容易。
优化:代码运行的时间空间效率是不是很高,进行优化。
目标代码生成:最终得到目标代码。
词法分析
主要任务是对输入的源程序字符串进行扫描和分解,识别出一个个的单词,并转化成机器比较容易使用的内码形式。一般内码可以用二元式(类号,内码)表示。
通过词法分析一般可以识别出以下5类单词:
- 基本字(关键字):void,int,float等
- 标识符:a,b,c,d,x,y等
- 整常数:50等
- 运算符:+,*,=等
- 界限符:{},;,()等
描述词法规则的有效工具是正规式和有限自动机。
正规式是用来确定单词是不是符合程序语言所要求的规范,用有限自动机来确定。
总之,词法分析就是扫描源程序字符串,按词法规则识别出正确的单词,并转换成统一规格(类号,内码)交语法分析使用。
语法分析
该阶段的任务就是组词成句。每一种语言都有一组规则,称之为语法规则或文法。按照这些文法,可以由单词组成语法单位,如短语、语句、过程和程序。语法分析就是通过语法分解,确定整个输入串是否能构成语法上正确的句子和程序等。
赋值语句的语法规则写成Backus-Naur-Form式,简称BNF。其形式是A::=B|C,也就是将A定义为B或C。比如:<句子>::=<主><谓><宾>,也就是句子由主语谓语宾语组成。还有其他语句的语法规则,如条件语句、循环语句、过程调用语句等规则。
语法分析有两种方法:推导(derive)和归约(reduce)。分别有最左推导、最右推导、最右归约、最左归约。
推导是从文法的开始符号开始,按照语法规则,每次选择某规则右部的一个候选式取代左部直至识别了句子或者找出错误为止。比如:采用最右推导语句x:=a+b*50的分析如下:
其中A就是Assignment的缩写,意思是赋值语句,称A为规则的开始符号。如果语句的语法错误的话,推不到原语句。
语法分析过程也可以用一棵倒着的树来表示,也就是语法树。叶子节点从左至右组合起来就是原语句x=a+b*50。
中间代码生成
主要任务是对语法分析识别出的各类语法范畴,分析其含义,进行和初步翻译,产生介于源代码和目标代码之间的一种代码。源代码生成这种代码很容易,并且这种代码不依赖于机器但又便于产生依赖于机器的目标代码。
分为两阶段工作
- 对每种语法范畴进行静态语义检查。
- 若语义正确,就进行中间代码的翻译。
中间代码有多种形式:四元式、三元式、逆波兰式。四元式使用得最广。
例如将x=a+b*50变成中间代码,下图可以看作将数存入寄存器。
由上表该源语句x:=a+b*50转换成中间代码形式是:
中间代码是为后续的优化和目标代码生成提供方便,因此中间代码的选择往往与所采用的优化技术和计算机硬件结构有关。
优化
优化的任务在于对前一阶段产生的中间代码进行加工变换,使得在最后阶段能产生出更为高效(省时间、空间)的目标代码。
比如上面的中间代码可变换为如下两句四元式代码:
T1:=B∗50.0T1:=B*50.0T1:=B∗50.0 x:=a+T1x:=a+T1x:=a+T1
优化主要包括:删除公共子表达式、合并已知量、删除无用赋值、循环优化等。优化所依据的原则是程序的等价变换规则。
例如,把程序段
转换成四元式和经优化后产生的四元式用下表表示:
显然,经优化后循环体缩小了,原来要做701条指令现在只做503条指令;原来要做300次加法,200次乘法,现在只做300次加法,2次乘法。编译程序所产生的目标代码质量的高低,主要取决于代码优化程序功能的强弱。
目标代码生成
这一阶段的主要任务是把经过优化的中间代码转化成特定机器上的低级语言代码。转换过程需涉及具体机器的指令系统以及寄存器分配等硬件功能。
例如,上述经优化后的中间代码可生成如下用汇编语言表示的目标代码:
LOAD R2,b
MUL R2,50.0
LOAD R1,a
ADD R1,R2
STORE R1,x
目标代码的形式
- 绝对指令代码:可立即执行的目标代码。也就是.exe文件。
- 汇编指令代码:汇编语言程序,需要通过汇编程序汇编后才能运行。
- 可重定位指令代码:先将各目标模块连接起来,确定变量、常数在主存中的位置,装入主存后才能成为可以运行的绝对指令代码。也就是.obj文件。
表格与表格管理
表格作用:
用以记录源程序的各种信息以及编译过程中的各种状况。
编译的五个阶段都需要与表格打交道,以便后继阶段使用,也即在编译过程的各个阶段都有查造表、填表等功能。
与编译前三阶段有关的表格有:
符号表、常数表、标号表、分程序入口表、中间代码表等。
-
符号表:登记源程序中的常量名、变量名、数组名、过程名等的性质、定义和引用状况。
-
常数表:登记源程序中出现的各种类型字面常数(直接量)的值。
-
标号表:登记源程序中出现的标号的定义和引用情况(此表可与符号表合并)。
-
分程序入口表:登记过程的层号、分程序符号表的入口(指分程序结构的语言)等,有时也可以并入符号表。
-
中间代码表:记录四元式序列的表。
例如,对下列FORTRAN程序段:
有下列各表:
符号表:记录了源程序中出现的三个变量名M、N和K的有关性质。
NAME | INFORMATION |
---|---|
M | 整型、变量地址 |
N | 整型、变量地址 |
K | 整型、变量地址 |
常数表(CT):记录了常数1和4的值(已经是内部二进制代码)
值 |
---|
1 |
4 |
入口名表:记录了子程序名INCWAP的入口地址,即为四元式表的序号1。
NAME | INFORMATION |
---|---|
… | … |
INCWAP | 二目子程序、四元式序号1 |
标号表:记录了标号10对应的四元式序号4。
NAME | INFORMATION |
---|---|
… | … |
10 | 四元式序号4 |
四元式序列表:记录了源程序翻译成的四元式序列。
序号 | OP | ARG1ARG_1ARG1 | ARG2ARG_2ARG2 | RESULT |
---|---|---|---|---|
1 | link | – | – | – |
2 | actpar | INCWAP | 1 | M |
3 | actpar | INCWAP | 2 | N |
4 | + | M | 1 | K |
5 | + | N | 4 | M |
6 | := | K | – | N |
7 | paract | INCWAP | 1 | M |
8 | paract | INCWAP | 2 | N |
9 | return | – | – | – |
下面解释各行序号的意义:
- (link, _ , _ ,_)表示保护返回地址和有关寄存器内容,它相当于宏
- (actpar,INCWAP,1,M)表示传递第一个实变元到M单元
- (actpar,INCWAP,2,N)表示传递第二个实变元到N单元
- (+,M,1,K)表示K:=M+1
- (+,N,4,M)表示M:=N+4
- (:=,K,_,N)表示N:=K
- (paract,INCWAP,1,M)表示把M送回到第一实变元所指地址单元
- (paract,INCWAP,2,N)表示把N送回到第二实变元所指地址单元
- (return, _ ,_ ,_)表示恢复寄存器内容,并把控制返回到调用程序
表格管理程序用来对表格的构造、查找和更新。对编译程序而言这只是工具,是事先编制好、供需要时调用的。选择一种好的表格结构和查找算法对于构造编译程序来说是至关重要的。这部分在数据结构中学过。
出错处理
任务是如果源程序有错误,编译程序应设法发现错误,并把有关的出错信息报告给用户。由出错处理程序完成。
错误类型:
- 语法错误:在词法分析和语法分析阶段检查出来。
- 语义错误:一般在语义分析阶段检测。
语句翻译实例过程
要翻译的语句为:
position:=initial+rate∗60position:=initial+rate*60position:=initial+rate∗60
1.经过词法分析器,得:
id1:=id2+id3∗60id1:=id2+id3*60id1:=id2+id3∗60
2.经过语法分析器,得:
3.经过语义分析器,得:
4.经过中间代码生成,得:
temp1:=inttoreal(60)temp1:=inttoreal(60)temp1:=inttoreal(60)temp2:=id3∗temp1temp2:=id3*temp1temp2:=id3∗temp1temp3:=id2+temp2temp3:=id2+temp2temp3:=id2+temp2id1:=temp3id1:=temp3id1:=temp3
5.经过优化,得:
temp1:=id3∗60.0temp1:=id3*60.0temp1:=id3∗60.0id1:=id2+temp1id1:=id2+temp1id1:=id2+temp1
6.经过目标代码生成器,得:
moveid3,R2move \ \ id3,R2move id3,R2move#60.0,R2move \ \ \#60.0,R2move #60.0,R2moveid2,R1move\ \ id2,R1move id2,R1moveR2,R1move \ \ R2,R1move R2,R1moveR1,id1move \ \ R1,id1move R1,id1
编写编译程序方式
- 直接用机器语言编写编译程序
- 用汇编语言编写编译程序
- 用高级语言编写编译程序(普遍采用的方法)
- 自编译
- 编译工具:LEX(词法分析)与YACC(用于自动产生LALR分析表)
- 移植(同种语言得编译程序在不同类型得机器之间移植)