8

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

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

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

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

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

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

4

2 回答 2

8

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

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

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

于 2011-03-19T01:42:20.327 回答
1

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

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

于 2011-06-11T23:33:12.963 回答