Bison中的运算符优先级如何工作?

时间:2018-06-19 21:28:37

标签: parsing bison yacc

这可能是一个简单的问题,已经有人提出过,但是我在理解Bison尤其是运算符优先级方面遇到困难。如果我有此代码,而+left association

%left '+'
%%
S:
   S E ’\n’ { printf("%d\n", $2); }
|
; 

E:
   num { $$ = $1; }
| E '+' E {$$ = $1 + $3;}
| E '*'  E {$$ = $1 * $3;}
; 
%%

输入为2+3+4*5,输出为25。我的回答是45。

有人可以一步一步告诉我野牛做什么吗?我的意思是如何将元素推入堆栈以及如何以及何时减少它们。甚至可能是解析树。

1 个答案:

答案 0 :(得分:3)

查看语法的最简单方法是启用野牛的跟踪工具,如bison manual section on debugging parsers中所述。在读取跟踪时,将状态机放在手边很有用,因为跟踪提供了通过状态机的路径。要查看状态机,请使用bison的-v选项,该选项将创建扩展名为.output的文件。

$ cat minimal.y
%{
#include <stdio.h>
#include <ctype.h>
int yylex(void);
void yyerror(const char* msg) {
  fprintf(stderr, "%s\n", msg);
}
%}
%token num
%left '+'
%%
S: S E '\n' { printf("%d\n", $2); }
 |
E: num { $$ = $1; }
 | E '+' E {$$ = $1 + $3;}
 | E '*'  E {$$ = $1 * $3;}
%%
int yylex(void) {
  int c;
  do c = getchar(); while (c == ' ');
  if (isdigit(c)) {
    yylval = c - '0';
    return num;
  }
  return c == EOF ? 0 : c;
}
int main(int argc, char* argv[]) {
#if YYDEBUG
  yydebug = 1;
#endif
  return yyparse();
}

编译并运行:

$ bison -t -v -o minimal.c minimal.y
minimal.y: warning: 3 shift/reduce conflicts [-Wconflicts-sr]
$ gcc -Wall -o minimal minimal.c
$ ./minimal <<<'2+3+4*5'
Starting parse
Entering state 0
Reducing stack by rule 2 (line 14):
-> $$ = nterm S ()
Stack now 0

我删除了踪迹(尽管您可以在答案的底部看到它)。在跟踪中查找表示正在读取令牌*的行:

Entering state 8
Reading a token: Next token is token '*' ()
Shifting token '*' ()
Entering state 7

这是minimal.output中状态8的定义,并带有平移-减少冲突(由将不采取操作的方括号表示)和默认分辨率:

State 8

    4 E: E . '+' E
    4  | E '+' E .
    5  | E . '*' E

    '*'  shift, and go to state 7

    '*'       [reduce using rule 4 (E)]
    $default  reduce using rule 4 (E)

这是完整的跟踪记录(尽管我强烈建议您在自己的计算机上进行实验):

Starting parse
Entering state 0
Reducing stack by rule 2 (line 14):
-> $$ = nterm S ()
Stack now 0
Entering state 1
Reading a token: Next token is token num ()
Shifting token num ()
Entering state 3
Reducing stack by rule 3 (line 16):
   $1 = token num ()
-> $$ = nterm E ()
Stack now 0 1
Entering state 4
Reading a token: Next token is token '+' ()
Shifting token '+' ()
Entering state 5
Reading a token: Next token is token num ()
Shifting token num ()
Entering state 3
Reducing stack by rule 3 (line 16):
   $1 = token num ()
-> $$ = nterm E ()
Stack now 0 1 4 5
Entering state 8
Reading a token: Next token is token '+' ()
Reducing stack by rule 4 (line 17):
   $1 = nterm E ()
   $2 = token '+' ()
   $3 = nterm E ()
-> $$ = nterm E ()
Stack now 0 1
Entering state 4
Next token is token '+' ()
Shifting token '+' ()
Entering state 5
Reading a token: Next token is token num ()
Shifting token num ()
Entering state 3
Reducing stack by rule 3 (line 16):
   $1 = token num ()
-> $$ = nterm E ()
Stack now 0 1 4 5
Entering state 8
Reading a token: Next token is token '*' ()
Shifting token '*' ()
Entering state 7
Reading a token: Next token is token num ()
Shifting token num ()
Entering state 3
Reducing stack by rule 3 (line 16):
   $1 = token num ()
-> $$ = nterm E ()
Stack now 0 1 4 5 8 7
Entering state 9
Reading a token: Next token is token '\n' ()
Reducing stack by rule 5 (line 18):
   $1 = nterm E ()
   $2 = token '*' ()
   $3 = nterm E ()
-> $$ = nterm E ()
Stack now 0 1 4 5
Entering state 8
Next token is token '\n' ()
Reducing stack by rule 4 (line 17):
   $1 = nterm E ()
   $2 = token '+' ()
   $3 = nterm E ()
-> $$ = nterm E ()
Stack now 0 1
Entering state 4
Next token is token '\n' ()
Shifting token '\n' ()
Entering state 6
Reducing stack by rule 1 (line 13):
   $1 = nterm S ()
   $2 = nterm E ()
   $3 = token '\n' ()
25
-> $$ = nterm S ()
Stack now 0
Entering state 1
Reading a token: Now at end of input.
Shifting token $end ()
Entering state 2
Stack now 0 1 2
Cleanup: popping token $end ()
Cleanup: popping nterm S ()