



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