代码之家  ›  专栏  ›  技术社区  ›  Zifre

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

  •  5
  • Zifre  · 技术社区  · 15 年前

    我正在用lex和yacc(实际上是flex和bison)创建编译器。该语言允许对任何符号(如c)进行无限制的前向引用。问题是,在不知道标识符是什么的情况下解析语言是不可能的。

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

    我不太关心效率(尽管它仍然很重要),因为我会在完成编译器后重写它本身,但我希望该版本速度更快(因此,如果有任何快速的通用技术不能在lex/yacc中完成,但可以用手完成,请也推荐它们)。所以现在,发展的便利性是最重要的因素。

    这个问题有什么好的解决办法吗?在C语言或Java语言的编译器中通常是怎么做的?

    2 回复  |  直到 11 年前
        1
  •  7
  •   U62    15 年前

    完全可以解析它。尽管标识符和关键字之间存在歧义,但是lex将通过赋予关键字优先级来很高兴地处理这一问题。

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

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

        2
  •  1
  •   BCS Adam Robinson    15 年前

    一种选择是处理转发引用,只需扫描和缓存令牌,直到找到您知道如何实现的内容(类似于“紧急模式”错误恢复)。运行完thought完整文件后,请返回并尝试重新解析以前未解析的位。

    至于必须手工编写lexer;不要这样做,使用lex生成一个普通的解析器,然后通过一个手工编写的填充程序从中读取,该填充程序允许您返回并从缓存中提供解析器以及lex所做的。

    关于编写几个语法,在yacc文件上使用预处理器有点意思,您应该能够使它们都使用相同的原始源代码。

    推荐文章