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

时间:2011-10-04 20:24:50

标签: grammar bison yacc

以下语法(其中INTEGER是数字序列)引起减少/减少冲突,因为例如-4可以通过expr减少 - > -expr或expr - > num - > -整数。在我的语法中,num和expr返回不同的类型,因此我必须区分-num和-expr。我的目标是-5减少数量,例如 - (...)是一个expr。我怎么能做到这一点?

%token      INTEGER

%left       '+'     '-'

%%

start: expr
    ;

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

num:  INTEGER
    | '-' INTEGER
    ;
%%

2 个答案:

答案 0 :(得分:2)

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

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

并且只识别带括号的表达式的否定。然而,这不会识别双重否定(例如- - x),更重要的是,如果您尝试添加其他一元运算符,它将无法缩放。

现在您可以简单地在num规则之前放置expr规则,并允许默认的reduce / reduce冲突解决方案来处理它(如果两者都是,则将使用文件中出现的第一个规则可能),但这有点难看,因为你每次跑野牛都会得到这些冲突警告,当你不确切知道发生了什么事情时忽略它们是一个坏主意。

解决这种歧义的一般方法是将语法分解为将违规规则分成两个规则,并在每个上下文中使用适当的版本,这样就不会产生冲突。在这种情况下,对于以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 ')'
            ;

基本上,expr的每个以RHS上的expr开头的规则都需要重复,expr的其他用法可能需要更改为其中一个变体,因此为了避免冲突。

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

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上完成

答案 1 :(得分:0)

您不清楚为什么num需要代表负数。我不知道你是否在语法的其他地方使用num。您也没有说明为什么您希望numexpr不同。

通常,负数在lexer级别处理。在您的情况下,规则将类似于-?[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)。

等等。根本问题是你在两个不同的地方有否定,并且没有办法不会模棱两可。