ANTLR4访问者模式简单的算术例子 [英] ANTLR4 visitor pattern on simple arithmetic example
问题描述
我是一个完整的ANTLR4新手,所以请原谅我的无知。我遇到了此演示文稿,其中定义了一个非常简单的算术表达式语法。它看起来像:
I am a complete ANTLR4 newbie, so please forgive my ignorance. I ran into this presentation where a very simple arithmetic expression grammar is defined. It looks like:
grammar Expressions;
start : expr ;
expr : left=expr op=('*'|'/') right=expr #opExpr
| left=expr op=('+'|'-') right=expr #opExpr
| atom=INT #atomExpr
;
INT : ('0'..'9')+ ;
WS : [ \t\r\n]+ -> skip ;
这很棒,因为它会生成一个非常简单的二叉树,可以使用访问者模式遍历在幻灯片中解释,例如,这里是访问 expr
的函数:
Which is great because it will generate a very simple binary tree that can be traversed using the visitor pattern as explained in the slides, e.g., here's the function that visits the expr
:
public Integer visitOpExpr(OpExprContext ctx) {
int left = visit(ctx.left);
int right = visit(ctx.right);
String op = ctx.op.getText();
switch (op.charAt(0)) {
case '*': return left * right;
case '/': return left / right;
case '+': return left + right;
case '-': return left - right;
default: throw new IllegalArgumentException("Unkown opeator " + op);
}
}
我想补充的另一件事是支持括弧。所以我修改了 expr
,如下所示:
The next thing I would like to add is support for parentheses. So I modified the expr
as follows:
expr : '(' expr ')' #opExpr
| left=expr op=('*'|'/') right=expr #opExpr
| left=expr op=('+'|'-') right=expr #opExpr
| atom=INT #atomExpr
;
不幸的是,上面的代码失败了,因为当遇到括号时,三个属性 op
, left
和 right
为空(NPE失败)。
Unfortunately, the code above fails because when encountering parentheses the three attributes op
,left
and right
are null (fails with NPE).
我想我可以通过定义一个新属性来解决这个问题,例如 parenthesized ='('expr')'
,然后处理在访问者代码中使用它。但是,如果有一个额外的节点类型来表示括号中的表达式,那对我来说似乎有点过分了。一个更简单但更丑陋的解决方案是在 visitOpExpr
方法的开头添加以下代码行:
I think I could work around that by defining a new attribute, e.g., parenthesized='(' expr ')'
, and then deal with that in the visitor code. However, it seems overkill to me to have a whole extra node type to represent an expression in parentheses. A simpler but uglier solution is to add the following line of code at the beginning of the visitOpExpr
method:
if (ctx.op == null) return visit(ctx.getChild(1)); // 0 and 2 are the parentheses!
我完全不喜欢上述内容,因为它非常脆弱,并且高度依赖于语法结构。
I don't like the above at all because it's very fragile and highly dependent on the grammar structure.
我想知道是否有办法告诉ANTLR只是吃掉括号并像对待孩子那样对待表达。在那儿?有一个更好的方法吗?
I am wondering if there is a way to tell ANTLR to just "eat" the parentheses and treat the expression like a child. Is there? Is there a better way to do this?
注意:我的最终目标是扩展示例以包含本身可以包含算术表达式的布尔表达式,例如(2 + 4 * 3)/ 10> = 11
,也就是说,算术表达式之间的关系(<,>,==,〜=等)可以定义一个原子布尔值表达。这是直截了当的,我已经草拟了语法,但我的括号有同样的问题,即我需要能够编写类似的内容(我还会添加对变量的支持):
Note: My end goal is to extend the example to include boolean expressions that can themselves contain arithmetic expressions, e.g., (2+4*3)/10 >= 11
, that is, a relation (<,>,==,~=,etc.) between arithmetic expressions can define an atomic boolean expression. This is straight forward and I already have the grammar sketched out but I have the same problem with parenthesis, i.e., I need to be able to write stuff like (I will also add support for variables):
((2+4*x)/10 >= 11) | ( x>1 & x<3 )
编辑:修正了括号表达式的优先级,括号始终具有更高的优先级。
EDIT: Fixed the precedence of the parenthesized expression, parenthesis always have higher precedence.
推荐答案
当然,只需标记它就不同了。毕竟,替代'('expr')'
不是 #opExpr
:
Sure, just label it differently. After all, the alternative '(' expr ')'
isn't a #opExpr
:
expr : left=expr op=('*'|'/') right=expr #opExpr
| left=expr op=('+'|'-') right=expr #opExpr
| '(' expr ')' #parenExpr
| atom=INT #atomExpr
;
在您的访问者中,您可以执行以下操作:
And in your visitor, you'd do something like this:
public class EvalVisitor extends ExpressionsBaseVisitor<Integer> {
@Override
public Integer visitOpExpr(@NotNull ExpressionsParser.OpExprContext ctx) {
int left = visit(ctx.left);
int right = visit(ctx.right);
String op = ctx.op.getText();
switch (op.charAt(0)) {
case '*': return left * right;
case '/': return left / right;
case '+': return left + right;
case '-': return left - right;
default: throw new IllegalArgumentException("Unknown operator " + op);
}
}
@Override
public Integer visitStart(@NotNull ExpressionsParser.StartContext ctx) {
return this.visit(ctx.expr());
}
@Override
public Integer visitAtomExpr(@NotNull ExpressionsParser.AtomExprContext ctx) {
return Integer.valueOf(ctx.getText());
}
@Override
public Integer visitParenExpr(@NotNull ExpressionsParser.ParenExprContext ctx) {
return this.visit(ctx.expr());
}
public static void main(String[] args) {
String expression = "2 * (3 + 4)";
ExpressionsLexer lexer = new ExpressionsLexer(new ANTLRInputStream(expression));
ExpressionsParser parser = new ExpressionsParser(new CommonTokenStream(lexer));
ParseTree tree = parser.start();
Integer answer = new EvalVisitor().visit(tree);
System.out.printf("%s = %s\n", expression, answer);
}
}
如果你运行上面的课程,你会看到以下输出:
If you run the class above, you'd see the following output:
2 * (3 + 4) = 14
这篇关于ANTLR4访问者模式简单的算术例子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!