ANTLR4访问者模式上的简单算术示例

我是ANTLR4的新手,请原谅我的无知。我遇到了这个演示,其中定义了一个非常简单的算术表达式语法。看起来像:

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

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如下:

expr  : '(' expr ')'                      #opExpr

| left=expr op=('*'|'/') right=expr #opExpr

| left=expr op=('+'|'-') right=expr #opExpr

| atom=INT #atomExpr

;

不幸的是,上面的代码失败了,因为遇到三个属性时会加上括号opleft并且right它们为null(NPE失败)。

我认为我可以通过定义一个新属性(例如)来解决此问题parenthesized='(' expr

')',然后在访问者代码中进行处理。但是,对于我来说,拥有一个完整的额外节点类型来用括号来表示一个表达式似乎有点矫kill过正。一个更简单但更难看的解决方案是在该visitOpExpr方法的开头添加以下代码行:

if (ctx.op == null) return visit(ctx.getChild(1)); // 0 and 2 are the parentheses!

我一点都不喜欢上面的内容,因为它非常脆弱并且高度依赖于语法结构。

我想知道是否有一种方法可以告诉ANTLR只是“吃掉”括号并像孩子一样对待表达式。在那儿?有一个更好的方法吗?

注意 :我的最终目标是将示例扩展为包括布尔表达式,这些布尔表达式本身可以包含算术表达式,例如(2+4*3)/10 >=

11,即算术表达式之间的关系(<,>,==,〜=等)可以定义原子布尔表达式。这很简单,我已经勾勒出语法,但是括号也有同样的问题,即我需要能够编写类似的东西(我还将添加对变量的支持):

((2+4*x)/10 >= 11) | ( x>1 & x<3 )

:固定了括号表达式的优先级,括号始终具有更高的优先级。

回答:

当然可以,只是贴上不同的标签。毕竟,替代'(' expr ')'方法不是#opExpr

expr  : left=expr op=('*'|'/') right=expr #opExpr

| left=expr op=('+'|'-') right=expr #opExpr

| '(' expr ')' #parenExpr

| atom=INT #atomExpr

;

在访问者中,您将执行以下操作:

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);

}

}

如果运行上面的类,您将看到以下输出:

2 *(3 + 4)= 14

以上是 ANTLR4访问者模式上的简单算术示例 的全部内容, 来源链接: utcz.com/qa/401500.html

回到顶部