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

Parsing 如何在编译器中实现前向引用?,parsing,yacc,bison,compiler-development,forward-reference,Parsing,Yacc,Bison,Compiler Development,Forward Reference,我正在用Lex和YACC(实际上是Flex和Bison)创建一个编译器。该语言允许无限向前引用任何符号(如C#)。问题是,在不知道标识符是什么的情况下,不可能解析语言 我所知道的唯一解决方案是对整个源代码进行lex,然后进行“宽度优先”解析,以便在使用类声明和函数声明的函数之前解析更高级别的内容。然而,对于大文件来说,这将占用大量内存,并且很难使用YACC处理(我必须为每种类型的声明/正文创建单独的语法)。我还必须手工编写lexer(这不是什么大问题) 我不太在乎效率(尽管它仍然很重要),因为一

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

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

我不太在乎效率(尽管它仍然很重要),因为一旦我完成它,我将重写编译器本身,但我希望该版本是快速的(因此,如果有任何快速通用技术不能在Lex/YACC中完成,但也可以手动完成,请也建议它们)。因此,目前,发展的便利性是最重要的因素


这个问题有什么好的解决办法吗?在C#或Java等语言的编译器中,这通常是如何实现的?

完全可以解析它。虽然标识符和关键字之间存在歧义,但lex将通过优先考虑关键字来解决这一问题

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


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

< P>一个选项是通过扫描和缓存令牌来处理正向引用,直到你碰到了你知道如何真实的东西(类似于“恐慌模式”错误恢复)。运行完完整文件后,返回并尝试重新解析以前未解析的位

至于必须手写词法;不要,使用lex生成一个普通的解析器,然后通过一个手工编写的垫片从中读取,该垫片允许您返回并从缓存向解析器提供lex所做的一切


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

这与关键字无关。它更像是:A.B.C(A.B包)。(C类),(A包)。(B类)。(C区)或(A区)。(B区)。(C区)等等。那么我的答案的第二段适用。你不需要知道这些来解析。在语法中将“.”视为运算符。在你的AST过程中,你可以对照符号表检查它们。好吧,我想我只需要做一个解析树而不是AST。正如你所说,他们是不同的。如果没有其他人提出更好的答案,我会接受,但我真的不想这样做……我并不真的担心手写lexer,这并不难(因为我的语言有类似Python的缩进,所以可能会稍微容易一点)。将预处理器与YACC一起使用听起来似乎可行,但有没有办法更改开始符号?我们是YACC的预处理器,这正是我们的想法。在不明确定义开始符号的情况下定义完整语法,然后交换一小段文件(通过#include或#define)来选择起点。一种方法是使用形式为“Root::=MacroRule;”的开始规则,并使用此版本所需的任何内容替换MacroRule。