为什么要用C ++编写编译器?


14

我想知道为什么C ++是编写编译器的好选择。当然,C对此也有好处,因为许多编译器都是用C或C ++编写的,但是这次我对C ++更加感兴趣。有什么好的理由吗?我一直在互联网上寻找该信息,但找不到任何好的理由。


3
“许多编译器都是用C ++编写的”-有参考吗?哪个?是什么让您认为C ++比其他流行语言更常用于编译器构造?
布朗

6
@DocBrown好吧,Clang和MSVC主要是用C ++编写的,现在gcc中有一些C ++,Java JVM是用C ++编写的stackoverflow.com/questions/410320/what-is-java-write-in超级用户。 com / questions / 136136 /…
克莱姆(Klaim)2012年

@DocBrown DMD D的参考编译器是用C ++编写的
棘手怪胎

3
谁说这是一个好选择?
菲尔(Phil)2012年

1
@Phil您认为他们是在没有考虑替代方案的情况下做出此选择的吗?这不是一个“好的”选择,而是一个“高效的”选择。
克莱姆2012年

Answers:


24

C ++有两个方面。它具有低级开发方面,这使其看起来像是用于执行低级操作(如代码生成)的自然语言。它还具有高级方面(C则没有),可以让您以逻辑,面向对象的方式构造复杂的应用程序(如编译器),同时仍保持性能。由于它同时具有低级和高级功能,因此对于需要低级功能或性能的大型应用程序是一个不错的选择。


11
据我所知,编译器内部的许多逻辑都是功能性的(将复杂的数据结构转换为其他数据结构),所以我不确定是否面向对象的工具(更面向大型编程) ,架构方面)为过程编程风格的编译器构造带来了真正的优势。只是我的2美分。
乔治

5
@Giorgio具有对象有助于编译器编写的许多其他方面。例如,编译器在优化时有很多状态要处理,而这类东西很适合OOP。而且,OOP和函数式编程可以互为补充,因此,仅因为算法可能主要是函数式的,并不意味着对象将无济于事。
奥列克西(Oleksi)

3
@ Giorgio和Oleksi:我可以确认你们两个。我用Haskell为现实世界的语言编写了一个编译器。非常合适。但是有时候我错过了一些面向对象的知识。如果必须编写另一个编译器,则肯定会选择Haskell,但这确实是一种特殊情况。对于其他类型的项目,我会毫不犹豫地选择Haskell。
围巾岭

27
为什么您需要一种具有“低级方面”的语言来进行代码生成?我看不到这两者之间如何建立联系。
phant0m 2012年

5
您不需要“低级方面”来进行代码生成,只需要能够将日语文本写入文件的Unicode 标识符即可。
2012年

17

我的经验与您在这里的前提不同。实际上,对于高级通用语言,使用与源语言(正在编译的语言)相同的语言编写编译器一种非常普遍的做法。例如:

  • Sun的Java编译器是用Java编写的
  • Scala编译器是用Scala编写的
  • Mono的C#编译器是用C#编写的
  • Squick的Smalltalk编译器是用Smalltalk编写的
  • ... 还有很多

一个例外是为现有编译器框架(例如GCC,LLVM或Polyglot)编写的编译器前端,然后以该框架的语言编写,或者依赖于现有解析器生成器(例如Yacc)的编译器。由于GCC,LLVM和Yacc是通用的,用C和C ++编写的成熟工具,因此激励了编译器编写者使用它们,这可能导致C和C ++在编译器实现语言分发中占有很大份额。


2
我认为这与编写编译器的人员了解得更多,并且非常喜欢他们编写编译器的语言有关,而不是出于客观的技术原因。
Thomas Bonini 2012年

1
@Krelp我同意这不是出于客观的技术原因,但这也不是真正的“喜欢”,它只是一种语言的习俗而已,“它是否足够成熟,能够用作其自己的实现语言?编译器”。
橡树

