Flex和Bison需要从对方那里得到什么? [英] What do flex and bison need from each other?

查看:0
本文介绍了Flex和Bison需要从对方那里得到什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

同时使用flex和bison时

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

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

推荐答案

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

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

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

这对于解析器和扫描器之间的经典接口来说很好,它们使用全局变量进行通信。它还或多或少地与可重入(纯)解析器一起工作,在这个解析器中,语义值(和位置,如果使用)通过参数传递给yylex,尽管yylex的声明不是自动的这一事实更令人讨厌。

当扫描仪也是可再入时,它开始出现故障。在这种情况下,解析器必须使用yyscan_t类型的不透明扫描器上下文对象调用扫描器。yyscan_t";属于";扫描仪,因此只能在扫描仪的标题中定义它。但如上所述,这将导致循环依赖链。这揭示了传统模型的弱点:解析器是扫描器的客户端,因此扫描器依赖解析器定义基本数据结构的事实是依赖倒置。

这是一个非常实际的问题,因为可重入扫描器的公共接口包括其原型需要特定于解析器的数据类型(YYSTYPEYYLTYPE)的函数,而解析器原型几乎肯定需要接受扫描器上下文对象作为参数,因此在没有特定于扫描器的数据类型yyscan_t的情况下无法声明它。

此问题的通常解决方案是打破封装,注意yyscan_t只是一个void*,因此可以在解析器中将其声明为#include,从而避免解析器访问扫描器标头(只要它不需要访问该标头中声明的任何其他公共方法)。

在我看来,一个不那么难看的解决方案是完全避免这种特定的配置。Bison允许您请求可重入推送解析器,该解析器可与可重入扫描程序一起使用,而不会出现上述任何复杂情况。

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

除了简化两个组件之间的标头依赖关系之外,推式解析器通常还简化了扫描器本身内部的控制流。在许多使用情况下,单个扫描仪模式将导致识别多个令牌。在传统模型中,扫描器必须保留一个令牌队列,在解析器调用时一次释放一个令牌。但在推送模型中,扫描器操作可以简单地多次调用解析器,为每个标识的令牌调用一次。该模型由Lemon解析器生成器(Sqlite3的一部分)推广,随后由包括Bison在内的其他解析器生成器实现。

这篇关于Flex和Bison需要从对方那里得到什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