首选语言:C/C++、Java 和 Ruby。

我正在寻找一些关于如何编写自己的编译器的有用书籍/教程,仅用于教育目的。我最熟悉 C/C++、Java 和 Ruby,因此我更喜欢涉及这三者之一的资源,但任何好的资源都可以接受。

有帮助吗?

解决方案

资源大清单:

传奇:

  • ¶ 链接到 PDF 文件
  • $ 印刷书籍的链接

其他提示

我认为这是一个相当模糊的问题;只是因为涉及到的话题的深度。然而,编译器可以分解为两个独立的部分;上半部和下半部。上半部分通常采用源语言并将其转换为中间表示,下半部分负责平台特定的代码生成。

尽管如此,解决该主题的一种简单方法(至少是我们在编译器类中使用的方法)的一个想法是用上述两部分构建编译器。具体来说,只需构建上半部分,您就可以很好地了解整个过程。

只需完成上半部分,您就可以获得编写词法分析器和解析器的经验,并生成一些“代码”(我提到的中间表示)。因此,它将获取您的源程序并将其转换为另一种表示形式并进行一些优化(如果您愿意),这是编译器的核心。然后,下半部分将采用该中间表示并生成在特定架构上运行程序所需的字节。例如,下半部分将采用中间表示并生成 PE 可执行文件。

我发现一些关于这个主题特别有帮助的书是 编译器原理和技术 (或《龙之书》,因为封面上有可爱的龙)。它有一些很棒的理论,并且确实以一种真正易于理解的方式涵盖了上下文无关语法。另外,为了构建词法分析器和解析器,您可能会使用 *nix 工具 lex 和 yacc。有趣的是,这本书名叫“lex 和 yacc”从《龙之书》这部分中断的地方继续。

我认为 机器学习中的现代编译器实现 是最好的编译器编写入门文本。有一个 Java版本 和一个 C版 同样,考虑到您的语言背景,其中任何一个都可能更容易理解。本书包含许多有用的基础材料(扫描和解析、语义分析、激活记录、指令选择、RISC 和 x86 本机代码生成)和各种“高级”主题(编译 OO 和函数式语言、多态性、垃圾收集、优化和单个静态分配形式)占用相对较小的空间(约 500 页)。

与《Dragon》一书相比,我更喜欢《现代编译器实现》,因为《现代编译器实现》对这个领域的调查较少——相反,它确实涵盖了编写一个严肃的、像样的编译器所需的所有主题。读完本书后,如果需要,您将准备好直接处理研究论文以获得更深入的了解。

我必须承认我对 Niklaus Wirth 的作品情有独钟 编译器构建。 这是 在线提供 作为 PDF。我发现 Wirth 的编程美学非常漂亮,但是有些人发现他的风格太简单了(例如 Wirth 喜欢递归下降解析器,但大多数 CS 课程都专注于解析器生成器工具;Wirth 的语言设计相当保守。)《编译器构建》是对 Wirth 基本思想的非常简洁的升华,因此无论您是否喜欢他的风格,我都强烈建议您阅读这本书。

我同意《龙之书》的参考文献;IMO,它是编译器构建的权威指南。不过,请准备好接受一些核心理论。

如果你想要一本理论性较浅的书, 精通游戏脚本 对你来说可能是一本更好的书。如果您是编译器理论的新手,它会提供更温和的介绍。它没有涵盖更实用的解析方法(选择非预测递归下降而不讨论 LL 或 LR 解析),而且据我记得,它甚至没有讨论任何类型的优化理论。另外,它不是编译为机器代码,而是编译为应该在您也编写的虚拟机上运行的字节码。

这仍然是一本不错的读物,特别是如果你可以在亚马逊上以便宜的价格买到它的话。如果您只想简单地介绍编译器,那么《精通游戏脚本》是一个不错的选择。如果你想一开始就变得硬核,那么你应该选择《龙之书》。

“让我们构建一个编译器” 很棒,但是有点过时了。(我并不是说这会降低它的有效性。)

