为逻辑执行构建自定义语言
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
我的客户想要编写如下所示的规则:
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