



编译原理在计算机科学中占据着至关重要的地位。因为高级语言的广泛使用,才有了今日无所不在的软件和绚烂多彩的信息世界,而这一过程中居功至伟的便是编译器——负责将高级语言程序转换成计算机能够执行的目标代码。编译的基本理论和方法研究主要集中在20世纪六七十年代,至20世纪70年代末80年代初基本成熟,所以就有了1986年Aho等人的经典著作(俗称“龙书”):《编译原理》。神奇的是,20年后该书才推出经修订扩充的第2版,这对于日新月异、高速发展的计算机领域而言非常罕见。其修订版的序言中有力地阐明了编译技术仍然重要的原因:“我们认识到,很少有读者会为一种主要的编程语言构建甚至维护编译器;然而,与编译器相关的模型、理论和算法可以广泛应用于软件设计和软件开发的各类问题中。”
编译原理与技术是计算机专业相对困难的一门课程,这体现在理论和实践两个方面。在理论方面,编译程序设计需要掌握上下文无关文法的分析方法——自顶向下的预测或自底向上的归约,以及相应的数据结构和算法实现,还有其他的一些理论和算法。在实践方面,困难更为明显:对任何一种稍复杂的语言,我们很难通过软件扩充的方式去实现编译器,换句话说,在一个已有的语言编译器中加入新的语言特性是非常困难的。我们可能知道在工程领域存在大量Linux或其他操作系统相关的定制工作,尤其在嵌入式的系统中;但我们在应用中很少听说过对GNU编译工具的修改或扩展。个中原因,除去需求相对少之外,复杂性大概是主要的障碍:编译器不同过程间的联系和依赖更紧密、更一体化,相关工作很多时候让人无从下手。例如,gcc编译器在转换过程中会生成寄存器传递语言(RTL)的中间代码,而该语言本身就是非常复杂的。这就意味着,为了转换新加入的语法项,需要对编译器已经实现的各种细节加以了解,这自然是非常困难的。所以,在编译课程的程序实践中,我们给出的都是一些很小的语言玩具问题,可借助工具自动生成或是自主进行高级语言编程,浅尝辄止。
在本书中,借助递归函数,通过微编译遍的(nanopass)方式实现编译器,对上述问题给出了有效的解决方法。从支持基本整数运算的简单语言起步,先加入变量,再加入布尔值、条件表达式和while循环;接着进一步扩展语言能力,纳入元组和内存垃圾回收机制,以及函数和词法作用域函数(即lambda式),再辅以较新的特性,例如动态类型、渐变类型和泛型等,从而逐步丰富语言特性至接近实际编程语言的程度。而在加入这些特性的过程中,每次编译扩展都会生成一个中间语言,每一个中间语言都加入了若干微编译遍,典型的有唯一化变量、移除复杂操作数、详细控制、选择指令、分配变量存储、修补指令与生成起始和收尾代码等多个微编译遍,这样就可完成高级语言到汇编语言的转换。这一方法在有效降低编译器设计与开发难度的同时,也完美体现了软件工程中的开闭原则。
本书作者Jeremy G. Siek是印第安纳大学信息、计算与工程学院的计算机科学教授,长期教授编程、编程语言、编译原理、逻辑等计算机科学领域的课程,专注于通用及高性能语言设计。他在语言方面的贡献之一,是与Walid Taha一起发明了渐变类型方法,实现在同一语言中混合使用静态和动态类型。本书凝聚了他多年相关教学与实践的经验和精华。
本书行文流畅,内容编排由浅入深,表达精准且逻辑严谨。各章后面配有相应的练习,官方网站提供配套程序与资源;书中引用的参考文献也为读者进行深入的研究提供了便利。本书有内容基本相同且篇幅相近的两个版本,分别基于Python和Racket实现。本书为Racket实现版本。
本书由北京邮电大学的刘晓鸿和李文生负责翻译,其中李文生有编译相关教材出版经历。具体分工为:刘晓鸿翻译了本书的序言和第1、2、6、7、8、11章及附录,李文生翻译了第3、4、5、9、10章。
由于时间紧迫和译者水平有限,译文若有错误及不妥之处,恳请读者批评指正。
译者
2025年6月于北京邮电大学