Bison 语法 shift/reduce 与其他一元运算符一起处理阶乘时发生冲突

Bison grammar shift/reduce conflicts handling factorial along with other unary operators

当我读一本关于它的书时,我正在研究野牛,我正在研究一个简单的中缀计算器,我使用语法来决定优先级而不是使用 %left/%right 和顺序此类声明。

我见过的大多数 bison 计算器都将阶乘作为函数而不是运算符,因此这些示例没有帮助。

目前我有:

 15 calclist:
 16   | calclist expr EOL { printf(" = %d\n", ); }
 17   | calclist EOL { /* nada */ }
 18   ;
 19 expr:
 20     term
 21   | expr '+' term { $$ =  + ; }
 22   | expr '-' term { $$ =  - ; }
 23   ;
 24 term:
 25     factor
 26   | term '*' factor { $$ =  * ; }
 27   | term '/' factor { $$ =  / ; }
 28   ;
 29 factor:
 30     '|' factor { $$ =  > 0 ?  : -; }
 31   | '-' factor { $$ = 0 - ; }
 32   | '(' expr ')' { $$ = ; }
 33   | '(' expr ')' '!' { $$ = factorial(); }
 34   | NUMBER '!' { $$ = factorial(); }
 35   | NUMBER
 36   ;

第 33 行和第 34 行似乎过于冗长。我觉得这样做的正确方法是将 33/34 替换为:

33   | factor '!' { $$ = factorial(); }

但后来我得到 shift/reduce 与 negation/abs 值运算符冲突,当我查看使用 bison -v.[=16= 生成的 calc.output 文件时,这是有道理的]

有没有一种更简洁的方法可以在保持定义运算符优先级的 expr/term/factor 语法的同时做到这一点?或者我的工作解决方案是我能做的最好的解决方案吗(自我强加或其他)。

-2!是什么意思?如果添加 factor: factor '!',则会产生歧义,因为您可以将其解析为 - <factor: 2 !><factor: - 2> !,这在语义上显然不同。 [注1]

我认为正常的理解是 ! 等后缀运算符优先于任何前缀运算符,因此 -2! 的自然解释为 -(2!) 应该应用。 [注2]

所以必须有一个比一元前缀运算符绑定更紧密的优先级。并且每引入一个新的优先级,就需要引入一个新的对应的非终结符(除非你使用优先级声明;详情和例子见bison手册)。

这导致:

expr:
    term
  | expr '+' term { $$ =  + ; }
  | expr '-' term { $$ =  - ; }
  ;
term:
  factor
  | term '*' factor { $$ =  * ; }
  | term '/' factor { $$ =  / ; }
  ;
factor:
    postfix
  | '|' postfix { $$ =  > 0 ?  : -; }
  | '-' postfix { $$ = 0 - ; }
  ;
postfix:
    postfix '!'  { $$ = factorial(); }
  | '(' expr ')' { $$ = ; }
  | NUMBER
  ;

备注

  1. (-2)! 可能是也可能不是语义错误,但似乎没有任何可能使其成为语法错误,因为语法不知道 (3-5)! 可能是。从语义上讲,如果要将阶乘推广到欧拉伽玛函数,这对负数是有意义的,条件是负 整数 的 Γ 是无限的。但这完全是题外话。

  2. 在像C这样后缀为++的语言中,这个规则也是正常的; -x++ 表示 -(x++)。更常见的是,这种情况下的前缀运算符是 *.