简介
1.1 为什么要了解编译器?
编译器是一个非常有趣的话题。你每天都在使用它们。它们将你编写的文本形式的源代码,转换成计算机能够执行的二进制指令。你是否曾好奇过它们是如何工作的呢?你可以通过自己编写一个小型编译器来了解这一切,从而揭开心中的谜团。
人们常说,学习C语言有助于更好地理解计算机。而比单纯学习C语言更好的是学习编译器和汇编语言,因为即使学完了C语言,计算机和编译器对你来说可能仍然像黑匣子一样神秘。不过,本书并不要求读者事先具备C语言或汇编语言的经验。
编译器构造是计算机科学和软件工程教育中包含的一门课程。然而,如今的许多程序员并没有接受过正规的计算机科学教育。像编译器、数据库、操作系统等基础知识,常常被视为神奇的黑匣子。这就是我开启 “自己动手构建X” 系列图书的原因。通过简洁精炼的书籍,采用 “从零开始” 的方法来学习和教授这些基础知识。
这也被认为是一个有趣的业余项目,只需要你花费一些空闲时间即可。
1.2 编译器的组成部分
让我们从源代码开始讲起:一些用编程语言编写的文本文件。要对源代码文件进行任何有意义的处理(无论是使用编译器还是集成开发环境),都必须将文本 “解析” 成某些结构。这个过程的专业术语叫做 “解析器”。
对于大型编程语言的解析器,通常分为两个阶段:首先,将文本分割成 “词法单元” 流(关键字、名称、运算符,就像文章中的单词一样),然后解析器将这些词法单元组合成各种结构(语法)。第一个阶段通常被称为 “词法分析器” 或 “扫描器”,不过这个阶段并非必不可少。
经过解析的结构通常被称为 “语法树”。人们可以直接从语法树模拟程序的运行。这被称为 “解释器”,而且是一种简单的解释器。
而更高级一些的解释器会包含额外的步骤。比如将语法树转换为称为 “字节码” 的指令列表。程序的执行是基于字节码,而不是语法树。为什么要增加这一步呢?稍后你就会明白。
生成字节码的组件也被称为 “编译器”,从这里开始,编译器和解释器之间的界限就变得模糊了。像GCC这样的编译器最终会输出一个二进制可执行文件 —— 这是机器码的载体 —— 它同样包含一个指令列表,这些才是CPU真正执行的指令。
在语法树和最终的机器码之间通常会有多个步骤,并且会有出于各种目的的 “中间表示”(IR)。字节码也可以是一种中间表示。不过,我们在本节中描述的是典型编译器和解释器的组成部分;此外,还有许多其他不同的编译器/解释器设计和实现,你可以尽情探索它们。
1.3 本书内容
和我的其他书籍一样,本书采用循序渐进的方式。你将从简单的内容入手,比如一个计算器,然后是一个简单的解释器。接着,你将逐步攻克编译器相关的内容,学习编译成字节码、学习汇编语言,最后生成原生可执行文件。
最终的成果是一门小型的、静态类型的语言,它类似于C语言的一个子集,能够编译成适用于x64 Linux系统的ELF可执行文件。而且它有一个与机器无关的中间表示,所以将目标平台改为其他平台(比如ARM)也并不困难,这将是你日后可以尝试的一个挑战。