或查看 俚语. 。这类似于“让我们构建一个编译器”,但这是一个更好的资源,特别是对于初学者来说。附带一个 pdf 教程,该教程采用 7 个步骤的方法来教您编译器。添加 quora 链接,因为它具有到 SLANG 的所有各个端口(C++、Java 和 JS)的链接,还有 python 和 java 的解释器,最初是使用 C# 和 .NET 平台编写的。

如果您希望使用强大的、更高级别的工具而不是构建 一切 你自己,浏览项目和阅读材料 这个课程 是一个相当不错的选择。这是 Java 解析器引擎 ANTLR 作者的语言课程。您可以从以下位置获取该课程的 PDF 版本: 务实的程序员.

本课程将介绍您在其他地方看到的标准编译器的内容:解析、类型和类型检查、多态性、符号表和代码生成。几乎唯一没有涉及到的就是优化。最终的项目是一个程序 编译 C 的子集. 。因为您使用 ANTLR 和 LLVM 等工具,所以在一天内编写整个编译器是可行的(我有一个存在证明,尽管我的意思是大约 24 小时)。它侧重于使用现代工具的实际工程,而理论内容较少。

顺便说一句,LLVM 简直太棒了。在许多情况下,您通常可以编译为汇编,但最好编译为 LLVM的中间表示 反而。它的级别更高,跨平台,并且 LLVM 非常擅长从中生成优化的程序集。

如果你时间不多的话我推荐 Niklaus Wirth 的“编译器构造”(Addison-Wesley.1996), ,一本你一天就能读完的小册子,但它解释了基础知识(包括如何实现词法分析器、递归下降解析器和你自己的基于堆栈的虚拟机)。之后,如果你想深入了解,就像其他评论者所建议的那样,没有办法绕过《龙》这本书。

您可能想研究一下 Lex/Yacc(或 Flex/Bison,无论您如何称呼它们)。Flex 是一个词法分析器,它将解析和识别语言的语义组件(“标记”),而 Bison 将用于定义解析每个标记时会发生什么。这可以是但绝对不限于打印出 C 代码,供编译器编译为 C,或者动态运行指令。

此常见问题解答 应该对你有帮助,并且 本教程 看起来很有用。

一般来说,编译器没有五分钟的教程,因为这是一个复杂的主题,编写编译器可能需要几个月的时间。您必须自己进行搜索。

Python 和 Ruby 通常是解释型的。也许您也想从口译员开始。一般来说比较容易。

第一步是编写正式的语言描述,即编程语言的语法。然后你必须将你想要根据语法编译或解释的源代码转换为抽象语法树,即计算机理解并可以操作的源代码的内部形式。此步骤通常称为解析,解析源代码的软件称为解析器。通常,解析器是由解析器生成器生成的,解析器生成器将形式语法转换为源代码或机器代码。对于解析的良好非数学解释,我推荐解析技术 - 实用指南。维基百科对解析器生成器进行了比较,您可以从中选择适合您的一个。根据您选择的解析器生成器,您可以在 Internet 上找到教程,对于真正流行的解析器生成器(如 GNU bison),也有书籍。

为您的语言编写解析器可能非常困难,但这取决于您的语法。所以我建议保持语法简单(与 C++ 不同);LISP 就是一个很好的例子。

在第二步中,抽象语法树从树结构转换为线性中间表示。Lua 的字节码经常被引用作为一个很好的例子。但中间表示实际上取决于您的语言。

如果您正在构建解释器,则只需解释中间表示即可。您也可以即时编译它。我推荐使用 LLVM 和 libjit 进行即时编译。为了使该语言可用,您还必须包含一些输入和输出函数,也许还有一个小型标准库。

如果你要编译语言,那就更复杂了。您将必须为不同的计算机体系结构编写后端,并从这些后端中的中间表示生成机器代码。我推荐 LLVM 来完成这项任务。

有几本关于这个主题的书,但我不推荐任何一本用于一般用途。其中大多数过于学术或过于实用。没有“在 21 天内自学编译器编写”,因此,您必须购买几本书才能很好地理解整个主题。如果您搜索互联网,您会发现一些在线书籍和讲义。也许您附近有一个大学图书馆,您可以在那里借到有关编译器的书籍。

