在Bison中使用优先级为一元减号不能解决转移/减少冲突

时间:2012-05-27 07:57:48

标签: grammar bison unary-operator shift-reduce-conflict

我正在设计一个非常简单的语法,我使用一元减操作数。但是,我得到了一个转变/减少冲突。在Bison手册和我看的其他地方,它说我应该定义一个新的令牌并赋予它比二进制减去操作数更高的优先级,然后在规则中使用“%prec TOKEN”。

我已经这样做了,但我仍然收到警告。为什么呢?

我正在使用野牛(GNU Bison)2.4.1。语法如下所示:

%{
#include <string>
extern "C" int yylex(void);
%}

%union {
    std::string token;
}

%token <token> T_IDENTIFIER T_NUMBER
%token T_EQUAL T_LPAREN T_RPAREN

%right T_EQUAL
%left T_PLUS T_MINUS
%left T_MUL T_DIV
%left UNARY

%start program

%%

program : statements expr
;

statements : '\n'
           | statements line
;

line : assignment
     | expr
;

assignment : T_IDENTIFIER T_EQUAL expr
;

expr : T_NUMBER
     | T_IDENTIFIER
     | expr T_PLUS expr
     | expr T_MINUS expr
     | expr T_MUL expr
     | expr T_DIV expr
     | T_MINUS expr   %prec UNARY
     | T_LPAREN expr T_RPAREN
;

2 个答案:

答案 0 :(得分:7)

%prec并没有像你希望的那样多。它告诉Bison,在您- a * b的情况下,您希望将其解析为(- a) * b而不是- (a * b)。换句话说,此处它会优先于UNARY规则T_MUL规则。在任何一种情况下,您都可以确定最终会应用UNARY规则,这只是输入减少到一元参数的顺序问题。

在你的语法中,事情是非常不同的。 line非终端的任何序列都将构成sequence,并且没有任何说明line非终端必须在行尾结束。实际上,任何表达式都可以是line。所以这里基本上有两种解析a - b的方法:或者作为带有二进制减号的单行,或者作为两个“行”,第二种是以一元减号开头。没有什么可以决定适用哪些规则,因此基于规则的优先权在这里不起作用。

您的解决方案是通过要求每个line实际结束或后跟行尾符号来纠正您的行拆分。

如果你真的想要你的语法关于行结尾的行为,你需要两个单独的非终端表达式,这些表达式可以并且不能以T_MINUS开头。你必须在树上传播它:第一个line可能以一元减号开头,但后续的一个不能。在括号内,从减号开始就可以了。

答案 1 :(得分:3)

expr规则正常(没有%prec UNARY)。你的转变/减少冲突来自规则:

statements : '\n'
           | statements line
;

规则不符合你的想法。例如,你可以写:

a + b c + d

我认为这不应该是有效的输入。

但程序规则也不是很理智:

program : statements expr
;

规则应该是这样的:

program: lines;

lines: line | lines line;

line: statement "\n" | "\n";

statement: assignment | expr;