我正在http://coffeescript.org/网站上研究CoffeeScript ,其中包含以下文字:
CoffeeScript编译器本身是用CoffeeScript编写的
编译器如何自行编译,或者该语句是什么意思?
我正在http://coffeescript.org/网站上研究CoffeeScript ,其中包含以下文字:
CoffeeScript编译器本身是用CoffeeScript编写的
编译器如何自行编译,或者该语句是什么意思?
Answers:
编译器的第一版不能通过专用于它的编程语言来机器生成。您的困惑是可以理解的。可以由第一个编译器构建具有更多语言功能的更高版本的编译器(源代码用新语言的第一个版本重写)。然后,该版本可以编译下一个编译器,依此类推。这是一个例子:
注意:我不确定CoffeeScript版本的确切编号方式,这只是一个示例。
在Unix的发起者之一Ken Thompson的《Reflection on Trusting Trust》一书中,写了一篇关于C编译器如何进行自我编译的迷人(且易于阅读)的概述。类似的概念可以应用于CoffeeScript或任何其他语言。
编译自己的代码的编译器的思想与quine相似:源代码在执行时会产生原始源代码作为输出。 这是 CoffeeScript quine的一个示例。汤普森举了一个C quine的例子:
char s[] = {
'\t',
'0',
'\n',
'}',
';',
'\n',
'\n',
'/',
'*',
'\n',
… 213 lines omitted …
0
};
/*
* The string s is a representation of the body
* of this program from '0'
* to the end.
*/
main()
{
int i;
printf("char\ts[] = {\n");
for(i = 0; s[i]; i++)
printf("\t%d,\n", s[i]);
printf("%s", s);
}
接下来,您可能想知道如何告诉编译器如何将转义序列'\n'
表示为ASCII代码10。答案是,在C编译器中的某个地方,存在一个例程来解释字符文字,该例程包含一些可识别反斜杠序列的条件:
…
c = next();
if (c != '\\') return c; /* A normal character */
c = next();
if (c == '\\') return '\\'; /* Two backslashes in the code means one backslash */
if (c == 'r') return '\r'; /* '\r' is a carriage return */
…
因此,我们可以在上面的代码中添加一个条件...
if (c == 'n') return 10; /* '\n' is a newline */
…生成知道'\n'
表示ASCII 10的编译器。有趣的是,该编译器以及由其编译的所有后续编译器 “知道”该映射,因此在下一代源代码中,您可以将最后一行更改为
if (c == 'n') return '\n';
……它会做正确的事!将10
来自编译器,而不再需要在编译器的源代码被明确定义。1个
这是用C代码实现的C语言功能的一个示例。现在,对每种语言功能都重复该过程,您将拥有一个“自托管”编译器:一个用C编写的C编译器。
1本文描述的情节转折之处在于,由于编译器可能会被“教”为事实,因此以难以检测的方式生成特洛伊木马可执行文件也可能会被误导,并且这种破坏行为会持续存在在受污染的编译器产生的所有编译器中。
您已经获得了很好的答案,但是我想为您提供不同的观点,希望会对您有所启发。首先让我们建立两个我们都可以同意的事实:
我相信您可以同意#1和#2都是正确的。现在,看看这两个语句。您现在看到CoffeeScript编译器能够编译CoffeeScript编译器是完全正常的吗?
编译器不在乎它编译什么。只要它是用CoffeeScript编写的程序,就可以对其进行编译。CoffeeScript编译器本身恰好就是这样的程序。CoffeeScript编译器并不关心它是正在编译的CoffeeScript编译器本身。它所看到的只是一些CoffeeScript代码。期。
编译器如何自行编译,或者该语句是什么意思?
是的,这正是该声明的含义,我希望您现在可以看到该声明的正确性。
编译器如何自行编译,或者该语句是什么意思?
就是这个意思。首先,要考虑一些事情。我们需要查看四个对象:
现在,很明显,您可以使用CoffeScript编译器的生成的程序集-可执行文件-编译任意CoffeScript程序,并为该程序生成程序集。
现在,CoffeScript编译器本身只是一个任意的CoffeScript程序,因此可以由CoffeScript编译器进行编译。
看来,你的困惑来源于这样的事实,当你创建自己的新的语言,你不要有编译器还可以用来编译的编译器。这肯定看起来像个鸡蛋问题,对吧?
介绍称为引导程序的过程。
现在,您需要添加新功能。假设您仅实现了while
-loops,但还需要for
-loops。这不是问题,因为您可以用for
-loop这样的方式重写任何while
-loop。这意味着您只能while
在编译器的源代码中使用-loops,因为您手头的程序集只能编译这些代码。但是您可以在编译器内部创建函数,从而可以for
对其进行Pase 和编译-loops。然后,使用已有的程序集,并编译新的编译器版本。现在,您有了一个编译器的程序集,该程序集也可以解析和编译for
-loops!现在,您可以返回到编译器的源文件,并将while
不需要的任何-loops 重写为for
-loops。
漂洗并重复直到可以使用编译器编译所需的所有语言功能。
while
而for
显然只是例子,但这个工程的任何你想要的新的语言特性。然后您处于CoffeScript处于这种情况:编译器自行编译。
那里有很多文献。关于信任的思考信任是每个对该主题感兴趣的人的经典著作,至少应阅读一次。
这里不是编译器的问题,而是语言的表达性的问题,因为编译器只是用某种语言编写的程序。
当我们说“一种语言被编写/实现”时,实际上是指实现了该语言的编译器或解释器。有一些编程语言,您可以在其中编写实现该语言的程序(相同语言的编译器/解释器)。这些语言称为通用语言。
为了能够理解这一点,请考虑一下金属车床。它是用于成型金属的工具。仅通过使用该工具,就可以通过创建其零件来创建另一个相同的工具。因此,该工具是通用机器。当然,第一个是使用其他方式(其他工具)创建的,并且质量可能较低。但是第一个用于构建更高精度的新产品。
3D打印机几乎是通用机器。您可以使用3D打印机来打印整个3D打印机(您不能构建熔化塑料的笔尖)。
编译器采用高级规范,然后将其转换为低级实现,例如可以在硬件上执行的规范。因此,除了目标语言的语义外,规范的格式与实际执行之间没有任何关系。
交叉编译器从一个系统迁移到另一个系统,跨语言编译器将一种语言规范编译为另一种语言规范。
基本上,编译只是一种翻译,级别通常是高级语言到低级语言,但是有许多变体。
引导编译器当然是最令人困惑的,因为它们会编译其编写的语言。不要忘记引导的第一步,即至少需要一个最小的现有可执行版本。许多自举式编译器会首先使用编程语言的最小功能,然后再添加其他复杂的语言功能,只要新功能可以使用以前的功能表示即可。如果不是这种情况,则需要事先用另一种语言开发“编译器”的一部分。
self-hosting
编译器。见programmers.stackexchange.com/q/263651/6221