代码之家  ›  专栏  ›  技术社区  ›  Tim unnamed eng

flex和bison彼此需要什么?

  •  0
  • Tim unnamed eng  · 技术社区  · 6 年前

    同时使用flex和bison时,

    • 为什么弹性文件需要 #include 一个由野牛创建的C头?

    • 编译需要bison和flex创建的C源文件。bison和flex创建的c源文件彼此需要什么?

    谢谢。

    1 回复  |  直到 6 年前
        1
  •  2
  •   rici    6 年前

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

    头还声明 YYSTYPE 语义类型和变量 yylval (具有该类型)用于将每个标记的语义值传递给解析器。(至少,对于具有语义值的标记)同样,如果解析器使用位置信息,那么头将定义 YYLTYPE 位置类型和变量 yylloc 那种类型的。

    由于头依赖项不能是循环的,所以分析器在扫描仪上没有任何头依赖项。因此,您的bison输入文件必须包含一个声明 yylex .

    对于解析器和扫描器之间的经典接口来说,这是很好的,后者使用全局变量进行通信。它也或多或少地与可重入的(“纯”)解析器一起工作,其中语义值(和位置,如果使用)通过参数传递给 伊莱克斯 尽管事实上 伊莱克斯 不是自动的更烦人。

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

    这是一个非常真实的问题,因为可重入扫描器的公共接口包括其原型需要特定于解析器的数据类型的函数。( 耶斯提普 yy型 ,而解析器原型几乎肯定需要接受一个扫描器上下文对象作为参数,因此如果没有扫描器特定的数据类型,就不能声明它。 耶斯坎特 .

    解决这个问题的通常方法是通过注意到 耶斯坎特 仅仅是一个 void* ,因此可以在解析器中声明它,从而避免解析器需要 #include 扫描器头(只要它不需要访问该头中声明的任何其他公共方法)。

    在我看来,一个不那么难看的解决方案是完全避免这种特殊的配置。Bison允许您请求一个可重入的“push-parser”,它可以与一个可重入的扫描器一起使用,而不会出现上述任何问题。

    在push解析器模型中,scanner是用来解析文件的顶级函数,scanner每次识别一个令牌时都会调用解析器。头依赖项不再是循环的,因为解析器不需要知道关于扫描器上下文对象的任何信息,或者关于 yylex() . 扫描器现在是解析器的客户机,因此对解析器具有自然的头依赖性,因此解析器定义令牌枚举、语义和位置数据类型的事实不再是例外。

    推送解析器除了简化两个组件之间的头依赖性之外,还常常简化扫描器内部的控制流。在许多用例中,单个扫描器模式将导致标识多个令牌。在传统模型中,扫描器必须保留一个令牌队列,在解析器调用时一次释放一个。但在推送模型中,扫描器操作只需对每个标识的令牌多次调用解析器。该模型由Lemon解析器生成器(sqlite3的一部分)推广,随后由其他解析器生成器(包括bison)实现。