阅读 303

【国外精选课程】编译原理入门之编译阶段概述

编译原理入门及编译阶段概述

特别说明

这是一个由simviso团队进行的关于编译原理入门内容分享的翻译文档,内容并非直译,其中有一些是译者自身的思考。

视频翻译文字版权归 simviso所有,未经授权,请勿转载

第一集:b23.tv/av65512165

第二集:b23.tv/av65673080

参与人员名单:

1. 为什么要学习编译器

编译器涉及了计算机科学领域的很多方面。包括高级和低级的编程范式,上下文无关的文法定义,栈(Stack)、链表、哈希表(hash table)、图和树这些动态数据结构的应用,以及计算机内存的访问和管理方式。对于计算机专业的学生而言,可能会去开发属于他们自己的高级编程语言以及配套的编译器。好消息是了解这些是一条很好的成长捷径。通过这个文档展示了我们即将开始的细节,如果你想的话,里面涉及的很多工具可以帮助你走的更远。

这是关于编译原理入门的第一部分。如你所见,编译器涉及了计算机科学领域的很多方面。的确,这个主题所涉及的范围实在太广。以至于大家可能会产生比视频所给出的(内容)更多的疑问(译者注:即基于给定内容产生更多想法)。尽管如此,这些视频将会让你深入了解许多常见编译器的一些概念以及功能。通过这个系列的第一部分来对各个编译阶段进行概述。

2. 编译阶段概述

编译指的是将程序员用某种高级语言编写的源代码转换成目标代码,即计算机能够认识的可执行机器代码。编译是由一个叫编译器的程序完成的。最初,没有一种方式可以用来编译源代码。也就是说,新的编程语言和它的编译器通常是一起开发的。一个编译器的内部工作方式很大程度取决于被编译源代码的语言。同时因为程序需要被编译运行在特定类型的处理器上,所以,具体如何实现编译器还取决于目标机器的体系结构。

所以该怎样开发一个好的编译器?编译器设计之初就是为了必须让高级语言所写的程序正确运行。而且必须检测到所有的静态错误,也就是说它应该识别所有的不符合编程语言规则的错误。不要指望编译器来捕获动态错误,这些错误只能在运行时检测到,如果没有被捕获到可能会导致程序崩溃。也不要指望编译器去发现代码逻辑上的错误。也就是说这些错误不会使程序运行时崩溃,但会导致程序输出结果有误。

一个编译器应该做出明确的有意义的诊断。如果程序在编译时错误就被发现,那么输出的错误信息就应该是明确的,并且精准地指出源代码错误的位置。一个好的编译器不会一遇到错误就停止处理。在一次尝试对源代码进行编译的过程中,编译器应尽可能的发现并报告更多的错误。一个好的编译器会生成最佳的机器码。这里,最佳意味着完美。虽然任何编译器都不可能生成完美的机器码,但是一个好的编译器会尽最大努力优化编译出来的机器码。为了可以让机器执行的效率更高,它会寻找语句和构造方法的替代方案。编译速度必须要快。这样,程序员在敏捷开发过程中多次编译和测试他们的源代码的时候,完全不必因为一次次改变而一次次等待编译。编译器应当易于使用。许多编译器可以从命令行启动,并提供大量选项来控制与目标代码一起生成的调试信息量。还有一些其他编译器在集成开发环境下加入了一个内置的简单菜单选项。当尝试从IDE中运行一些新代码时,编译器甚至可能会自动启动。在各个组件之间建立一个耦合度尽可能小的模块化编译器需要很多的知识储备。这种方式允许编译器的各个部分可以被多种编程语言和多种目标机器架构平台进行重用。最后,就如同好的软件一样,一个好的编译器必须要有一个详细的文档,而且易于维护。

当我们对一个程序进行编译的时候,会经历如下三个过程。词法分析就好比是,我们写了一个简单的英文句子,然后将它分割成一个个的单词和标点符号。语法分析器就是用来检查这个句子是什么意思。机器码生成的这个过程就是将一句话翻译成另一种语言。编译器生成了能被处理器所理解的0和1的机器码,但它同时也对该机器码进行了速度和空间上的优化。因此,代码生成以及优化通常被作为一个阶段来认知。从理论上而言,编译器的一个阶段会跟着下一个阶段,但你很快就知道实际并非如此。

例如,词法分析和语法分析是一起进行的。在高级编程语言编译的时候,要进行词法和语法分析。但我们的目标机器所处架构平台是独立的,因此,词法和语法分析被称为编译器的前端操作。另一方面,代码生成与优化仅在根据目标机器的指令集生成机器代码时使用。这个阶段在编译过程中被称之为后端操作。

词法分析是由编译器中的词法分析器来执行的。这通常也被称为Lexer(词法分析器)或者说scanner(扫描器)。语法分析则是由语法分析器来执行,通常也叫parser(语法分析器)。这里,程序源码将作为一个文本流来输入到词法分析器中。词法分析器将源程序的各个单词转换成词法单元流并输出。并在请求时,将词法单元流一个一个送入到语法分析器中。语法分析器会构建出用于代表源程序的抽象语法树。抽象语法树是一种动态数据结构,用来表示源程序的层级结构。当语法树构建完毕,编译器会使用它检查源代码是否遵从了编程语言的语法规则。(编译时)词法分析器同时会创建一个符号表,符号表在编译过程的所有阶段都会被频繁的访问和修改。符号表包含了程序员在源代码中使用的名称的信息,例如变量和函数名。对于某些编译器,抽象语法树是源代码到机器码的唯一中间表现形式。抽象语法树是语法分析器的输出,也是编译器前端的最终输出。

接下来抽象语法树将被直接转换为机器码。然而,有些编译器在前端做了更多的工作。在构造抽象语法树之前,编译器可能会先构建一棵简易的树。我们称它为解析树(语法分析树),它是源程序的一种轻量表示。通过遍历抽象语法树获得的信息与符号表的信息进行组合。生成源代码的另一种中间表示形式。解析这颗树的一系列步骤,看起来像汇编代码。三位址码是中间代码的一种表现形式。一些编译器在构建抽象语法树后会创建更低级的代码。其他的一些编译器在构建抽象语法树时会省略中间的代码。实际上,一些编译器会通过构建语法树来完成它所允许的语法检测和语义检测。但最终只会留下中间代码,低级的中间代码同样可以被优化。在独立的目标机器架构上进行优化。低级的中间代码更易生成机器码(借助目标机器所支持的指令集来生成机器码)。对于部分编译器来说,这是编译过程的最后阶段。但有些编译器还会尝试在这上面进行改进,在接下来的视频中会对这些编译阶段所做的事情进行深入讲解。

欢迎加群和我们交流

如果感兴趣可以关注我们的微信公众号