如何在编译器中实现前向引用?

时间:2009-05-31 18:20:01

标签: parsing yacc bison compiler-development forward-reference

我正在用Lex和YACC创建一个编译器(实际上是Flex和Bison)。该语言允许无限制地向前引用任何符号(如C#)。问题是在不知道标识符是什么的情况下解析语言是不可能的。

我所知道的唯一解决方案是lex整个源代码,然后执行“广度优先”解析,因此在使用它们的函数之前,会对类声明和函数声明等更高级别的内容进行解析。但是,这会占用大文件的大量内存,并且使用YACC很难处理(我必须为每种类型的声明/正文创建单独的语法)。我还必须手写lexer(这不是一个问题)。

我并不关心效率(尽管它仍然很重要),因为一旦完成它我就会重写编译器,但我希望那个版本快速(所以如果有的话)任何快速的通用技术都无法在Lex / YACC中完成但可以手工完成,请同时提出建议。所以现在,易于开发是最重要的因素。

这个问题有什么好的解决方案吗?这通常是在C#或Java等语言的编译器中完成的吗?

2 个答案:

答案 0 :(得分:7)

完全可以解析它。虽然标识符和关键字之间存在歧义,但lex会通过优先考虑关键字来满足这一要求。

我没有看到其他问题。您无需在解析阶段确定标识符是否有效。在解析时,您正在构建解析树或抽象语法树(差异很微妙,但与本讨论的目的无关)。之后,通过对在解析期间生成的AST执行传递来构建嵌套的符号表结构。然后你对AST进行另一次传递以检查所使用的标识符是否有效。通过AST上的一个或多个附加解析来生成输出代码或其他一些中间数据结构,您就完成了!

编辑:如果你想看看它是如何完成的,请检查Mono C#编译器的源代码。这实际上是用C#而不是C或C ++编写的,但它确实使用Jay的.NET端口,它与yacc非常相似。

答案 1 :(得分:1)

一种选择是通过扫描和缓存令牌来处理前向引用,直到你遇到你知道如何实现的东西(有点像“恐慌模式”错误恢复)。一旦你运行了整个文件,请返回并尝试重新解析之前未解析的位。

至于必须手写lexer;不要,使用lex生成一个普通的解析器,只需通过一个手写的填充程序读取它,让你返回并从缓存中提供解析器以及lex所做的。

关于制作几个语法,使用yacc文件上的预处理器有点乐趣,你应该能够使用相同的原始源来制作它们