Parsing 是否可以从另一个yacc解析器调用一个yacc解析器来解析特定的令牌子流?

Parsing 是否可以从另一个yacc解析器调用一个yacc解析器来解析特定的令牌子流?,parsing,grammar,bison,yacc,Parsing,Grammar,Bison,Yacc,假设我已经有一个完整的YACC语法。例如,让它成为C语法。现在,我想用简单的语法为特定于域的语言创建一个单独的解析器,只是它仍然需要解析完整的C类型声明。我不想用相关的处理代码复制原始语法中的长规则,而是想调用原始解析器来处理一条规则(让我们称之为“声明器”) 如果是递归下降解析器,那么每个规则都有一个函数,很容易调用。但是YACC及其隐式堆栈自动机呢?基本上,不。编写LR语法并不容易,bison也没有提供太多帮助 但这一切并没有失去。没有什么能阻止您包含整个语法(除了%start声明),只使用

假设我已经有一个完整的YACC语法。例如,让它成为C语法。现在,我想用简单的语法为特定于域的语言创建一个单独的解析器,只是它仍然需要解析完整的C类型声明。我不想用相关的处理代码复制原始语法中的长规则,而是想调用原始解析器来处理一条规则(让我们称之为“声明器”)


如果是递归下降解析器,那么每个规则都有一个函数,很容易调用。但是YACC及其隐式堆栈自动机呢?

基本上,不。编写LR语法并不容易,bison也没有提供太多帮助

但这一切并没有失去。没有什么能阻止您包含整个语法(除了
%start
声明),只使用其中的一部分,除了一个小细节:bison会抱怨无用的产品

如果这对您来说是一个阻碍,那么您可以使用一个技巧来创建具有多个开始规则的语法。事实上,您可以创建一个语法,它允许您在每次调用解析器时指定开始符号;它甚至不需要烘烤。然后,您可以将其塞进一个库中,并使用您想要的任何解析器

当然,这也是有代价的:代价是解析器比其他情况下需要的更大。但是,它不应该再慢,或者至少不会太慢——可能会有一些缓存效应——与编译器的其余部分相比,额外的大小可能微不足道

黑客在中有很多详细的描述,所以我在这里只做一个概述:对于您想要支持的每个开始生产,您创建一个额外的生产,该生产以一个伪令牌开始(也就是说,词法代码永远不会由lexer生成)。例如,您可以执行以下操作:

%start meta_start
%token START_C START_DSL

meta_start: START_C c_start | START_DSL dsl_start;

现在,您只需安排lexer在其首次启动时生成适当的启动令牌。有多种方法可以做到这一点;常见问题解答建议使用全局变量,但如果使用可重入flex scanner,只需将所需的开始令牌置于扫描状态(以及在发送开始令牌时设置的标志)。

基本上不是。编写LR语法并不容易,bison也没有提供太多帮助

但这一切并没有失去。没有什么能阻止您包含整个语法(除了
%start
声明),只使用其中的一部分,除了一个小细节:bison会抱怨无用的产品

如果这对您来说是一个阻碍,那么您可以使用一个技巧来创建具有多个开始规则的语法。事实上,您可以创建一个语法,它允许您在每次调用解析器时指定开始符号;它甚至不需要烘烤。然后,您可以将其塞进一个库中,并使用您想要的任何解析器

当然,这也是有代价的:代价是解析器比其他情况下需要的更大。但是,它不应该再慢,或者至少不会太慢——可能会有一些缓存效应——与编译器的其余部分相比,额外的大小可能微不足道

黑客在中有很多详细的描述,所以我在这里只做一个概述:对于您想要支持的每个开始生产,您创建一个额外的生产,该生产以一个伪令牌开始(也就是说,词法代码永远不会由lexer生成)。例如,您可以执行以下操作:

%start meta_start
%token START_C START_DSL

meta_start: START_C c_start | START_DSL dsl_start;

现在,您只需安排lexer在其首次启动时生成适当的启动令牌。有多种方法可以做到这一点;常见问题解答建议使用全局变量,但如果使用可重入flex scanner,则只需将所需的开始令牌置于scanner状态(以及发送开始令牌时设置的标志)。

感谢您提供详细答案,并采用这种方式,目前为止效果不错。我不太关心解析的速度,我确信BISON足够聪明地将规则保持在类似于结构的集合中,而不考虑不适用于当前非终结符的规则。到目前为止最大的麻烦就是扫描器,它与C扫描器共享:我的DSL有它特定的关键字,这会污染C令牌空间。我想,我最终需要使用两个独立的flex扫描仪。@pfalcon,如果它只是关键字,那么flex解决方案很简单;使用如下规则:
“dslkeyword”{if(在_dsl中)返回dslkeyword;else返回ID;}
。但这可能会变得丑陋;另一个简单的解决方案是使用启动条件:感谢您提供了详细的答案,并采用了这种方式,到目前为止效果不错。我不太关心解析的速度,我确信BISON足够聪明地将规则保持在类似于结构的集合中,而不考虑不适用于当前非终结符的规则。到目前为止最大的麻烦就是扫描器,它与C扫描器共享:我的DSL有它特定的关键字,这会污染C令牌空间。我想,我最终需要使用两个独立的flex扫描仪。@pfalcon,如果它只是关键字,那么flex解决方案很简单;使用如下规则:
“dslkeyword”{if(在_dsl中)返回dslkeyword;else返回ID;}
。但这可能会变得丑陋;另一个简单的解决方案是使用启动条件: