5

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

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

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

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

4

2 回答 2

8

完全可以解析它。尽管标识符和关键字之间存在歧义,但 lex 很乐意通过给予关键字优先级来解决这个问题。

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

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

于 2009-05-31T18:32:12.667 回答
1

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

至于必须手写词法分析器;不要,使用 lex 生成一个普通的解析器,然后通过一个手写的 shim 从中读取,让您返回并从缓存中提供解析器以及 lex 生成的内容。

至于制作几个语法,在 yacc 文件上使用预处理器会有点乐趣,你应该能够将它们全部从同一个原始源中制作出来

于 2009-06-01T16:43:30.120 回答