Bison 伯克利Yacc与GNU野牛:不同公差w.r.t跟踪标记

Bison 伯克利Yacc与GNU野牛:不同公差w.r.t跟踪标记,bison,yacc,Bison,Yacc,我遇到了一个解析器可移植性问题:使用byacc与bison--yacc翻译时的行为不同 Bison生成的解析器允许我调用yyparse来提取输入令牌序列的一些前缀,这些前缀可以通过语法规则从起始符号派生。这是因为Bison生成的解析器有一个$defaultreduce操作。我认为,这意味着“对于其他操作没有提到的任何前瞻标记(因为它们与语法不匹配),执行这个reduce” 相比之下,对于相同的状态和规则集,Berkeley Yacc没有这样一个默认的reduce规则。同样的减少也适用于专门匹配$

我遇到了一个解析器可移植性问题:使用
byacc
bison--yacc翻译时的行为不同

Bison生成的解析器允许我调用
yyparse
来提取输入令牌序列的一些前缀,这些前缀可以通过语法规则从起始符号派生。这是因为Bison生成的解析器有一个
$default
reduce操作。我认为,这意味着“对于其他操作没有提到的任何前瞻标记(因为它们与语法不匹配),执行这个reduce”

相比之下,对于相同的状态和规则集,Berkeley Yacc没有这样一个默认的reduce规则。同样的减少也适用于专门匹配
$end
符号换句话说,在Berkeley Yacc生成的解析器中,规则实际上是“锚定”到最后的,就像一个带有$的正则表达式一样。

(注意:Bison生成的解析器仍然将语法作为一个整体锚定到末尾,但它通过仅在开始符号的顶级规则中匹配
$end
来实现这一点;它不会扩散到下级规则中!)

区别很重要,因为我多次调用
yyparse
来提取连续的短语单位。这适用于Bison,因为在它有机会减少到需要隐式
$end
标记的开始符号之前,我接受了它。(好吧,这并不完全是“开箱即用”:但通过某种技巧,它可以发挥作用)。对于Berkeley Yacc,由于在派生开始符号的从属规则中看不到
$end
而导致的语法错误意味着整个方案已陷入困境

有没有办法让Berkeley Yacc对与语法定义的语法延续或语法终止不匹配的先行标记值进行默认缩减?换句话说,用默认值填充该状态的LALR(1)表中所有未使用的条目

想法:我在想,也许被提取的短语单位可以被卷入重复规则中。我可以通过这样一个新引入的填充规则来解析“序列
expr
”,而不是试图解析出单个
expr
;但在只得到一个后,立即接受,大致如下:

start : exprs { $$ = $1; };

exprs : expr { $$ = $1; YYACCEPT; /* Got just the one expr I really wanted! */ } 
      | exprs expr { /* Never reached! Byacc fooled! */ }
      ;
我将以这种方式进行尝试,但了解两个Yacc实现之间为何存在如此巨大的差异以及如何直接克服差异仍然是一件好事


编辑:在我的项目中,一个有效的黑客是按照以下伪代码进行的:

start : expr { YYACCEPT; } byacc_fool { /*notreached*/ abort(); }

byacc_fool : expr { abort(); }
           | /*nothing*/ { abort(); }
           ;
所有回归测试均通过Byacc或Bison测试

没有达到任何虚拟动作;它们用于创建语法规则,允许
expr
后面跟着另一个
expr
。但这是以这样一种方式实现的,即它实际上不会消耗第二个
expr
;在YYACCEPT调用时,只从任何以下表达式中使用一个前瞻令牌。(我有一个解决方案,可以在每次连续的
yyparse
调用之前恢复该令牌;该黑客现在在Byacc下工作。)


我仍然有一种感觉,我没有看到其他人都知道的简单事情。

FWIW,我认为分析有点不正确,并且对
$default
有误导性

$default
是一种压缩机制:我们对所有剩余的lookahead(包括无效的lookahead)执行此缩减,而不是对其缩减相同的一长串lookahead。这不允许解析器接受更多的句子;可以证明,错误将被其他规则捕获。它既不允许解析器接受更长的前缀:错误将在完全相同的点被捕获;然而,可能会进行更多的减少。作为交换,您的表更小,因为您需要编码的案例更少。今天,这可能是一个无用的优化,但当时的空间是非常关键的

我相信Bison和Byacc都实现了这种技术(实际上Bison和Byacc是同一个程序的分支,仍然共享很多代码)

然而,在野牛历史的某个时刻,添加了初始规则:
$accept:exp$end
,其中
exp
表示用户的开始符号。在处理此规则之前,在Bison(生成器)和生成的解析器中对其进行硬编码,甚至表也进行了手动调整,以处理
exp
$end
,而不使用此“规则0”。我改变了这一点,因为(I)LR解析器的理论确实使用了这个规则,所以在教授理论时让Bison显示不同的表是一个问题,(ii)代码更简单、更短,使用这个规则0

作为一个意外的结果(多年前就完成了,这是我第一次听说它是可观察的),
$end
标记被视为另一个标记,并且在
$default
操作中被融合。我想这是因为你能分辨出比亚克和野牛的区别


话虽如此,我没有比您更适合的解决方法:)除非,如果您准备只迁移到Bison(显然不是这样),那么就迁移到push解析器。这样做完全符合您的需要:交互式循环而不是批处理解析。

我认为
bison
行为是非标准的。在我自1979年以来使用的每一个arser生成器中,目标符号后面都隐含着EOF。我建议您真正应该做的是调整您的体系结构,以调用
yyparse()
一次,并在相关产品中执行每项操作,这毕竟是整个想法。@EJP“EJP:goal符号隐式后跟EOF…”但这是在fa中