Bison/YACC - 避免减少/减少与两个否定规则的冲突

发布于 2024-12-08 07:56:51 字数 421 浏览 0 评论 0原文

以下语法(其中 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

绅刃 2024-12-15 07:56:51

对于这种特定情况,您可以将否定表达式的规则更改为

expr: '-' '(' expr ')'

仅识别带括号的表达式上的否定。然而,这不会识别双重否定(例如 - - x),更重要的是,它不会扩展,因为如果您尝试添加其他一元运算符,它就会中断。

现在,您可以简单地将 num 规则放在 expr 规则之前,并允许默认的减少/减少冲突解决方案来处理它(将使用文件中出现的第一个规则)如果两者都可能),但这有点丑陋,因为每次运行 bison 时都会收到这些冲突警告,并且当您不知道到底发生了什么时忽略它们是一个坏主意。

解决这种歧义的一般方法是分解语法,将有问题的规则分成两个规则,并在每个上下文中使用适当的版本,这样就不会发生冲突。在这种情况下,您可以将 expr 拆分为 num_expr(对于以 num 开头的表达式)和 non_num_expr(对于其他表达式)表达式:

expr: num_expr | non_num_expr ;

num_expr: num_expr '+' expr
        | num_expr '-' expr
        | num
        ;

non_num_expr: non_num_expr '+' expr
            | non_num_expr '-' expr
            | '-' non_num_expr
            | '(' expr ')'
            ;

基本上,以 RHS 上的 expr 开头的 expr 的每条规则都需要重复,并且 expr 的其他用途可能需要复制更改为变体之一以便避免冲突。

不幸的是,在这种情况下,它不能干净地工作,因为您使用优先级来解决表达式语法固有的歧义,而因子规则会妨碍这一点——额外的一步规则会导致问题。因此,您需要将这些规则排除在外(在 RHS 上使用 expr 复制每条规则 - 一个使用 num_expr 版本,另一个使用 non_num_version< /code> 或者您需要使用优先级/关联性的额外规则重构语法。

expr: expr '+' term
    | expr '-' term
    | term
;

term: non_num_term | num_term ;

non_num_term: '-' non_num_term
            | '(' expr ')'
;

num_term: num ;

请注意,在这种情况下,num/non_num 分解是在 term 而不是 expr 上完成的

For this specific case, you could change the rule for negative expressions to

expr: '-' '(' expr ')'

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 the expr 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 into num_expr for expressions that start with a num and non_num_expr for other expressions:

expr: num_expr | non_num_expr ;

num_expr: num_expr '+' expr
        | num_expr '-' expr
        | num
        ;

non_num_expr: non_num_expr '+' expr
            | non_num_expr '-' expr
            | '-' non_num_expr
            | '(' expr ')'
            ;

Basically, every rule for expr that begins with an expr on the RHS needs to be duplicated, and other uses of expr 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 the num_expr version and one with the non_num_version OR you need to refactor your grammar with extra rules for the precedence/associativity

expr: expr '+' term
    | expr '-' term
    | term
;

term: non_num_term | num_term ;

non_num_term: '-' non_num_term
            | '(' expr ')'
;

num_term: num ;

Note in this case, the num/non_num factoring has been done on term rather than expr

魂牵梦绕锁你心扉 2024-12-15 07:56:51

您不清楚为什么 num 需要表示负数。我无法判断您是否在语法中的其他地方使用了 num 。您也没有说明为什么您希望 num 和 expr 不同。

通常,负数是在词法分析器级别处理的。在您的情况下,规则类似于 -?[0-9]+。这根本就不再需要 num 了,结果如下:

expr: expr '+' expr
    | expr '-' expr
    | '-' expr
    | '(' expr ')'
    | INTEGER
    ;

编辑: 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 use num elsewhere in your grammar. You also don't say why you want num and expr 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 for num at all, and results in the following:

expr: expr '+' expr
    | expr '-' expr
    | '-' expr
    | '(' expr ')'
    | INTEGER
    ;

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 the INTEGER lexer pattern (i.e. the pattern would be something like [0-9]+, which is what you're doing now, right?). The expr rule I gave above does not change.

  • A negative number (-5) parses as: '-' INTEGER, which becomes '-' expr (choice 5), then expr (choice 3).
  • A difference between two integers (3-2) parses as INTEGER '-' INTEGER, which becomes expr - expr (choice 5 twice), then expr (choice 2).
  • A difference between an integer and a negative integer (5--1) parses as INTEGER '-' '-' INTEGER, which becomes expr '-' '-' expr (choice 5 twice), then expr '-' expr (choice 3), then expr (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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文