在 Bison 中解析函数调用(例如 `exp '(' exp ')'`):导致 shift/reduce 错误(优先级问题)
Parsing a function call (e.g. `exp '(' exp ')'`) in Bison: results in shift/reduce errors (precedence issue)
我正在尝试解析一个函数调用(目前只有一个参数,但当我让它工作时我会考虑多个参数)。
假设exp
定义为
%left '+'
%precedence CALL
exp:
exp '+' exp { ... }
| exp '(' exp ')' %prec CALL { ... }
| LITERAL { ... }
;
这会产生歧义。如果我使用 -Wcounterexamples
那么它说 exp '+' exp · '(' exp ')'
可以用两种方式解析,在 '('
.
处移动或减少
calc.y: warning: shift/reduce conflict on token '(' [-Wcounterexamples]
Example: exp '+' exp • '(' exp ')'
Shift derivation
exp
↳ exp '+' exp
↳ exp • '(' exp ')'
Example: exp '+' exp • '(' exp ')'
Reduce derivation
exp
↳ exp '(' exp ')'
↳ exp '+' exp •
它似乎不知道调用的优先级应该高于或低于 +
。我该怎么做才能确保它知道 x+y(z)
应该等同于 x+(y(z))
而不是 (x+y)(z)
?
似乎相关。
您告诉 bison 产生式 exp: exp '(' exp ')'
和先行符号 +
的相对优先级。但是您还没有告诉 bison 产生式 exp: exp '+' exp
和先行符号 (
的相对优先级。这就是为什么你在前瞻符号 '('
上有冲突
请记住,优先级比较总是在产生式(可能会减少)和先行符号(可能会移动)之间进行。我知道 yacc/bison 让它看起来像是在两个符号之间进行比较,但这是一种错觉。每个产品都由一个符号表示——默认情况下是最右边的终端——但仅此而已。这种约定对于简单的情况很直观,但在其他情况下会造成混淆,恕我直言。
最简单的解决方案是用符号 (
来标识调用产生的优先级,而不是发明一个伪令牌。也就是说,将 %prec
声明更改为 %prec '('
并在您的优先规则中将 CALL
替换为 (
。
(事实上,您并不真正需要 %prec
声明,因为与 +
前瞻标记不可能发生冲突。)
我正在尝试解析一个函数调用(目前只有一个参数,但当我让它工作时我会考虑多个参数)。
假设exp
定义为
%left '+'
%precedence CALL
exp:
exp '+' exp { ... }
| exp '(' exp ')' %prec CALL { ... }
| LITERAL { ... }
;
这会产生歧义。如果我使用 -Wcounterexamples
那么它说 exp '+' exp · '(' exp ')'
可以用两种方式解析,在 '('
.
calc.y: warning: shift/reduce conflict on token '(' [-Wcounterexamples]
Example: exp '+' exp • '(' exp ')'
Shift derivation
exp
↳ exp '+' exp
↳ exp • '(' exp ')'
Example: exp '+' exp • '(' exp ')'
Reduce derivation
exp
↳ exp '(' exp ')'
↳ exp '+' exp •
它似乎不知道调用的优先级应该高于或低于 +
。我该怎么做才能确保它知道 x+y(z)
应该等同于 x+(y(z))
而不是 (x+y)(z)
?
您告诉 bison 产生式 exp: exp '(' exp ')'
和先行符号 +
的相对优先级。但是您还没有告诉 bison 产生式 exp: exp '+' exp
和先行符号 (
的相对优先级。这就是为什么你在前瞻符号 '('
请记住,优先级比较总是在产生式(可能会减少)和先行符号(可能会移动)之间进行。我知道 yacc/bison 让它看起来像是在两个符号之间进行比较,但这是一种错觉。每个产品都由一个符号表示——默认情况下是最右边的终端——但仅此而已。这种约定对于简单的情况很直观,但在其他情况下会造成混淆,恕我直言。
最简单的解决方案是用符号 (
来标识调用产生的优先级,而不是发明一个伪令牌。也就是说,将 %prec
声明更改为 %prec '('
并在您的优先规则中将 CALL
替换为 (
。
(事实上,您并不真正需要 %prec
声明,因为与 +
前瞻标记不可能发生冲突。)