在没有%prec或%left的情况下在Bison中设置优先级和关联性

时间:2012-09-14 03:02:11

标签: compiler-construction bison yacc lex flex-lexer

如何在不使用%prec或%left的情况下在Bison中设置优先级和关联性?有没有办法在没有必要的地方编写语法?

2 个答案:

答案 0 :(得分:7)

如果您不想使用%prec%left%right,则必须使用多个非终结符来确定优先级。

例如,请考虑这个语法:

%token NUMBER
%left '+'
%left '*'
%right '^'

%%

expression
    : NUMBER
    | expression '+' expression
    | expression '*' expression
    | expression '^' expression
    | '(' expression ')'
    ;

%%

让我们看看它如何与表达式1 + 2 * 3匹配。如果我们从上面的语法中删除优先级指令,那么语法可以通过两种方式匹配此表达式。这是一种方式:

expression(+)
    |
    +-- expression(NUMBER 1)
    |
    +-- expression(*)
            |
            +-- expression(NUMBER 2)
            |
            +-- expression(NUMBER 3)

这是另一种方式:

expression(*)
    |
    +-- expression(+)
    |       |
    |       +-- expression(NUMBER 1)
    |       |
    |       +-- expression(NUMBER 2)
    |
    +-- expression(NUMBER 3)

我们想写一个只能像第一种方式匹配的语法,其中*+绑定得更紧密。我们必须创建新的非终结符,并在新的非终结符中分割expression非终结符的制作,如下所示:

%token NUMBER

%%

primaryExpression
    : NUMBER
    | '(' expression ')'
    ;

exponentiationExpression
    : primaryExpression
    // Right-recursion makes this right-associative.
    | primaryExpression '^' exponentiationExpression
    ;

multiplicationExpression
    : exponentiationExpression
    // Left recursion makes this left-associative.
    | multiplicationExpression '*' exponentiationExpression
    ;

additionExpression
    : multiplicationExpression
    | additionExpression '+' multiplicationExpression
    ;

expression
    : additionExpression
    ;

让我们看看这个语法如何匹配表达式1 + 2 * 3。它只能以这种方式匹配:

expression
    |
    +-- additionExpression
            |
            +-- additionExpression
            |       |
            |       +-- multiplicationExpression
            |               |
            |               +-- exponentiationExpression
            |                   |
            |                   +-- primaryExpression(NUMBER 1)
            |
            +-- multiplicationExpression
                    |
                    +-- multiplicationExpression
                    |       |
                    |       +-- exponentiationExpression
                    |               |
                    |               +-- primaryExpression(NUMBER 2)
                    |
                    +-- exponentiationExpression
                            |
                            +-- primaryExpression(NUMBER 3)

虽然现在解析树中有更多级别,但它匹配所需的绑定优先级。

如果你想用这种方式编写你的语法,请记住,LALR解析器在处理正确的递归时通常比处理左递归时使用更多的内存。因此,通常将右递归(在exponentiationExpression中使用)重写为左递归,并修复代码中的关联性。

答案 1 :(得分:1)

是 - 您可以对不同级别的优先级使用单独的规则,并通过左右递归控制关联性。例如,要使+-的优先级低于*/,一个左关联,另一个 1 ,您可以做这样的事情:

number: literal | variable;

mul_expr: number
        | mul_expr MUL_OP number
        ;

add_expr: mul_expr
        | mul_expr ADD_OP add_expr
        ;

是的,这真的是类似yacc的伪代码;我确定yacc,byacc,Bison等会拒绝原样。


1 是的,这些通常都是关联的,但我只是这样做,以展示如果你想做一些正确的关联。