From 23b87d2e62a1f550ed7afd47dba27b34c9b9a01c Mon Sep 17 00:00:00 2001 From: Edoardo Vacchi Date: Wed, 19 Sep 2018 09:27:55 +0200 Subject: [PATCH] DROOLS-2823 FEEL Parser: refactor `not` unary negation and `not` function call; fix support to wildcard `?` (#2059) * wip * all tests pass * wip * fix input var type * fields order, notExitst builtin f * most compile-time tests pass * fix all compiler tests * fix all tests * grammar fix * cleanup * move method back into its original position * fix short-circuiting on null (notExists) * test support to listeners * fix error logging * remove decision table in feel expresion tests -- no longer in 1.2 * replace commented out tests with error-checking tests * move NOT to lexing section; * fix erroneous reference to non-existing symbol "Not_Key" --- .../kie/dmn/core/compiler/DMNFEELHelper.java | 29 +- .../AbstractModelEvaluator.java | 5 +- .../DecisionTableEvaluator.java | 2 +- .../dmn/core/DMNDecisionTableRuntimeTest.java | 6 +- .../java/org/kie/dmn/core/DMNRuntimeTest.java | 6 +- .../core/WrongConstraintsInItemDefinition.dmn | 2 +- .../kie/dmn/feel/parser/feel11/FEEL_1_1.g4 | 68 ++- .../feel11/CompiledFEELSemanticMappings.java | 24 +- .../codegen/feel11/CompiledFEELSupport.java | 99 +++++ .../feel11/CompilerBytecodeLoader.java | 15 +- .../codegen/feel11/DirectCompilerVisitor.java | 407 +++++++++++++----- .../org/kie/dmn/feel/lang/ast/InNode.java | 1 + .../kie/dmn/feel/lang/ast/InfixOpNode.java | 1 + .../org/kie/dmn/feel/lang/impl/FEELImpl.java | 44 +- .../feel/parser/feel11/ASTBuilderVisitor.java | 63 ++- .../feel/codegen/feel11/CodegenTestUtil.java | 5 + .../codegen/feel11/DirectCompilerTest.java | 6 +- .../feel11/DirectCompilerUnaryTestsTest.java | 45 +- .../examples/SimpleDecisionTablesTest.java | 239 ---------- .../feel/parser/feel11/FEELParserTest.java | 2 +- .../dmn/feel/runtime/FEELExpressionsTest.java | 17 +- .../dmn/feel/runtime/FEELOperatorsTest.java | 4 +- .../feel/runtime/FEELValuesConstantsTest.java | 3 +- .../feel/lang/examples/dt_formula_out.feel | 14 - .../dmn/feel/lang/examples/dthitpolicies.feel | 145 ------- .../dthitpolicies_multipleoutput.feel | 145 ------- .../lang/examples/simple_decision_tables.feel | 60 --- .../feel/lang/examples/t0004simpletableU.feel | 18 - 28 files changed, 595 insertions(+), 880 deletions(-) delete mode 100644 kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/examples/SimpleDecisionTablesTest.java delete mode 100644 kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dt_formula_out.feel delete mode 100644 kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dthitpolicies.feel delete mode 100644 kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dthitpolicies_multipleoutput.feel delete mode 100644 kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/simple_decision_tables.feel delete mode 100644 kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/t0004simpletableU.feel diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DMNFEELHelper.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DMNFEELHelper.java index e0e55670995..2909950580d 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DMNFEELHelper.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DMNFEELHelper.java @@ -21,7 +21,9 @@ import org.kie.dmn.core.util.Msg; import org.kie.dmn.core.util.MsgUtil; import org.kie.dmn.feel.FEEL; +import org.kie.dmn.feel.codegen.feel11.CompiledFEELSupport; import org.kie.dmn.feel.codegen.feel11.CompilerBytecodeLoader; +import org.kie.dmn.feel.codegen.feel11.DirectCompilerResult; import org.kie.dmn.feel.codegen.feel11.DirectCompilerVisitor; import org.kie.dmn.feel.lang.CompiledExpression; import org.kie.dmn.feel.lang.CompilerContext; @@ -30,6 +32,7 @@ import org.kie.dmn.feel.lang.impl.EvaluationContextImpl; import org.kie.dmn.feel.lang.impl.FEELEventListenersManager; import org.kie.dmn.feel.lang.impl.FEELImpl; +import org.kie.dmn.feel.lang.types.BuiltInType; import org.kie.dmn.feel.parser.feel11.FEELParser; import org.kie.dmn.feel.parser.feel11.FEEL_1_1Parser; import org.kie.dmn.feel.runtime.FEELFunction; @@ -249,14 +252,30 @@ public Queue getFeelEvents() { public String getSourceForUnaryTest(String packageName, String className, String input, DMNCompilerContext ctx, Type columntype) { Map variableTypes = new HashMap<>(); for ( Map.Entry entry : ctx.getVariables().entrySet() ) { - variableTypes.put( entry.getKey(), ((BaseDMNTypeImpl) entry.getValue()).getFeelType() ); + variableTypes.put( entry.getKey(), dmnToFeelType((BaseDMNTypeImpl) entry.getValue()) ); } variableTypes.put( "?", columntype ); - FEEL_1_1Parser parser = FEELParser.parse(null, input, variableTypes, Collections.emptyMap(), (( FEELImpl ) feel).getCustomFunctions(), Collections.emptyList()); - ParseTree tree = parser.expressionList(); - DirectCompilerVisitor v = new DirectCompilerVisitor(variableTypes, true); - return new CompilerBytecodeLoader().getSourceForUnaryTest(packageName, className, input, v.visit(tree)); + FEELEventListenersManager manager = new FEELEventListenersManager(); + CompiledFEELSupport.SyntaxErrorListener errorListener = new CompiledFEELSupport.SyntaxErrorListener(); + manager.addListener(errorListener); + FEEL_1_1Parser parser = FEELParser.parse( + manager, input, variableTypes, Collections.emptyMap(), (( FEELImpl ) feel).getCustomFunctions(), Collections.emptyList()); + ParseTree tree = parser.unaryTestsRoot(); + DirectCompilerResult result; + if (errorListener.isError()) { + result = CompiledFEELSupport.compiledErrorUnaryTest(errorListener.event().getMessage()); + } else { + DirectCompilerVisitor v = new DirectCompilerVisitor(variableTypes, true); + result = v.visit(tree); + } + return new CompilerBytecodeLoader().getSourceForUnaryTest(packageName, className, input, result); + } + + + public static Type dmnToFeelType(BaseDMNTypeImpl v) { + if (v.isCollection()) return BuiltInType.LIST; + else return v.getFeelType(); } public EvaluationContextImpl newEvaluationContext( Collection listeners, Map inputVariables) { diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/execmodelbased/AbstractModelEvaluator.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/execmodelbased/AbstractModelEvaluator.java index 6262e18a91b..6b3c6545525 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/execmodelbased/AbstractModelEvaluator.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/execmodelbased/AbstractModelEvaluator.java @@ -106,7 +106,10 @@ public EvaluatorResult evaluate( DMNRuntimeEventManager eventManager, DMNResult eventResults = processEvents(events, eventManager, ( DMNResultImpl ) dmnResult, node); - return new EvaluatorResultImpl( result, EvaluatorResult.ResultType.SUCCESS ); + return new EvaluatorResultImpl(result, + eventResults.hasErrors? + EvaluatorResult.ResultType.FAILURE : + EvaluatorResult.ResultType.SUCCESS ); } catch (RuntimeException e) { logger.error(e.toString(), e); throw e; diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/execmodelbased/DecisionTableEvaluator.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/execmodelbased/DecisionTableEvaluator.java index 4cf8e0bb929..e0fd7e03ac9 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/execmodelbased/DecisionTableEvaluator.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/execmodelbased/DecisionTableEvaluator.java @@ -54,7 +54,7 @@ private Object[] initInputs(DMNFEELHelper feel) { columnEvalCtxs[i] = feel.newEvaluationContext( Collections.singletonList( events::add ), evalCtx.getAllValues()); columnEvalCtxs[i].enterFrame(); - columnEvalCtxs[i].setValue( "?", inputs[i] ); + columnEvalCtxs[i].setValue( "?", result ); } return inputs; } diff --git a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNDecisionTableRuntimeTest.java b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNDecisionTableRuntimeTest.java index 53fd4b8b90b..c2c87b6878e 100644 --- a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNDecisionTableRuntimeTest.java +++ b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNDecisionTableRuntimeTest.java @@ -56,7 +56,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.internal.verification.VerificationModeFactory.times; -public class DMNDecisionTableRuntimeTest { +public class DMNDecisionTableRuntimeTest extends BaseInterpretedVsCompiledTest { + + public DMNDecisionTableRuntimeTest(boolean useExecModelCompiler) { + super(useExecModelCompiler); + } @Test public void testDecisionTableWithCalculatedResult() { diff --git a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNRuntimeTest.java b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNRuntimeTest.java index 4db6f50b816..5ce20191656 100644 --- a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNRuntimeTest.java +++ b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNRuntimeTest.java @@ -92,7 +92,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.internal.verification.VerificationModeFactory.times; -public class DMNRuntimeTest { +public class DMNRuntimeTest extends BaseInterpretedVsCompiledTest { + + public DMNRuntimeTest(boolean useExecModelCompiler) { + super(useExecModelCompiler); + } public static final Logger LOG = LoggerFactory.getLogger(DMNRuntimeTest.class); diff --git a/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/WrongConstraintsInItemDefinition.dmn b/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/WrongConstraintsInItemDefinition.dmn index e6384a63173..c15ae5e46d5 100644 --- a/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/WrongConstraintsInItemDefinition.dmn +++ b/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/WrongConstraintsInItemDefinition.dmn @@ -27,7 +27,7 @@ - - - + "DEFAULT" diff --git a/kie-dmn/kie-dmn-feel/src/main/antlr4/org/kie/dmn/feel/parser/feel11/FEEL_1_1.g4 b/kie-dmn/kie-dmn-feel/src/main/antlr4/org/kie/dmn/feel/parser/feel11/FEEL_1_1.g4 index 4a89bd17da1..8f524431192 100644 --- a/kie-dmn/kie-dmn-feel/src/main/antlr4/org/kie/dmn/feel/parser/feel11/FEEL_1_1.g4 +++ b/kie-dmn/kie-dmn-feel/src/main/antlr4/org/kie/dmn/feel/parser/feel11/FEEL_1_1.g4 @@ -208,8 +208,7 @@ comparisonExpression relationalExpression : additiveExpression #relExpressionAdd | val=relationalExpression between_key start=additiveExpression and_key end=additiveExpression #relExpressionBetween - | val=relationalExpression in_key '(' expressionList ')' #relExpressionValueList - | val=relationalExpression in_key '(' simpleUnaryTests ')' #relExpressionTestList + | val=relationalExpression in_key '(' positiveUnaryTests ')' #relExpressionTestList | val=relationalExpression in_key expression #relExpressionValue // includes simpleUnaryTest | val=relationalExpression instance_key of_key type #relExpressionInstanceOf ; @@ -247,9 +246,7 @@ unaryExpression ; unaryExpressionNotPlusMinus - : not_key '(' simpleUnaryTests ')' #negatedUnaryTests - | not_key unaryExpression #logicalNegation - | primary ('.' {helper.recoverScope();helper.enableDynamicResolution();} qualifiedName parameters? {helper.disableDynamicResolution();helper.dismissScope();} )? #uenpmPrimary + : primary ('.' {helper.recoverScope();helper.enableDynamicResolution();} qualifiedName parameters? {helper.disableDynamicResolution();helper.dismissScope();} )? #uenpmPrimary ; primary @@ -261,7 +258,7 @@ primary | list #primaryList | context #primaryContext | '(' expression ')' #primaryParens - | simpleUnaryTest #primaryUnaryTest + | simplePositiveUnaryTest #primaryUnaryTest | qualifiedName parameters? #primaryName ; @@ -282,13 +279,9 @@ booleanLiteral /************************** * OTHER CONSTRUCTS **************************/ -// #13 -simpleUnaryTests - : (simpleUnaryTest|primary) ( ',' (simpleUnaryTest|primary) )* - ; // #7 -simpleUnaryTest +simplePositiveUnaryTest : op='<' {helper.enableDynamicResolution();} endpoint {helper.disableDynamicResolution();} #positiveUnaryTestIneq | op='>' {helper.enableDynamicResolution();} endpoint {helper.disableDynamicResolution();} #positiveUnaryTestIneq | op='<=' {helper.enableDynamicResolution();} endpoint {helper.disableDynamicResolution();} #positiveUnaryTestIneq @@ -296,8 +289,43 @@ simpleUnaryTest | op='=' {helper.enableDynamicResolution();} endpoint {helper.disableDynamicResolution();} #positiveUnaryTestIneq | op='!=' {helper.enableDynamicResolution();} endpoint {helper.disableDynamicResolution();} #positiveUnaryTestIneq | interval #positiveUnaryTestInterval - | null_key #positiveUnaryTestNull - | '-' #positiveUnaryTestDash + ; + + +// #13 +simplePositiveUnaryTests + : simplePositiveUnaryTest ( ',' simplePositiveUnaryTest )* + ; + + +// #14 +simpleUnaryTests + : simplePositiveUnaryTests #positiveSimplePositiveUnaryTests + | not_key '(' simplePositiveUnaryTests ')' #negatedSimplePositiveUnaryTests + | '-' #positiveUnaryTestDash + ; + +// #15 +positiveUnaryTest + : expression + ; + +// #16 +positiveUnaryTests + : positiveUnaryTest ( ',' positiveUnaryTest )* + ; + + +unaryTestsRoot + : unaryTests EOF + ; + +// #17 (root for decision tables) +unaryTests + : + not_key '(' positiveUnaryTests ')' #unaryTests_negated + | positiveUnaryTests #unaryTests_positive + | '-' #unaryTests_empty ; // #18 @@ -338,11 +366,13 @@ qualifiedName ; nameRef - : st=Identifier { helper.startVariable( $st ); } nameRefOtherToken* + : ( st=Identifier { helper.startVariable( $st ); } + | not_st=NOT { helper.startVariable( $not_st ); } + ) nameRefOtherToken* ; -nameRefOtherToken - : { helper.followUp( _input.LT(1), _localctx==null ) }? ~('('|')'|'['|']'|'{'|'}'|'>'|'<'|'='|'!') +nameRefOtherToken // added some special cases here: we should rework a bit the lexing part, though --ev + : { helper.followUp( _input.LT(1), _localctx==null ) }? ~('('|')'|'['|']'|'{'|'}'|'>'|'<'|'='|'!'|'/'|'*'|'in'|',') ; /******************************** @@ -436,7 +466,7 @@ between_key ; not_key - : 'not' + : NOT ; null_key @@ -673,6 +703,10 @@ SUB : '-'; MUL : '*'; DIV : '/'; +NOT + : 'not' + ; + Identifier : JavaLetter JavaLetterOrDigit* ; diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompiledFEELSemanticMappings.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompiledFEELSemanticMappings.java index de7b5210b70..9bcde4a5e64 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompiledFEELSemanticMappings.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompiledFEELSemanticMappings.java @@ -173,7 +173,21 @@ private static Comparable asComparable(Object s) { throw new IllegalArgumentException("Unable to transform s " + s + "as Comparable"); } } - + + public static Boolean coerceToBoolean(EvaluationContext ctx, Object value) { + if (value instanceof Boolean) return (Boolean) value; + + ctx.notifyEvt( () -> new ASTEventBase( + FEELEvent.Severity.ERROR, + Msg.createMessage( + Msg.X_TYPE_INCOMPATIBLE_WITH_Y_TYPE, + value == null? "null" : value.getClass(), + "Boolean"), + null)); + + return null; + } + public static T coerceTo(Class paramType, Object value) { Object actual; if( paramType.isAssignableFrom( value.getClass() ) ) { @@ -346,6 +360,14 @@ public static Boolean eq(Object left, Object right) { return EvalHelper.isEqual(left, right, null); } + public static Boolean gracefulEq(EvaluationContext ctx, Object left, Object right) { + if (left instanceof List) { + return ((List) left).contains(right); + } else { + return eq(left, right); + } + } + public static Boolean between(EvaluationContext ctx, Object value, Object start, Object end) { if ( value == null ) { ctx.notifyEvt(() -> new ASTEventBase(FEELEvent.Severity.ERROR, Msg.createMessage(Msg.IS_NULL, "value"), null) ); return null; } diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompiledFEELSupport.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompiledFEELSupport.java index 0643d1878b1..1d646840805 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompiledFEELSupport.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompiledFEELSupport.java @@ -25,17 +25,47 @@ import java.util.Map; import java.util.function.Function; +import org.antlr.v4.runtime.tree.ParseTree; +import org.drools.javaparser.JavaParser; +import org.drools.javaparser.ast.Modifier; +import org.drools.javaparser.ast.NodeList; +import org.drools.javaparser.ast.body.FieldDeclaration; +import org.drools.javaparser.ast.body.Parameter; +import org.drools.javaparser.ast.body.VariableDeclarator; +import org.drools.javaparser.ast.expr.BooleanLiteralExpr; +import org.drools.javaparser.ast.expr.LambdaExpr; +import org.drools.javaparser.ast.expr.MethodCallExpr; +import org.drools.javaparser.ast.expr.NameExpr; +import org.drools.javaparser.ast.expr.NullLiteralExpr; +import org.drools.javaparser.ast.expr.StringLiteralExpr; +import org.drools.javaparser.ast.stmt.BlockStmt; +import org.drools.javaparser.ast.stmt.ExpressionStmt; +import org.drools.javaparser.ast.stmt.ReturnStmt; +import org.drools.javaparser.ast.stmt.Statement; +import org.drools.javaparser.ast.type.UnknownType; +import org.kie.dmn.api.feel.runtime.events.FEELEvent; import org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity; +import org.kie.dmn.api.feel.runtime.events.FEELEventListener; +import org.kie.dmn.feel.lang.CompiledExpression; +import org.kie.dmn.feel.lang.CompilerContext; import org.kie.dmn.feel.lang.EvaluationContext; +import org.kie.dmn.feel.lang.ast.BaseNode; import org.kie.dmn.feel.lang.ast.ForExpressionNode; import org.kie.dmn.feel.lang.ast.ForExpressionNode.ForIteration; import org.kie.dmn.feel.lang.ast.QuantifiedExpressionNode; import org.kie.dmn.feel.lang.ast.QuantifiedExpressionNode.QEIteration; import org.kie.dmn.feel.lang.ast.QuantifiedExpressionNode.Quantifier; +import org.kie.dmn.feel.lang.impl.CompiledExpressionImpl; import org.kie.dmn.feel.lang.impl.SilentWrappingEvaluationContextImpl; +import org.kie.dmn.feel.lang.types.BuiltInType; +import org.kie.dmn.feel.parser.feel11.ASTBuilderVisitor; +import org.kie.dmn.feel.parser.feel11.FEELParser; +import org.kie.dmn.feel.parser.feel11.FEEL_1_1Parser; import org.kie.dmn.feel.runtime.FEELFunction; import org.kie.dmn.feel.runtime.UnaryTest; import org.kie.dmn.feel.runtime.events.ASTEventBase; +import org.kie.dmn.feel.runtime.events.InvalidParametersEvent; +import org.kie.dmn.feel.runtime.events.SyntaxErrorEvent; import org.kie.dmn.feel.util.EvalHelper; import org.kie.dmn.feel.util.Msg; @@ -424,4 +454,73 @@ public static Object notifyCompilationError(EvaluationContext feelExprCtx, Strin public static Object coerceNumber(Object value) { return EvalHelper.coerceNumber(value); } + + + /** + * Generates a compilable class that reports a (compile-time) error at runtime + */ + public static CompiledFEELExpression compiledError(String expression, String msg) { + return new CompilerBytecodeLoader() + .makeFromJPExpression( + expression, + compiledErrorExpression(msg), + Collections.emptySet()); + } + + public static DirectCompilerResult compiledErrorUnaryTest(String msg) { + + LambdaExpr initializer = new LambdaExpr(); + initializer.setEnclosingParameters(true); + initializer.addParameter(new Parameter(new UnknownType(), "feelExprCtx")); + initializer.addParameter(new Parameter(new UnknownType(), "left")); + Statement lambdaBody = new BlockStmt(new NodeList<>( + new ExpressionStmt(compiledErrorExpression(msg)), + new ReturnStmt(new BooleanLiteralExpr(false)) + )); + initializer.setBody(lambdaBody); + String constantName = "UT_EMPTY"; + VariableDeclarator vd = new VariableDeclarator(JavaParser.parseClassOrInterfaceType(UnaryTest.class.getCanonicalName()), constantName); + vd.setInitializer(initializer); + FieldDeclaration fd = new FieldDeclaration(); + fd.setModifier(Modifier.PUBLIC, true); + fd.setModifier(Modifier.STATIC, true); + fd.setModifier(Modifier.FINAL, true); + fd.addVariable(vd); + + fd.setJavadocComment(" FEEL unary test: - "); + + MethodCallExpr list = new MethodCallExpr(null, "list", new NodeList<>(new NameExpr(constantName))); + + DirectCompilerResult directCompilerResult = DirectCompilerResult.of(list, BuiltInType.LIST); + directCompilerResult.addFieldDesclaration(fd); + return directCompilerResult; + + } + + + public static MethodCallExpr compiledErrorExpression(String msg) { + return new MethodCallExpr( + new NameExpr("CompiledFEELSupport"), + "notifyCompilationError", + new NodeList<>( + new NameExpr("feelExprCtx"), + new StringLiteralExpr(msg))); + } + + // thread-unsafe, but this is single-threaded so it's ok + public static class SyntaxErrorListener implements FEELEventListener { + private FEELEvent event = null; + @Override + public void onEvent(FEELEvent evt) { + if (evt instanceof SyntaxErrorEvent + || evt instanceof InvalidParametersEvent) { + this.event = evt; + } + } + public boolean isError() { return event != null; } + public FEELEvent event() { return event; } + } + + + } diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompilerBytecodeLoader.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompilerBytecodeLoader.java index 1d34dd837ed..f8ff9b200a2 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompilerBytecodeLoader.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/codegen/feel11/CompilerBytecodeLoader.java @@ -154,12 +154,21 @@ private CompilationUnit getCompilationUnitForUnaryTests( Class clazz, Str } ClassOrInterfaceDeclaration classDecl = classDecls.get(0); - fieldDeclarations.stream().sorted(new SortFieldDeclarationStrategy()).forEach(classDecl::addMember); + fieldDeclarations.stream() + .filter(fd -> !isUnaryTest(fd)) + .sorted(new SortFieldDeclarationStrategy()).forEach(classDecl::addMember); + fieldDeclarations.stream() + .filter(fd -> fd.getVariable(0).getName().asString().startsWith("UT")) + .sorted(new SortFieldDeclarationStrategy()).forEach(classDecl::addMember); LOG.debug("{}", cu); return cu; } + private boolean isUnaryTest(FieldDeclaration fd) { + return fd.getVariable(0).getName().asString().startsWith("UT"); + } + private String generateRandomPackage() { String uuid = UUID.randomUUID().toString().replaceAll("-", ""); return this.getClass().getPackage().getName() + ".gen" + uuid; @@ -171,7 +180,9 @@ private static class SortFieldDeclarationStrategy implements Comparator inputTypes) { * or ranges, they need to be converted into an equality test unary expression. * This way, we have to compile and check the low level AST nodes to properly * deal with this case - * * @param replaceEqualForUnaryTest use `true` to obtain the behavior described. */ public DirectCompilerVisitor(Map inputTypes, boolean replaceEqualForUnaryTest) { @@ -205,7 +209,7 @@ public DirectCompilerResult visitBooleanLiteral(FEEL_1_1Parser.BooleanLiteralCon result = new BooleanLiteralExpr(false); break; default: - throw new IllegalArgumentException("Reached for a boolean literal but was: "+literalText); + throw new IllegalArgumentException("Reached for a boolean literal but was: " + literalText); } return DirectCompilerResult.of(result, BuiltInType.BOOLEAN); } @@ -227,6 +231,13 @@ public DirectCompilerResult visitSignedUnaryExpressionPlus(FEEL_1_1Parser.Signed return unaryExpr; } +// FIXME +// @Override +// public DirectCompilerResult visitNonSignedUnaryExpression(FEEL_1_1Parser.NonSignedUnaryExpressionContext ctx) { +// DirectCompilerResult directCompilerResult = super.visitNonSignedUnaryExpression(ctx); +// return directCompilerResult; +// } + @Override public DirectCompilerResult visitNullLiteral(FEEL_1_1Parser.NullLiteralContext ctx) { NullLiteralExpr result = new NullLiteralExpr(); @@ -250,19 +261,6 @@ public DirectCompilerResult visitPrimaryParens(FEEL_1_1Parser.PrimaryParensConte return DirectCompilerResult.of(result, expr.resultType, expr.getFieldDeclarations()); } - @Override - public DirectCompilerResult visitLogicalNegation(FEEL_1_1Parser.LogicalNegationContext ctx) { - DirectCompilerResult unary = visit(ctx.unaryExpression()); - - // FEEL spec Table 39: Semantics of negation - // this is actually not delegated to the builtin FEEL function not(), but doesn't look like a problem for visitLogicalNegation. - if (unary.resultType == BuiltInType.BOOLEAN) { - return DirectCompilerResult.of(new UnaryExpr(unary.getExpression(), UnaryExpr.Operator.LOGICAL_COMPLEMENT), BuiltInType.BOOLEAN, unary.getFieldDeclarations()); - } else { - return DirectCompilerResult.of(new NullLiteralExpr(), BuiltInType.UNKNOWN, unary.getFieldDeclarations()); - } - } - @Override public DirectCompilerResult visitPowExpression(FEEL_1_1Parser.PowExpressionContext ctx) { DirectCompilerResult left = visit(ctx.powerExpression()); @@ -467,24 +465,7 @@ public DirectCompilerResult visitExpressionList(FEEL_1_1Parser.ExpressionListCon if (ctx.getChild(i) instanceof FEEL_1_1Parser.ExpressionContext) { FEEL_1_1Parser.ExpressionContext childCtx = (FEEL_1_1Parser.ExpressionContext) ctx.getChild(i); DirectCompilerResult child = visit(childCtx); - - if (!replaceEqualForUnaryTest) { - // we are NOT compiling unary test, so we continue as-is - exprs.add(child); - } else { - if (child.resultType == BuiltInType.UNARY_TEST) { - // is already a unary test, so we can add it as-is - exprs.add(child); - } else if (child.resultType == BuiltInType.RANGE) { - // being a range, need the `in` operator. - DirectCompilerResult replaced = createUnaryTestExpression(childCtx, child, UnaryOperator.IN); - exprs.add(replaced); - } else { - // implied a unarytest for the `=` equal operator. - DirectCompilerResult replaced = createUnaryTestExpression(childCtx, child, UnaryOperator.EQ); - exprs.add(replaced); - } - } + exprs.add(child); } } MethodCallExpr list = new MethodCallExpr(null, "list"); @@ -493,25 +474,119 @@ public DirectCompilerResult visitExpressionList(FEEL_1_1Parser.ExpressionListCon } @Override - public DirectCompilerResult visitRelExpressionValueList(FEEL_1_1Parser.RelExpressionValueListContext ctx) { - DirectCompilerResult value = visit(ctx.val); - DirectCompilerResult list = visit(ctx.expressionList()); + public DirectCompilerResult visitUnaryTestsRoot(FEEL_1_1Parser.UnaryTestsRootContext ctx) { + return visit(ctx.unaryTests()); + } + + @Override + public DirectCompilerResult visitUnaryTests_empty(FEEL_1_1Parser.UnaryTests_emptyContext ctx) { + DirectCompilerResult t = createEmptyUnaryTestExpression(); + MethodCallExpr list = new MethodCallExpr( + null, "list", new NodeList<>(t.getExpression())); + return DirectCompilerResult.of(list, BuiltInType.LIST, t.getFieldDeclarations()); + } + + @Override + public DirectCompilerResult visitUnaryTests_negated(FEEL_1_1Parser.UnaryTests_negatedContext ctx) { + DirectCompilerResult positiveTests = doPositiveUnaryTests(ctx.positiveUnaryTests()); + DirectCompilerResult result = createUnaryTestExpression(ctx, positiveTests, UnaryOperator.NOT); MethodCallExpr expression = new MethodCallExpr( null, - "exists", - new NodeList<>( - new NameExpr("feelExprCtx"), - list.getExpression(), - value.getExpression())); + "list", + new NodeList<>(result.getExpression())); - return DirectCompilerResult.of( - expression, - BuiltInType.UNARY_TEST, - mergeFDs(value, list)); + return DirectCompilerResult.of(expression, BuiltInType.UNARY_TEST, result.getFieldDeclarations()); + } + + @Override + public DirectCompilerResult visitUnaryTests_positive(FEEL_1_1Parser.UnaryTests_positiveContext ctx) { + ArrayList rs = new ArrayList<>(); + return doPositiveUnaryTests(ctx.positiveUnaryTests()); + } + + private DirectCompilerResult doPositiveUnaryTests(FEEL_1_1Parser.PositiveUnaryTestsContext ctx) { + ArrayList rs = new ArrayList<>(); + // fixme: + // this should be handled as a field in the DirectCompilerResult class, + // but for now it is cumbersome to bring it up the tree, let's remember to do this refactoring + for (FEEL_1_1Parser.PositiveUnaryTestContext positiveUnaryTestContext : ctx.positiveUnaryTest()) { + this.subExpressionContainsWildcard = false; + DirectCompilerResult result = visit(positiveUnaryTestContext); + if (result.resultType == BuiltInType.UNARY_TEST) { + rs.add(result); + } else if (this.subExpressionContainsWildcard) { + DirectCompilerResult unaryTestExpression = createWildcardUnaryTestExpression(positiveUnaryTestContext, result); + rs.add(unaryTestExpression); + } else if (result.resultType == BuiltInType.RANGE) { + // being a range, need the `in` operator. + DirectCompilerResult replaced = createUnaryTestExpression(positiveUnaryTestContext, result, UnaryOperator.IN); + rs.add(replaced); + } else if (result.resultType == BuiltInType.LIST) { + DirectCompilerResult replaced = createListUnaryTestExpression(positiveUnaryTestContext, result); + rs.add(replaced); + } else { + DirectCompilerResult unaryTestExpression = createRuntimeUnaryTestExpression(positiveUnaryTestContext, result); + rs.add(unaryTestExpression); + + } + + } + MethodCallExpr expression = + new MethodCallExpr( + null, + "list", + new NodeList<>(rs.stream().map(DirectCompilerResult::getExpression).collect(Collectors.toList()))); + return DirectCompilerResult.of(expression, BuiltInType.UNARY_TEST, DirectCompilerResult.mergeFDs(rs)); } + @Override + public DirectCompilerResult visitPositiveUnaryTests(FEEL_1_1Parser.PositiveUnaryTestsContext ctx) { + List rs = new ArrayList<>(); + for (FEEL_1_1Parser.PositiveUnaryTestContext positiveUnaryTestContext : ctx.positiveUnaryTest()) { + DirectCompilerResult result = visit(positiveUnaryTestContext); + if (result.resultType == BuiltInType.UNARY_TEST) { + rs.add(result); + } + else if (result.resultType == BuiltInType.RANGE) { + // being a range, need the `in` operator. + DirectCompilerResult replaced = createUnaryTestExpression(positiveUnaryTestContext, result, UnaryOperator.IN); + rs.add(replaced); + } else if (result.resultType == BuiltInType.LIST) { + DirectCompilerResult replaced = createListUnaryTestExpression(positiveUnaryTestContext, result); + rs.add(replaced); + } else { + DirectCompilerResult unaryTestExpression = createUnaryTestExpression(positiveUnaryTestContext, result, UnaryOperator.EQ); + rs.add(unaryTestExpression); + } + rs.add(result); + } + MethodCallExpr expression = + new MethodCallExpr( + null, + "list", + new NodeList<>(rs.stream().map(DirectCompilerResult::getExpression).collect(Collectors.toList()))); + return DirectCompilerResult.of(expression, BuiltInType.UNARY_TEST, DirectCompilerResult.mergeFDs(rs)); + } + +// @Override +// public DirectCompilerResult visitPositiveUnaryTest(FEEL_1_1Parser.PositiveUnaryTestContext positiveUnaryTestContext) { +// DirectCompilerResult result = visit(positiveUnaryTestContext.expression()); +// if (result.resultType == BuiltInType.UNARY_TEST) { +// return result; +//// } else if (result.resultType == BuiltInType.BOOLEAN) { //fixme TEMP +//// return result; +// } else if (result.resultType == BuiltInType.RANGE) { +// // being a range, need the `in` operator. +// return createUnaryTestExpression(positiveUnaryTestContext, result, UnaryOperator.IN); +// } else if (result.resultType == BuiltInType.LIST) { +// return createListUnaryTestExpression(positiveUnaryTestContext, result); +// } else { +// return createUnaryTestExpression(positiveUnaryTestContext, result, UnaryOperator.EQ); +// } +// } + @Override public DirectCompilerResult visitInterval(FEEL_1_1Parser.IntervalContext ctx) { DirectCompilerResult start = visit(ctx.start); @@ -567,7 +642,6 @@ public DirectCompilerResult visitInterval(FEEL_1_1Parser.IntervalContext ctx) { BuiltInType.RANGE, DirectCompilerResult.mergeFDs(start, end)); } - } private FieldDeclaration fieldDeclarationOf(String prefix, String originalText, Expression initializer) { @@ -605,7 +679,6 @@ public DirectCompilerResult visitPositiveUnaryTestIneq(FEEL_1_1Parser.PositiveUn * Create a DirectCompilerResult for an equivalent expression representing a Unary test. * That means the resulting expression is the name of the unary test, * which is referring to a FieldDeclaration, for a class field member using said name, of type UnaryTest and as value a lambda expression of a unarytest - * * @param ctx mainly used to retrieve original text information (used to build the FieldDeclaration javadoc of the original FEEL text representation) * @param endpoint the right of the unary test * @param op the operator of the unarytest @@ -702,33 +775,154 @@ private DirectCompilerResult createUnaryTestExpression(ParserRuleContext ctx, Di return directCompilerResult; } + private DirectCompilerResult createRuntimeUnaryTestExpression(FEEL_1_1Parser.PositiveUnaryTestContext ctx, DirectCompilerResult endpoint) { + String originalText = ParserHelper.getOriginalText(ctx); + + LambdaExpr initializer = new LambdaExpr(); + initializer.setEnclosingParameters(true); + initializer.addParameter(new Parameter(new UnknownType(), "feelExprCtx")); + initializer.addParameter(new Parameter(new UnknownType(), "left")); + Statement lambdaBody = null; + + MethodCallExpr expression = new MethodCallExpr(null, "gracefulEq"); + expression.addArgument(new NameExpr("feelExprCtx")); + expression.addArgument(endpoint.getExpression()); + expression.addArgument(new NameExpr("left")); + lambdaBody = new ExpressionStmt(expression); + + initializer.setBody(lambdaBody); + String constantName = "UT_" + CodegenStringUtil.escapeIdentifier(originalText); + VariableDeclarator vd = new VariableDeclarator(JavaParser.parseClassOrInterfaceType(UnaryTest.class.getCanonicalName()), constantName); + vd.setInitializer(initializer); + FieldDeclaration fd = new FieldDeclaration(); + fd.setModifier(Modifier.PUBLIC, true); + fd.setModifier(Modifier.STATIC, true); + fd.setModifier(Modifier.FINAL, true); + fd.addVariable(vd); + + fd.setJavadocComment(" FEEL unary test: " + originalText + " "); + + DirectCompilerResult directCompilerResult = DirectCompilerResult.of(new NameExpr(constantName), BuiltInType.UNARY_TEST, endpoint.getFieldDeclarations()); + directCompilerResult.addFieldDesclaration(fd); + return directCompilerResult; + } + + + + private DirectCompilerResult createListUnaryTestExpression(ParserRuleContext ctx, DirectCompilerResult endpoint) { + String originalText = ParserHelper.getOriginalText(ctx); + + LambdaExpr initializer = new LambdaExpr(); + initializer.setEnclosingParameters(true); + initializer.addParameter(new Parameter(new UnknownType(), "feelExprCtx")); + initializer.addParameter(new Parameter(new UnknownType(), "left")); + Statement lambdaBody = null; + + MethodCallExpr expression = new MethodCallExpr( + new EnclosedExpr(new CastExpr(TYPE_LIST, endpoint.getExpression())), + "contains"); + expression.addArgument(new NameExpr("left")); + lambdaBody = new ExpressionStmt(expression); + + initializer.setBody(lambdaBody); + String constantName = "UT_" + CodegenStringUtil.escapeIdentifier(originalText); + VariableDeclarator vd = new VariableDeclarator(JavaParser.parseClassOrInterfaceType(UnaryTest.class.getCanonicalName()), constantName); + vd.setInitializer(initializer); + FieldDeclaration fd = new FieldDeclaration(); + fd.setModifier(Modifier.PUBLIC, true); + fd.setModifier(Modifier.STATIC, true); + fd.setModifier(Modifier.FINAL, true); + fd.addVariable(vd); + + fd.setJavadocComment(" FEEL unary test: " + originalText + " "); + + DirectCompilerResult directCompilerResult = DirectCompilerResult.of(new NameExpr(constantName), BuiltInType.UNARY_TEST, endpoint.getFieldDeclarations()); + directCompilerResult.addFieldDesclaration(fd); + return directCompilerResult; + } + + private DirectCompilerResult createWildcardUnaryTestExpression(ParserRuleContext ctx, DirectCompilerResult endpoint) { + String originalText = ParserHelper.getOriginalText(ctx); + + LambdaExpr initializer = new LambdaExpr(); + initializer.setEnclosingParameters(true); + initializer.addParameter(new Parameter(new UnknownType(), "feelExprCtx")); + initializer.addParameter(new Parameter(new UnknownType(), "left")); + Statement lambdaBody = null; + + lambdaBody = new ExpressionStmt(new MethodCallExpr( + null, + "coerceToBoolean", + new NodeList<>( + new NameExpr("feelExprCtx"), + endpoint.getExpression()))); + + initializer.setBody(lambdaBody); + String constantName = "UT_" + CodegenStringUtil.escapeIdentifier(originalText); + VariableDeclarator vd = new VariableDeclarator(JavaParser.parseClassOrInterfaceType(UnaryTest.class.getCanonicalName()), constantName); + vd.setInitializer(initializer); + FieldDeclaration fd = new FieldDeclaration(); + fd.setModifier(Modifier.PUBLIC, true); + fd.setModifier(Modifier.STATIC, true); + fd.setModifier(Modifier.FINAL, true); + fd.addVariable(vd); + + fd.setJavadocComment(" FEEL unary test: " + originalText + " "); + + DirectCompilerResult directCompilerResult = DirectCompilerResult.of(new NameExpr(constantName), BuiltInType.UNARY_TEST, endpoint.getFieldDeclarations()); + directCompilerResult.addFieldDesclaration(fd); + return directCompilerResult; + } + + + private DirectCompilerResult createEmptyUnaryTestExpression() { + LambdaExpr initializer = new LambdaExpr(); + initializer.setEnclosingParameters(true); + initializer.addParameter(new Parameter(new UnknownType(), "feelExprCtx")); + initializer.addParameter(new Parameter(new UnknownType(), "left")); + Statement lambdaBody = new ExpressionStmt(new BooleanLiteralExpr(true)); + initializer.setBody(lambdaBody); + String constantName = "UT_EMPTY"; + VariableDeclarator vd = new VariableDeclarator(JavaParser.parseClassOrInterfaceType(UnaryTest.class.getCanonicalName()), constantName); + vd.setInitializer(initializer); + FieldDeclaration fd = new FieldDeclaration(); + fd.setModifier(Modifier.PUBLIC, true); + fd.setModifier(Modifier.STATIC, true); + fd.setModifier(Modifier.FINAL, true); + fd.addVariable(vd); + + fd.setJavadocComment(" FEEL unary test: - "); + + DirectCompilerResult directCompilerResult = DirectCompilerResult.of(new NameExpr(constantName), BuiltInType.UNARY_TEST); + directCompilerResult.addFieldDesclaration(fd); + return directCompilerResult; + } + @Override - public DirectCompilerResult visitSimpleUnaryTests(FEEL_1_1Parser.SimpleUnaryTestsContext ctx) { - List tests = - Stream.concat( - ctx.primary().stream(), - ctx.simpleUnaryTest().stream()) - .map(this::visit) - .collect(Collectors.toList()); - - MethodCallExpr testList = new MethodCallExpr( + public DirectCompilerResult visitRelExpressionTestList(FEEL_1_1Parser.RelExpressionTestListContext ctx) { + DirectCompilerResult relationalExpression = visit(ctx.relationalExpression()); + DirectCompilerResult unaryTests = visit(ctx.positiveUnaryTests()); + + MethodCallExpr expression = new MethodCallExpr( null, - "list", + "exists", new NodeList<>( - tests.stream() - .map(DirectCompilerResult::getExpression) - .collect(Collectors.toList()))); + new NameExpr("feelExprCtx"), + unaryTests.getExpression(), + relationalExpression.getExpression() + )); + return DirectCompilerResult.of( - testList, - BuiltInType.LIST, - mergeFDs(tests)); + expression, + BuiltInType.BOOLEAN, + mergeFDs(relationalExpression, unaryTests)); } // <(value.getExpression())); - } else if (expr.resultType.equals(BuiltInType.RANGE)){ + } else if (expr.resultType.equals(BuiltInType.RANGE)) { expression = new MethodCallExpr( - null, - "includes", - new NodeList<>( - new NameExpr("feelExprCtx"), - expr.getExpression(), - value.getExpression())); + null, + "includes", + new NodeList<>( + new NameExpr("feelExprCtx"), + expr.getExpression(), + value.getExpression())); } else { expression = new MethodCallExpr( - null, - "exists", - new NodeList<>( - new NameExpr("feelExprCtx"), - expr.getExpression(), - value.getExpression())); + null, + "exists", + new NodeList<>( + new NameExpr("feelExprCtx"), + expr.getExpression(), + value.getExpression())); } return DirectCompilerResult.of( expression, - BuiltInType.UNARY_TEST, + BuiltInType.BOOLEAN, mergeFDs(value, expr)); } - @Override - public DirectCompilerResult visitPositiveUnaryTestNull(FEEL_1_1Parser.PositiveUnaryTestNullContext ctx) { - return DirectCompilerResult.of(new NullLiteralExpr(), BuiltInType.UNKNOWN); - } - @Override public DirectCompilerResult visitPositiveUnaryTestDash(FEEL_1_1Parser.PositiveUnaryTestDashContext ctx) { return DirectCompilerResult.of(DASH_UNARY_TEST, BuiltInType.UNARY_TEST); @@ -968,11 +1157,9 @@ private DirectCompilerResult declareExternalFunction(FEEL_1_1Parser.FunctionDefi functionDefExpr.addArgument(new MethodCallExpr(new NameExpr("feelExprCtx"), "current")); DirectCompilerResult result = DirectCompilerResult.of(functionDefExpr, BuiltInType.FUNCTION); return result; - } else { throw new FEELCompilationError(Msg.createMessage(Msg.UNABLE_TO_FIND_EXTERNAL_FUNCTION_AS_DEFINED_BY, null)); } - } private DirectCompilerResult declareInternalFunction(ExpressionContext bodyCtx, FEEL_1_1Parser.FormalParametersContext parametersCtx) { @@ -1145,6 +1332,9 @@ private DirectCompilerResult visitQuantExpr(Expression quantOp, IterationContext public DirectCompilerResult visitNameRef(FEEL_1_1Parser.NameRefContext ctx) { String nameRefText = ParserHelper.getOriginalText(ctx); Type type = scopeHelper.resolveType(nameRefText).orElse(BuiltInType.UNKNOWN); + if (nameRefText.equals("?")) { + this.subExpressionContainsWildcard = true; + } NameExpr scope = new NameExpr("feelExprCtx"); MethodCallExpr getFromScope = new MethodCallExpr(scope, "getValue"); getFromScope.addArgument(new StringLiteralExpr(nameRefText)); @@ -1209,8 +1399,9 @@ public DirectCompilerResult visitParametersPositional(FEEL_1_1Parser.ParametersP @Override public DirectCompilerResult visitPrimaryName(FEEL_1_1Parser.PrimaryNameContext ctx) { - String originalTextOfName = ParserHelper.getOriginalText(ctx.qualifiedName()); DirectCompilerResult name = visit(ctx.qualifiedName()); + // fixme this should be handled in DirectCompilerResult but we should bring it up the entire tree. use global for now -ev +// this.subExpressionContainsWildcard = ctx.qualifiedName().n1.getText().equals("?"); if (ctx.parameters() != null) { return buildFunctionCall(ctx, name, ctx.parameters()); } else { @@ -1224,28 +1415,14 @@ public DirectCompilerResult visitPrimaryName(FEEL_1_1Parser.PrimaryNameContext c // } private DirectCompilerResult buildFunctionCall(ParserRuleContext ctx, DirectCompilerResult name, ParseTree params) { - String functionName = getFunctionName(name); - if ("not".equals(functionName)) { - return buildNotCall(ctx, name, params); - } else { - DirectCompilerResult parameters = visit(params); - MethodCallExpr invokeCall = new MethodCallExpr(new NameExpr(CompiledFEELSupport.class.getSimpleName()), "invoke"); - invokeCall.addArgument(new NameExpr("feelExprCtx")); - invokeCall.addArgument(name.getExpression()); - invokeCall.addArgument(parameters.getExpression()); - return DirectCompilerResult.of(invokeCall, name.resultType).withFD(name).withFD(parameters); - } - } - - private String getFunctionName(DirectCompilerResult name) { - Expression expression = name.getExpression(); - if (expression.toString().contains("not")) { - return ((NameExpr)expression).getName().asString(); - } - return null; + DirectCompilerResult parameters = visit(params); + MethodCallExpr invokeCall = new MethodCallExpr(new NameExpr(CompiledFEELSupport.class.getSimpleName()), "invoke"); + invokeCall.addArgument(new NameExpr("feelExprCtx")); + invokeCall.addArgument(name.getExpression()); + invokeCall.addArgument(parameters.getExpression()); + return DirectCompilerResult.of(invokeCall, name.resultType).withFD(name).withFD(parameters); } - private DirectCompilerResult buildNotCall(ParserRuleContext ctx, DirectCompilerResult name, ParseTree params) { if (params.getChildCount() == 1) { DirectCompilerResult parameter = visit(params.getChild(0)); @@ -1295,11 +1472,11 @@ public DirectCompilerResult visitFilterPathExpression(FEEL_1_1Parser.FilterPathE // filterWithCall.addArgument(filter.getExpression()); // } else // { - // Then is the case Table 54: Semantics of lists, ROW: e1 is a list and type(FEEL(e2 , s')) is boolean + // Then is the case Table 54: Semantics of lists, ROW: e1 is a list and type(FEEL(e2 , s')) is boolean // currently delegated to runtime instead: - Expression anonFunctionClass = anonFunctionEvaluationContext2Object(filter.getExpression()); - filterWithCall.addArgument(anonFunctionClass); + Expression anonFunctionClass = anonFunctionEvaluationContext2Object(filter.getExpression()); + filterWithCall.addArgument(anonFunctionClass); // } return DirectCompilerResult.of(filterWithCall, BuiltInType.UNKNOWN).withFD(expr).withFD(filter); } else if (ctx.qualifiedName() != null) { @@ -1363,10 +1540,4 @@ public DirectCompilerResult visitCompilation_unit(FEEL_1_1Parser.Compilation_uni return visit( ctx.expression() ); } - @Override - public DirectCompilerResult visitNegatedUnaryTests(FEEL_1_1Parser.NegatedUnaryTestsContext ctx) { - FEEL_1_1Parser.SimpleUnaryTestsContext child = (FEEL_1_1Parser.SimpleUnaryTestsContext) ctx.getChild(2); - DirectCompilerResult notExpr = DirectCompilerResult.of(new NameExpr("not"), BuiltInType.BOOLEAN); - return buildFunctionCall(ctx, notExpr, child); - } } diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InNode.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InNode.java index fe31c6f4cda..80c5a966f17 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InNode.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InNode.java @@ -55,6 +55,7 @@ public void setExprs(BaseNode exprs) { @Override public Boolean evaluate(EvaluationContext ctx) { + if (exprs == null) return null; Object value = this.value.evaluate( ctx ); Object expr = this.exprs.evaluate( ctx ); if ( expr != null ) { diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InfixOpNode.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InfixOpNode.java index 420af36da73..3d5cda0d126 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InfixOpNode.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InfixOpNode.java @@ -161,6 +161,7 @@ public Type getResultType() { @Override public Object evaluate(EvaluationContext ctx) { + if (left == null) return null; Object left = this.left.evaluate( ctx ); Object right = this.right.evaluate( ctx ); switch ( operator ) { diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/impl/FEELImpl.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/impl/FEELImpl.java index 02c0942f6ff..6a88bcaf0a0 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/impl/FEELImpl.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/impl/FEELImpl.java @@ -27,15 +27,11 @@ import java.util.Set; import org.antlr.v4.runtime.tree.ParseTree; -import org.drools.javaparser.ast.NodeList; import org.drools.javaparser.ast.expr.Expression; -import org.drools.javaparser.ast.expr.MethodCallExpr; -import org.drools.javaparser.ast.expr.NameExpr; -import org.drools.javaparser.ast.expr.StringLiteralExpr; -import org.kie.dmn.api.feel.runtime.events.FEELEvent; import org.kie.dmn.api.feel.runtime.events.FEELEventListener; import org.kie.dmn.feel.FEEL; import org.kie.dmn.feel.codegen.feel11.CompiledFEELExpression; +import org.kie.dmn.feel.codegen.feel11.CompiledFEELSupport; import org.kie.dmn.feel.codegen.feel11.CompilerBytecodeLoader; import org.kie.dmn.feel.codegen.feel11.DirectCompilerResult; import org.kie.dmn.feel.codegen.feel11.DirectCompilerVisitor; @@ -124,12 +120,12 @@ public CompiledExpression compile(String expression, CompilerContext ctx) { // Use JavaParser to translate FEEL to Java: Set listeners = new HashSet<>(ctx.getListeners()); // add listener to syntax errors, and save them - CompilerErrorListener errorListener = new CompilerErrorListener(); + CompiledFEELSupport.SyntaxErrorListener errorListener = new CompiledFEELSupport.SyntaxErrorListener(); listeners.add(errorListener); FEEL_1_1Parser parser = FEELParser.parse(getEventsManager(listeners), expression, ctx.getInputVariableTypes(), ctx.getInputVariables(), ctx.getFEELFunctions(), profiles); ParseTree tree = parser.compilation_unit(); - if (errorListener.evt != null) { - return compiledError(expression, errorListener.evt.getMessage()); + if (errorListener.isError()) { + return CompiledFEELSupport.compiledError(expression, errorListener.event().getMessage()); } try { DirectCompilerVisitor v = new DirectCompilerVisitor(ctx.getInputVariableTypes()); @@ -137,7 +133,7 @@ public CompiledExpression compile(String expression, CompilerContext ctx) { Expression expr = directResult.getExpression(); return new CompilerBytecodeLoader().makeFromJPExpression(expression, expr, directResult.getFieldDeclarations()); } catch (FEELCompilationError e) { - return compiledError(expression, e.getMessage()); + return CompiledFEELSupport.compiledError(expression, e.getMessage()); } } else { // "legacy" interpreted AST compilation: FEEL_1_1Parser parser = FEELParser.parse(getEventsManager(ctx.getListeners()), expression, ctx.getInputVariableTypes(), ctx.getInputVariables(), ctx.getFEELFunctions(), profiles); @@ -149,25 +145,9 @@ public CompiledExpression compile(String expression, CompilerContext ctx) { } } - /** - * Generates a compilable class that reports a (compile-time) error at runtime - */ - private CompiledFEELExpression compiledError(String expression, String msg) { - return new CompilerBytecodeLoader() - .makeFromJPExpression( - expression, - new MethodCallExpr( - new NameExpr("CompiledFEELSupport"), - "notifyCompilationError", - new NodeList<>( - new NameExpr("feelExprCtx"), - new StringLiteralExpr(msg))), - Collections.emptySet()); - } - public CompiledExpression compileExpressionList(String expression, CompilerContext ctx) { FEEL_1_1Parser parser = FEELParser.parse(getEventsManager(ctx.getListeners()), expression, ctx.getInputVariableTypes(), ctx.getInputVariables(), ctx.getFEELFunctions(), profiles); - ParseTree tree = parser.expressionList(); + ParseTree tree = parser.unaryTestsRoot(); ASTBuilderVisitor v = new ASTBuilderVisitor(ctx.getInputVariableTypes()); BaseNode expr = v.visit(tree); CompiledExpression ce = new CompiledExpressionImpl(expr); @@ -325,16 +305,4 @@ public FEELEventListenersManager getEventsManager(Collection return listenerMgr; } - // thread-unsafe, but this is single-threaded so it's ok - private static class CompilerErrorListener implements FEELEventListener { - FEELEvent evt; - @Override - public void onEvent(FEELEvent feelEvent) { - if (feelEvent.getSeverity() == FEELEvent.Severity.ERROR) { - evt = feelEvent; - } - } - } - - } diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/parser/feel11/ASTBuilderVisitor.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/parser/feel11/ASTBuilderVisitor.java index af97465079b..74762860334 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/parser/feel11/ASTBuilderVisitor.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/parser/feel11/ASTBuilderVisitor.java @@ -18,7 +18,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.List; @@ -132,15 +132,6 @@ public BaseNode visitPrimaryParens(FEEL_1_1Parser.PrimaryParensContext ctx) { return visit( ctx.expression() ); } - @Override - public BaseNode visitLogicalNegation(FEEL_1_1Parser.LogicalNegationContext ctx) { - BaseNode name = ASTBuilderFactory.newNameRefNode( ctx.not_key(), BuiltInType.BOOLEAN ); - BaseNode node = visit( ctx.unaryExpression() ); - ListNode params = ASTBuilderFactory.newListNode( ctx.unaryExpression(), Arrays.asList( node ) ); - - return buildFunctionCall( ctx, name, params ); - } - @Override public BaseNode visitPowExpression(FEEL_1_1Parser.PowExpressionContext ctx) { BaseNode left = visit( ctx.powerExpression() ); @@ -184,13 +175,6 @@ public BaseNode visitExpressionList(FEEL_1_1Parser.ExpressionListContext ctx) { return ASTBuilderFactory.newListNode( ctx, exprs ); } - @Override - public BaseNode visitRelExpressionValueList(FEEL_1_1Parser.RelExpressionValueListContext ctx) { - BaseNode value = visit( ctx.val ); - BaseNode list = visit( ctx.expressionList() ); - return ASTBuilderFactory.newInNode( ctx, value, list ); - } - @Override public BaseNode visitInterval(FEEL_1_1Parser.IntervalContext ctx) { BaseNode start = visit( ctx.start ); @@ -208,10 +192,10 @@ public BaseNode visitPositiveUnaryTestIneq(FEEL_1_1Parser.PositiveUnaryTestIneqC } @Override - public BaseNode visitSimpleUnaryTests(FEEL_1_1Parser.SimpleUnaryTestsContext ctx) { + public BaseNode visitPositiveUnaryTests(FEEL_1_1Parser.PositiveUnaryTestsContext ctx) { List tests = new ArrayList<>(); for ( int i = 0; i < ctx.getChildCount(); i++ ) { - if ( ctx.getChild( i ) instanceof FEEL_1_1Parser.SimpleUnaryTestContext || + if ( ctx.getChild( i ) instanceof FEEL_1_1Parser.PositiveUnaryTestContext || ctx.getChild( i ) instanceof FEEL_1_1Parser.PrimaryContext) { tests.add( visit( ctx.getChild( i ) ) ); } @@ -222,7 +206,7 @@ public BaseNode visitSimpleUnaryTests(FEEL_1_1Parser.SimpleUnaryTestsContext ctx @Override public BaseNode visitRelExpressionTestList(FEEL_1_1Parser.RelExpressionTestListContext ctx) { BaseNode value = visit( ctx.val ); - BaseNode list = visit( ctx.simpleUnaryTests() ); + BaseNode list = visit( ctx.positiveUnaryTests() ); return ASTBuilderFactory.newInNode( ctx, value, list ); } @@ -233,11 +217,6 @@ public BaseNode visitRelExpressionValue(RelExpressionValueContext ctx) { return ASTBuilderFactory.newInNode( ctx, value, test ); } - @Override - public BaseNode visitPositiveUnaryTestNull(FEEL_1_1Parser.PositiveUnaryTestNullContext ctx) { - return ASTBuilderFactory.newNullNode( ctx ); - } - @Override public BaseNode visitPositiveUnaryTestDash(FEEL_1_1Parser.PositiveUnaryTestDashContext ctx) { return ASTBuilderFactory.newDashNode( ctx ); @@ -488,11 +467,29 @@ private String getFunctionName(BaseNode name) { private BaseNode buildFunctionCall(ParserRuleContext ctx, BaseNode name, ListNode params) { String functionName = getFunctionName( name ); - if( "not".equals( functionName ) ) { - return buildNotCall( ctx, name, params ); - } else { - return ASTBuilderFactory.newFunctionInvocationNode( ctx, name, params ); - } + return ASTBuilderFactory.newFunctionInvocationNode( ctx, name, params ); + } + + @Override + public BaseNode visitUnaryTestsRoot(FEEL_1_1Parser.UnaryTestsRootContext ctx) { + return visit(ctx.unaryTests()); + } + + @Override + public BaseNode visitUnaryTests_empty(FEEL_1_1Parser.UnaryTests_emptyContext ctx) { + return ASTBuilderFactory.newListNode(ctx, Collections.singletonList(ASTBuilderFactory.newDashNode(ctx))); + } + + @Override + public BaseNode visitUnaryTests_positive(FEEL_1_1Parser.UnaryTests_positiveContext ctx) { + return visit( ctx.positiveUnaryTests() ); + } + + @Override + public BaseNode visitUnaryTests_negated(FEEL_1_1Parser.UnaryTests_negatedContext ctx) { + BaseNode name = ASTBuilderFactory.newNameRefNode( ctx.not_key(), BuiltInType.BOOLEAN ); // negating a unary tests: BOOLEAN-type anyway + ListNode value = (ListNode) visit( ctx.positiveUnaryTests() ); + return ASTBuilderFactory.newListNode(ctx, Collections.singletonList(buildNotCall(ctx, name, value))) ; } private BaseNode buildNotCall(ParserRuleContext ctx, BaseNode name, ListNode params) { @@ -584,10 +581,4 @@ public BaseNode visitCompilation_unit(FEEL_1_1Parser.Compilation_unitContext ctx return visit( ctx.expression() ); } - @Override - public BaseNode visitNegatedUnaryTests(FEEL_1_1Parser.NegatedUnaryTestsContext ctx) { - BaseNode name = ASTBuilderFactory.newNameRefNode( ctx.not_key(), BuiltInType.BOOLEAN ); // negating a unary tests: BOOLEAN-type anyway - ListNode value = (ListNode) visit( ctx.simpleUnaryTests() ); - return buildFunctionCall( ctx, name, value ); - } } diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/CodegenTestUtil.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/CodegenTestUtil.java index 5d5cfcdb5da..770eb401b3d 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/CodegenTestUtil.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/CodegenTestUtil.java @@ -2,6 +2,7 @@ import org.kie.dmn.feel.lang.EvaluationContext; import org.kie.dmn.feel.lang.impl.EvaluationContextImpl; +import org.kie.dmn.feel.lang.impl.FEELEventListenersManager; import org.kie.dmn.feel.util.ClassLoaderUtil; public class CodegenTestUtil { @@ -10,6 +11,10 @@ private CodegenTestUtil() { // only static methods for util class. } + public static EvaluationContext newEmptyEvaluationContext(FEELEventListenersManager mgr) { + return new EvaluationContextImpl(ClassLoaderUtil.findDefaultClassLoader(), mgr); + } + public static EvaluationContext newEmptyEvaluationContext() { return new EvaluationContextImpl(ClassLoaderUtil.findDefaultClassLoader(), null); } diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/DirectCompilerTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/DirectCompilerTest.java index dd01dee2f17..e58539f478b 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/DirectCompilerTest.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/DirectCompilerTest.java @@ -25,6 +25,7 @@ import org.antlr.v4.runtime.tree.ParseTree; import org.drools.javaparser.ast.expr.Expression; +import org.junit.Ignore; import org.junit.Test; import org.kie.dmn.feel.lang.EvaluationContext; import org.kie.dmn.feel.lang.FEELProperty; @@ -172,8 +173,9 @@ public void test_exponentiationExpression() { @Test public void test_logicalNegationExpression() { - assertThat(parseCompileEvaluate("not true"), is(false)); - assertThat(parseCompileEvaluate("not false"), is(true)); + // this is all invalid syntax + assertThat(parseCompileEvaluate("not true"), nullValue()); + assertThat(parseCompileEvaluate("not false"), nullValue()); assertThat(parseCompileEvaluate("not null"), nullValue()); assertThat(parseCompileEvaluate("not 3"), nullValue()); } diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/DirectCompilerUnaryTestsTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/DirectCompilerUnaryTestsTest.java index 706a84f2940..e8e2fd947ff 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/DirectCompilerUnaryTestsTest.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/codegen/feel11/DirectCompilerUnaryTestsTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.kie.dmn.feel.lang.EvaluationContext; import org.kie.dmn.feel.lang.Type; +import org.kie.dmn.feel.lang.impl.FEELEventListenersManager; import org.kie.dmn.feel.parser.feel11.FEELParser; import org.kie.dmn.feel.parser.feel11.FEEL_1_1Parser; import org.kie.dmn.feel.util.EvalHelper; @@ -39,14 +40,23 @@ public class DirectCompilerUnaryTestsTest { public static final Logger LOG = LoggerFactory.getLogger(DirectCompilerUnaryTestsTest.class); - + private List parseCompileEvaluate(String feelLiteralExpression, Object l) { Object left = EvalHelper.coerceNumber(l); - CompiledFEELUnaryTests compiledUnaryTests = parse(feelLiteralExpression); + FEELEventListenersManager mgr = new FEELEventListenersManager(); + CompiledFEELSupport.SyntaxErrorListener listener = new CompiledFEELSupport.SyntaxErrorListener(); + mgr.addListener(listener); + EvaluationContext emptyContext = CodegenTestUtil.newEmptyEvaluationContext(mgr); + CompiledFEELUnaryTests compiledUnaryTests = parse(feelLiteralExpression, mgr, listener); LOG.debug("{}", compiledUnaryTests); - - EvaluationContext emptyContext = CodegenTestUtil.newEmptyEvaluationContext(); - List result = compiledUnaryTests.getUnaryTests().stream().map(ut -> ut.apply(emptyContext, left)).collect(Collectors.toList()); + List result = compiledUnaryTests.getUnaryTests() + .stream() + .map(ut -> ut.apply(emptyContext, left)) + .collect(Collectors.toList()); + if (listener.isError()) { + LOG.debug("{}", listener.event()); + return Collections.emptyList(); + } LOG.debug("{}", result); return result; } @@ -54,7 +64,7 @@ private List parseCompileEvaluate(String feelLiteralExpression, Object @Test public void test_Dash() { assertThat(parseCompileEvaluate("-", 1), is(Arrays.asList(true))); - assertThat(parseCompileEvaluate("-, -", 1), is(Arrays.asList(true, true))); + assertThat(parseCompileEvaluate("-, -", 1), is(Collections.emptyList())); } @Test @@ -81,7 +91,7 @@ public void test_positiveUnaryTestIneq_forEQ() { @Test public void test_not() { - assertThat(parseCompileEvaluate("not(=47), not(<1), not(!=1)", 1), is(Arrays.asList(true, true, true))); + assertThat(parseCompileEvaluate("not(=47), not(<1), not(!=1)", 1), is(Collections.emptyList())); } @Test @@ -92,18 +102,21 @@ public void test_simpleUnaryTest_forRANGE() { assertThat(parseCompileEvaluate("(1..2], [2..3]", 2), is(Arrays.asList(true, true))); } - private CompiledFEELUnaryTests parse(String input) { - return parse( input, Collections.emptyMap() ); + private CompiledFEELUnaryTests parse(String input, FEELEventListenersManager mgr, CompiledFEELSupport.SyntaxErrorListener listener) { + return parse( input, Collections.emptyMap(), mgr, listener ); } - private CompiledFEELUnaryTests parse(String input, Map inputTypes) { - FEEL_1_1Parser parser = FEELParser.parse(null, input, inputTypes, Collections.emptyMap(), Collections.emptyList(), Collections.emptyList()); - - ParseTree tree = parser.expressionList(); + private CompiledFEELUnaryTests parse(String input, Map inputTypes, FEELEventListenersManager mgr, CompiledFEELSupport.SyntaxErrorListener listener) { + FEEL_1_1Parser parser = FEELParser.parse(mgr, input, inputTypes, Collections.emptyMap(), Collections.emptyList(), Collections.emptyList()); - DirectCompilerVisitor v = new DirectCompilerVisitor(inputTypes, true); - DirectCompilerResult directResult = v.visit(tree); - + ParseTree tree = parser.unaryTestsRoot(); + DirectCompilerResult directResult; + if (listener.isError()) { + directResult = CompiledFEELSupport.compiledErrorUnaryTest(listener.event().getMessage()); + } else { + DirectCompilerVisitor v = new DirectCompilerVisitor(inputTypes, true); + directResult = v.visit(tree); + } Expression expr = directResult.getExpression(); CompiledFEELUnaryTests cu = new CompilerBytecodeLoader().makeFromJPUnaryTestsExpression(input, expr, directResult.getFieldDeclarations()); diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/examples/SimpleDecisionTablesTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/examples/SimpleDecisionTablesTest.java deleted file mode 100644 index e7db79c7c57..00000000000 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/examples/SimpleDecisionTablesTest.java +++ /dev/null @@ -1,239 +0,0 @@ -package org.kie.dmn.feel.lang.examples; - -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; -import org.hamcrest.TypeSafeMatcher; -import org.hamcrest.collection.IsCollectionWithSize; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; -import org.kie.dmn.api.feel.runtime.events.FEELEvent; -import org.kie.dmn.api.feel.runtime.events.FEELEventListener; -import org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity; -import org.kie.dmn.feel.FEEL; -import org.kie.dmn.feel.runtime.events.HitPolicyViolationEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -public class SimpleDecisionTablesTest - extends ExamplesBaseTest { - private static final Logger logger = LoggerFactory.getLogger( ExamplesTest.class ); - private static FEEL feel; - - @BeforeClass - public static void setupTest() { - feel = FEEL.newInstance(); - feel.addListener(evt -> { - if (evt.getSeverity() == FEELEvent.Severity.ERROR) { - logger.error("{}", evt); - if ( evt.getSourceException().getCause() != null ) { - Throwable c = evt.getSourceException().getCause(); - while (c != null) { - logger.error(" caused by: {} {}", c.getClass(), c.getMessage() != null ? c.getMessage() : ""); - c = c.getCause(); - } - logger.error(" [stacktraces omitted.]"); - } - } else if (evt.getSeverity() == FEELEvent.Severity.WARN) { - logger.warn("{}", evt); - } - } ); - } - - @Test - public void testMain() { - List events = new ArrayList<>(); - FEELEventListener listener = evt -> events.add(evt); - feel.addListener(listener); - - String expression = loadExpression( "simple_decision_tables.feel" ); - Map context = (Map) feel.evaluate( expression ); - - System.out.println( printContext( context ) ); - - assertThat( context.get( "result1" ), is( "Adult" ) ); - assertThat( context.get( "result2" ), is( "Medium" ) ); - assertThat( (Map) context.get( "result3" ), hasSize(2)); - assertThat( (Map) context.get( "result3" ), hasEntry("Out1", "out1b" )); - assertThat( (Map) context.get( "result3" ), hasEntry("Out2", "out2b" )); - assertThat( (Map) context.get( "result4" ), hasSize(2)); - assertThat( (Map) context.get( "result4" ), hasEntry("Out1", "io1a" )); - assertThat( (Map) context.get( "result4" ), hasEntry("Out2", "io2a" )); - assertThat( context.get( "result5" ), nullValue() ); - assertTrue( events.stream().anyMatch(e -> e instanceof HitPolicyViolationEvent - && e.getSeverity().equals(Severity.WARN) ) ); - - feel.removeListener(listener); - } - - @Test - public void testDecisionTableFormulaOut() { - String expression = loadExpression( "dt_formula_out.feel" ); - Map context = (Map) feel.evaluate( expression ); - - System.out.println( printContext( context ) ); - - assertThat( context.get( "square area" ), is( BigDecimal.valueOf( 25 ) ) ); - assertThat( context.get( "circle area" ), is( BigDecimal.valueOf( 78.5 ).setScale( 2 ) ) ); - } - - @Test - public void testt0004simpletableU() { - String expression = loadExpression( "t0004simpletableU.feel" ); - Map context = (Map) feel.evaluate( expression ); - - System.out.println( printContext( context ) ); - - assertThat( context.get( "result1" ), is( "Approved" ) ); - assertThat( context.get( "result2" ), nullValue() ); - assertThat( context.get( "result3" ), is( "Declined" ) ); - } - - @Test - public void testdthitpolicies() { - String expression = loadExpression( "dthitpolicies.feel" ); - Map context = (Map) feel.evaluate( expression ); - - System.out.println( printContext( context ) ); - - assertThat( context.get( "DTunique10" ), is( "row2" ) ); - assertThat( context.get( "DTunique11" ), nullValue() ); - - assertThat( context.get( "DTfirst11" ), is( "row1" ) ); - - assertThat( context.get( "DTAny10" ), is( "B" ) ); - assertThat( context.get( "DTAny11" ), nullValue() ); - - assertThat( context.get( "DTruleOrder10" ), is( Arrays.asList(new String[]{"B", "B"}) ) ); - assertThat( context.get( "DTruleOrder11" ), is( Arrays.asList(new String[]{"A", "B", "B"}) ) ); - - assertTrue( ((List)context.get( "DTcollect11" )).contains("A")); - assertTrue( ((List)context.get( "DTcollect11" )).contains("B")); - assertTrue( ((List)context.get( "DTcollect11" )).size() == 2 ); - - assertThat( context.get( "DTpriority11" ), is( "B" ) ); - - assertTrue( ((List)context.get( "DToutputOrder11" )).size() == 2 ); - assertTrue( ((List)context.get( "DToutputOrder11" )).get(0).equals("B")); - assertTrue( ((List)context.get( "DToutputOrder11" )).get(1).equals("A")); - - assertThat( context.get( "DTcount10" ), is( new BigDecimal(1) ) ); - assertThat( context.get( "DTcount11" ), is( new BigDecimal(2) ) ); - - assertThat( context.get( "DTsum10" ), is( new BigDecimal(5) ) ); - assertThat( context.get( "DTsum11" ), is( new BigDecimal(6) ) ); - - assertThat( context.get( "DTmin10" ), is( new BigDecimal(2) ) ); - assertThat( context.get( "DTmin11" ), is( new BigDecimal(1) ) ); - - assertThat( context.get( "DTmax10" ), is( new BigDecimal(3) ) ); - assertThat( context.get( "DTmax11" ), is( new BigDecimal(3) ) ); - } - - @Test - public void testdthitpoliciesMultipleOutput() { - String expression = loadExpression( "dthitpolicies_multipleoutput.feel" ); - Map context = (Map) feel.evaluate( expression ); - - System.out.println( printContext( context ) ); - - assertThat( (Map) context.get( "DTunique10" ), hasSize(2)); - assertThat( (Map) context.get( "DTunique10" ), hasEntry("Out1", "row2" )); - assertThat( (Map) context.get( "DTunique10" ), hasEntry("Out2", new BigDecimal(2) )); - assertThat( context.get( "DTunique11" ), nullValue() ); - - assertThat( (Map) context.get( "DTfirst11" ), hasSize(2)); - assertThat( (Map) context.get( "DTfirst11" ), hasEntry("Out1", "row1" )); - assertThat( (Map) context.get( "DTfirst11" ), hasEntry("Out2", new BigDecimal(1) )); - - assertThat( (Map) context.get( "DTAny10" ), hasSize(2)); - assertThat( (Map) context.get( "DTAny10" ), hasEntry("Out1", "B" )); - assertThat( (Map) context.get( "DTAny10" ), hasEntry("Out2", new BigDecimal(7) )); - assertThat( context.get( "DTAny11" ), nullValue() ); - - assertThat( (List>) context.get( "DTruleOrder10" ), IsCollectionWithSize.hasSize( 2 )); - assertThat( ((List>) context.get( "DTruleOrder10" )).get( 0 ), hasEntry( is("Out1"), is( "B" ) ) ); - assertThat( ((List>) context.get( "DTruleOrder10" )).get( 0 ), hasEntry( is("Out2"), is( BigDecimal.valueOf( 2 ) ) ) ); - assertThat( ((List>) context.get( "DTruleOrder10" )).get( 1 ), hasEntry( is("Out1"), is( "B" ) ) ); - assertThat( ((List>) context.get( "DTruleOrder10" )).get( 1 ), hasEntry( is("Out2"), is( BigDecimal.valueOf( 3 ) ) ) ); - - assertThat( (List>) context.get( "DTruleOrder11" ), IsCollectionWithSize.hasSize( 3 )); - assertThat( ((List>) context.get( "DTruleOrder11" )).get( 0 ), hasEntry( is("Out1"), is( "A" ) ) ); - assertThat( ((List>) context.get( "DTruleOrder11" )).get( 0 ), hasEntry( is("Out2"), is( BigDecimal.valueOf( 1 ) ) ) ); - assertThat( ((List>) context.get( "DTruleOrder11" )).get( 1 ), hasEntry( is("Out1"), is( "B" ) ) ); - assertThat( ((List>) context.get( "DTruleOrder11" )).get( 1 ), hasEntry( is("Out2"), is( BigDecimal.valueOf( 2 ) ) ) ); - assertThat( ((List>) context.get( "DTruleOrder11" )).get( 2 ), hasEntry( is("Out1"), is( "B" ) ) ); - assertThat( ((List>) context.get( "DTruleOrder11" )).get( 2 ), hasEntry( is("Out2"), is( BigDecimal.valueOf( 3 ) ) ) ); - - assertThat( (List>) context.get( "DTcollect11" ), IsCollectionWithSize.hasSize( 2 )); - assertThat( ((List>) context.get( "DTcollect11" )).get( 0 ), hasEntry( is("Out1"), is( "A" ) ) ); - assertThat( ((List>) context.get( "DTcollect11" )).get( 0 ), hasEntry( is("Out2"), is( BigDecimal.valueOf( 1 ) ) ) ); - assertThat( ((List>) context.get( "DTcollect11" )).get( 1 ), hasEntry( is("Out1"), is( "B" ) ) ); - assertThat( ((List>) context.get( "DTcollect11" )).get( 1 ), hasEntry( is("Out2"), is( BigDecimal.valueOf( 2 ) ) ) ); - - assertThat( (Map) context.get( "DTpriority11" ), hasSize(2)); - assertThat( (Map) context.get( "DTpriority11" ), hasEntry("Out1", "B" )); - assertThat( (Map) context.get( "DTpriority11" ), hasEntry("Out2", new BigDecimal(2) )); - - assertThat( (List>) context.get( "DToutputOrder11" ), IsCollectionWithSize.hasSize( 2 )); - assertThat( ((List>) context.get( "DToutputOrder11" )).get( 0 ), hasEntry( is("Out1"), is( "B" ) ) ); - assertThat( ((List>) context.get( "DToutputOrder11" )).get( 0 ), hasEntry( is("Out2"), is( BigDecimal.valueOf( 2 ) ) ) ); - assertThat( ((List>) context.get( "DToutputOrder11" )).get( 1 ), hasEntry( is("Out1"), is( "A" ) ) ); - assertThat( ((List>) context.get( "DToutputOrder11" )).get( 1 ), hasEntry( is("Out2"), is( BigDecimal.valueOf( 1 ) ) ) ); - - assertThat( (Map) context.get( "DTcount10" ), hasSize(2)); - assertThat( (Map) context.get( "DTcount10" ), hasEntry("Out1", new BigDecimal(1) )); - assertThat( (Map) context.get( "DTcount10" ), hasEntry("Out2", new BigDecimal(2) )); - assertThat( (Map) context.get( "DTcount11" ), hasSize(2)); - assertThat( (Map) context.get( "DTcount11" ), hasEntry("Out1", new BigDecimal(2) )); - assertThat( (Map) context.get( "DTcount11" ), hasEntry("Out2", new BigDecimal(3) )); - - assertThat( (Map) context.get( "DTsum10" ), hasSize(2)); - assertThat( (Map) context.get( "DTsum10" ), hasEntry("Out1", new BigDecimal(5) )); - assertThat( (Map) context.get( "DTsum10" ), hasEntry(is("Out2"), nullValue() )); - assertThat( (Map) context.get( "DTsum11" ), hasSize(2)); - assertThat( (Map) context.get( "DTsum11" ), hasEntry("Out1", new BigDecimal(6) )); - assertThat( (Map) context.get( "DTsum11" ), hasEntry(is("Out2"), nullValue() )); - - assertThat( (Map) context.get( "DTmin10" ), hasSize(2)); - assertThat( (Map) context.get( "DTmin10" ), hasEntry("Out1", new BigDecimal(2) )); - assertThat( (Map) context.get( "DTmin10" ), hasEntry("Out2", "B" )); - assertThat( (Map) context.get( "DTmin11" ), hasSize(2)); - assertThat( (Map) context.get( "DTmin11" ), hasEntry("Out1", new BigDecimal(1) )); - assertThat( (Map) context.get( "DTmin11" ), hasEntry("Out2", "A" )); - - assertThat( (Map) context.get( "DTmax10" ), hasSize(2)); - assertThat( (Map) context.get( "DTmax10" ), hasEntry("Out1", new BigDecimal(3) )); - assertThat( (Map) context.get( "DTmax10" ), hasEntry("Out2", "C" )); - assertThat( (Map) context.get( "DTmax11" ), hasSize(2)); - assertThat( (Map) context.get( "DTmax11" ), hasEntry("Out1", new BigDecimal(3) )); - assertThat( (Map) context.get( "DTmax11" ), hasEntry("Out2", "C" )); - } - - public static Matcher> hasSize(final int z) { - return new TypeSafeMatcher>() { - public boolean matchesSafely(Map arg0) { - return arg0.size() == z; - } - public void describeTo(Description arg0) { - arg0.appendText("not matching size "+z); - } - }; - } - - -} diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/parser/feel11/FEELParserTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/parser/feel11/FEELParserTest.java index c96b55c915c..12ae762888d 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/parser/feel11/FEELParserTest.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/parser/feel11/FEELParserTest.java @@ -231,7 +231,7 @@ public void testLogicalNegation() { BaseNode neg = parse( inputExpression ); assertThat( neg, is( instanceOf( FunctionInvocationNode.class ) ) ); - assertThat( neg.getResultType(), is( BuiltInType.BOOLEAN ) ); + assertThat( neg.getResultType(), is( BuiltInType.UNKNOWN ) ); assertThat( neg.getText(), is( "not ( true )" ) ); FunctionInvocationNode not = (FunctionInvocationNode) neg; diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELExpressionsTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELExpressionsTest.java index c44b2612d24..c76a764c2b1 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELExpressionsTest.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELExpressionsTest.java @@ -58,21 +58,8 @@ public static Collection data() { {"{ is minor : < 18, bob is minor : is minor(16) }.bob is minor", Boolean.TRUE , null}, // negated unary tests - {"10 in ( not( <5, >=20, =15, !=10 ) )", Boolean.TRUE, null}, - {"10 in ( not( <5, >=20, =10 ) )", Boolean.FALSE, null}, - {"10 in ( not( <5 ) )", Boolean.TRUE, null}, - {"10 in ( not( (10..20] ) )", Boolean.TRUE, null}, - {"10 in ( not( 10 ) )", Boolean.FALSE, null}, - {"10 in ( not( 5 ) )", Boolean.TRUE, null}, - {"10 in ( not( 5, (5+5), (20+10) ) )", Boolean.FALSE, null}, - {"10 in ( not( 5, (20+10) ) )", Boolean.TRUE, null}, - {"10 in ( not( >5*20 ) )", Boolean.TRUE , null}, - {"10 in ( not( 10 ), not( 20 ) )", Boolean.TRUE , null}, - {"10 in ( not( null, 10 ) )", Boolean.FALSE , null}, - {"10 in ( not( 5, 10 ) )", Boolean.FALSE , null}, - {"null in ( not( 10, null ) )", Boolean.FALSE , null}, - {"\"Boston\" in ( not( \"Toronto\", \"Montreal\" ) )", Boolean.TRUE , null}, - {"\"Boston\" in ( not( \"Toronto\", \"Boston\" ) )", Boolean.FALSE , null}, + {"10 in ( not( <5, >=20, =15, !=10 ) )", Boolean.FALSE, FEELEvent.Severity.ERROR}, + {"\"Boston\" in ( not( \"Toronto\", \"Montreal\" ) )", Boolean.FALSE , FEELEvent.Severity.ERROR}, // Unary tests with ? character {"{ ? foo : 5, result : ? foo < 10 }.result", Boolean.TRUE , null}, diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELOperatorsTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELOperatorsTest.java index bc8fc9db4cf..bd5d388c711 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELOperatorsTest.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELOperatorsTest.java @@ -31,7 +31,7 @@ public static Collection data() { { "not( false )", Boolean.TRUE , null}, { "not( 10 = 3 )", Boolean.TRUE , null}, { "not(list contains([1,2,3,4,5,6], 3))", Boolean.FALSE , null}, - { "not( \"foo\" )", UnaryTest.class , null}, + { "not( \"foo\" )", null, FEELEvent.Severity.ERROR}, {"{x : 10, r : not( x = 3 )}.r", Boolean.TRUE, null}, {"{x : 3, r : not( x = 3 )}.r", Boolean.FALSE, null}, @@ -54,7 +54,7 @@ public static Collection data() { { "10 in ( > 50, < 5 )", Boolean.FALSE , null}, { "10 in ( > 5, < -40 )", Boolean.TRUE , null}, { "null in ( > 20, null )", Boolean.TRUE , null}, - { "null in -", Boolean.TRUE , null}, + { "null in -", null, FEELEvent.Severity.ERROR}, { "10 in [5..20]", Boolean.TRUE , null}, { "10 in [10..20)", Boolean.TRUE , null}, { "10 in (10..20)", Boolean.FALSE , null}, diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELValuesConstantsTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELValuesConstantsTest.java index 3172a61fadd..bce00be7406 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELValuesConstantsTest.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELValuesConstantsTest.java @@ -20,6 +20,7 @@ import java.util.Collection; import org.junit.runners.Parameterized; +import org.kie.dmn.api.feel.runtime.events.FEELEvent; public class FEELValuesConstantsTest extends BaseFEELTest { @@ -32,7 +33,7 @@ public static Collection data() { { "false", Boolean.FALSE , null}, // dash is an unary test that always matches, so for now, returning true. // have to double check to know if this is not the case - { "-", UnaryTest.class , null}, + { "-", null, FEELEvent.Severity.ERROR }, { ".872", new BigDecimal( "0.872" ) , null}, { "-.872", new BigDecimal( "-0.872" ) , null}, { "+.872", new BigDecimal( "0.872" ) , null}, diff --git a/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dt_formula_out.feel b/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dt_formula_out.feel deleted file mode 100644 index 409335175a7..00000000000 --- a/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dt_formula_out.feel +++ /dev/null @@ -1,14 +0,0 @@ -{ - area DT : decision table( - outputs: "Area", - input expression list: ["Shape", "Radio", "Side"], - rule list: [ - [ "Circle", -, -, "3.14 * Radio ** 2" ], - [ "Square", -, -, "Side ** 2" ] - ], - hit policy: "U" - ), - - square area: area DT( "Square", null, 5 ), - circle area: area DT( "Circle", 5, null ) -} \ No newline at end of file diff --git a/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dthitpolicies.feel b/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dthitpolicies.feel deleted file mode 100644 index 831a254254d..00000000000 --- a/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dthitpolicies.feel +++ /dev/null @@ -1,145 +0,0 @@ -{ - DTunique : decision table ( - outputs: "Out", - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"row1\"" ], - [ >0, -, "\"row2\"" ] - ], - hit policy: "U" - ), - - DTunique10 : DTunique(1,0), - DTunique11 : DTunique(1,1), - - DTfirst : decision table ( - outputs: "Out", - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"row1\"" ], - [ >0, -, "\"row2\"" ], - [ - , -, "\"row3\"" ] - ], - hit policy: "F" - ), - - DTfirst11 : DTfirst(1,1), - - DTany : decision table ( - outputs: "Out", - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"" ], - [ >0, -, "\"B\"" ], - [ - , -, "\"B\"" ] - ], - hit policy: "A" - ), - - DTAny10 : DTany(1,0), - DTAny11 : DTany(1,1), - - DTruleOrder : decision table ( - outputs: "Out", - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"" ], - [ >0, -, "\"B\"" ], - [ - , -, "\"B\"" ] - ], - hit policy: "R" - ), - - DTruleOrder10 : DTruleOrder(1,0), - DTruleOrder11 : DTruleOrder(1,1), - - DTcollect : decision table ( - outputs: "Out", - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"" ], - [ >0, -, "\"B\"" ] - ], - hit policy: "C" - ), - - DTcollect11 : DTcollect(1,1), - - DTpriority : decision table ( - outputs: "Out", - output values: ["B", "A"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"" ], - [ >0, -, "\"B\"" ] - ], - hit policy: "P" - ), - - DTpriority11 : DTpriority(1,1), - - DToutputOrder : decision table ( - outputs: "Out", - output values: ["C", "B", "A"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"" ], - [ >0, -, "\"B\"" ] - ], - hit policy: "O" - ), - - DToutputOrder11 : DToutputOrder(1,1), - - DTcount : decision table ( - outputs: "Out", - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"" ], - [ >0, -, "\"B\"" ], - [ - , -, "\"B\"" ] - ], - hit policy: "C#" - ), - DTcount10 : DTcount(1,0), - DTcount11 : DTcount(1,1), - - DTsum : decision table ( - outputs: "Out", - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "1" ], - [ >0, -, "2" ], - [ - , -, "3" ] - ], - hit policy: "C+" - ), - DTsum10 : DTsum(1,0), - DTsum11 : DTsum(1,1), - - DTmin : decision table ( - outputs: "Out", - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "1" ], - [ >0, -, "2" ], - [ - , -, "3" ] - ], - hit policy: "C<" - ), - DTmin10 : DTmin(1,0), - DTmin11 : DTmin(1,1), - - DTmax : decision table ( - outputs: "Out", - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "1" ], - [ >0, -, "2" ], - [ - , -, "3" ] - ], - hit policy: "C>" - ), - DTmax10 : DTmax(1,0), - DTmax11 : DTmax(1,1) -} \ No newline at end of file diff --git a/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dthitpolicies_multipleoutput.feel b/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dthitpolicies_multipleoutput.feel deleted file mode 100644 index 1e8df2599b1..00000000000 --- a/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/dthitpolicies_multipleoutput.feel +++ /dev/null @@ -1,145 +0,0 @@ -{ - DTunique : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"row1\"", "1" ], - [ >0, -, "\"row2\"", "2" ] - ], - hit policy: "U" - ), - - DTunique10 : DTunique(1,0), - DTunique11 : DTunique(1,1), - - DTfirst : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"row1\"", "1" ], - [ >0, -, "\"row2\"", "2" ], - [ - , -, "\"row3\"", "3" ] - ], - hit policy: "F" - ), - - DTfirst11 : DTfirst(1,1), - - DTany : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"", "1" ], - [ >0, -, "\"B\"", "7" ], - [ - , -, "\"B\"", "7" ] - ], - hit policy: "A" - ), - - DTAny10 : DTany(1,0), - DTAny11 : DTany(1,1), - - DTruleOrder : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"", "1" ], - [ >0, -, "\"B\"", "2" ], - [ - , -, "\"B\"", "3" ] - ], - hit policy: "R" - ), - - DTruleOrder10 : DTruleOrder(1,0), - DTruleOrder11 : DTruleOrder(1,1), - - DTcollect : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"", "1" ], - [ >0, -, "\"B\"", "2" ] - ], - hit policy: "C" - ), - - DTcollect11 : DTcollect(1,1), - - DTpriority : decision table ( - outputs: ["Out1", "Out2"], - output values: [ ["C", "B", "A"], [1, 2] ], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"", "1" ], - [ >0, -, "\"B\"", "2" ] - ], - hit policy: "P" - ), - - DTpriority11 : DTpriority(1,1), - - DToutputOrder : decision table ( - outputs: ["Out1", "Out2"], - output values: [ ["C", "B", "A"], [1, 2] ], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"", "1" ], - [ >0, -, "\"B\"", "2" ] - ], - hit policy: "O" - ), - - DToutputOrder11 : DToutputOrder(1,1), - - DTcount : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "\"A\"" , "1"], - [ >0, -, "\"B\"" , "2"], - [ - , -, "\"B\"" , "3"] - ], - hit policy: "C#" - ), - DTcount10 : DTcount(1,0), - DTcount11 : DTcount(1,1), - - DTsum : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "1", "\"A\"" ], - [ >0, -, "2", "\"B\"" ], - [ - , -, "3", "\"C\"" ] - ], - hit policy: "C+" - ), - DTsum10 : DTsum(1,0), - DTsum11 : DTsum(1,1), - - DTmin : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "1", "\"A\"" ], - [ >0, -, "2", "\"B\"" ], - [ - , -, "3", "\"C\"" ] - ], - hit policy: "C<" - ), - DTmin10 : DTmin(1,0), - DTmin11 : DTmin(1,1), - - DTmax : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1", "In2"], - rule list: [ - [ >0, >0, "1", "\"A\"" ], - [ >0, -, "2", "\"B\"" ], - [ - , -, "3", "\"C\"" ] - ], - hit policy: "C>" - ), - DTmax10 : DTmax(1,0), - DTmax11 : DTmax(1,1) -} \ No newline at end of file diff --git a/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/simple_decision_tables.feel b/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/simple_decision_tables.feel deleted file mode 100644 index ddb0f0fa786..00000000000 --- a/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/simple_decision_tables.feel +++ /dev/null @@ -1,60 +0,0 @@ -{ - myDecisionTable : decision table ( - outputs: "Adult", - input expression list: ["Age"], - rule list: [ - [ <18, "\"Young\""], - [ >18, "\"Adult\""] - ], - hit policy: "U" - ), - - biggerDecisionTable : decision table ( - outputs: "Applicant Risk Rating", - input expression list: ["Applicant Age", "Medical History"], - rule list: [ - [ >60, "good", "\"Medium\"" ], - [ >60, "bad", "\"High\"" ], - [ [25..60], -, "\"Medium\"" ], - [ <25, "good", "\"Low\"" ], - [ <25, "bad", "\"Medium\"" ] - ], - hit policy: "U" - ), - - multipleOutputs : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1"], - rule list: [ - [ <=1, "\"out1a\"", "\"out2a\"" ], - [ >=2, "\"out1b\"", "\"out2b\"" ] - ], - hit policy: "U" - ), - - multipleInOut : decision table ( - outputs: ["Out1", "Out2"], - input expression list: ["In1", "In2"], - rule list: [ - [ <10, >20, "\"io1a\"", "\"io2a\"" ], - [ >=10, <20, "\"io1b\"", "\"io2b\"" ] - ], - hit policy: "U" - ), - - checkNull : decision table ( - outputs: "Adult", - input expression list: ["Age"], - rule list: [ - [ <18, "\"Young\""], - [ [>18, <99], "\"Adult\""] - ], - hit policy: "U" - ), - - result1: myDecisionTable( 34 ), - result2: biggerDecisionTable( 35, "good" ), - result3: multipleOutputs(2), - result4: multipleInOut(In2: 47, In1:2), - result5: checkNull(9999) -} \ No newline at end of file diff --git a/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/t0004simpletableU.feel b/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/t0004simpletableU.feel deleted file mode 100644 index 27f273c9192..00000000000 --- a/kie-dmn/kie-dmn-feel/src/test/resources/org/kie/dmn/feel/lang/examples/t0004simpletableU.feel +++ /dev/null @@ -1,18 +0,0 @@ -{ - t0004simpletableU : decision table ( - outputs: "Approval Status", - input expression list: ["Age", "RiskCategory", "isAffordable"], - input values list: [-, ["High", "Low", "Medium"], -], - rule list: [ - [ >=18, ["Medium", "Low"], true , "\"Approved\""], - [ <18 , ["Medium", "Low"], true , "\"Declined\""], - [ - , "High" , true , "\"Declined\""], - [ - , - , false, "\"Declined\""] - ], - hit policy: "U" - ), - - result1: t0004simpletableU(18, "Medium", true), - result2: t0004simpletableU(18, "WRONG VALUE", false), - result3: t0004simpletableU(18, "Low", false) -} \ No newline at end of file