如何解决如何使用 ANTLR 访问者处理逻辑连接的表达式
在我们的软件中,我们可以点击一个查询来选择元素的子集。在内存中,完成的查询是表示过滤条件的列表和对象 (CLO) 的构造。现在我将要实现一种查询语言,通过使用 ANTLR 对其进行解析来创建相同的内容。
语言必须仅涵盖简单的表达式及其逻辑连接“AND”和“OR”。
name LIKE 'foo'
name LIKE 'bar' AND size > 42 OR comment LIKE 'ignore'
单词“name”、“size”和“comment”是属性。由“AND”连接的每个属性都将是列表中的对象 A。与“OR”相连的每个属性都将是一个对象 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”中的位置是否正确,还是应该首先出现基本表达式?
解决方法
我只是去找一个可以即时评估事物的访问者(而不是创建自定义对象并存储它们)。此类访问者的快速演示如下所示:
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
,
我认为您对事情发生的时间有一些误解。
当您解析您的输入时,会根据您的解析规则构建一个解析树。
然后,您可以使用访问者或侦听器作为便利类,帮助您导航从解析返回的解析树。
如果您设置 ANTLR 书中概述的“grun”功能,然后使用“-gui”选项来获得访问者或侦听器将处理的解析树的可视化呈现,这可能会有所帮助。
>通常,侦听器界面更易于使用,因为它会为您导航解析树,并在进入或退出节点时回调您的方法(您无需执行任何代码来处理导航。
>访问者界面的工作级别稍低,但您必须自己处理导航。
在您了解清楚之前,很难知道如何回答您的问题,而且我怀疑这可能会改变您表达问题的方式(如果您仍有疑问)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。