我有一个YACC语法用于在C ++中解析表达式。这是精简版:
// yacc.y
%token IDENT
%%
expr:
call_expr
| expr '<' call_expr
| expr '>' call_expr
;
call_expr:
IDENT
| '(' expr ')'
| IDENT '<' args '>' '(' args ')'
;
args:
IDENT
| args ',' IDENT
;
%%
当我想支持带模板参数的函数调用时,我遇到了shift/reduce
冲突。
当我们输入IDENT '<' IDENT
时,yacc不知道我们是应该转移还是减少。
我希望IDENT '<' args '>' '(' args ')'
的优先级高于expr '<' call_expr
,因此我可以解析以下的exprs。
x < y
f<x>(a,b)
f<x,y>(a,b) < g<x,y>(c,d)
我看到C ++ / C#都支持这种语法。有没有办法用yacc解决这个问题?
如何修改.y
文件?
谢谢!
答案 0 :(得分:1)
您想要yacc / bison的-v
选项。它将为您提供一个.output文件,其中包含有关生成的shift / reduce解析器的所有信息。用你的语法,野牛给你:
State 1 conflicts: 1 shift/reduce
:
state 1
4 call_expr: IDENT .
6 | IDENT . '<' args '>' '(' args ')'
'<' shift, and go to state 5
'<' [reduce using rule 4 (call_expr)]
$default reduce using rule 4 (call_expr)
显示问题所在。看到IDENT
后,当下一个标记为<
时,它不知道是否应减少call_expr
(最终匹配规则expr: expr '<' call_expr
)或是否应该转移到匹配规则6。
仅使用1个令牌前瞻解析这个很难,因为你有两个不同的令牌<
(小于或打开角括号),这意味着取决于后来的令牌。
这种情况实际上更糟糕,因为它是模棱两可的,像
这样的输入a < b > ( c )
可能是一个模板调用,两个args列表都是单例,但它也可能是
( a < b ) > ( c )
所以只是解构语法也无济于事。您最好的选择是使用更强大的解析方法,例如bison的%glr-parser
选项或btyacc