如果你想让你的项目变得严肃起来,我还建议你具备理论计算机科学和图论方面的良好背景知识。计算机科学学位也会有所帮助。

看看下面这本书吧。作者是以下内容的创造者 ANTLR.

语言实现模式:创建您自己的特定领域和通用编程语言.

alt text

尚未推荐但非常重要的一本书是 “链接器和加载器” 作者:约翰·莱文。如果您不使用外部汇编器,则需要一种方法来输出可链接到最终程序的目标文件。即使您使用的是外部汇编器,您也可能需要了解重定位以及整个程序加载过程如何工作才能制作一个可用的工具。本书收集了有关各种系统(包括 Win32 和 Linux)的此过程的大量随机知识。

龙之书 绝对是“构建编译器”的书,但是如果您的语言不像当前一代语言那么复杂,您可能需要查看解释器模式 设计模式.

书中的例子设计了一种类似正则表达式的语言,并且经过了深思熟虑,但正如书中所说,它有助于思考整个过程,但实际上只对小语言有效。然而,使用这种模式为小型语言编写解释器比学习所有不同类型的解析器、yacc 和 lex 等要快得多......

如果您愿意使用 LLVM,请查看: http://llvm.org/docs/tutorial/. 。它教您如何使用 LLVM 框架从头开始编写编译器,并且不假设您对此主题有任何了解。

本教程建议您编写自己的解析器和词法分析器等,但我建议您在了解后研究一下 bison 和 flex。它们让生活变得更加轻松。

我发现《Dragon》这本书太难读了,因为它过于关注语言理论,而在实践中编写编译器并不是真正需要的。

我会添加 奥伯龙 本书包含令人惊讶的快速且简单的 Oberon 编译器的完整源代码 奥布朗计划.

Alt text

我记得大约七年前问过这个问题,当时我对编程还很陌生。

当我提出这个问题时,我非常小心,令人惊讶的是,我没有受到像你在这里收到的那么多批评。然而,他们确实为我指明了“龙书“在我看来,这是一本非常棒的书,它解释了编写编译器所需了解的所有内容(当然,您必须掌握一两种语言。你知道的语言越多,就越快乐。)。

是的,很多人说读这本书很疯狂,你不会从中学到任何东西,但我完全不同意这一点。

很多人还说编写编译器是愚蠢且毫无意义的。编译器开发有用的原因有很多:

  • 因为这很有趣。
  • 它具有教育意义,在学习如何编写编译器时,您将学到很多关于计算机科学和其他在编写其他应用程序时有用的技术。
  • 如果没有人编写编译器,现有的语言就不会变得更好。

我没有立即编写自己的编译器,但在询问后我知道从哪里开始。而现在,在学习了多种不同的语言并阅读了《龙书》之后,写作已经不是什么大问题了。(我也在学习计算机工程 atm,但我所知道的大部分编程知识都是自学的。)

总之,《龙书》是一本很棒的“教程”。但在尝试编写编译器之前,请花一些时间掌握一两种语言。不过,不要期望在未来十年左右成为编译器大师。

如果您想学习如何编写解析器/解释器,这本书也很好。

“……让我们构建一个编译器......”

我想第二 http://compilers.iecc.com/crenshaw/ 经过 @sasb. 。暂时忘记购买更多书籍。

为什么?工具和语言。

所需的语言是 Pascal,如果我没记错的话,是基于 Turbo-Pascal。如果你去的话就会发生这种情况 http://www.freepascal.org/ 并下载 Pascal 编译器,所有示例都可以直接从页面运行 ~ http://www.freepascal.org/download.var Free Pascal 的美妙之处在于您几乎可以使用您喜欢的任何处理器或操作系统。

一旦您掌握了课程,就可以尝试更高级的课程 "龙书" ~ http://en.wikipedia.org/wiki/Dragon_book

我正在研究同样的概念,并发现了 Joel Pobar 写的这篇很有前途的文章,

为 .NET Framework 创建一个语言编译器 - 不确定它去了哪里

为 .NET Framework 创建语言编译器 - 原始文档的 pdf 副本

