理论教育 编译器设计之路:基本结构

编译器设计之路:基本结构

时间:2023-11-04 理论教育 版权反馈
【摘要】:3)分析句子的基本含义,进行初步翻译。中间表示生成将根据输入源程序的语义生成语义等价中间表示形式。中间表示是一种由编译器设计人员定义、使用的形式,对于用户是完全透明的。7.出错处理对于输入源程序的各种错误,编译器必须给出比较准确的出错报告,以便用户及时准确地定位、修改。当然,这是基于编译器预测机制实现的。图1-2描述了编译器各个阶段之间的关系,读者只需参考结构图了解各个阶段的任务及其输入、输出情况

编译器设计之路:基本结构

作为系统软件,编译器的设计与实现是非常复杂的。对于一个相对复杂的系统,通常的解决方法是将系统分解成若干较小且便于处理的小系统,分别实现后将其组织成一个完整的复杂系统,这就是“分治法”的思想。实际上,计算机科学家正是运用这种思想来设计与实现编译器、操作系统、网络通信协议等复杂的大型系统软件的。

编译器的翻译过程是非常复杂的,但就过程本身而言,与自然语言翻译却有不少相近之处。例如,把英语句子翻译为汉语句子时,通常需要经过下列几个步骤:

1)对句子中的每个英语单词进行识别。

2)对句子的语法结构进行分析。

3)分析句子的基本含义,进行初步翻译。

4)修饰译文,使之更加符合汉语的表达习惯。

5)将译文整理书写记录。

编译器的工作过程与自然语言翻译过程比较类似,亦可划分为五个阶段:词法分析、语法分析、语义分析与中间表示生成、代码优化、代码生成。

1.词法分析

词法分析的任务就是对输入的源程序进行扫描分析,识别出一个个的单词(Token),并进行归类。这里的“单词”可以理解为源程序中具有独立含义的不可分割的字符序列,与自然语言中的单词概念有一定区别。一般而言,根据程序设计语言的特点,单词可以分为五类:关键字、标识符、常量、运算符、界符。以一个C语言的条件语句为例:

if(aa&&10==0)aa=100;词法分析的结果是识别出如下的单词符号:

978-7-111-32164-4-Chapter01-4.jpg

这里,读者只需了解词法分析的任务即可。其算法实现将在第2章中详述。

2.语法分析

语法分析的任务就是在词法分析的基础上,根据程序设计语言的语法规则(文法),把单词流分解成各类语法单位(语法范畴),如“语句”、“表达式”等。理论上讲,通过语法分析,编译器可以准确无误地判断输入源程序是否满足语言的语法规则。例如,语法分析可以判断如下语句是错误的。

ifaa%%10==9aa=100;

T0rIl<UI1十十:

不过,实际情况并非完全如此,这主要与文法定义的细化程度有直接的关系。当程序设计语言的设计人员把文法定义得比较宽泛时,也就意味着依据此语法规则,编译器不能在语法分析阶段发现所有的语法错误,只能将错误遗留给后续阶段处理。表面上看,语法分析并不像词法分析有直观的输出结果,而仅仅完成了输入源程序的语法判定工作。实际上,语法分析是编译器前面三个阶段(合称为前端)的主控模块。语法分析的设计思想与算法实现是经典编译理论重点讨论的话题,将在第3章中详述。(www.daowen.com)

3.语义分析与中间表示生成

语义分析与中间表示生成的任务就是在语法分析的基础上,分析各语法单位的含义,并进行初步的翻译,即生成中间表示形式。有时,这两个任务是密不可分的,故通常将其合并为一个阶段讨论。语义分析主要是检查输入源程序的语义是否正确,例如,变量使用前是否定义、同一作用域内变量是否重名等。中间表示生成将根据输入源程序的语义生成语义等价中间表示形式。中间表示是一种由编译器设计人员定义、使用的形式,对于用户是完全透明的。中间表示形式的定义是值得深入研究的,因为它直接决定了编译器前、后端的设计复杂度,也决定了编译器前端与目标语言之间的耦合程度。中间表示的形式也非常多,包括四元组、三元组、语法树、DAG图等,并不一定是读者理解的通常的代码形式。例如,lcc的中间表示就是一种DAG的形式。当然,近似于汇编指令形式的四元组、三元组可能是最为常见的中间表示形式。由于这个阶段的内容繁多,笔者将以符号表系统、中间表示、表达式语义三个主题予以介绍。相关内容分布在本书的第4~6章中。

