【发布时间】:2015-01-09 21:18:15
【问题描述】:
我想要一个仅包含二进制非终结符的语法和求值器(ANTLR parse tree walker),而无需在访问表达式节点时打开运算符来确定要执行的操作(例如 pre-左因子语法,因为访问者会访问一个“additionNode”,所以访问者可以静态假设它必须进行一个加法)。
相当直接的问题。 ANTLR 支持左递归,所以这是一个有效的语法
expr :
| expr ('+'|'-') expr
| expr ('*'|'/') expr
| '(' expr ')'
| literal
;
很不错,但是任何为此的 walker/visitor/compiler-back-end 现在都必须对类型进行自己的调度,这很糟糕:
onVisitExit(ExprContext ctx){
left = compiledMap.get(ctx.getChild(0));
right = compiledMap.get(ctx.getChild(2));
operator = ctx.getChild(1);
switch(operator.getToken()){
case "+": compiledMap.put(ctx, left + right);
case "-": compiledMap.put(ctx, left - right);
case "*": compiledMap.put(ctx, left * right);
case "/": compiledMap.put(ctx, left / right);
}
}
这种策略的优缺点:
- antlr 为我构建了一个二叉树,其中(二进制)每个规则都有一个左右参数,这意味着我不必担心用于 kleene 闭包的 while 循环。我真的很喜欢这个
- 我必须手动调度(切换)令牌,而不是节点的类型。
使用更传统的左因子语法
expr : addOrSub ;
addOrSub : multOrDiv (('+'/'-') multOrDiv)* ;
multOrDiv : bracks (('*'/'/') backs)* ;
bracks : '(' expr ')' | literal ;
literal : TOKEN ;
对此的相应访问者与上面的两种语法具有相反的优点和缺点:ANTLR 将为我执行此类型的分派——嗯,大多数情况下,仍然需要区分“+”和“-”——但是现在我必须为那些 kleene 闭包包含 while 循环,因为我不再有严格的二叉树,这很烦人。
我想我理想的语法应该是这样的
expression : expr ;
fragment expr :
(addition | subtraction)
| (multiplication | division)
| brackets
| literal
;
addition : expr '+' expr ;
subtraction : expr '-' expr ;
multiplication : expr '*' expr ;
division : expr '/' expr ;
brackets : '(' expr ')' ;
literal : TOKEN ;
这会解决我所有的问题,当然这在 ANTLR 中是非法的
【问题讨论】:
标签: parsing antlr grammar visitor-pattern left-recursion