他讨论了编译器的高级概念,并继续为 .Net 框架发明了自己的语言。尽管它针对的是 .Net Framework,但许多概念应该能够重现。文章涵盖:

  1. 语言定义
  2. 扫描器
  3. 解析器(我主要感兴趣的部分)
  4. 针对 .Net Framework
  5. 代码生成器

还有其他主题,但你得到的是公正的。

它的目标人群是初学者,用 C#(不完全是 Java)编写

华泰

骨头

创建编译器的一个简单方法是使用 bison 和 flex(或类似的),构建树 (AST) 并生成 C 代码。生成 C 代码是最重要的一步。通过生成 C 代码,您的语言将自动在所有具有 C 编译器的平台上运行。

生成 C 代码就像生成 HTML 一样简单(只需使用 print 或等效项),而这又比编写 C 解析器或 HTML 解析器容易得多。

来自 comp.编译器常见问题解答:

Per Brinch Hansen Prentice-Hall 1982 ISBN 0-13-730283-5的“编程个人计算机”

不幸的是,这本书用一种名为爱迪生的帕斯卡式语言解释了Micros的单用户编程环境的设计和创建。作者介绍了Edison编译器和简单支持操作系统的分步实现的所有源代码和解释,所有这些都用Edison本身编写(除了在PDP 11/23的符号汇编器中编写的较小的支持内核;也可以为IBM PC订购完整的源。

这本书最有趣的地方是:1)其能够演示如何创建一个完整,独立,自我维护,有用的编译器和操作系统的能力,以及2)关于语言设计和规范问题和权衡的有趣讨论。

Per Brinch Hansen Prentice-Hall 1985 ISBN 0-13-083098-4

这是另一本轻松的繁重的杂文,这是一本书。作者为Pascal-(Pascal“减去”)提供了编译器和P代码解释器的设计,实现和完整的源代码,这是一个带有布尔和整数类型的Pascal子集(但没有字符,真实的,real,subsranged或枚举类型) ,常数和可变定义以及数组和记录类型(但没有包装,变体,集合,指针,无名,更名或文件类型),表达式,分配语句,带值和变量参数的嵌套过程定义和开始端块(但没有功能定义,过程参数,goto语句和标签,案例语句,重复语句,语句以及语句)。

编译器和解释器用Pascal*(Pascal“ Star”)编写,这是一个帕斯卡(Pascal),具有一些用于创建软件开发系统的爱迪生风格的功能。作者出售了IBM PC的Pascal*编译器,但很容易将本书的Pascal-Compiler移植到任何方便的Pascal平台上。

这本书使编译器的设计和实现看起来很容易。我特别喜欢作者关注质量,可靠性和测试的方式。编译器和解释器可以轻松地用作更多参与语言或编译器项目的基础,尤其是如果您被迫快速启动并运行某些内容时。

你应该看看大流士培根的“伊希宾斯”,这是一个小型 Lisp 方言的编译器,以 C 为目标,只有 6 页多的代码。与大多数玩具编译器相比,它的优点是语言足够完整,编译器是用它编写的。(压缩包还包括一个解释器来引导该东西。)

还有更多关于我发现在学习在我的计算机上编写编译器时有用的内容 乌尔计划 网页。

Python 捆绑了一个用 Python 编写的 python 编译器。你可以看到源代码,它包括所有阶段,从解析、抽象语法树、发出代码等。破解它。

