【问题标题】:Starting off a simple (the simplest perhaps) C compiler?从一个简单的(也许是最简单的)C 编译器开始?
【发布时间】:2010-02-28 00:02:05
【问题描述】:

我遇到了这个:Writing a compiler using Turbo Pascal

我很好奇是否有任何教程或参考资料解释了如何创建一个简单的 C 编译器。我的意思是,如果它能让我达到理解算术运算的水平就足够了。看了Ken Thompson的这篇文章,我真的很好奇了。编写能够理解自身的东西的想法似乎令人兴奋。

为什么我提出这个问题而不是问 Google?我试过谷歌,Pascal 是第一个链接。其余的似乎无关紧要并补充...在这些方面总是比谷歌更好。我想阅读一些与我上面列出的具有相同精神的文章,但至少突出了构建简单 C 编译器的引导阶段。

另外,我不知道最好的学习方法。我是否开始用 C 或其他语言构建 C 编译器?我是编写 C 编译器还是其他语言?一旦我有一些探索的方向,我觉得这样的问题会得到更好的回答。有什么建议吗?

有什么建议吗?

【问题讨论】:

  • 尝试创建带有初学者标志的 C(甚至是子集)编译器有点令人惊讶。你应该先尝试一些更简单的东西。
  • 编写自己的编译器会很有趣。但请考虑先获得 CS 学位。必须拥有杀死巨龙的武器。
  • @Phong:编译器领域的初学者 :) 有几年的编程经验……但我一直在做事情,却不知道它们实际上是如何“工作”的. @nobugz:它在我的清单上 :) 只是没有什么能真正阻止我自己先做。
  • 规范编译器参考问题是 stackoverflow.com/questions/1669/learning-to-write-a-compiler 。顺便说一句——作为第一次介绍,我是 Crenshaw 编译器的忠实粉丝。将 pascal 翻译成 c 并不麻烦,所以请随意使用它。不过,如果您要坚持使用编译器,您很快就会想要一份更完整的参考资料。
  • 是的,看起来我会先从 Crenshaw 开始,然后参考这里的野兽链接。

标签: c compiler-construction programming-languages


【解决方案1】:

一个编译器由三部分组成:

  1. 解析器
  2. 抽象语法树 (AST)
  3. 汇编代码生成器

有很多不错的解析器生成器都是从语言语法开始的。也许 ANTLR 将是您开始的好地方。如果你想坚持 C 根,试试 lex/yacc 或 bison。

有 C 的语法,但我认为 C 的整体是复杂的。您最好从该语言的一个子集开始,然后逐步提高。

一旦有了 AST,就可以使用它来生成要运行的机器代码。

这是可行的,但不是微不足道的。

我还会在亚马逊上查看有关编写编译器的书籍。龙书是经典,但也有更现代的。

更新:堆栈溢出也有类似的问题,例如this one。也请查看这些资源。

【讨论】:

  • 太棒了!非常感谢...该线程本身似乎是一头野兽..将开始深入研究...
  • +1 用于 ANTLR。它可能不是最好的解析器生成器,但调试和测试工具很难超越。
【解决方案2】:

我建议你这个教程:

这是一个关于如何实现“小语言”编译器的小例子。源码很小,一步一步讲解。

还有 LLVM(代表程序内部结构的低级虚拟机)库的 C 前端库:

【讨论】:

  • @duffymo:谢谢,我在本教程中真正喜欢的是它们不依赖任何外部软件来实现词法分析器/解析器功能。
  • 是的,真的很棒。很好的发现。感谢您发布它。
【解决方案3】:

不管怎样,Tiny C Compiler 是一个相当全功能的 C 编译器,位于一个相对较小的源代码包中。您可能会从研究该源代码中受益,因为它可能比尝试理解 GCC 的所有源代码库更容易理解。

