flex和野牛互相需要什么?

时间:2019-02-07 22:45:58

标签: bison flex-lexer

同时使用flex和bison时,

  • 为什么flex文件需要#include由bison创建的C标头?

  • 编译需要使用bison和flex创建的C源文件。由bison和flex创建的C源文件相互有什么要求?

谢谢。

1 个答案:

答案 0 :(得分:2)

在bison生成的标头中,最重要的是用于标识令牌类型的枚举值(即通过词法操作返回到解析器的值)。

标头还声明了YYSTYPE的语义类型,以及变量yylval(具有该类型),用于将每个令牌的语义值传递给解析器。 (至少,对于具有语义值的标记。)类似地,如果解析器使用位置信息,则标头定义YYLTYPE位置类型和该类型的变量yylloc

由于标头依赖性不能为循环的,因此解析器对扫描程序没有任何标头依赖性。出于这个原因,您的野牛输入文件必须包含yylex声明。

这对于解析器和扫描器之间的经典接口很好,它们使用全局变量进行通信。而且,它或多或少都与可重入(“ pure”)解析器一起使用,在该解析器中,语义值(和位置,如果使用的话)通过yylex的参数进行通信,尽管事实上{ {1}}不是自动的会更烦人。

当扫描仪也重新进入时,它开始崩溃的地方。在这种情况下,解析器必须使用类型为yylex的不透明扫描器上下文对象来调用扫描器。 yyscan_t“属于”扫描仪,因此只能在扫描仪的标头中定义。但是如上所述,这将导致循环依赖链。这揭示了传统模型的弱点:解析器是扫描器的客户端,因此,扫描器依赖于解析器定义基本数据结构的事实是依赖关系倒置。

这是一个非常现实的问题,因为重入扫描器的公共接口包含其原型需要解析器特定数据类型(yyscan_tYYSTYPE)的函数,而解析器原型几乎肯定会需要接受扫描程序上下文对象作为参数,因此如果没有特定于扫描程序的数据类型YYLTYPE,则无法声明该对象。

此问题的通常解决方案是通过注意yyscan_t只是yyscan_t来破坏封装,因此可以在解析器中声明它,从而避免了解析器需要{ {1}}扫描程序标头(只要它不需要访问在该标头中声明的任何其他公共方法)。

在我看来,一个不太丑陋的解决方案是完全避免这种特殊配置。 Bison允许您请求可重入的“推送解析器”,并且可以与可重入的扫描器一起使用,而不会出现上述任何复杂情况。

在推式解析器模型中,扫描程序是用于解析文件的顶级功能,并且扫描程序在每次识别令牌时都会调用解析器。标头的依赖关系不再是循环的,因为解析器无需了解任何有关扫描程序上下文对象的知识,也无需了解void*的原型。扫描程序现在是解析器的客户端,因此对解析器具有自然的标头依赖性,因此,解析器定义令牌枚举以及语义和位置数据类型的事实不再例外。

除了简化两个组件之间的标头依赖关系外,推送解析器通常还简化了扫描程序本身内部的控制流。在许多用例中,单个扫描器模式将导致多个令牌的标识。在传统模型中,扫描器必须保留一队令牌,在解析器调用时一次释放一个令牌。但是在推入模型中,扫描程序动作可以简单地多次调用解析器,每次为每个标识的令牌调用一次。该模型在Lemon解析器生成器(sqlite3的一部分)中得到了普及,随后由包括bison在内的其他解析器生成器实现。