LCC 编译器(维基百科) (项目主页Fraser 和 Hanson 的书《A Retargetable C Compiler:设计与实施”。它非常易读,并解释了整个编译器,直至代码生成。

抱歉,它是西班牙语的,但这是阿根廷一门名为“Compiladores e Intérpretes”(编译器和口译员)的课程的参考书目。

该课程从形式语言理论到编译器构建,这些是您至少构建一个简单编译器所需的主题:

  • C 编译器设计
    艾伦 I.霍鲁布

    普伦蒂斯·霍尔。1990年。

  • 编译者。Teoría y Construcción。
    SanchísLlorca,FJ加兰·帕斯夸尔,C.社论Paraninfo。1988年。

  • 编译器构建。
    尼克劳斯·沃斯

    艾迪生-韦斯利。1996年。

  • 语言、语法和自动机。注重实践。
    Pedro IsasiViñuela,PalomaMartínezFernández,Daniel BorrajoMillán。Addison-Wesley Iberoamericana(España)。1997年。

  • 编译器设计的艺术。理论与实践。
    托马斯·皮特曼(Thomas Pittman),詹姆斯·彼得斯(James Peters)。

    普伦蒂斯·霍尔。1992年。

  • 面向对象的编译器构造。
    吉姆·霍姆斯。
    新泽西州Englewood Cliffs的Prentice Hall1995年

  • 编译者。基本概念。
    B.托菲尔,S.施密特,T.托菲尔。

    艾迪生-韦斯利伊比利亚美洲。1995年。

  • 自动机理论、语言和计算简介。

    约翰·E。霍普克罗夫特。杰夫·D.厄尔曼。
    艾迪生-韦斯利。1979 年。

  • 形式语言简介。
    吉尔吉·E.雷维斯。

    麦格劳·希尔。1983年。

  • 解析技术。实用指南。
    Dick Grune,Ceriel Jacobs。
    Impreso por los autorores。1995年
    http://www.cs.vu.nl/~dick/PTAPG.html

  • 亚克:另一个编译器-编译器。
    斯蒂芬·C·约翰逊
    计算科学技术报告Nº32,1975。贝尔实验室。新默里山
    球衣。

  • 莱克斯:词法分析器生成器。
    M。E.莱斯克,E.施密特。计算科学技术报告Nº39,1975年。贝尔实验室。新泽西州默里山。

  • 莱克斯和雅克。
    约翰·R。莱文、托尼·梅森、道格·布朗。
    奥莱利建筑事务所。1995年。

  • 计算理论的要素。
    哈里·R。刘易斯(Christos H.)帕帕迪米特里乌。第二版。普伦蒂斯·霍尔。1998.

  • Un Algoritmo Eficiente para la Construcción del Grafo de Dependencia de Control。
    萨尔瓦多五世卡瓦迪尼。
    Trabajo Final de Grado para obtener el Título de Ingeniero en Computación。
    数学应用学院。U.C.S.E.2001年。

  1. 这是一个广阔的课题。不要低估这一点。并且不要低估我的观点,不要低估它。
  2. 我听到 龙书 是一个(?)开始的地方,还有搜索。:) 更好地搜索,最终这将成为你的生活。
  3. 构建自己的编程语言绝对是一个很好的练习!但要知道它最终永远不会用于任何实际目的。例外情况很少,而且 非常 之间很远。

不是一本书,而是一篇技术论文,如果您想了解有关编译器(和元编译器)的更多信息,这是一次非常有趣的学习体验......该网站将引导您构建一个完全独立的编译器系统,该系统可以编译自身和其他语言:

教程:元编译器第 1 部分

这一切都基于一篇令人惊叹的 10 页技术论文:

瓦尔肖雷 元二:一种面向语法的编译器编写语言

从1964年开始。早在 1970 年,我就从中学会了如何构建编译器。当你终于明白编译器如何自我再生时,有一个令人兴奋的时刻......

我在大学时代就认识该网站的作者,但我与该网站无关。

这里有很多很好的答案,所以我想我应该再添加一个到列表中:

十多年前,我得到了一本名为 Project Oberon 的书,其中有一些关于编译器的写得很好的文字。这本书确实很突出,因为它的来源和解释非常实用且可读。完整文本(2005 年版)已提供 pdf 版本,因此您可以立即下载。第 12 章讨论了编译器:

http://www-old.oberon.ethz.ch/WirthPubl/ProjectOberon.pdf

尼克劳斯·维尔特、尤尔格·古特克内西特

(处理不像他关于编译器的书那么广泛)

我读过几本关于编译器的书,可以秒龙书,花在这本书上的时间是非常值得的。

如果您有兴趣为函数式语言(而不是过程式语言)编写编译器,请参阅 Simon Peyton-Jones 和 David Lester 的“实现函数式语言:教程”是一个很好的指南。

功能评估如何工作的概念基础由简单但功能强大的功能语言(称为“核心”)的示例指导。此外,Core 语言编译器的每个部分都通过 Miranda(一种与 Haskell 非常相似的纯函数式语言)中的代码示例进行了解释。

