1. 首页 > 经验  > 正文

编译器结构

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

基本介绍

中文:编译器结构外文名:Compiler construction所属学科:计算机网路套用领域:系统设计工作步骤:7步编写语言:Pascal语言

介绍

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

工作过程

编译器的翻译过程是非常複杂的,但就过程本身而言,与自然语言翻译却有不少相近之处。例如,把英语句子翻译为汉语子时,通常需要经过下列几个步骤:
1)对句子中的每个英语单词进行识别
2)对句子的语法结构进行分析
3)分析句子的基本含义,进行初步翻译。
4)修饰译文,使之更加符合汉语的表达习惯
5)将译文整理书写记录
编译器的工作过程与自然语言翻译过程比较类似,亦可划分五个阶段词法分析、语法分析、语义分析与中间表示生成、代码最佳化、代码生成。
1.词法分析
词法分析的任务就是对输入的源程式进行扫描分析,识别出一个个的单词(Token),并进行归类。这里的"单词"可以理解为源程式中具有独立含义的不可分割的字元序列,与自然语言中的单词概念有一区别。一般而言,根据程式设计语言的特点,单词可以分为五类关键字、标识符、常量、运算符、界符。以一个C语言的条件语句为例
if(aa&&10==0)aa=100;
词法分析的结果是识别出如下的单词符号
关键字
界符
标识符
运算符
常量
运算符
if
(
aa
&&
10
==
常量
界符
标识符
运算符
常量
界符
0
)
aa
=
100
;
2.语法分析
语法分析的任务就是在词法分析的基础上,根据程式设计语言的语法规则(文法),把单词流分解成各类语法单位(语法範畴),如"语句"、"表达式"等。理论上讲,通过语法分析,编译器可以準确无误地判断输入源程式是否满足语言的语法规则。例如,语法分析可以判断如下语句是错误的。
ifaa%%10==9aa=100; for(i<0)i++;
不过,实际情况并非完全如此,这主要与文法定义的细化程度有直接的关係。当程式设计语言的设计人员把文法定义得比较宽泛时,也就意味着依据此语法规则,编译器不能在语法分析阶段发现所有的语法错误,只能将错误遗留给后续阶段处理。表面上看,语法分析并不像词法分析有直观的输出结果,而仅仅完成了输入源程式的语法判定工作。实际上,语法分析是编译器前面三个阶段(合称为前端)的主控模组。
3.语义分析与中间表示生成
语义分析与中间表示生成的任务就是在语法分析的基础上,分析各语法单位的含义,并进行初步的翻译,即生成中间表示形式。有时,这两个任务是密不可分的,故通常将其合併为一个阶段讨论。语义分析主要是检查输入源程式的语义是否正确,例如,变数使用前是否定义、同一作用域内变数是否重名等。中间表示生成将根据输入源程式的语义生成语义等价中间表示形式。中间表示是一种由编译器设计人员定义、使用的形式,对于用户是完全透明的。中间表示形式的定义是值得深入研究的,因为它直接决定了编译器前、后端的设计複杂度,也决定了编译器前端与目标语言之间的耦合程度。中间表示的形式也非常多,包括四元组、三元组、语法树、DAG图等,并不一定是读者理解的通常的代码形式。例如,lcc的中间表示就是一种DAG的形式。当然,近似于彙编指令形式的四元组、三元组可能是最为常见的中间表示形式。
4.代码最佳化
代码最佳化的任务就是对生成的中间表示进行最佳化处理,得到占用空间较小、执行效率较高的目标代码。由于中间表示是由计算机自动生成的,不可能像手工编码那样精炼,因此,编译器需要藉助最佳化处理对目标代码进行精简。随着前端技术的相对成熟,最佳化技术逐渐成为编译技术领域的核心研究课题之一。
5.代码生成
代码生成的任务就是将中间表示形式翻译成目标语描述的程式代码。本阶段是与目标计算机硬体系统结构密切相关的,其工作也非常複杂,如暂存器的调度、指令的选择、指令的调度等。并且,这个阶段还涉及许多与目标计算机硬体系统有关的最佳化技术,称为"指令级最佳化"。通常,目标代码的形式可以是彙编语言代码、机器语言代码、其他语言的代码。早期,目标代码多为机器语言代码。然而,随着彙编器、连结器的成熟,彙编语言代码逐渐取代了机器语言代码,成为目标代码的首选。近年来,随着虚拟机技术的出现与发展,目标代码的概念可能还会改变。例如,由于.NET Framework的盛行,许多编译器(如Delphi .NET、ML、SmallTalk等)都可以生成面向CLR的目标程式。
以上五个阶段是一种典型的分类观点。事实上,并非所有的编译器都包含这五个阶段。例如,有些简单语言的编译器完全可以省去中间表示形式,直接生成目标代码。再如,一些并不苛求最佳化的编译器完全可以省去最佳化阶段。这里所提及的五个阶段只是大多数编译器的设计经验而已。除了以上五个阶段之外,有些编译器还包括两个非常重要的组成部分:符号表管理、出错处理。
6.符号表管理
符号表是一系列用于记录各个分析阶段所获取信息(如变数名、作用域、函式形参等)的数据表格,这些表格的维护贯穿于整个编译过程。显然符号表的设计和管理是编译器构造过程中的一项极其重要的任务。
7.出错处理
对于输入源程式的各种错误,编译器必须给出比较準确的出错报告,以便用户及时準确地定位修改。编译过程的每一个阶段都可能检测出错误,其中,绝大多数错误可以在编译的前三个阶段检测出来。当然,真正的商用编译器并不限于此,可能还涉及更複杂的出错恢复。出错恢复主要是当编译器检测到错误之后,儘可能按照语义修正错误。注意,出错恢复的目的并不在于真正修复用户程式,而只是试图一次检测更多的错误。当然,这是基于编译器预测机制实现的。

编译器结构

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

语言

目标代码是机器语言或彙编语言,彙编语言可以通过彙编器生成机器码。彙编语言的定义取决于CPU的体系架构,目前主要有三种:x86/x64, ARM, MIPS。
中间代码是虚拟机的机器语言,虚拟机目前主要有四种:CLR, JVM, Parrot, LLVM。CLR用于.Net平台,JVM用于Java语言,这两个是基于栈的虚拟机。Parrot用于脚本语言,比如Perl,Python,Ruby等;LLVM用于C、C++等语言,这两个是基于暂存器的虚拟机。在性能上比较而言,基于暂存器的虚拟机优于基于栈的虚拟机。
标準Pascal语言作为设计蓝本,主要有如下几个原因
(1)Pascal语言是一门严谨且优美的程式设计语言。
(2)Pascal语言功能非常强大,适合各种系统软体、套用软体设计。
(3)Pascal语言数据类型非常丰富。例如,集合类型、函式类型、指针类型等。
(4)Pascal语言的语义複杂度不如C语言,故有利于编译器设计与实现。同时,便于学者学习理解。
正由于上述优点,Pascal仍然是算法设计的主要描述语言,也是IOI(国际奥林匹克信息学竞赛)三种参赛程式设计语言之一。

本文由'虞青梅'发布,不代表演示站立场,转载/删除联系作者,如需删除请-> 关于侵权处理说明