问题描述
|
我正在上一个编译器设计类,我们必须在其中实现自己的编译器(使用flex和bison)。我有解析的经验(编写EBNF和递归下降解析器),但这是我第一次编写编译器。
语言设计是开放式的(教授把它留给了我们)。上课时,教授结束了生成中间代码的工作。他说,我们在解析时不必构造抽象语法树或解析树,并且我们可以在进行过程中生成中间代码。
我发现这令人困惑,原因有两个:
如果在定义函数之前调用该函数怎么办?如何解决分支目标?我猜您将必须成为一个规则,即必须在使用函数之前定义函数,或者可能要预先定义函数(如C一样?)
您将如何处理有条件的?如果您有一个
if-else
甚至是一个if
,那么当条件为false
时(如果您正在生成代码)如何解决resolve1ѭ的分支目标?
我计划生成一个AST,然后在创建它之后遍历树,以解析功能和分支目标的地址。这是正确的还是我错过了什么?
解决方法
解决这两个问题的一般方法是保留一个需要“修补”的地址列表。生成代码并为缺少的地址或偏移量留空。在编译单元的末尾,您将浏览孔列表并进行填充。
在FORTH中,补丁的“列表”保留在控制堆栈中,并在每个控制结构终止时解开。见第四尺寸
轶事:早期的Lisp编译器(我相信它是Lisp)生成了符号格式的机器代码指令列表,并带有对条件语句每个分支的机器代码列表的正向引用。然后,它生成了将列表向后移动的二进制代码。这样,当需要发出分支指令时,就可以知道所有前向分支的代码位置。
, Crenshaw教程是不使用任何AST的具体示例。它构建了一个可工作的编译器(显然包括条件编译器),并且可以针对m68k汇编立即生成代码。
您可以在一个下午通读该文档,这是值得的。