【讨论】:

    【解决方案4】:

    这是我的观点(和猜想),如果不了解本科(大专)计算机科学课程中通常涵盖的数据结构,将很难编写编译器。这并不意味着您不能,但您需要了解基本的数据结构,例如链表和树。

    与其编写完整的或符合标准的 C 语言编译器(至少在开始时),我建议将自己限制在语言的基本子集上,例如通用运算符、仅整数支持以及基本函数和指针。一个典型的例子是 Ron Cain 的 Small-C,我相信在 1980 年代,Dr. Dobbs Journal 写的一系列文章很受欢迎。他们与 James Hendrix 的绝版书 A Small-C Compiler 一起发布了 CD

    我的建议是遵循 Crenshaw 的教程,但是为类 C 语言编译器编写它,以及您希望定位的任何 CPU 目标(Crenshaw 的目标是摩托罗拉 68000 CPU)。为了做到这一点,您需要知道要在哪个目标上运行已编译程序的基本程序集。这可能包括用于 68000 或 MIPS 的模拟器,它们可以说是比 Intel x86(16/32 位)古老的 CISC 指令集更好的汇编指令集。

    有许多潜在的书籍可以作为学习编译器/翻译器理论(和实践)的起点。阅读comp.compilers FAQ,以及各种在线书商的评论。大多数介绍性书籍都是作为大二到高年级本科计算机科学课程的教科书编写的,因此如果没有 CS 背景,它们可能会读得很慢。一本可能更具介绍性但比 "The Dragon Book" 更容易阅读的旧书是 Thomas Parsons 的 Introduction to Compiler Construction。它较旧,因此您应该能够以合理的价格从您选择的在线书商那里找到二手书。

    所以我想说,尝试从 Jack Crenshaw 的 Let's Build a Compiler 教程开始,按照他的示例编写自己的教程,并构建简单编译器的基础知识。一旦你有了这个工作,你就可以更好地决定你希望从那个点开始。

    添加:

    关于引导过程。由于现有的 C 编译器可以免费使用,因此您无需担心引导。使用单独的现有工具(GCC、Visual C++ Express、Mingw / djgpp、tcc)编写您的编译器,您可以在稍后的阶段自行编译您的项目。我对这部分问题感到惊讶,直到我意识到通过阅读 Ken Thomas 的 ACM 图灵奖演讲Reflections on Trusting Trust,您想到了编写自己的编译器,该演讲确实进入了编译器引导过程。这是一个有节制的高级主题,也很麻烦。我什至发现在包含 C 编译器的旧 Unix 系统(64 位 Alpha 上的数字 OSF/1)下引导 GCC C 编译器是一个缓慢且耗时且容易出错的过程。

    另一个问题是像 Yacc 这样的编译器工具实际上做了什么。 Yacc (Yet Another Compiler Compiler 或 Bison from GNU) 是一种旨在使编写编译器(或翻译器)解析器更容易的工具。根据您输入到 yacc 的目标语言的形式语法,它会生成一个解析器,它是编译器整体设计的一部分。接下来是用于生成 词法分析器 或扫描器的 Lex(或来自 GNU 的 flex),它通常与 yacc 生成的解析器结合使用以形成编译器前端的骨架。这些工具使编写器成为前端可以说比自己编写词法分析器和解析器更容易。 Crenshaw 的教程没有使用这些工具,您也不需要,许多编译器编写者并不总是使用它们。当然,Crenshaw 承认本教程的解析器非常基础。

    Crenshaw 的教程也跳过了生成 AST(抽象语法树),这简化了但也限制了教程编译器。它缺乏大部分(如果不是全部)优化,并且与编译器“后端”发出的特定编程语言和特定汇编语言密切相关。通常,AST 是可以执行一些优化的中间部分,用于在设计中解耦编译器前端和后端。对于没有计算机科学背景的初学者,我建议不要担心您的第一个编译器(或至少它的第一个版本)没有 AST。我认为保持它的小而简单将有助于您在第一个版本中完成编译器的编写,然后您可以从那里决定接下来要如何进行。

    【讨论】:

    • 我花了很长时间才消化你写的东西。非常翔实的帖子。感谢您的宝贵时间...
    • 谢谢你,很高兴你发现它有用。我希望我能以有帮助的方式回答您的问题,并鼓励您取得成功。
    【解决方案5】:

    您可能对这本书/课程感兴趣The Elements of Computing Systems:Building a Modern Computer from First Principles

    请注意,这不是用你从 newegg 购买的东西来构建“电脑”。它从对布尔逻辑基础的描述开始,并构建了一个从最低抽象级别到逐渐更高抽象级别的虚拟计算机。课程资料都是在线的,而且这本书本身在亚马逊上相当便宜。

    在课程中,除了“构建硬件”之外,您还将逐步实现汇编程序、虚拟机、编译器和基本操作系统。我认为这将为您提供足够的背景知识,以便通过其他答案中列出的一些更常用的推荐资源来深入研究主题领域。

    【讨论】:

    • 我知道我并没有真正问过这个问题,但你几乎给了我一个解决我将来要问的一系列问题的方法:) 看起来很有趣的书。昨天下单...只是想看看从根源开始的感觉...再次感谢...
    • 这本书看起来是我很久以来想要的。我部分通过了 CS 的 MS,但我没有 CS 本科,并且知道我缺乏大量的低级知识。这本书看起来是一个很好的起点。谢谢。
    【解决方案6】:

    The Unix Programming Environment 中,Kernighan 和 Pike 进行了 5 次迭代,使计算器从简单的基于 C 的词法分析和立即执行到抽象机器的 yacc/lex 解析和代码生成。因为他们写得太精彩了,我不能建议更流畅的介绍。它肯定比 C 小,但这可能对您有利。

    【讨论】:

    • 哦...我想我在某个地方有那本书...!谢谢
    【解决方案7】:

    我如何[开始编写]一个简单的 C 编译器?

    编译 C 并不简单。最好的简单 C 编译器是 Chris Fraser 和 David Hanson 的 lcc。他们花了 10 年的时间进行设计,以使其尽可能简单,同时仍然生成相当好的代码。如果您可以访问大学图书馆,您应该可以得到他们的书。

    我是否开始用 C 或其他语言构建 C 编译器?

    其他语言。有一次我问汉森,他和弗雷泽花了 10 年时间在 lcc 项目上学到了什么。汉森说的主要是

    C 是一种用于编写编译器的糟糕语言。

    您最好使用 Haskell 或 ML 的某些方言。两种语言都提供了代数数据类型的函数,这与编译器编写者面临的问题完美匹配。如果您仍想继续学习 C,可以从 George Necula 的 CIL 开始,这是用 ML 编写的 C 编译器的一大块。

    我想阅读一些文章,其精神与我上面列出的文章相同,但至少突出了引导阶段...

    您不会再找到像 Ken 的文章了。但是 Andrew Appel 写了一篇不错的文章,名为 Axiomatic Bootstrapping: A Guide for Compiler Hackers 我找不到免费版本,但很多人都可以访问 ACM 数字图书馆。

    有什么建议吗?

    如果你想写一个编译器,

    • 使用 Haskell 或 ML 作为您的实现语言。

    • 对于您的第一个编译器,请选择一种非常简单的语言,例如 Oberon 或 Niklaus Wirth 的书算法 + 数据结构 = 程序中的 P0。 Wirth 以设计易于编译的语言而闻名。

    你可以为你的second编译器编写一个C编译器。

    【讨论】:

    • 我不会将 CIL 称为 C 编译器。 CIL 由一个读取 C 文件的解析器和一个输出等效 C 文件的编写器组成。它确实有助于代码分析和转换。
    • 我不认为我确实将 CIL 称为编译器。这是一个前端。正如我所说,对于 C,这是一个“大块”。
    【解决方案8】:

    编译器是一个复杂的主题,涵盖了各个方面

    • 涉及 Lexing、Parsing 的输入处理
    • 为使用的每个变量(例如抽象语法树 (AST))构建符号存储
    • 从 AST 树中,转置并根据语法构建机器码二进制文件

    这绝不是详尽无遗的,因为它是山顶的抽象鸟瞰图,归结为正确的语法符号并确保格式错误的输入不会丢掉它,实际上是一个很好的输入处理无论向它抛出的输入有多么畸形、可怕、滥用,都不应该屈服。而且,在决定和知道输出将是什么时,它是否是机器代码,这意味着您可能必须深入了解处理器指令......包括变量的内存寻址等等......

    这里有一些链接供您开始使用:

    • 有一个 Jack Crenshaw 的 port 的 C 代码....(我记得几个月前下载过...)
    • 这是一个类似问题的链接 here 在 SO 上。
    • 另外,这里还有一个小的 compiler tutorial,用于从 Basic 到 x86 汇编器编译器。
    • Tiny C Compiler
    • 找到了 Hendrix 的小型 C 编译器 here

    【讨论】:

      【解决方案9】:

      学习函数式编程也可能是值得的。函数式语言非常适合编写infor 的编译器。我学校的编译器入门课程包含对函数式语言的介绍,并且作业都在 OCaml 中。

      有趣的是,您今天应该问这个问题,因为就在几天前,我写了一个 lambda 演算解释器。 Lambda 演算是所有函数式语言的鼻祖。它只有 200 行长(在 C++ 中,包括错误报告、一些漂亮的打印、一些 unicode)并且具有两阶段结构,具有可用于生成代码的中间格式。

      不仅从小处着手,构建最实用的编译器方法,还鼓励良好的模块化组织实践。

      【讨论】:

      • @Legend:顺便说一句,不要听所有反对者的话。您可能不会取得突破,甚至不会成为具有竞争力的语言,但第一次尝试拥有学位的人也不会。只要你玩得开心,你总能找到更多的成就。
      • 如此真实...有时我确实会在看到“真正的”CS 毕业生时感到紧张...事实上,最近在 slashdot 上的一篇帖子:ask.slashdot.org/comments.pl?sid=10/02/19/147251 让我想得更多。但归根结底,我想这一切都取决于我们的想法...... :)
      【解决方案10】:

      编译器是一个非常大的项目,尽管我想尝试一下不会有什么坏处。

      我知道至少有一个用 Pascal 编写的 C 编译器,所以这不是你能做的疯狂的事情。我个人会选择一种更现代的语言来实现我的 C 编译器项目,这既是为了简单(为 Python、Ruby、C、C++ 或 Java 编写包很容易),也是因为它在你的简历上看起来会更好。

      不过,为了将编译器作为初学者项目,您需要喝掉所有的Agile kool-aid

      总是有一些东西在运行,即使它没有做很多事情。仅在小步骤中将内容添加到您的编译器。 (“频繁发布”。)选择一个非常小的语言子集并首先实现它。 (最初只支持i = 0;,然后从那里扩展。)

      【讨论】:

      • 抱歉,只是想理解一下……您是否建议使用 Python 或 Ruby 等高级语言编写一些东西?如果是,当然,我也愿意尝试。正如我所提到的,我在这个领域是一个完整的 n00b,所以任何让我理解主要概念的东西都是受欢迎的......你能澄清一下简历部分吗? :D
      • 用其中一种现代语言编写的重要程序会在您的简历上看起来不错。用 Ruby 或 Python 之类的东西实现编译器要容易得多。 Haskell 绝非易事,但它可能会引导您获得更好的编译器。
      • 明白了...我只听说过 Haskell...不过从未尝试过。我从来没有想过用 Python 或 Ruby 写一个。如果是这样的话,我肯定会调查的。谢谢
      • 我们可以跳过哪种语言更好比赛吗?尽管我个人很喜欢 Perl 或 Python,但 OP 说 C 是理想的语言。
      • 同意 mctylr,但至少我的背景使函数式语言看起来比 Python 更好的“宿主”。编译器涉及快速而仔细地构建结构,而“简单”的语言在涉及标识符绑定及其内置数据结构的潜在含义等事情时变得过于繁琐。在敏捷部分也不同意。对于初学者项目,这应该是一个人一步一步地工作。 保存你的测试用例。 不要执着于你的计划。
      【解决方案11】:

      如果您想要一个令人兴奋的体验,教您如何编写可自行编译的编译器,您需要阅读 1964 的这篇论文。

      META II a syntax-oriented compiler writing language 来自 Val Schorre。

      在 10 页中,它告诉您如何编写编译器,如何编写元编译器,提供虚拟元编译器指令集,以及使用元编译器构建的示例编译器。

      我在 60 年代后期从这篇论文中学会了如何编写编译器,并利用这些想法为几台小型计算机和微处理器构建了类 C 语言。

      如果论文本身太多(不是!)有an online tutorial 将引导您完成整个事情。

      如果因为您不是 ACM 成员而从原始链接获取论文很尴尬,您会发现教程包含所有详细信息。 (恕我直言,就价格而言,纸张本身非常值得)。

      10 页!

      【讨论】:

      • 原创文章(Schorre 的)不是在 ACM 的付费墙后面吗?如果有,请注意。我碰巧是 ACM 成员,但不是每个人都是。
      • 我只是不确定是否可以在此处发布链接,但我确信可以通过 Google Scholar 获得替代版本...非常感谢 Ira Baxter。
      • 关于使用 Python 遵循 META II 论文的(公共)ACM 队列文章的链接。 META II: Digital Vellum in the Digital Scriptorium Dave Long(2015 年 1 月,第 13 卷,第 1 期)。
      • @mctylr:他关于 MetaII 的文章比 MetaII 论文本身还要长:-}
      【解决方案12】:

      我不建议从 C 作为实现语言开始,也不建议从任何编译器生成器或解析器生成器工具开始。 C 是一种非常棘手的语言,最好只编写自己的语言。它可能有点像 C(例如,如果你想指示函数体,请使用花括号,使用相同的类型名称,这样你就不必记住你调用的所有内容)。

      用于制作编译器和解析器的工具很棒,但存在一个真正的速记符号问题。如果您不知道如何以简写方式创建编译器,那么简写方式会显得晦涩难懂、不必要的限制等。所以首先编写您自己的简单编译器,然后从那里继续。我还建议你不要开始生成实际的机器代码,除非你吃和呼吸汇编程序。使用 VM 创建您自己的字节码解释器。

      关于你应该使用什么语言来创建你的第一个编译器:这并不重要,只要语言相当完整。您将读取输入文本,从中构建数据结构并写出二进制数据。因此,如果一种语言以任何方式使这些事情变得更容易,那就是支持它的一点。选择你熟悉的语言,这样你就可以专注于创建编译器,而不是学习语言。我通常使用 OO 语言,这使得语法树更容易编写,如果你熟悉的话,函数式语言可能也可以工作。

      我写了很多关于编程语言的博客,所以你可能会在这里找到一些有用的帖子:http://orangejuiceliberationfront.com/category/language-design/

      特别是,http://orangejuiceliberationfront.com/how-to-write-a-compiler/ 是解析常见结构并从中生成有用信息的细节的入门者,还有 http://orangejuiceliberationfront.com/generating-machine-code-at-runtime/ 谈到实际吐出执行某些操作的 Intel 指令。

      哦,关于编译器的引导:您可能无法从一开始就做到这一点。创建编译器涉及大量工作。因此,编写引导编译器不仅涉及编写编译器(用其他语言),一旦你拥有它,你就必须使用它自己编写编译器的第二个版本。这是工作量的两倍,加上现有和引导的新编译器所需的调试,直到一切正常。也就是说,一旦你有一个工作的编译器,这是测试其完整性的好方法。好吧,也许不是两倍的工作,而是更多的工作。我会先追求轻松的成功,然后再继续前进。

      无论如何,玩得开心!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-26
        • 2014-06-20
        • 1970-01-01
        • 2010-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多