Bison/YACC - 避免减少/减少与两个否定规则的冲突
以下语法(其中 INTEGER 是数字序列)会引起归约/归约冲突,因为例如 -4 可以通过 expr -> 来归约。 -expr 或 expr ->数字 -> -整数。在我的语法中,num 和 expr 返回不同的类型,因此我必须区分 -num 和 -expr。我的目标是 -5 减少 num,而例如 -(...) 是一个 expr。我怎样才能做到这一点?
%token INTEGER
%left '+' '-'
%%
start: expr
;
expr: expr '+' expr
| expr '-' expr
| '-' expr
| '(' expr ')'
| num
;
num: INTEGER
| '-' INTEGER
;
%%
The following grammar (where INTEGER is a sequence of digits) gives rise to a reduce/reduce conflict, because e.g. -4 can be reduced by expr -> -expr or expr -> num -> -INTEGER. In my grammar, num and expr return different types so that I have to distinguish -num and -expr. My goal is that -5 is reduced by num while e.g. -(...) is an expr. How could I achieve this?
%token INTEGER
%left '+' '-'
%%
start: expr
;
expr: expr '+' expr
| expr '-' expr
| '-' expr
| '(' expr ')'
| num
;
num: INTEGER
| '-' INTEGER
;
%%
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对于这种特定情况,您可以将否定表达式的规则更改为
仅识别带括号的表达式上的否定。然而,这不会识别双重否定(例如
- - x
),更重要的是,它不会扩展,因为如果您尝试添加其他一元运算符,它就会中断。现在,您可以简单地将
num
规则放在expr
规则之前,并允许默认的减少/减少冲突解决方案来处理它(将使用文件中出现的第一个规则)如果两者都可能),但这有点丑陋,因为每次运行 bison 时都会收到这些冲突警告,并且当您不知道到底发生了什么时忽略它们是一个坏主意。解决这种歧义的一般方法是分解语法,将有问题的规则分成两个规则,并在每个上下文中使用适当的版本,这样就不会发生冲突。在这种情况下,您可以将
expr
拆分为num_expr
(对于以num
开头的表达式)和non_num_expr
(对于其他表达式)表达式:基本上,以 RHS 上的
expr
开头的expr
的每条规则都需要重复,并且expr
的其他用途可能需要复制更改为变体之一以便避免冲突。不幸的是,在这种情况下,它不能干净地工作,因为您使用优先级来解决表达式语法固有的歧义,而因子规则会妨碍这一点——额外的一步规则会导致问题。因此,您需要将这些规则排除在外(在 RHS 上使用
expr
复制每条规则 - 一个使用num_expr
版本,另一个使用non_num_version< /code> 或者您需要使用优先级/关联性的额外规则重构语法。
请注意,在这种情况下,num/non_num 分解是在
term
而不是expr
上完成的For this specific case, you could change the rule for negative expressions to
and only recognize negations on parenthesized expressions. This however won't recognize double-negatives (eg
- - x
) and, more importantly, won't scale in that it will break if you try to add other unary operators.Now you could simply put the
num
rules BEFORE theexpr
rules and allow the default reduce/reduce conflict resolution to deal with it (the first rule appearing in the file will be used if both are possible), but that's kind of ugly in that you get these conflict warnings every time you run bison, and ignoring them when you don't know exactly what is going on is a bad idea.The general way of addressing this kind of ambiguity is by factoring the grammar to split the offending rule into two rules and using the appropriate version in each context so that you don't get conflicts. In this case, you'd split
expr
intonum_expr
for expressions that start with anum
andnon_num_expr
for other expressions:Basically, every rule for
expr
that begins with anexpr
on the RHS needs to be duplicated, and other uses ofexpr
may need to be changed to one of the variants so as to avoid the conflict.Unfortunately, in this case, it doesn't work cleanly, as you're using precedence levels to resolve the inherent ambiguity of the expression grammar, and the factored rules get in the way of that -- the extra one-step rules cause problems. So you need to either factor those rules out of existence (duplicating every rule with
expr
on the RHS -- one with thenum_expr
version and one with thenon_num_version
OR you need to refactor your grammar with extra rules for the precedence/associativityNote in this case, the num/non_num factoring has been done on
term
rather thanexpr
您不清楚为什么
num
需要表示负数。我无法判断您是否在语法中的其他地方使用了num
。您也没有说明为什么您希望 num 和 expr 不同。通常,负数是在词法分析器级别处理的。在您的情况下,规则类似于
-?[0-9]+
。这根本就不再需要num
了,结果如下:编辑: Chris Dodd 有道理。因此,您需要将否定完全移至解析器中。您仍然摆脱
num
,只是不要在INTEGER
词法分析器模式中测试负数(即模式类似于[0-9]+
,这就是你现在正在做的事情,对吧?)。我上面给出的expr
规则不会改变。-5
) 解析为:'-' INTEGER
,变为'-' expr
(选择 5),然后expr
(选择 3)。3-2
) 解析为INTEGER '-' INTEGER
,即变为expr - expr
(选择 5 两次),然后expr
(选择2)。5--1
) 之间的差异将解析为INTEGER '-' '-' INTEGER
,即变为expr '-' ' -' expr
(选择 5 两次),然后expr '-' expr
(选择 3),然后expr
(选择 2)。等等。根本问题是你在两个不同的地方进行否定,并且没有办法不产生歧义。
You are not clear on why
num
needs to represent negative numbers. I can't tell if you usenum
elsewhere in your grammar. You also don't say why you wantnum
andexpr
to be distinct.Normally, negative numbers are handled at the lexer level. In your case, the rule would be something like
-?[0-9]+
. This eliminates the need fornum
at all, and results in the following:EDIT: Chris Dodd has a point. So you need to move negation entirely into the parser. You still get rid of
num
, just don't test for negatives in theINTEGER
lexer pattern (i.e. the pattern would be something like[0-9]+
, which is what you're doing now, right?). Theexpr
rule I gave above does not change.-5
) parses as:'-' INTEGER
, which becomes'-' expr
(choice 5), thenexpr
(choice 3).3-2
) parses asINTEGER '-' INTEGER
, which becomesexpr - expr
(choice 5 twice), thenexpr
(choice 2).5--1
) parses asINTEGER '-' '-' INTEGER
, which becomesexpr '-' '-' expr
(choice 5 twice), thenexpr '-' expr
(choice 3), thenexpr
(choice 2).And so forth. The fundamental problem is you have negation in two different places and there is no way that can't be ambiguous.