假设您正在为一种新语言创建一个编译器,比如Big-Lang。如果您要为Big-Lang引导编译器,您可以为Big-Lang-lite编写一个编译器,这是该语言的最小可能子集。大朗。现在,我想知道Big-Lang-lite的编译器可以用Big-Lang编写,或者如果我们要编写一个bootstrap编译器,我们将不得不使用其他语言?
答案 0 :(得分:7)
这是引导皮带的常用方法。
从技术上讲,您可以跳过内核语言并只实现完整语言。我不推荐这个,因为它通常较慢(按数量级)
答案 1 :(得分:3)
(非常有趣的问题,但也许是stackoverflow上的边界线; https://softwareengineering.stackexchange.com/可能是这个问题的更好的地方)
除了Jozefg's answer之外,我还要补充一点,在实践中,步骤是一个循环:
您编写了一个糟糕的编译器(原始编译器,概念上只使用过一次)PC,用于不同语言Y中上述X语言的子集;这个编译器 PC 应该是一个快速而肮脏的工作,因为你将在概念上使用它一次,你将成为 PC 的单个用户:你不是关于良好诊断的问题(因此在第一次错误时中止是可以的);你不介意表现;你并不关心目标机器(你可以让PC针对一些糟糕的C ++生成的代码,或者你喜欢的任何东西)。顺便说一句,PC可以是一名翻译。
您在X中编写了一个更好的编译器BC来编译X(这里目标语言T很重要)。在这一点上,你可能会发现你的X语言设计缺乏一些功能;在这种情况下,转到步骤1(通过增强X的设计)
此外,您可以通过首先对该语言进行解释,然后对编译器进行编码(首先在该解释器上运行)来引导语言。
您可能(或不想)在各种版本的PC和BC上工作。特别是可能发生BC的当前版本不可编译(相同或前一版本);然后你必须暂时玩各种版本 - 甚至在BC内添加临时黑客。
一旦你有一个BC能够编译自己,你可以扔PC。
重点是设计和实现语言是一项循环工作(通过实现您的语言,您可以更好地理解您想要的内容以及如何实现它们。)
当然,您需要保留BC的工作版本。这意味着,例如您备份(甚至版本控制)BC编译的BC的“目标”代码的快照。在Ocaml中,它是字节码文件bootstrap/ocamlc
(事实上,Ocaml有一个可移植的字节码虚拟机有很多帮助); Scheme 48和Chicken Scheme采用了类似的方法;在MELT(我正在努力扩展和自定义GCC编译器的类似lisp的域特定语言)中,它是生成的C ++文件melt/generated/*.cc
; Ocaml和MELT都将“编译的编译器”保留在版本控制之下(并分发它......)。在MELT中,原始编译器PC是一个Common-Lisp程序(接受当前MELT语言的一个很小的子集),BC是自举的MELT编译器(MELT源代码的文件melt/warmelt*.melt
和{{1} }用于生成的C ++代码)。随意在googlegroup list上询问有关MELT bootstrap的问题。 Rust语言的方法略有不同:它的引导构建在Web上获取一些可执行文件(编译器的某些旧版本)。
引导语言更像是一门艺术,而不是一门科学。根据经验,通常有必要采取小步骤。您可能想要阅读J.Pitrat artificial beings : the conscience of a conscious machine(J.Pitrat最喜欢的主题是强大的人工智能是一个bootstrapping过程:您需要强大的AI来实现强大的AI,另请参阅this paper) 。您还可以阅读C.Queinnec Lisp in Small Pieces(如果您阅读法语,请阅读最新的french version),解释如何启动类似Lisp的实现。
你绝对应该阅读J.Pitrat's blog(因为J.Pitrat的职业生涯中有一半是在人工智能的基础上进行的。)