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
;
备注
(-2)!
可能是也可能不是语义错误,但似乎没有任何可能使其成为语法错误,因为语法不知道 (3-5)!
可能是。从语义上讲,如果要将阶乘推广到欧拉伽玛函数,这对负数是有意义的,条件是负 整数 的 Γ 是无限的。但这完全是题外话。
在像C这样后缀为++
的语言中,这个规则也是正常的; -x++
表示 -(x++)
。更常见的是,这种情况下的前缀运算符是 *.
当我读一本关于它的书时,我正在研究野牛,我正在研究一个简单的中缀计算器,我使用语法来决定优先级而不是使用 %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
;
备注
(-2)!
可能是也可能不是语义错误,但似乎没有任何可能使其成为语法错误,因为语法不知道(3-5)!
可能是。从语义上讲,如果要将阶乘推广到欧拉伽玛函数,这对负数是有意义的,条件是负 整数 的 Γ 是无限的。但这完全是题外话。在像C这样后缀为
++
的语言中,这个规则也是正常的;-x++
表示-(x++)
。更常见的是,这种情况下的前缀运算符是 *.