我正在 yacc / bison 中编写一个简单的计算器。
表达式的语法看起来像这样:
expr
: NUM
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr { $$ = $1 / $3; }
| '+' expr %prec '*' { $$ = $1; }
| '-' expr %prec '*' { $$ = $1; }
| '(' expr ')' { $$ = $2; }
| expr expr { $$ = $1 '*' $2; }
;
我已经宣布了这样的运营商的优先权。
%left '+' '-'
%left '*' '/'
%nonassoc '('
问题在于最后一条规则:
expr expr { $$ = $1 $2; }
我想要这个规则,因为我希望能够在我的计算器中编写像5(3+4)(3-24)
这样的表达式。
是否有可能使这个语法明确无误?
答案 0 :(得分:3)
由于允许一元运算符(- expr
)这一事实导致歧义,因此2 - 2
可以解析为简单减法(产生0)或隐式产品(2和 - ) 2,屈服-4)。
很明显,减法是有意的(否则无法表示减法)所以如果右侧的第二个expr: expr expr
是一元操作,则必须禁止生成expr
。
使用优先级声明无法完成(或者至少不能以明显的方式完成),因此最好的解决方案是明确写出语法,而不依赖于优先级来消除歧义。
您还必须准确确定隐式乘法的优先级:与显式乘法/除法相同,或更强。这会影响ab/cd
的解析方式。我所知道的并没有达成共识,所以它或多或少取决于你。
在下文中,我假设隐式乘法更紧密地绑定。我还确保将-ab
解析为(-a)b
,尽管-(ab)
具有相同的最终结果(直到您开始处理非算术类型和自动转换等事情)。所以请以此为例。
term: NUM
| '(' expr ')'
unop: term
| '-' unop
| '+' unop
conc: unop
| conc term
prod: conc
| prod '*' conc
| prod '/' conc
expr: prod
| expr '+' prod
| expr '-' prod