3

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

4

2 回答 2

8

如果您不想使用%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)重写为左递归,并修复代码中的关联性。

于 2012-09-14T03:35:01.663 回答
1

是的——您可以对不同级别的优先级使用单独的规则,并通过左递归与右递归控制关联性。例如,要获得+and-的优先级低于*and /,一个左关联,另一个右关联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是的,这些通常都是左关联的,但我这样做只是为了演示如何根据需要制作右关联的东西。

于 2012-09-14T03:26:22.203 回答