我正在使用编译器设计类,我们必须实现自己的编译器(使用flex和bison)。我有解析(编写EBNF和递归下降解析器)的经验,但这是我第一次编写编译器。
语言设计非常开放(教授把它留给了我们)。在课堂上,教授过去生成中间代码。他说我们没有必要在解析时构造一个抽象语法树或一个解析树,并且我们可以生成中间代码。
我发现这令人困惑有两个原因:
如果在定义之前调用函数怎么办?你如何解决分支目标?我想你必须制定一个规则,你必须在使用之前定义函数,或者预先定义它们(就像C一样?)
你会如何处理条件?如果您有if-else
甚至只有if
,那么当条件为if
时,如果您生成代码,如何解析false
的分支目标你去)?
我计划生成AST,然后在创建它之后遍历树,以解析函数和分支目标的地址。这是正确的还是我错过了什么?
答案 0 :(得分:8)
您的两个问题的一般解决方案是保留需要“修补”的地址列表。您生成代码并为丢失的地址或偏移留下漏洞。在编译单元的末尾,您将浏览孔列表并将其填入。
在FORTH中,补丁的“列表”保留在控制堆栈上,并在每个控制结构终止时展开。见FORTH Dimensions
轶事:一个早期的Lisp编译器(我相信它是Lisp)生成了一个符号格式的机器代码指令列表,其中包含对条件的每个分支的机器代码列表的前向引用。然后它生成了列表向后的二进制代码。这样,当需要发出分支指令时,所有正向分支的代码位置都是已知的。
答案 1 :(得分:1)
Crenshaw tutorial是使用任何类型的AST的 not 的具体示例。它构建了一个有效的编译器(显然包括条件),可立即生成针对m68k汇编的代码。
您可以在下午阅读该文档,这是值得的。