为什么自托管的编译器被视为新语言的通行典礼?


30

我现在已经在许多地方听到人们希望语言使用或至少具有自托管的编译器以值得尊重。

我很好奇为什么会这样。编译器似乎是编写非常重要的软件,我想并不是所有语言都非常适合创建它们。将精力花在可以带来更好结果的事情上是否更有意义?


17
“编译器似乎是编写非常重要的软件,我想并不是所有语言都非常适合创建它们。”:我认为这是尝试以新语言编写编译器的很好理由,即证明语言符合任务。
乔治

13
除非它是一种特殊用途的语言,否则不太适合编写编译器的语言可能也不适合我想要做的任何事情。
CodesInChaos

3
AFAIK,Fortran并不总是如此。某些Fortran编译器(例如,gfortran来自GCC ...)在Fortran中进行编码。
Basile Starynkevitch 2014年

Answers:


29

将精力花在可以带来更好结果的事情上是否更有意义?

像什么?

关于编译器的好处是它们没有太多的依赖关系。这使它们成为可能还没有非常庞大或多样化的标准库的新语言的良好候选者。

更好的是,它们需要各种各样的东西,同时也需要充分研究。多样性有助于确保您的示例测试语言的各个部分。受到良好的学习意味着您需要与其他编译器进行比较-并使您更了解自己正在做的学术工作。

尽管编译器似乎需要大量工作,但在宏伟的设计方案中它们却很小。如果语言实施者甚至无法使用新语言来完成以前做过的事情,那么他们将如何去做新颖的事情?他们将如何处理标准库或IDE等非常大的东西?


顺便提一句,我想提一下,尽管很不错,但仍有多种原因可能会用另一种语言编写编译器。例如,大多数JavaScript引擎都不是用javascript编写的。造成这种情况的原因有很多:与其他软件集成,链接到现有库/依赖项,高级工具,性能,旧代码...有时,语言自编译很好,但是将核心编译器保留在其中仍然有意义另一个。然而,语言本身是有意义的。只是您通常无力重建整个生态系统。
dagnelies,2014年

2
@arnaud以及Javascript编译器需要Javascript环境这一事实,因为Javascript需要Javascript环境<repeat paradoxically>,所以不能用Javascript编写,因为操作系统没有提供Javascript环境(如果是的,它不会用Java脚本编写)。
Qix 2014年

3
@Qix en.wikipedia.org/wiki/Bootstrapping_%28compilers%29但主要没有理由使用它。它被广泛认为是一种较差的语言,浏览器不使用它进行编译,因为它们可以控制情况:),而我们其他人则无法在网络上进行选择。
2014年

3
我对“没有太多依赖”的说法不太确定。对于编译器前端可能是正确的。但是,一旦有了AST,就推出自己的优化器和代码生成器似乎并不可行。除了现代优化技术需要复杂的形式逻辑引擎(可能要使用第三方库)的事实外,没有理由为每种新语言重新发明轮子,而不是建立在像GCC这样的行业实力基础上或LLVM。
5gon12eder

30

使用正在编译的语言进行编译的目标通常是“ 吃自己的狗粮 ”实践的一部分。它向全世界证明,您认为支持模块和工具的语言,编译器和生态系统“足以胜任严肃的工作”或“已做好生产准备”。

它还具有迫使那些最接近语言,编译器和运行时设计的人员直接面对他们所做出的所有决策以及他们所选择的开发优先级(缺陷和全部)的影响的有益作用。这通常会导致一个核心小组,该小组不仅在理论上理解语言环境,而且在艰苦的真实单词条件下使用语言/工具具有丰富的实践经验。


1
为了完整性:自己吃狗粮;看狗喂(adj。)或dogfooding(动词)
Qix 2014年

17

人们创建新的通用语言的主要原因之一是:他们讨厌其他每种语言的至少一件事。这就是为什么这么多语言没有铺天盖地的原因。您对可以改善编程寿命的语言有一个不错的主意,但是您必须以一种至少以一种方式使您烦恼的语言进行第一个实现。自托管意味着您不再需要使用那种古老的烦人的语言。这就是语言的创造者朝着这一步骤努力并将其视为重要里程碑的原因。

许多语言功能在纸上看起来都不错,但是当您开始在实际项目中使用它们时,就会开始看到它们的局限性。举个例子,很多语言起初都没有不错的unicode支持。完成一个大型项目有助于确保已经遇到并处理了许多此类情况,并且自托管编译器与任何项目一样好。这就是为什么除语言创建者之外的其他人将其视为重要里程碑的原因。

这并不意味着它是唯一值得注意的里程碑。编译器没有行使某些功能,例如数据库集成,图形界面,联网等。


我觉得自己像一个(本地)语言是一种语言时,它可以编译本身 Linux内核可能被移植到它(因为它涵盖了大多数/所有任务所必需的最现代的操作系统对功能)。
2014年

但是,编写编译器并不需要真正的Unicode支持。
圣保罗Ebermann

11

史蒂夫·耶格 Steve Yegge)写了一篇很棒的博客文章,在某种程度上间接地解决了这个问题。

要点1:编译器几乎涵盖了计算机科学的各个方面。它们是一门高级课程,因为您需要了解在计算机科学课程中学习的所有其他内容才可以开始。数据结构,搜索和排序,渐近性能,图形着色?全部都在那里。

尽管Knuth最初只是(作为)编译器教科书,但几十年来一直在致力于他具有纪念意义(并且永无休止)的“计算机编程艺术”是有原因的。就像卡尔·萨根(Carl Sagan)所说的“如果要从头制作苹果派,必须首先发明宇宙”一样,如果要编写编译器,则必须首先处理计算机科学的几乎所有方面。

