【问题标题】:How to process logically connected expressions with ANTLR visitor如何使用 ANTLR 访问者处理逻辑连接的表达式
【发布时间】:2021-01-20 19:41:38
【问题描述】:

在我们的软件中,我们可以一起单击查询来选择元素的子集。在内存中,完成的查询是表示过滤条件的列表和对象 (CLO) 的构造。现在我要实现一种查询语言,通过用 ANTLR 解析它来创建相同的东西。

该语言必须只涵盖简单的表达式及其逻辑连接“AND”和“OR”。

name LIKE 'foo'
name LIKE 'bar' AND size > 42 OR comment LIKE 'ignore'

“名称”、“大小”和“评论”这些词是属性。由“AND”连接的每个属性都将是列表中的对象 A。每个与“或”连接的属性都将是一个对象 B 并进入 A 的列表。

我的语法(摘录):

expr
    : expr AND expr                   # andExpr
    | expr OR expr                    # orExpr
    | IDENTIFIER numOp INT            # numExpr
    | IDENTIFIER strOp STR            # strExpr
    ;
    
numOp : (GT | LT | EQ);
strOp : (EQ | LIKE);

使用带有 ANTLR 的访问者模式,我重写了 numExpr 和 strExpr 的方法并返回 A 对象。但我还需要将这些对象与逻辑运算符“AND”|“OR”相关联。 当我重写 logicExpr 方法并调用 visit() 方法时,首先计算两个逻辑运算符,然后按顺序计算基本表达式。这感觉有点尴尬,因为表达式与逻辑运算符相当分离。

OR
AND
name
size
comment

问:如何根据前面的逻辑运算符处理相应的表达式?什么是好的方法?

问:此外,逻辑表达式语句在语法规则'expr'中的正确位置还是应该先出现基本表达式?

【问题讨论】:

  • 如果您引用 AND/OR,则 AND 优先。

标签: java antlr4


【解决方案1】:

我只是去找一个即时评估事物的访问者(而不是创建自定义对象并存储它们)。此类访问者的快速演示如下所示:

public class EvalVisitor extends ExprBaseVisitor<Object> {

  private final Map<String, Object> attributes;

  public EvalVisitor(Map<String, Object> attributes) {
    this.attributes = attributes;
  }

  @Override
  public Boolean visitEval(ExprParser.EvalContext ctx) {
    return (Boolean) visit(ctx.expr());
  }

  @Override
  public Boolean visitStrExpr(ExprParser.StrExprContext ctx) {
    String lhs = this.attributes.get(ctx.IDENTIFIER().getText()).toString();
    String rhs = ctx.STR().getText().substring(1, ctx.STR().getText().length() - 1);
    int op = ctx.strOp().start.getType();

    switch (op) {
      case ExprLexer.EQ:
        return lhs.equals(rhs);
      case ExprLexer.LIKE:
        return lhs.toLowerCase().contains(rhs.toLowerCase());
      default:
        throw new RuntimeException("unknown operator: " + ctx.strOp().getText());
    }
  }

  @Override
  public Object visitAndExpr(ExprParser.AndExprContext ctx) {
    Boolean lhs = (Boolean) this.visit(ctx.expr(0));
    Boolean rhs = (Boolean) this.visit(ctx.expr(1));

    return lhs && rhs;
  }

  @Override
  public Object visitOrExpr(ExprParser.OrExprContext ctx) {
    Boolean lhs = (Boolean) this.visit(ctx.expr(0));
    Boolean rhs = (Boolean) this.visit(ctx.expr(1));

    return lhs || rhs;
  }

  @Override
  public Boolean visitNumExpr(ExprParser.NumExprContext ctx) {
    Integer lhs = (Integer) this.attributes.get(ctx.IDENTIFIER().getText());
    Integer rhs = Integer.valueOf(ctx.INT().getText());
    int op = ctx.numOp().start.getType();

    switch (op) {
      case ExprLexer.GT:
        return lhs > rhs;
      case ExprLexer.LT:
        return lhs < rhs;
      case ExprLexer.EQ:
        return lhs.equals(rhs);
      default:
        throw new RuntimeException("unknown operator: " + ctx.numOp().getText());
    }
  }
}

可以这样测试:

String source = "name LIKE 'bar' AND size > 42 OR comment LIKE 'ignore'";
ExprLexer lexer = new ExprLexer(CharStreams.fromString(source));
ExprParser parser = new ExprParser(new CommonTokenStream(lexer));

Map<String, Object> attributes = new HashMap<>();

attributes.put("name", "Barista");
attributes.put("size", 40);
attributes.put("comment", "Ignore this please");

Object result = new EvalVisitor(attributes).visit(parser.eval());

System.out.printf("source:\n  %s\n\nresult:\n  %s\n", source, result);

结果整数:

source:
  name LIKE 'bar' AND size > 42 OR comment LIKE 'ignore'

result:
  true

【讨论】:

    【解决方案2】:

    我认为你对事情发生的时间有点误解。

    当您解析您的输入时,会构建一个解析树作为评估您的解析规则的结果。

    然后,您可以将访问者或侦听器用作便利类,帮助您浏览从解析中返回的解析树。

    如果您设置了 ANTLR 书中概述的“grun”功能,然后使用“-gui”选项获得访问者或侦听器将处理的解析树的可视化呈现,这可能会有所帮助。

    通常,侦听器接口更易于使用,因为它会为您导航解析树,并在进入或退出节点时回调您的方法(您无需编写任何代码来处理导航。

    访问者界面的工作水平略低,但您必须自己处理导航。

    在您理解之前,很难知道如何回答您的问题,我怀疑这可能会改变您提出问题的方式(如果您仍有问题)

    【讨论】:

    • 我使用 IntelliJ 的 ANTLR 插件,它确实显示了 AST 以及解析规则的层次结构。我确实理解 AND 比 OR 更强烈地绑定两个表达式,因此 OR 是要访问的第一个标记。我会看看听众。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-05
    • 1970-01-01
    • 1970-01-01
    • 2019-10-26
    • 1970-01-01
    相关资源
    最近更新 更多