自举仍然需要外部支持


96

我听说过引导语言的想法,即为语言本身编写编译器/解释器。我想知道如何做到这一点,环顾四周,看到有人说这只能由任何一个人完成

  • 用另一种语言编写初始编译器。
  • 在Assembly中手动编码初始编译器,这似乎是第一个的特殊情况

在我看来,这两者似乎都不是在引导语言,因为它们都需要外部支持。有没有办法用自己的语言实际编写编译器?


我对这种事情不是很有经验,但是我认为初始编译器必须用另一种语言编写。我相当肯定,“引导”,参考编译器,仅仅指的是写一个编译器在它的意思编译,不是写语言的语言第一在它的意思是编译语言的语言编译器。
jdd

1
信息很有帮助,谢谢大家。当以最初编写有限的编译器的想法进行解释,然后在此之上进行构建时,自举的想法更有意义。我本学期要参加编译器课程,这一决定在很大程度上受到了史蒂夫·耶格Steve Yegge)的帖子的影响,该帖子对编译器中的一类课程的重要性进行了决定,我刚刚从亚马逊链接购买了一本Dragon图书的副本,因此早些时候就对它进行了降级。
pbh101

1
另请参见类似的问题:本身实现编译器
Urban Vagabond

Answers:


107

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

必须使用某种现有语言来编写新的编译器。如果要编写新的C ++编译器,则只需用C ++编写,然后首先使用现有的编译器进行编译。另一方面,如果您正在为一种新语言创建编译器,我们称其为Yazzleof,则需要首先使用另一种语言编写新的编译器。通常,这将是另一种编程语言,但并非必须如此。它可以是汇编,也可以是机器代码。

如果你要引导用于Yazzleof一个编译器,你一般不会写最初的全语言的编译器。相反,您将为Yazzle-lite(Yazzleof的最小可能子集)编写编译器(至少,是一个很小的子集)。然后,在Yazzle-lite中,您将编写完整语言的编译器。(显然,这可以迭代而不是一次跳转。)由于Yazzle-lite是Yazzleof的适当子集,因此您现在有了一个可以自行编译的编译器。

关于从最低的级别引导编译器(在现代计算机上基本上是十六进制编辑器)进行引导的文章非常不错,标题为“ 从零开始引导简单的编译器”。可以在https://web.archive.org/web/20061108010907/http://www.rano.org/bcompiler.html上找到。



7

在Unix联合创建者Ken ThompsonTuring Award演讲中,对此进行了非常有趣的讨论

他开始时:

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

并继续展示他如何编写Unix C编译器的版本,该版本始终允许他不用密码登录,因为C编译器会识别登录程序并添加特殊代码。

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


9
这是题外话。有趣,但令人困惑,而不是问题的答案。
blueshift

5

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

这是自举的定义

一个简单的系统激活一个功能相同的更复杂系统的过程。

编辑:有关编译器引导Wikipedia文章比我更好地介绍了该概念。




3

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

您可以通过阅读原始的McCarthy论文(符号表达式的递归函数及其通过机器进行的计算,第一部分)来进行检查


第2部分和第3部分发生了什么事?...我怎么没注意到@Wing在我之前3年发布了同样的东西?我是笨蛋 至少我在帮助下链接了这篇论文。
luser droog 2013年

2

另一种选择是为您的语言创建一个字节码机器(如果功能不是很特殊,则使用现有的字节码机器),然后使用字节码或所需的语言使用另一种中间语言(例如解析器工具包,将AST输出为XML,然后使用XSLT(或另一种模式匹配语言和基于树的表示形式)将XML编译为字节码。它不会消除对另一种语言的依赖,但是可能意味着更多的引导工作最终会在最终系统中完成。


2

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

实际上,我认为Lisp几乎可以胜任。查看其Wikipedia条目。根据这篇文章,Lisp eval函数可以在IBM 704上用机器代码实现,而完整的编译器(由Lisp自己编写)于1962年在MIT诞生


2

自举一种我能想到的语言(CPyPy)的示例都是在有一个可用的编译器之后完成的。您必须从某个地方开始,重新实现一种语言本身首先需要用另一种语言编写编译器。

否则它将如何工作?我认为从概念上讲,甚至不可能做到。


4
至少第一个Lisp编译器是使用现有的Lisp 解释器引导的。所以不是语义上的另一种语言,而是另一种语言的实现。

0

一些自举的编译器或系统会将源表单和对象表单都保留在其存储库中:

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

  • 编译器下载(使用wget,所以你需要一个工作的Internet连接)的二进制以前版本的编译本身。

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

  • J.Pitrat的CAIA人工智能系统完全是自动生成的。它可以作为成千上万个[A-Z]*.c生成文件的集合(也带有生成的dx.h头文件)和成千上万个_[0-9]*数据文件的集合。

  • 还引导了多个Scheme编译器。Scheme48,鸡肉方案,...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.