Compilation 编译器如何编译自己?

Compilation 编译器如何编译自己?,compilation,Compilation,我在网站上研究咖啡脚本,它有文本 CoffeeScript编译器本身就是用CoffeeScript编写的 编译器如何编译自己,或者这句话是什么意思?编译器的第一版不能由特定于它的编程语言机器生成;你的困惑是可以理解的。第一个编译器可以构建具有更多语言功能的更高版本的编译器(在新语言的第一个版本中重写源代码)。该版本可以编译下一个编译器,依此类推。下面是一个例子: 第一个CoffeeScript编译器是用Ruby编写的,生成了CoffeeScript的版本1 CS编译器的源代码在CoffeeScr

我在网站上研究咖啡脚本,它有文本

CoffeeScript编译器本身就是用CoffeeScript编写的


编译器如何编译自己,或者这句话是什么意思?

编译器的第一版不能由特定于它的编程语言机器生成;你的困惑是可以理解的。第一个编译器可以构建具有更多语言功能的更高版本的编译器(在新语言的第一个版本中重写源代码)。该版本可以编译下一个编译器,依此类推。下面是一个例子:

  • 第一个CoffeeScript编译器是用Ruby编写的,生成了CoffeeScript的版本1
  • CS编译器的源代码在CoffeeScript 1中重写
  • 原始的CS编译器将新代码(用CS 1编写)编译成编译器的版本2
  • 对编译器源代码进行更改以添加新的语言功能
  • 第二个CS编译器(第一个是用CS编写的)将修订后的新源代码编译成编译器的版本3
  • 对每个迭代重复步骤4和5
  • 注意:我不确定CoffeeScript版本是如何编号的,这只是一个例子


    这个过程通常被称为。另一个引导编译器的例子是rustc,它是.

    的编译器。您已经得到了一个非常好的答案,但是我想为您提供一个不同的视角,希望能对您有所启发。让我们首先确定两个我们都能达成一致的事实:

  • CoffeeScript编译器是一个可以编译用CoffeeScript编写的程序的程序
  • CoffeeScript编译器是用CoffeeScript编写的程序
  • 我相信你会同意#1和#2都是正确的。现在,看看这两个陈述。您现在看到了吗,CoffeeScript编译器能够编译CoffeeScript编译器是完全正常的

    编译器不在乎它编译什么。只要它是用CoffeeScript编写的程序,它就可以编译它。而CoffeeScript编译器本身恰好就是这样一个程序。CoffeeScript编译器并不关心它正在编译的是CoffeeScript编译器本身。它看到的只是一些咖啡脚本代码。句号

    编译器如何编译自己,或者这句话是什么意思

    是的,那正是那句话的意思,我希望你现在能明白那句话是怎么回事。

    一个小而重要的澄清 在这里,“编译器”一词掩盖了一个事实,即涉及两个文件。一种是可执行文件,它将CoffeScript中编写的输入文件作为输入文件,并生成另一个可执行文件、可链接对象文件或共享库作为输出文件。另一个是CoffeeScript源文件,它恰好描述了编译CoffeeScript的过程

    您将第一个文件应用于第二个文件,生成第三个文件,该文件能够执行与第一个文件相同的编译操作(如果第二个文件定义了第一个文件未实现的功能,则可能会执行更多),因此如果您愿意,可以替换第一个文件。

    在本文中,Unix的创始人之一Ken Thompson,写了一篇关于C编译器如何编译自己的引人入胜(且易于阅读)的概述。类似的概念也适用于CoffeeScript或任何其他语言

    编译自己的代码的编译器的思想与源代码的思想大致相似,源代码在执行时产生原始源代码作为输出。一杯咖啡。汤普森举了一个C奎因的例子:

    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论文中描述的情节扭曲是,由于编译器可以被“教导”这样的事实,它也可能被错误教导以难以检测的方式生成特洛伊木马可执行文件,并且这种破坏行为可能会持续存在于受污染编译器生成的所有编译器中

    编译器如何编译自己,或者这句话是什么意思

    就是这个意思。首先,要考虑的一些事情。我们需要查看四个对象:

    • 任意CoffeScript程序的源代码
    • 任意CoffeScript程序的(生成的)程序集
    • CoffeScript编译器的源代码
    • CoffeScript编译器的(生成的)程序集
    现在,很明显,您可以使用CoffeScript编译器生成的程序集(可执行文件)编译任意CoffeScript程序,并为该程序生成程序集

    现在,CoffeScript编译器本身只是一个任意的CoffeScript程序,因此,它可以由CoffeScript编译器编译

    您的困惑似乎源于这样一个事实:当您创建自己的新语言时,您还没有可以用来编译编译器的编译器。这看起来肯定像只