1
Sun的Java编译器是用C ++编写的:stackoverflow.com/questions/410320/what-is-java-write-in
Klaim,2012年

12
@Klaim,您在这里混淆了两种产品。一种是Sun的Java编译器(javac命令行),它将Java编译为Java字节码。它是用Java编写的-我本人已经对其进行了多次修改,您可以在线浏览其Java源代码。另一个是嵌入在Hotspot JVM中的即时编译器,它将Java 字节码编译为本地机器代码。像大多数JVM一样,它是用C ++编写的,但它不是Java编译器 -实际上,它对Java语言一无所知。
橡树

@Oak,绝对正确!换句话说,JVM!= javac
Paul Draper

6

要编译什么到什么?编译器将源代码从一种语言(源语言)转换为另一种语言(目标语言),但这并没有说明目标语言的低级性。

  • CoffeeScript编译为JavaScript,该编译器使用CoffeeScript编写。
  • Script#将C 编译为JavaScript,如果我还记得的话,它是用C#编写的。
  • 等等

您选择用来编写编译器的语言取决于上下文。例如,在一个将来自PHP的语言编译成本地PHP代码的项目中,我混合使用了PHP和C#来编写编译器,因为鉴于我的技能,这对我来说最有意义。另一个人会选择Python,Java和PHP或C ++以及一些JavaScript,或者其他任何选择。

C或C ++是受欢迎的选择,因为它支持编译器相关工具(请参阅Telastyn的答案),并且由于这两种语言都使您真正地本机化。但是选择另一种语言没有错。

请注意,为了变得更加怪异,您可以选择源语言来编写编译器本身。这就是CoffeeScript编译器和许多其他编译器的情况。它在IDE中也很流行:第一个Visual Studio之一是使用相同的Visual Studio构建的。


5
自托管并非令人讨厌,它是移植编译器的重要属性。

5
原因是,它可以立即使编译器本身成为测试程序。在很长一段时间内,它很可能也是该编译器的最大程序。

6

我倾向于在这里质疑基本前提。尽管C和C ++可以很好地编写编译器,但其他许多语言似乎也可以很好地完成任务。

不过,这取决于您正在编译的语言。对于小型,简单的语言,C和Pascal可以很好地工作。如果您要编译大而复杂的东西,那么编译器也会变得又大又复杂-在这种情况下,C ++用于组织和使用较大程序的额外功能显然很方便。不过,这并不是真正针对编译的,只是一般而言对大型程序有用的功能。

我认为也值得一提。初学者(似乎)认为编译器主要是在进行文本操作,因此他们认为Perl之类的东西将对编写编译器产生巨大的帮助。实际上,直到构建AST之后,大多数有趣的编译部分才真正开始。虽然我确定Perl可以很好地完成这项工作,但是它的文本处理功能也并没有真正赋予它巨大的优势(文本处理主要在lexer中,而C语言之类的lexer生成器无论如何都支持RE)。


2
AST =抽象语法树,RE =正则表达式
chaotic3quilibrium,2012年

5

编译器可以用任何现代语言来实现。但是,编译器最重要的要求之一就是要快。

C ++在这里有明显的优势。C ++中的优化并不便宜。但是,由于该语言的低级性质,与其他任何语言相比,手动优化C ++代码都是可能的(非汇编语言的Assembly除外)。


11
另一个重要的要求是生成的代码正确-我宁愿拥有一个我可以信任的慢速编译器,而不是一个快速生成错误代码的编译器。

2
当然可以极大地优化C ++,但是有很多相当不错的东西……而不是最佳C ++代码。
Donal Fellows

2
@DonalFellows换一种方式:用任何一种语言编写的代码都可能少于最佳代码,但是有些优化无法在除C ++以外的其他语言(汇编语言除外)中启用。高层结构允许更强的内联)。
克莱姆(Klaim)2012年

@ user1249-C ++代码的速度没有理由使它成为任何越野车。我宁愿拥有一个快速正确的编译器,也不愿拥有一个缓慢正确的编译器。
gnasher729

