Compiler construction 在没有%prec或%left的Bison中设置优先级和关联性

Compiler construction 在没有%prec或%left的Bison中设置优先级和关联性,compiler-construction,bison,yacc,lex,flex-lexer,Compiler Construction,Bison,Yacc,Lex,Flex Lexer,如果不使用%prec或%left,如何在Bison中设置优先级和关联性?有没有一种方法可以在不需要的地方编写语法?是的——您可以对不同的优先级使用不同的规则,并通过左递归和右递归控制关联性。例如,要获得优先级低于*和/的+和-,一个为左关联,另一个为右1,可以执行以下操作: number: literal | variable; mul_expr: number | mul_expr MUL_OP number ; add_expr: mul_expr

如果不使用%prec或%left,如何在Bison中设置优先级和关联性?有没有一种方法可以在不需要的地方编写语法?

是的——您可以对不同的优先级使用不同的规则,并通过左递归和右递归控制关联性。例如,要获得优先级低于
*
/
+
-
,一个为左关联,另一个为右1,可以执行以下操作:

number: literal | variable;

mul_expr: number
        | mul_expr MUL_OP number
        ;

add_expr: mul_expr
        | mul_expr ADD_OP add_expr
        ;
%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
    ;
是的,这真的是类似于yacc的伪代码;我敢肯定yacc、byacc、Bison等公司会原封不动地拒绝它



1是的,这些通常都是左关联的,但我这样做只是为了演示如何使某些东西右关联,如果你想的话。

如果你不想使用
%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)
我们想要编写一种语法,它只能像第一种方法那样匹配,其中
*
+
绑定得更紧密。我们必须创建新的非终结符,并在新的非终结符之间拆分
表达式
非终结符的产品,如下所示:

number: literal | variable;

mul_expr: number
        | mul_expr MUL_OP number
        ;

add_expr: mul_expr
        | mul_expr ADD_OP add_expr
        ;
%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解析器在处理右递归时通常比处理左递归时使用更多的内存。因此,通常将右递归(用于
指数表达式
)重写为左递归,并修复代码中的关联性