我想要一个只包含二进制非终端的语法和求值程序(ANTLR解析树遍历器),而不需要在访问表达式节点时打开运算符来确定要执行的操作(就像预先执行的操作一样)由于访问者会访问" 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);
}
}
这一策略的利弊:
使用更传统的&已经左派的语法
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中是非法的
答案 0 :(得分:2)
在写完这个问题之后,它让我想到了更多功能
长话短说,我使用语法
expr :
| expr (plus|minus) expr
| expr (multi|div) expr
| '(' expr ')'
| literal
;
plus : '+' ;
minus : '-' ;
multi : '*' ;
div : '/' ;
给了我们听力访客:
onVisitExit(ExprContext ctx){
left = values.get(ctx.child(0));
right = values.get(ctx.child(2));
operator = binaryOperators.get(ctx.child(1));
result = operator.doUsing(left, right);
values.put(ctx, result);
}
onVisitExit(PlusContext ctx){
binaryOperators.put(ctx, (left, right) -> left + right);
}
onVisitExit(MinusContext ctx){
binaryOperators.put(ctx, (left, right) -> left - right);
}
//...
它解决了我的所有问题 - 实际上第一个访问者实现可能没有一个if
或for
语句,这真的很漂亮。
但要长篇大论:
标准多态技术会让您尝试将交换机中的功能推送到您启用的变量中。所以代码
switch(operator.getToken()){
case "+": do(left + right);
//...
变为
operator.doOperationUsing(left, right);
然后运算符的各种实现会做不同的事情。问题是为运营商生成不同的实施方案。对于典型的多态性,如果你只使用枚举,那么每个枚举实例的自定义实现的枚举确实不仅仅是切换。但是在这里,我们可以使用访问的非终端规则来生成该实现,使用lambda :)