这意味着,如果编译器是自托管的,那么无论我在做什么,都可以肯定地做到了我所需要的。相反,如果您使用语言编写编译器,则很有可能会错过对某人真正​​重要的内容,因为语言实现者无需编写程序就可以要求他们考虑所有这些问题。

要点2:从30,000英尺高处,令人惊讶的问题看起来就像编译器一样。

编译器获取符号流,根据某些特定于域的预定义规则找出它们的结构,然后将它们转换为另一个符号流。听起来很一般,不是吗?好吧,是的

无论您是否在Visual C ++团队中,都经常会发现自己需要做一些看起来像编译器一部分的事情。我每天都会这样做。

与大多数其他专业不同,程序员不仅使用工具,还构建自己的工具。不能(由于缺乏技能或缺乏用于构建其他工具的可用工具)而编写工具的程序员将永远受到限制,仅限于其他人提供的工具。

如果一种语言“不适合创建”程序,该程序可能会使用一系列符号,将规则应用于它们,然后将其转换为另一种符号流,这听起来像是一种有限的语言,那不是一种有用的语言。对我来说。

(幸运的是,我认为没有多少编程语言不适合转换符号。C可能是当今使用的最糟糕的语言之一,但C编译器通常是自托管的,因此永远不会停止任何人。)

从个人经验来看,我最后要提到的第三个原因是Yegge没有提及(因为他不是在写“为什么要自我托管”):它消除了许多错误。当你写一个编译器,这意味着你每次建造(不只是你每次都运行它),你依靠它来工作,并正常工作对一个体面的代码库(编译器本身)。

这个月我一直在使用一个相对较新的著名的非自托管编译器(您可能会猜到哪个),而且我不能在两天之内对问题进行隔离。我想知道设计师实际上需要使用多少。


8

如果您想让X语言的编译器具有自托管功能,那么您首先要用某种其他语言(例如Y)来实现它,以便它接受X语言的输入并吐出汇编代码或某些中间代码,甚至运行编译器的计算机的目标代码。您希望选择语言Y,使其与语言X尽可能相似,因为有时您会将以Y编写的代码转换为X。

但是,您不想使用多余的语言Y来编写更多的编译器,因此,首先,您仅实现该语言的一个子集-消除多余的构造。在使用“ C”类型语言的情况下,while不能fordo while如果但没有案例或第三纪要。没有结构,联合或枚举。等等,您所剩下的只是足够的语言来编写用于语言X的解析器和基本代码生成器,然后检查输出。再次。

完成这项工作后,您可以将用语言Y编写的编译器源重写为语言X,并使用用语言Y编写的编译器来编译语言X源。输出将是用新语言X编写的新编译器,该编译器编译语言X,即它现在是自托管的。但是,由于您仅使用语言Y实现了该语言的子集,因此尚不完整。

因此,现在您添加缺少的功能,测试每个(或一组功能)它们生成正确的代码。也就是说,一旦在编译器中实现了该功能,就可以使用新功能编写测试程序,进行编译和测试,但是您不应该在编译器源代码中使用它们。验证新功能后,您便可以在编译器源本身中使用这些新功能-也许替换一些用语言子集编写的原始代码-使用带有新功能的版本重新编译编译器源。

现在,您有了一种向语言中添加新功能的机制-并且一旦验证了这些功能的代码生成正确无误,就可以在下一代编译器本身中使用它们。

大约60年前,当计算机首次出现在现场时(后来又是在微处理器首次出现时),还没有其他适合于实现初始编译器的语言。因此,第一个编译器必须用汇编代码编写,然后当足够多的编译器运行时,汇编代码将被用新语言编写的版本替换。也没有汇编程序?整个处理器又下降了一层,汇编器最初是用机器代码编写的


2

是否有可能产生一种设计得不是为编写编译器而设计得很好但为其他目的而设计得很好的编程语言?

查看像SQL这样的语言,我想答案是肯定的。但是,这种性质的语言并不是通用的。


1
接受挑战:用SQL编写C编译器。
Qix

2

谁说的?...无论如何,这只是一种意见。有些人可能同意,有些人可能不同意,这里没有对与错。有些语言本身就是编写编译器的,而有些则没有。随你。

但是,如果一种语言能够“自我编译”,那我认为这是一个很好的执行/概念证明...只是...不错...它证明了该语言适合于做一些复杂的事情。

我还要提及的是,尽管很好,但仍有多种原因可能会用另一种语言编写编译器。 例如,大多数JavaScript引擎都不是用javascript编写的。造成这种情况的原因有很多:与其他软件集成,链接到现有库/依赖项,高级工具,性能,旧代码...有时,语言自编译很好,但是将核心编译器保留在其中仍然有意义另一个。然而,语言本身是有意义的。只是您通常无力重建整个生态系统。


2

Clang用C ++编写。在Objective-C中重写Clang Objective-C编译器并不是很难,但是那样就没用了。C ++编译器中的任何更改都必须在Objective-C中重做,反之亦然。所以为什么?

现在有一个Clang Swift编译器。当然可以在Swift中重写编译器。但是它的目的是什么?要证明该语言足够强大,可以在其中编写编译器?没人关心您是否可以在Swift中编写编译器。人们确实会在乎您是否可以在Swift中编写用户界面,并且可以证明。

如果您有一个经过良好测试的编译器,可以轻松地适应不同的语言,那么除非用一种不同的语言进行重写会使其更易于使用该编译器,否则将其重写为不同的语言是毫无意义的。例如,如果用Swift编写Clang有意义,那么Clang C,C ++和Objective-C编译器将用Swift编写。

除了证明您可以使用某种编程语言编写编译器之外,还有许多重要的事情要做。


1

它表明该语言能够处理复杂的字符串处理并转换为另一种语言/解释自身。

在创建编译器(第一个大项目)的过程中,将会出现一些问题。

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.