当我读一本关于它的书时,我正在和野牛一起乱画,我正在研究一个简单的中缀计算器,我使用语法来指示优先权,而不是使用%left/%right
和此类声明的顺序。
我见过的大多数野牛计算器都将因子作为函数而不是运算符,因此这些示例没有用处。
目前我有:
15 calclist:
16 | calclist expr EOL { printf(" = %d\n", $2); }
17 | calclist EOL { /* nada */ }
18 ;
19 expr:
20 term
21 | expr '+' term { $$ = $1 + $3; }
22 | expr '-' term { $$ = $1 - $3; }
23 ;
24 term:
25 factor
26 | term '*' factor { $$ = $1 * $3; }
27 | term '/' factor { $$ = $1 / $3; }
28 ;
29 factor:
30 '|' factor { $$ = $2 > 0 ? $2 : -$2; }
31 | '-' factor { $$ = 0 - $2; }
32 | '(' expr ')' { $$ = $2; }
33 | '(' expr ')' '!' { $$ = factorial($2); }
34 | NUMBER '!' { $$ = factorial($1); }
35 | NUMBER
36 ;
第33和34行似乎过于冗长。我觉得这样做的正确方法是用以下内容替换33/34:
33 | factor '!' { $$ = factorial($1); }
然后我得到了使用否定/ abs值运算符的移位/减少冲突,这在我查看使用calc.output
生成的bison -v
文件时有意义。
在维护定义运算符优先级的expr / term / factor语法时,是否有更简洁的方法?或者我的工作解决方案是我能够做到的最好的约束(自我强加或其他)。
答案 0 :(得分:2)
-2!
是什么意思?如果您添加factor: factor '!'
,则会产生歧义,因为您可以将其解析为- <factor: 2 !>
或<factor: - 2> !
,这显然在语义上不同。 [注1]
我认为正常的理解是后缀运算符(例如!)优先于任何前缀运算符,因此应该-2!
作为-(2!)
的自然解释。 [注2]
因此必须有一个优先级比一元前缀运算符更紧密地绑定。每次引入新的优先级时,都需要引入一个新的相应的非终端(除非你使用优先级声明;有关详细信息和示例,请参阅bison手册)。
这导致:
expr:
term
| expr '+' term { $$ = $1 + $3; }
| expr '-' term { $$ = $1 - $3; }
;
term:
factor
| term '*' factor { $$ = $1 * $3; }
| term '/' factor { $$ = $1 / $3; }
;
factor:
postfix
| '|' postfix { $$ = $2 > 0 ? $2 : -$2; }
| '-' postfix { $$ = 0 - $2; }
;
postfix:
postfix '!' { $$ = factorial($1); }
| '(' expr ')' { $$ = $2; }
| NUMBER
;
(-2)!
可能是也可能不是语义错误,但似乎没有任何可能使它成为语法错误,因为语法无法知道(3-5)!
可能是什么。从语义上讲,如果要将阶乘推广到欧拉伽马函数,那么它对于负数是有意义的,条件是负整数的Γ是无穷大的。但这是一个完整的题外话。
在具有后缀++
的C语言中,此规则也正常; -x++
表示-(x++)
。更常见的是,在这种情况下,前缀运算符将是 * 。