描述了几种不同类型的编译器,但即使您只遵循所谓的 Core 模板编译器,您也会很好地理解函数式编程的原理。

我喜欢 克伦肖教程 也是如此,因为它非常清楚地表明编译器只是另一个读取一些输入并写入一些输出的程序。

阅读。

如果您愿意,可以尝试一下,然后再看看另一个参考资料,了解如何真正编写更大、更完整的编译器。

并阅读 论信任信任, ,以获取有关该领域中可以完成的不明显事情的线索。

您可以使用 BCEL 由 Apache 软件基金会提供。使用此工具,您可以生成类似汇编程序的代码,但它是带有 BCEL API 的 Java。您可以了解如何生成中间语言代码(在本例中为字节代码)。

简单的例子

  1. 使用此函数创建一个 Java 类:

    public String maxAsString(int a, int b) {
        if (a > b) {
            return Integer.valueOf(a).toString();
        } else if (a < b) {
            return Integer.valueOf(b).toString();
        } else {
            return "equals";
        }
    }
    

现在使用此类运行 BCELifier

BCELifier bcelifier = new BCELifier("MyClass", System.out);
bcelifier.start();

您可以在控制台上看到整个类的结果(如何构建字节码 MyClass.java)。该函数的代码是这样的:

private void createMethod_1() {
  InstructionList il = new InstructionList();
  MethodGen method = new MethodGen(ACC_PUBLIC, Type.STRING, new Type[] { Type.INT, Type.INT }, new String[] { "arg0", "arg1" }, "maxAsString", "MyClass", il, _cp);

  il.append(InstructionFactory.createLoad(Type.INT, 1)); // Load first parameter to address 1
  il.append(InstructionFactory.createLoad(Type.INT, 2)); // Load second parameter to adress 2
    BranchInstruction if_icmple_2 = InstructionFactory.createBranchInstruction(Constants.IF_ICMPLE, null); // Do if condition (compare a > b)
  il.append(if_icmple_2);
  il.append(InstructionFactory.createLoad(Type.INT, 1)); // Load value from address 1 into the stack
  il.append(_factory.createInvoke("java.lang.Integer", "valueOf", new ObjectType("java.lang.Integer"), new Type[] { Type.INT }, Constants.INVOKESTATIC));
  il.append(_factory.createInvoke("java.lang.Integer", "toString", Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
  il.append(InstructionFactory.createReturn(Type.OBJECT));
  InstructionHandle ih_13 = il.append(InstructionFactory.createLoad(Type.INT, 1));
  il.append(InstructionFactory.createLoad(Type.INT, 2));
    BranchInstruction if_icmpge_15 = InstructionFactory.createBranchInstruction(Constants.IF_ICMPGE, null); // Do if condition (compare a < b)
  il.append(if_icmpge_15);
  il.append(InstructionFactory.createLoad(Type.INT, 2));
  il.append(_factory.createInvoke("java.lang.Integer", "valueOf", new ObjectType("java.lang.Integer"), new Type[] { Type.INT }, Constants.INVOKESTATIC));
  il.append(_factory.createInvoke("java.lang.Integer", "toString", Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
  il.append(InstructionFactory.createReturn(Type.OBJECT));
  InstructionHandle ih_26 = il.append(new PUSH(_cp, "equals")); // Return "equals" string
  il.append(InstructionFactory.createReturn(Type.OBJECT));
  if_icmple_2.setTarget(ih_13);
  if_icmpge_15.setTarget(ih_26);
  method.setMaxStack();
  method.setMaxLocals();
  _cg.addMethod(method.getMethod());
  il.dispose();
}

到目前为止,这本书尚未包含在列表中:

编译器设计基础知识(Torben Mogensen)(来自部门哥本哈根大学计算机科学博士)

我也有兴趣了解编译器,并计划在未来几年内进入该行业。据我所知,这本书是开始学习编译器的理想理论书。它可以免费复制和复制,编写清晰、仔细,并以简单的英语提供给您,无需任何代码,但仍然通过说明和图表等方式呈现机制。我认为值得一看。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top