【问题标题】:Generating intermediate code in a compiler. Is an AST or parse tree always necessary when dealing with conditionals?在编译器中生成中间代码。处理条件时是否总是需要 AST 或解析树?
【发布时间】:2011-07-18 13:55:02
【问题描述】:

我正在参加一个编译器设计课程,我们必须在其中实现我们自己的编译器(使用 flex 和 bison)。我有过解析(编写 EBNF 和递归下降解析器)的经验,但这是我第一次编写编译器。

语言设计是相当开放的(教授已经把它留给了我们)。在课堂上,教授复习了生成中间代码。他说我们在解析的时候不需要构造抽象语法树或者解析树,可以边做边生成中间代码。

我发现这令人困惑有两个原因:

  • 如果您要调用一个函数定义之前怎么办?如何解决分支目标?我想您必须制定一个规则,即您必须在使用函数之前定义它们,或者可能预先定义它们(就像 C 那样?)

  • 您将如何处理条件语句?如果您有一个if-else 甚至只是一个if,当条件为false 时,如何解析if 的分支目标(如果您正在生成代码)?

我计划生成一个 AST,然后在创建它之后遍历树,以解析函数和分支目标的地址。这是正确的还是我遗漏了什么?

【问题讨论】:

    标签: parsing language-design compiler-construction intermediate-code


    【解决方案1】:

    解决这两个问题的一般方法是保留一份需要“修补”的地址列表。您生成代码并为丢失的地址或偏移量留下漏洞。在编译单元结束时,您会浏览漏洞列表并将它们填满。

    在 FORTH 中,补丁“列表”保存在控制堆栈中,并在每个控制结构终止时展开。见FORTH Dimensions

    轶事:早期的 Lisp 编译器(我相信它是 Lisp)生成了一个符号格式的机器代码指令列表,其中前向引用了每个条件分支的机器代码列表。然后它生成了遍历列表向后的二进制代码。这样,当需要发出分支指令时,所有前向分支的代码位置都是已知的。

    【讨论】:

    • 您也可以发出汇编代码,让汇编程序担心这部分问题。还是作弊?
    • 当然,那么汇编器将需要对代码进行两次传递(如果使用修补程序,则需要传递一次半)。许多编译器,特别是 FORTH,直接针对二进制机器代码。这可能是出于性能(速度和空间)或其他实际原因(例如没有可用的汇编程序)。
    • 我们中的一些人将补丁推送到链接器上。这意味着您可以吐出一个目标代码流(带有补丁的占位符;我实际上尽可能将补丁值链接在一起)并在知道最终位置时发布补丁,至少对于我构建的非常快速的一次性编译器.
    【解决方案2】:

    Crenshaw tutorialnot 使用任何类型的 AST 的具体示例。它构建了一个工作编译器(显然包括条件),并立即生成针对 m68k 程序集的代码。

    一个下午就可以通读文档,值得。

    【讨论】:

      猜你喜欢
      • 2020-09-29
      • 1970-01-01
      • 1970-01-01
      • 2011-11-12
      • 2013-09-14
      • 1970-01-01
      • 2011-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多