引导仍然需要外部支持

时间:2008-08-17 06:46:11

标签: compiler-construction bootstrapping

我听说过引导一种语言的想法,就是为这种语言编写一个编译器/解释器。我想知道如何才能完成并环顾四周,看到有人说它只能通过其中任何一个来完成

  • 用不同的语言编写初始编译器。
  • 在Assembly中手动编写初始编译器,这似乎是第一个
  • 的特例

对我而言,这些似乎都不是 bootstrapping 一种语言,因为它们都需要外部支持。有没有办法用自己的语言编写编译器?

11 个答案:

答案 0 :(得分:101)

  

有没有办法用自己的语言编写编译器?

有一些现有的语言来编写你的新编译器。如果你正在编写一个新的,比如说C ++编译器,你只需用C ++编写它并用现有的编译器编译它第一。另一方面,如果您正在为新语言创建编译器,我们称之为Yazzleof,您需要先用另一种语言编写新的编译器。通常,这将是另一种编程语言,但它不一定是。它可以是装配,也可以是机器代码。

如果 要为Yazzleof引导编译器,通常最初不会为完整语言编写编译器。相反,你会为Yazzle-lite编写一个编译器,这是Yazzleof的最小可能子集(好吧,至少是相当小的子集)。然后在Yazzle-lite中,您将编写完整语言的编译器。 (显然这可以迭代而不是一次跳转。)因为Yazzle-lite是Yazzleof的正确子集,所以现在你有一个可以编译自己的编译器。

有一个关于从最低级别(在现代机器上基本上是一个十六进制编辑器)引导编译器的真正好的写法,标题为从无提取引导简单的编译器。它可以在https://web.archive.org/web/20061108010907/http://www.rano.org/bcompiler.html找到。

答案 1 :(得分:19)

你读过的解释是正确的。在 Compilers: Principles, Techniques, and Tools (龙书)中对此进行了讨论:

  • 在语言Y
  • 中为语言X编写编译器C1
  • 使用编译器C1为语言X中的语言X编写编译器C2
  • 现在C2是一个完全自托管的环境。

答案 2 :(得分:7)

超级有趣的discussion of this是在Unix联合创作者Ken ThompsonTuring Award讲座中。

他开始时:

  

我要描述的是编译器用他们自己的语言编写时出现的许多“鸡和蛋”问题之一。在这方面,我将使用C编译器中的一个特定示例。

并继续展示他是如何编写一个Unix C编译器的版本,它总是允许他在没有密码的情况下登录,因为C编译器会识别登录程序并添加特殊代码。

  

第二种模式针对C编译器。替换代码是Stage I自我复制程序,它将两个特洛伊木马插入编译器。这需要一个学习阶段,如第二阶段的例子。首先,我们使用普通的C编译器编译修改后的源代码,以生成错误的二进制文件。我们将这个二进制文件作为官方C安装。我们现在可以从编译器源中删除错误,新的二进制文件将在编译时重新插入错误。当然,login命令将保持错误状态,并且在任何地方都没有跟踪源。

答案 3 :(得分:5)

我听说过的方法是用另一种语言编写一个极其有限的编译器,然后使用它编译一个用新语言编写的更复杂的版本。然后可以使用第二个版本编译自己和下一个版本。每次编译时都会使用最后一个版本。

这是bootstrapping:

的定义
  

一个简单系统激活一个更复杂系统的过程,该系统具有相同的目的。

编辑:Wikipedia article on compiler bootstrapping比我更好地涵盖了这个概念。

答案 4 :(得分:4)

查看播客Software Engineering Radio episode 61(2007-07-06),其中讨论了GCC编译器内部以及GCC引导过程。

答案 5 :(得分:4)

Donald E. Knuth通过在其中编写编译器实际构建WEB,然后将其手工编译为程序集或机器代码。

答案 6 :(得分:3)

据我了解,第一个Lisp解释器是通过手工编译构造函数和令牌读取器来引导的。然后从源头读入其余的翻译。

您可以通过阅读原始麦卡锡论文 Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I 来查看自己。

答案 7 :(得分:2)

引导我能想到的语言(CPyPy)的每个示例都是在有一个正常工作的编译器之后完成的。你必须从某个地方开始,并且重新实现一种语言本身需要首先用另一种语言编写一个编译器。

它还有什么用呢?我不认为在概念上甚至不可能这样做。

答案 8 :(得分:2)

这是鸡与蛋悖论的计算机科学版本。我想不出一种不用汇编程序或其他语言编写初始编译器的方法。如果它可以完成,我应该Lisp可以做到。

实际上,我认为Lisp几乎符合条件。查看its Wikipedia entry。根据这篇文章,Lisp eval函数可以在机器代码的IBM 704上实现,完整的编译器(用Lisp本身编写)于1962年MIT出现。

答案 9 :(得分:2)

另一种方法是为您的语言创建一个字节码机器(或者如果它的功能不是很少,则使用现有的机器)并将编译器写入字节码,可以是字节码,也可以是使用其他中间件的所需语言 - 例如,将AST作为XML输出的解析器工具包,然后使用XSLT(或其他模式匹配语言和基于树的表示)将XML编译为字节码。它不会消除对另一种语言的依赖,但可能意味着更多的自举工作最终会在最终系统中完成。

答案 10 :(得分:0)

某些引导编译器或系统将源表单和对象表单都保存在其存储库中:

  • ocaml是一种语言,它既包含字节码解释器(即Ocaml字节码的编译器),也包含本机编译器(x86-64或ARM等...汇编程序)。它的svn存储库包含编译器的源代码(文件*/*.{ml,mli})和字节码(文件boot/ocamlc)形式。因此,在构建时,首先使用其字节码(编译器的先前版本)进行编译。之后,新编译的字节码能够编译本机编译器。所以Ocaml svn存储库包含*.ml[i]个源文件和boot/ocamlc字节码文件。

  • rust编译器下载(使用wget,因此您需要有效的Internet连接)以及其二进制文件的先前版本进行自我编译。

  • MELT是一种类似Lisp的语言,用于自定义和扩展GCC。它由一个自举翻译器翻译成C ++代码。生成的转换器的C ++代码是分布式的,因此svn存储库包含翻译器的*.melt源文件和melt/generated/*.cc“对象”文件。

  • J.Pitrat的CAIA人工智能系统完全是自我生成的。它可以作为数千个[A-Z]*.c生成的文件(也包含生成的dx.h头文件)的集合提供,其中包含数千个_[0-9]*数据文件的集合。

  • 还引导了几个Scheme编译器。 Scheme48,Chicken Scheme,...