为逻辑执行构建自定义语言

Building a custom language for logic execution

我的客户想要编写如下所示的规则:

factA or factB and (factC or factD)

如果计算结果为真,则表明存在 XYZ。 现在每个事实都有一个与之相关的分数,范围为 0-1。 每个事实也有一个与之关联的“置信度”分数,表示客户确定结果有用的范围。

如果对于每个事实,输入中与其关联的分数是:

factA: 0.56
factB: 0.44
factC: 0.99
factD: 0.75

及其对应的高置信度分数为:

factA: 0.80
factB: 0.75
factC: 0.80
factD: 0.75

可以通过以下方式评估规则:

valueOfFactA > 0.80 or valueOfFactB > 0.75 and (valueOfFactC > 0.80 or valueOfFactD > 0.75)

在某些情况下,我们不想做大于号,而是想做 <。 规则编写者希望对计算的完成方式保持不可知,并且只想说明 ands/OR 条件。

为了创建此类内容,我查阅了 Antlr(https://www.antlr.org/),它提供了编写您自己的自定义语言解析器的能力。

我能够获得以下列方式编写的允许解析规则的基本语法:

fact_set: logical_expr* EOF;

logical_expr
    : logical_expr AND logical_expr
    | logical_expr OR logical_expr
    | LPAREN logical_expr RPAREN
    | FACT
    ;
AND: 'and' | 'AND';
OR: 'or' |'OR';
LPAREN: '(';
RPAREN: ')';
FACT: [a-zA-Z_][a-zA-Z_0-9]*;
WS : [ \r\t\u000C\n]+ -> skip ;

我是否正确地处理了这个问题,或者有没有我可以考虑的替代方法? 据我了解,我扩展了 BaseVistor class

public class MyVisitor extends testBaseVisitor<Boolean> {

    @Override public Boolean visitFact_set(testParser.Fact_setContext ctx) { return visitChildren(ctx); }
    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override public Boolean visitLogical_expr(testParser.Logical_exprContext ctx) { return visitChildren(ctx); }
}

但我无法理解在此处提到的各种方法中必须拆分哪些代码。将不胜感激任何帮助。谢谢!

快速演示如何动态评估它。我对你的语法做了一点小改动(我在 expr 规则中添加了 alternative labels):

grammar Fact;

fact_set : expr EOF;

expr
 : lhs=expr AND rhs=expr #andExpr
 | lhs=expr OR rhs=expr  #orExpr
 | LPAREN expr RPAREN    #nestedExpr
 | FACT                  #factExpr
 ;

AND    : 'and' | 'AND';
OR     : 'or' |'OR';
LPAREN : '(';
RPAREN : ')';
FACT   : [a-zA-Z_][a-zA-Z_0-9]*;
WS     : [ \r\t\u000C\n]+ -> skip;

生成解析器 类 后,您可以将以下源代码粘贴到名为 FactDemo.java 的文件中:

import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;

import java.util.HashMap;
import java.util.Map;

public class FactDemo {

  private static void test(String source) {
    FactEvaluator evaluator = new FactEvaluator(source, new HashMap<String, Fact>(){{
      put("factA", new Fact(0.56, 0.80));
      put("factB", new Fact(0.76, 0.75));
      put("factC", new Fact(0.99, 0.80));
      put("factD", new Fact(0.75, 0.75));
    }});

    System.out.println(source + " = " + evaluator.eval());
  }

  public static void main(String[] args) {
    test("factA");
    test("factB");
    test("factA or factB");
    test("factA or factB and (factC or factD)");
  }
}

class Fact {

  public final double score;
  public final double confidence;

  public Fact(double score, double confidence) {
    this.score = score;
    this.confidence = confidence;
  }
}

class FactEvaluator extends FactBaseVisitor<Boolean> {

  private final String source;
  private final Map<String, Fact> facts;

  public FactEvaluator(String source, Map<String, Fact> facts) {
    this.source = source;
    this.facts = facts;
  }

  public Boolean eval() {
    FactLexer lexer = new FactLexer(CharStreams.fromString(this.source));
    FactParser parser = new FactParser(new CommonTokenStream(lexer));
    return this.visit(parser.fact_set());
  }

  // fact_set : expr EOF;
  @Override
  public Boolean visitFact_set(FactParser.Fact_setContext ctx) {
    return this.visit(ctx.expr());
  }

  // lhs=expr AND rhs=expr                   #andExpr
  @Override
  public Boolean visitAndExpr(FactParser.AndExprContext ctx) {
    return this.visit(ctx.lhs) && this.visit(ctx.rhs);
  }

  // lhs=expr OR rhs=expr                    #orExpr
  @Override
  public Boolean visitOrExpr(FactParser.OrExprContext ctx) {
    return this.visit(ctx.lhs) || this.visit(ctx.rhs);
  }

  // LPAREN expr RPAREN              #nestedExpr
  @Override
  public Boolean visitNestedExpr(FactParser.NestedExprContext ctx) {
    return this.visit(ctx.expr());
  }

  // FACT                            #factExpr
  @Override
  public Boolean visitFactExpr(FactParser.FactExprContext ctx) {
    Fact fact = this.facts.get(ctx.FACT().getText());
    return fact.score > fact.confidence;
  }
}

在 运行 之后,您将在控制台上看到以下内容:

factA = false
factB = true
factA or factB = true
factA or factB and (factC or factD) = true