【问题标题】:Compiler-Programming: What are the most fundamental ingredients?编译器编程:最基本的成分是什么?
【发布时间】:2010-10-08 05:23:55
【问题描述】:

我有兴趣编写一个非常简约的编译器。

我想编写一个满足以下条件的小软件(用 C/C++ 编写):

  • 以 ELF 格式输出 (*nix)
  • 输入是单个文本文件
  • 类 C 语法和句法
  • 没有链接器
  • 没有预处理器
  • 非常小(最多 1-2 KLOC)

语言特点:

  • 原生数据类型:char、int 和 floats
  • 数组(适用于所有原生数据类型)
  • 变量
  • 控制结构(if-else)
  • 功能
  • 循环(会很好)
  • 简单代数(div、add、sub、mul、布尔表达式、位移等)
  • 内联 asm(用于系统调用)

谁能告诉我如何开始?我不知道编译器由哪些部分组成(至少在我可以立即开始的意义上)以及如何对它们进行编程。谢谢你的想法。

【问题讨论】:

标签: c++ c compiler-construction low-level


【解决方案1】:

作为初学者,我总是推荐flexbison 用于此类工作。以后你总是可以学习编写自己的扫描器和解析器的细节,尽管它们可能会增加代码大小,至少它们会由工具为你生成。 :)

【讨论】:

    【解决方案2】:

    恕我直言,一组非常好的免费参考资料是:

    总体编译器教程:让我们构建一个编译器,Jack Crenshaw (http://compilers.iecc.com/crenshaw/) 有点啰嗦,但我喜欢。

    汇编程序:NASM (nasm.us) 适用于 Linux 和 Windows/DOS,最重要的是大量文档和示例/教程。 (FASM 也不错,但文档/教程较少)

    其他来源 PC 组装书 (http://www.drpaulcarter.com/pcasm/index.php)

    我正在尝试编写 LISP,所以我使用了Lisp 1.5 Manual。您可能想要获取您正在编写的任何语言的语言规范。

    就 1-2KLOC 而言,假设您使用高级语言(如 Py 或 Rb),如果您不是太雄心勃勃,那么您应该接近。

    【讨论】:

    • 既然他想用 C/C++ 编写它(不管那是什么意思),我会选择 NASM。 FASM 很好,但是是用汇编编写的,而 NASM 是用 C 编写的。NASM 可能会提供更有用的代码。
    【解决方案3】:

    首先,您需要决定是要制作编译器还是解释器。编译器将您的代码翻译成可以直接在硬件上、在解释器中运行的东西,或者被编译成另一种语言,然后以某种方式被解释。两种类型的语言都是图灵完备的,因此它们具有相同的表达能力。我建议您创建一个编译器,将您的代码编译为 .net 或 Java 字节码,因为它为您提供了一个非常优化的解释器来运行以及许多标准库。

    一旦您做出决定,就需要遵循一些常见的步骤

    1. 语言定义首先,您必须定义您的语言在语法上的外观。

    2. Lexer 第二步是创建代码的关键字,称为标记。在这里,我们谈论的是非常基本的元素,例如数字、加法符号和字符串。

    3. 解析 下一步是创建与您的标记列表匹配的语法。您可以使用例如定义您的语法上下文无关的语法。许多工具可以使用其中一种语法并为您创建解析器。通常,已解析的标记被组织成一棵解析树。解析树是将语法表示为可以在其中移动的数据结构。

    4. 编译或解释 最后一步是在解析树上运行一些逻辑。制作自己的解释器的一种简单方法是创建一些与树中的每个节点类型相关联的逻辑,然后自下而上或自上而下地遍历树。如果你想编译成另一种语言,你可以在节点中插入如何翻译代码的逻辑。

    维基百科非常适合学习更多信息,您可能想开始here

    关于现实世界的阅读材料,我建议 David A Watt 和 Deryck F Brown 撰写的“Java 中的编程语言处理器”。我在编译器课程中使用了这本书,并且通过示例学习在这个领域非常有用。

    【讨论】:

      【解决方案4】:

      对于您希望完成的所有任务,最具挑战性的要求可能是“非常小(最多 1-2 KLOC)”。我认为仅您的第一个要求(生成 ELF 输出)本身就可能需要超过一千行代码。

      一种简化问题的方法,至少从一开始就是用汇编语言文本生成代码,然后将其输入现有的汇编程序(nasm 将是一个不错的选择)。汇编器将负责生成实际的机器代码,以及构建实际可运行的可执行文件所需的所有 ELF 特定代码。然后你的工作就变成了语言解析和汇编代码生成。当您的项目成熟到您想要删除对汇编程序的依赖时,您可以自己重写这部分并随时插入。

      如果我是你,我可能会从一个汇编器开始,然后在它之上构建部件。最简单的“编译器”可能会采用一种只包含一些非常简单的语句的语言:

      print "hello"
      a = 5
      print a
      

      并将其翻译成汇编语言。一旦你开始工作,你就可以构建一个词法分析器和解析器以及抽象语法树和代码生成器,这是现代块结构语言所需的大部分部分。

      祝你好运!

      【讨论】:

      • 更简单的是,让它生成 C 作为其输出。许多成功的编译器都走这条路。
      • 请注意,NASM 是用 C 语言编写的,因此您可以使用 NASM 中的代码翻译成机器代码。
      【解决方案5】:

      最重要的是一本关于编译器编写的书。很多人会告诉你阅读 Aho 等人的“Dragon Book”,但我读过的关于编译器的最好的书是“Brinch Hansen on Pascal Compilers”。我怀疑它已经绝版了(亚马逊是你的朋友),但它会带你完成使用递归下降设计和编写编译器的所有步骤,这是编译器新手最容易理解的方法。

      尽管本书使用 Pascal 作为实现语言和目标语言,但所介绍的课程和技术同样适用于所有其他语言。

      【讨论】:

      • Brinch Hansen +1。它在编译器设计的技术和实用信息之间取得了最佳平衡。
      【解决方案6】:

      这些示例都是用 Perl 编写的,但 Exploring Programming Language Architecture in Perl 是一本好书(而且免费)。

      【讨论】:

        【解决方案7】:

        我不知道你希望从中得到什么,但如果它正在学习,并且查看现有代码对你有用,那么总会有tcc

        【讨论】:

          【解决方案8】:

          这些是绝对必要的部分:

          • 扫描仪:这会将输入文件分解为标记
          • 解析器:它根据扫描器识别的标记构造抽象语法树 (AST)。
          • 代码生成:生成 AST 的输出。

          您可能还想要:

          • 错误处理:这会告诉解析器遇到意外标记时该怎么做
          • 优化:这将使编译器生成更高效的机器代码

          编辑:你已经设计了语言吗?如果没有,您也需要研究语言设计。

          【讨论】:

          • '研究语言设计':您是指特定的资源还是范式?还是只是我需要在脑海中旋转的东西?
          • 您必须创建与您要使用的解析器类型兼容的语言语法。我会先看看自上而下与自下而上的解析器。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-09-29
          • 1970-01-01
          • 2018-12-05
          • 2021-01-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多