编译原理_编译过程概述

文章目录

  • 概述
  • 词法分析
  • 语法分析
  • 语义分析
  • 中间代码生成
  • 代码优化
  • 目标代码生成
  • 符号表管理(表格管理)
  • 出错处理

概述

编译程序即是将高级语言书写的源程序翻译成与之等价的目标程序(汇编语言或机器语言)。其工作可分为六个阶段,见下图:

编译原理_编译过程概述_第1张图片

对于编译的各个阶段,逻辑上可以划分为前端和后端两部分。前端包括词法分析到中间代码生成中各个阶段的工作,后端则是优化及目标代码生成的阶段。

以中间代码为分水岭的原因是把编译过程分解为与机器有关和无关两个部分。这样对同一种程序语言只需要一个前端,只要针对不同的机器开发不同的后端即可,前后端结合形成编译器。当语言改动时也只涉及前端部分的维护。而对于不同的程序语言只要有不同的前端,然后与同一个后端结合就可以得到程序语言在某种机器上的编译器了。

下面简单介绍一下每个步骤(模块)的目的和工作,注意,下文步骤中使用的X,Y,Z等符号,在词法分析后实际上会是一个内部标识符,之所以照用原文只是为了方便理解。

词法分析

源程序可以看做是多行的字符串,词法分析是编译过程的第一阶段,其任务是对源程序逐字符扫描,从中识别出一个个“单词”,“单词”又叫做符号,它是程序语言的基本语法单位,如关键字(保留字)、标识符、常数、运算符、分隔符等。

词法分析程序输出的“单词”以二元组的形式输出,即其种类和值。词法分析过程依据语言的词法规则,即描述“单词”结构的规则。

如:

PASCAL源程序一则:

VAR X,Y,Z:real;
X:= Y+Z*60;

词法分析阶段将语句分割成以下单词序列:

类型 类型
1. 关键字 VAR 9. 分号 ;
2. 标识符 X 10. 标识符 X
3. 逗号 , 11. 赋值号 :=
4. 标识符 Y 12. 标识符 Y
5. 逗号 , 13. 加号 +
6. 标识符 Z 14. 标识符 Z
7. 冒号 : 15. 乘号 *
8. 标准标识符 real 16. 整常数 60
17. 分号 ;

语法分析

在词法分析结果的基础上,语法分析是根据语言的规则将单词符号序列分解成语法单位,如“表达式”、“语句”、”程序“等。语法规则就是语法单位的构成规则,通过语法分析确定整个输入串能否构成一个语法上正确的程序。如果程序没有错误,语法分析后就能正确的构造出 语法树;否则就会指出语法错误,并给出诊断。

词法分析和语法分析本质上都是在对源程序的结构进行分析。

如:

根据上面词法分析的结果可以构造出的语法树如下图:

编译原理_编译过程概述_第2张图片

语义分析

语义分析阶段主要分析语法结构的含义,检查源程序是否包含静态语义错误,并收集类型信息供代码生成阶段使用。只有语法和语义都正确的源程序才能翻译成正确的目标程序。

语义分析的主要工作就是对类型进行分析和检查,一般类型检查包括两点:类型载体及在其上的运算。如,整除取余运算符只能对整数使用,如果运算对象是浮点数就认为是类型错误。

在确定源程序语法和语义后,就可以对其进行翻译并给出源程序的内部表示。对于声明语句,要记录所遇到的符号信息,此阶段还负责填写校验 符号表,对于可执行语句则分析其结构合理性,并补充必要的步骤。

如:

对于上面的变量声明语句VAR X,Y,Z:real;应生成下面的符号表(real类型占4位)

符号 类型 逻辑地址
X real 0
Y real 4
Z real 8

对于上面变量赋值语句X:= Y+Z*60;生成的语法树经过语义分析后应如下图,其中增加了一个语义处理点 inttoreal,它的作用是将整型数转换为浮点数:

编译原理_编译过程概述_第3张图片

中间代码生成

该阶段是根据语义分析的结果生成中间代码,“中间”即意味着并非可执行的机器码,它是一种简单含义明确的记号系统,有若干种形式,但所有中间代码的共同特征均为与具体机器无关,类似于算法伪代码的作用。最常用的中间代码是一种与汇编高度类似的三地址码,采用四元式实现,其形式为:(运算符, 运算对象1, 运算对象2, 运算结果)

如:

对于上述提到的赋值语句X:= Y+Z*60;可根据语义分析的结果生成以下四元序列式:

  1. (inttoreal, 60, -, t1)
  2. (*, Z, t1, t2)
  3. (+, Y, t2, t3)
  4. (:=, t3, -, X)

其中t1, t2, t3均为编译程序生成的临时变量,用于存放临时的结果。

代码优化

由于编译器将源程序翻译成中间代码的工作是按固定模式进行的,因此可以发现中间代码中往往在时间和空间上均有较大浪费。当要生成高效的目标代码则必须进行优化,优化可以在中间代码生成阶段进行,也可以在目标代码生成阶段执行。

由于中间代码与具体机器无关,因此对中间代码的优化主要是对程序的控制流和数据流的分析之上

如:

对上述赋值语句X:= Y+Z*60;生成的四元序列式优化,可以发现60是已知的常数,将它转换为浮点数60.0也可以在编译时完成,没有必要生成一个四元式。同时,发现t3的作用只是将结果传递给X,同样也不需要生成一个四元式。因此可优化成如下等价四元序列式:

  1. (*, Z, 60.0, t1)
  2. (+, Y, t1, X)

当然这只是很简单的优化,实际上的优化要复杂的多,会涉及公共子表达式的提取等更多技术。

目标代码生成

目标代码生成是编译器的最后一个阶段,这一阶段将中间代码转化为目标机器上的绝对指令代码、可重定位指令代码或汇编指令代码,这一阶段与具体机器密切相关。

如:

使用两个寄存器R1和R2将上述的四元序列式生成下面的目标代码:

  1. MOVF Z, R2
  2. MULF #60.0, R2
  3. MOVF Y, R1
  4. ADDF R2, R1
  5. MOVF R1, X

符号表管理(表格管理)

符号表的主要作用就是记录符号的信息,以辅助语义检查和代码生成。在编译过程中需要对符号表进行快速的查找插入、修改、删除等操作。

符号表的建立一般始于词法分析阶段,但也可以始于词法分析和语义分析阶段。有时候符号表的使用还会伴随到目标代码的运行阶段。

出错处理

源程序不可避免的会出现错误,这些错误大致分为静态错误和动态错误。

动态错误又称为动态语义错误,它们发生在程序运行时,例如变量为0时做除数、引用数组元素下标错误等。

静态错误是编译阶段发现的程序错误,可分为语法错误和静态语义错误,如关键字拼写错误、标点符号错误、表达式缺少操作数、括号不匹配等问题就是语法错误,语义错误主要指语义分析阶段发现的运算符与运算对象类型不合法等错误。

在编译发生错误时,编译过程应想办法能够绕过去,以便在一次编译中发现尽可能多的错误。

你可能感兴趣的:(计算机公共知识,编译原理)