4.代码优化

代码优化的任务就是对生成的中间表示进行优化处理,得到占用空间较小、执行效率较高的目标代码。由于中间表示是由计算机自动生成的,不可能像手工编码那样精炼,因此,编译器需要借助优化处理对目标代码进行精简。随着前端技术的相对成熟,优化技术逐渐成为编译技术领域的核心研究课题之一。第7章将详细讨论优化的相关话题。

5.代码生成

代码生成的任务就是将中间表示形式翻译成目标语言描述的程序代码。本阶段是与目标计算机硬件系统结构密切相关的,其工作也非常复杂,如寄存器的调度、指令的选择、指令的调度等。并且,这个阶段还涉及许多与目标计算机硬件系统有关的优化技术,称为“指令级优化”。通常,目标代码的形式可以是汇编语言代码、机器语言代码、其他语言的代码。早期,目标代码多为机器语言代码。然而,随着汇编器、链接器的成熟,汇编语言代码逐渐取代了机器语言代码,成为目标代码的首选。近年来,随着虚拟机技术的出现与发展,目标代码的概念可能还会改变。例如,由于.NET Framework的盛行,许多编译器(如Delphi.NET、ML、SmallTalk等)都可以生成面向CLR的目标程序。第8~9章将详细讨论运行时环境与代码生成的相关技术。

以上五个阶段是一种典型的分类观点。事实上,并非所有的编译器都包含这五个阶段。例如,有些简单语言的编译器完全可以省去中间表示形式,直接生成目标代码。再如,一些并不苛求优化的编译器完全可以省去优化阶段。这里所提及的五个阶段只是大多数编译器的设计经验而己。除了以上五个阶段之外,有些编译器还包括两个非常重要的组成部分:符号表管理、出错处理。

6.符号表管理

符号表是一系列用于记录各个分析阶段所获取信息(如变量名、作用域、函数形参等)的数据表格,这些表格的维护贯穿于整个编译过程。显然符号表的设计和管理是编译器构造过程中的一项极其重要的任务。这里,设计者更多关注的是表格的完整性与访问效率。第4章将详细讨论符号表的构造。

7.出错处理

对于输入源程序的各种错误,编译器必须给出比较准确的出错报告,以便用户及时准确地定位、修改。编译过程的每一个阶段都可能检测出错误,其中,绝大多数错误可以在编译的前三个阶段检测出来。当然,真正的商用编译器并不限于此,可能还涉及更复杂的出错恢复。出错恢复主要是当编译器检测到错误之后,尽可能按照语义修正错误。注意,出错恢复的目的并不在于真正修复用户程序,而只是试图一次检测更多的错误。当然,这是基于编译器预测机制实现的。

图1-2描述了编译器各个阶段之间的关系,读者只需参考结构图了解各个阶段的任务及其输入、输出情况即可。

978-7-111-32164-4-Chapter01-5.jpg

图1-2 编译器结构图

这里,还有必要了解一个比较重要的概念:端(End)。

根据完成任务不同,可以将编译器的组成部分划分为前端(Front End)与后端(Back End)。前端主要指与源语言有关但与目标机无关的部分,包括词法分析、语法分析、语义分析与中间表示生成。后端主要指与目标机有关的部分,包括代码优化和目标代码生成等。“端”概念的提出对于编译技术的发展起到了至关重要的作用,它使编译器的框架更明晰,更利于集成与构造。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