3

我怀疑使用它们的主要动机是Lex / Yacc / Bison输出(主要是C)。由于长期以来一直是标准,因此它很有动力。

并不是那些特别好的理由...


实际上,这并不令我满意,但感谢您的尝试。
科布拉2012年

那不能回答“为什么要选择C ++而不是C进行编译器构造”的问题。
布朗

3
这根本不是一个很好的理由。Lex和Yacc的类似工具可用于许多平台。例如,PLY和ANTLR。
user16764 2012年

而且,大多数流行的现实世界编译器(例如,我可以肯定的说是Abuot Clang和GCC)都使用手写的解析器。

@delnan:是的,但是他们可能开始使用生成的东西开始工作。解析器的手工生成是您真正不希望执行的优化步骤,直到您可以证明其他方法正在起作用。
马丁·约克

1

我对此事有经验。我已经用C和C ++编写了编译器。C和C ++之间的主要区别是C没有自动进行动态内存管理。C语言中的所有内存管理都必须明确完成。编写编译器需要处理字符串处理和数组管理。在C语言中,您被迫考虑声明的每个字符串和每个数组的大小,并在访问这些对象时检查索引(如果您希望代码安全稳定)。当然,在C中可以进行动态内存管理,但是没有什么是自动的。您必须使用malloc()和free()显式分配和释放内存,将动态对象的大小保留在单独的变量中,以确保您不会超出范围访问它们。

在C ++中,您可以具有相同的机制,但是它确实是开发时高效的,因为所有内存管理都可以封装在构造函数和析构函数中,而不必显式调用。因此,编译器正在为您分配和释放资源。如果创建自己的类,则动态对象的大小也可以封装,并且可以通过重载运算符[]检查索引的边界访问。这些抽象有助于使您的代码更整洁,更易于理解和调试,并且绝对可以加快开发速度。

如果使用C创建编译器,肯定会花费更多时间。C ++将使您在更少的时间内完成项目。C和C ++具有相同的性能,但是C ++具有C没有的许多优点。


0

CompCert项目是未在C或C ++编写的研究C编译器,但更多的OCaml中和勒柯克。

观察一下C ++曾经被翻译成C(在Cfront中)。现在,您可以将GCC前端用于Gimple,然后将Gimple转储到某个数据库,然后将Gimple编写到汇编翻译器中。但是出于法律原因(GCC运行时库例外),要求此类编译器为开源。向您的律师询问详细信息,我不是律师。GCC的旧变体已经用C(+多种领域特定语言)编写,并且前端带有一些C ++变体。OpenWatcom可能是用C编写的C ++编译器(我留给您检查)。

Compcert的源可免费用于学术和研究目的。如果要在工业上(合法地)使用它,则需要获得Absint的许可。

另请参阅答案,以回答两个相关问题。

如果我在2020年受命从头开始编写C(或C ++)编译器(在Linux上运行,也许是一些交叉编译器),那么我可能不会用C ++编写它。我会考虑使用OcamlGoRust编写它。如果允许的话,我可以基于Frama-C。如果需要用C或C ++编写代码,我首先会为其编写一个垃圾收集器库,可能是一些持久层- 对于整个程序优化非常有用-然后我将考虑使用元编程方法(生成大多数C或C ++代码)。我的临时工具(可能是BismonRefPerSys)编译器 如果允许的话)。

您可以找到一些用Common Lisp或Python(例如ShivyCnqcc)编码的(或多或少开源)C编译器。还可以查看ZetaC

请注意,GCC的最新版本在技​​术上不是用纯C ++编码的,它们是GCC涉及的十几种特定领域的语言(其中一些是图灵完备的)。另请参阅我的旧版GCC MELT项目。

如果在将来的GCC版本中将某些PythonGuile解释器嵌入其中(例如,作为GCC通道管理器的替代品),我不会感到惊讶。

另请参阅MILEPOST GCC项目。

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.