当前位置: 首页 > 面试题库 >

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

傅越
2023-03-14
问题内容

我是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访问者的帮助下开发JavaScript编译器。我已经用Java实现了这一点,但无法弄清楚如何用JavaScript做到这一点。也许有人可以回答我几个问题? 1:在Java中,有一个Visitor.visit函数。如果我说得对,那么使用Javascript是不可能的。有没有解决的办法? 2:我的Javascript访问者获得了所有生成的访问函数,但是当我使用c

  • 主要内容:介绍,实现,ComputerPart.java,Keyboard.java,Monitor.java,Mouse.java,Computer.java,ComputerPartVisitor.java,ComputerPartDisplayVisitor.java,VisitorPatternDemo.java在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行

  • 在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。 介绍 意图:主要将数据结构与数据操作分离。 主要解决:稳定的数据结构和易变的操作耦合问题。 何时使用:需要对一个对象结构中的对象进行

  • 简介 访问者模式是一种将算法与对象结构分离的软件设计模式。 这个模式的基本想法如下:首先我们拥有一个由许多对象构成的对象结构,这些对象的类都拥有一个accept方法用来接受访问者对象;访问者是一个接口,它拥有一个visit方法,这个方法对访问到的对象结构中不同类型的元素作出不同的反应;在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施accept方法,在每一个元素的accept方

  • 在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。 介绍 意图:主要将数据结构与数据操作分离。 主要解决:稳定的数据结构和易变的操作耦合问题。 何时使用:需要对一个对象结构中的对象进行

  • 访问者模式 不知不觉当中,我们就到了最后一种设计模式,即访问者模式。访问者模式,听上去复杂一些。但是,这种模式用简单的一句话说,就是不同的人对不同的事物有不同的感觉。比如说吧,豆腐可以做成麻辣豆腐,也可以做成臭豆腐。可是,不同的地方的人未必都喜欢这两种豆腐。四川的朋友可能更喜欢辣豆腐,江浙的人就可能对臭豆腐更喜欢一些。那么,这种情况应该怎么用设计模式表达呢? typedef struct _Tof