Skip to content

Commit

Permalink
Support generic range type in DMN engine (apache#6123)
Browse files Browse the repository at this point in the history
  • Loading branch information
baldimir authored and rgdoliveira committed Oct 24, 2024
1 parent 16c8d29 commit 9a52c69
Show file tree
Hide file tree
Showing 24 changed files with 242 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
import static org.kie.dmn.core.util.DynamicTypeUtils.entry;
import static org.kie.dmn.core.util.DynamicTypeUtils.mapOf;
import static org.kie.dmn.core.util.DynamicTypeUtils.prototype;
import static org.kie.dmn.feel.codegen.feel11.CodegenTestUtil.newEmptyEvaluationContext;
import static org.kie.dmn.feel.util.EvaluationContextTestUtil.newEmptyEvaluationContext;

public class DMNCompilerTest extends BaseVariantTest {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ type
helper.popScope();
}
: {_input.LT(1).getText().equals("list")}? sk=Identifier LT type GT #listType
| {_input.LT(1).getText().equals("range")}? sk=Identifier LT type GT #rangeType
| {_input.LT(1).getText().equals("context")}? sk=Identifier LT Identifier COLON type ( COMMA Identifier COLON type )* GT #contextType
| FUNCTION #qnType
| FUNCTION LT (type ( COMMA type )*)? GT RARROW type #functionType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
import org.kie.dmn.feel.lang.ast.QualifiedNameNode;
import org.kie.dmn.feel.lang.ast.QuantifiedExpressionNode;
import org.kie.dmn.feel.lang.ast.RangeNode;
import org.kie.dmn.feel.lang.ast.RangeTypeNode;
import org.kie.dmn.feel.lang.ast.SignedUnaryNode;
import org.kie.dmn.feel.lang.ast.StringNode;
import org.kie.dmn.feel.lang.ast.TemporalConstantNode;
Expand Down Expand Up @@ -136,6 +137,7 @@
import static org.kie.dmn.feel.codegen.feel11.DMNCodegenConstants.QUALIFIEDNAMENODE_CT;
import static org.kie.dmn.feel.codegen.feel11.DMNCodegenConstants.QUANTIFIEDEXPRESSIONNODE_CT;
import static org.kie.dmn.feel.codegen.feel11.DMNCodegenConstants.RANGENODE_CT;
import static org.kie.dmn.feel.codegen.feel11.DMNCodegenConstants.RANGETYPENODE_CT;
import static org.kie.dmn.feel.codegen.feel11.DMNCodegenConstants.SIGNEDUNARYNODE_CT;
import static org.kie.dmn.feel.codegen.feel11.DMNCodegenConstants.STRINGNODE_CT;
import static org.kie.dmn.feel.codegen.feel11.DMNCodegenConstants.TEMPORALCONSTANTNODE_CT;
Expand Down Expand Up @@ -431,6 +433,12 @@ public BlockStmt add(RangeNode n) {
endExpression), n.getText());
}

public BlockStmt add(RangeTypeNode n) {
Expression genTypeNodeExpression = getNodeExpression(n.getGenericTypeNode());
return addVariableDeclaratorWithObjectCreation(RANGETYPENODE_CT, NodeList.nodeList(genTypeNodeExpression),
n.getText());
}

public BlockStmt add(SignedUnaryNode n) {
Expression signExpression = getEnumExpression(n.getSign());
Expression expressionExpression = getNodeExpression(n.getExpression());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.kie.dmn.feel.lang.ast.QualifiedNameNode;
import org.kie.dmn.feel.lang.ast.QuantifiedExpressionNode;
import org.kie.dmn.feel.lang.ast.RangeNode;
import org.kie.dmn.feel.lang.ast.RangeTypeNode;
import org.kie.dmn.feel.lang.ast.SignedUnaryNode;
import org.kie.dmn.feel.lang.ast.StringNode;
import org.kie.dmn.feel.lang.ast.TemporalConstantNode;
Expand Down Expand Up @@ -253,6 +254,12 @@ public BlockStmt visit(RangeNode n) {
return compilerHelper.add(n);
}

@Override
public BlockStmt visit(RangeTypeNode n) {
LOGGER.trace("visit {}", n);
return compilerHelper.add(n);
}

@Override
public BlockStmt visit(SignedUnaryNode n) {
LOGGER.trace("visit {}", n);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.kie.dmn.feel.lang.ast.QualifiedNameNode;
import org.kie.dmn.feel.lang.ast.QuantifiedExpressionNode;
import org.kie.dmn.feel.lang.ast.RangeNode;
import org.kie.dmn.feel.lang.ast.RangeTypeNode;
import org.kie.dmn.feel.lang.ast.SignedUnaryNode;
import org.kie.dmn.feel.lang.ast.StringNode;
import org.kie.dmn.feel.lang.ast.TemporalConstantNode;
Expand Down Expand Up @@ -142,6 +143,8 @@ public class DMNCodegenConstants {
public static final ClassOrInterfaceType LISTNODE_CT = parseClassOrInterfaceType(ListNode.class.getCanonicalName());
public static final ClassOrInterfaceType LISTTYPENODE_CT =
parseClassOrInterfaceType(ListTypeNode.class.getCanonicalName());
public static final ClassOrInterfaceType RANGETYPENODE_CT =
parseClassOrInterfaceType(RangeTypeNode.class.getCanonicalName());
public static final ClassOrInterfaceType NAMEDEFNODE_CT =
parseClassOrInterfaceType(NameDefNode.class.getCanonicalName());
public static final ClassOrInterfaceType NAMEDPARAMETERNODE_CT =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the
* License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing permissions and limitations under the License.
*/

package org.kie.dmn.feel.lang.ast;

import org.antlr.v4.runtime.ParserRuleContext;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.Type;
import org.kie.dmn.feel.lang.types.GenRangeType;

public class RangeTypeNode extends TypeNode {

private final TypeNode genericTypeNode;

public RangeTypeNode(ParserRuleContext ctx, TypeNode gen) {
super(ctx);
this.genericTypeNode = gen;
}

public RangeTypeNode(TypeNode genericTypeNode, String text) {
this.genericTypeNode = genericTypeNode;
this.setText(text);
}

@Override
public Type evaluate(EvaluationContext ctx) {
Type gen = genericTypeNode.evaluate(ctx);
return new GenRangeType(gen);
}

@Override
public <T> T accept(Visitor<T> v) {
return v.visit(this);
}

public TypeNode getGenericTypeNode() {
return genericTypeNode;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public interface Visitor<T> {
T visit(QualifiedNameNode n);
T visit(QuantifiedExpressionNode n);
T visit(RangeNode n);
T visit(RangeTypeNode n);
T visit(SignedUnaryNode n);
T visit(StringNode n);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.kie.dmn.feel.lang.ast.QualifiedNameNode;
import org.kie.dmn.feel.lang.ast.QuantifiedExpressionNode;
import org.kie.dmn.feel.lang.ast.RangeNode;
import org.kie.dmn.feel.lang.ast.RangeTypeNode;
import org.kie.dmn.feel.lang.ast.SignedUnaryNode;
import org.kie.dmn.feel.lang.ast.StringNode;
import org.kie.dmn.feel.lang.ast.UnaryTestListNode;
Expand Down Expand Up @@ -194,6 +195,11 @@ public T visit(RangeNode n) {
return defaultVisit(n);
}

@Override
public T visit(RangeTypeNode n) {
return defaultVisit(n);
}

@Override
public T visit(SignedUnaryNode n) {
return defaultVisit(n);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the
* License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing permissions and limitations under the License.
*/

package org.kie.dmn.feel.lang.types;

import org.kie.dmn.feel.lang.SimpleType;
import org.kie.dmn.feel.lang.Type;
import org.kie.dmn.feel.runtime.impl.RangeImpl;

public class GenRangeType implements SimpleType {

/**
* Represents the "generic" type of the current list
*/
private final Type gen;


public GenRangeType(Type gen) {
this.gen = gen;
}

@Override
public boolean isInstanceOf(Object o) {
if (o instanceof RangeImpl rangeImpl) {
return gen.isInstanceOf(rangeImpl.getLowEndPoint()) && gen.isInstanceOf(rangeImpl.getHighEndPoint());
} else {
return false;
}
}

@Override
public boolean isAssignableValue(Object value) {
if ( value == null ) {
return true; // a null-value can be assigned to any type.
}
if (!(value instanceof RangeImpl)) {
return gen.isAssignableValue(value);
}
return isInstanceOf(value);
}

@Override
public String getName() {
return "[anonymous]";
}

public Type getGen() {
return gen;
}

@Override
public boolean conformsTo(Type t) {
return (t instanceof GenRangeType && this.gen.conformsTo(((GenRangeType) t).gen)) || t == BuiltInType.RANGE;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.kie.dmn.feel.lang.ast.QualifiedNameNode;
import org.kie.dmn.feel.lang.ast.QuantifiedExpressionNode;
import org.kie.dmn.feel.lang.ast.RangeNode;
import org.kie.dmn.feel.lang.ast.RangeTypeNode;
import org.kie.dmn.feel.lang.ast.StringNode;
import org.kie.dmn.feel.lang.ast.TypeNode;
import org.kie.dmn.feel.lang.ast.UnaryTestListNode;
Expand Down Expand Up @@ -586,6 +587,12 @@ public BaseNode visitListType(FEEL_1_1Parser.ListTypeContext ctx) {
return new ListTypeNode(ctx, type);
}

@Override
public BaseNode visitRangeType(FEEL_1_1Parser.RangeTypeContext ctx) {
TypeNode type = (TypeNode) visit(ctx.type());
return new RangeTypeNode(ctx, type);
}

@Override
public BaseNode visitContextType(FEEL_1_1Parser.ContextTypeContext ctx) {
List<String> pNames = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.kie.dmn.feel.lang.types.DefaultBuiltinFEELTypeRegistry;
import org.kie.dmn.feel.lang.types.FEELTypeRegistry;
import org.kie.dmn.feel.lang.types.GenListType;
import org.kie.dmn.feel.lang.types.GenRangeType;
import org.kie.dmn.feel.lang.types.ScopeImpl;
import org.kie.dmn.feel.lang.types.SymbolTable;
import org.kie.dmn.feel.lang.types.VariableSymbol;
Expand Down Expand Up @@ -144,6 +145,10 @@ public void recoverScope( String name ) {
scopeType = ((GenListType) scopeType).getGen();
}

if (scopeType instanceof GenRangeType) {
scopeType = ((GenRangeType) scopeType).getGen();
}

if (resolved != null && scopeType instanceof CompositeType) {
pushScope(scopeType);
CompositeType type = (CompositeType) scopeType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
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.util.EvaluationContextTestUtil;
import org.kie.dmn.feel.util.NumberEvalHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -98,7 +99,7 @@ private List<Boolean> parseCompileEvaluate(String feelLiteralExpression, Object
FEELEventListenersManager mgr = new FEELEventListenersManager();
SyntaxErrorListener listener = new SyntaxErrorListener();
mgr.addListener(listener);
EvaluationContext emptyContext = CodegenTestUtil.newEmptyEvaluationContext(mgr);
EvaluationContext emptyContext = EvaluationContextTestUtil.newEmptyEvaluationContext(mgr);
CompiledFEELUnaryTests compiledUnaryTests = parse(feelLiteralExpression, mgr, listener);
LOG.debug("{}", compiledUnaryTests);
List<Boolean> result = compiledUnaryTests.getUnaryTests()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.kie.dmn.feel.runtime.FEELTernaryLogicTest;
import org.kie.dmn.feel.runtime.functions.CustomFEELFunction;
import org.kie.dmn.feel.util.CompilerUtils;
import org.kie.dmn.feel.util.EvaluationContextTestUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -226,7 +227,7 @@ void filter_path_tricky1() {
CompiledFEELExpression nameRef = CompilerUtils.parseCodegen("[ {x:1, y:2}, {x:2, y:3} ][x]");
LOG.debug("{}", nameRef);

EvaluationContext context = CodegenTestUtil.newEmptyEvaluationContext();
EvaluationContext context = EvaluationContextTestUtil.newEmptyEvaluationContext();
context.setValue("x", 2);
Object result = nameRef.apply(context);
LOG.debug("{}", result);
Expand All @@ -239,7 +240,7 @@ void filter_path_tricky2() {
CompiledFEELExpression nameRef = CompilerUtils.parseCodegen("[ {x:1, y:2}, {x:2, y:3} ][x]");
LOG.debug("{}", nameRef);

EvaluationContext context = CodegenTestUtil.newEmptyEvaluationContext();
EvaluationContext context = EvaluationContextTestUtil.newEmptyEvaluationContext();
context.setValue("x", false);
Object result = nameRef.apply(context);
LOG.debug("{}", result);
Expand Down Expand Up @@ -410,7 +411,7 @@ void nameReference() {
CompiledFEELExpression nameRef = parseCodegen(inputExpression, mapOf(entry("someSimpleName", BuiltInType.STRING) ) );
LOG.debug("{}", nameRef);

EvaluationContext context = CodegenTestUtil.newEmptyEvaluationContext();
EvaluationContext context = EvaluationContextTestUtil.newEmptyEvaluationContext();
context.setValue("someSimpleName", 123L);
Object result = nameRef.apply(context);
LOG.debug("{}", result);
Expand All @@ -425,7 +426,7 @@ void qualifiedName() {
CompiledFEELExpression qualRef = parseCodegen(inputExpression, mapOf(entry("My Person", personType) ) );
LOG.debug("{}", qualRef);

EvaluationContext context = CodegenTestUtil.newEmptyEvaluationContext();
EvaluationContext context = EvaluationContextTestUtil.newEmptyEvaluationContext();
context.setValue("My Person", mapOf( entry("Full Name", "John Doe"), entry("Age", 47) ));
Object result = qualRef.apply(context);
LOG.debug("{}", result);
Expand Down Expand Up @@ -456,7 +457,7 @@ void qualifiedName2() {
CompiledFEELExpression qualRef = parseCodegen(inputExpression, mapOf(entry("My Person", personType) ) );
LOG.debug("{}", qualRef);

EvaluationContext context = CodegenTestUtil.newEmptyEvaluationContext();
EvaluationContext context = EvaluationContextTestUtil.newEmptyEvaluationContext();
context.setValue("My Person", new MyPerson());
Object result = qualRef.apply(context);
LOG.debug("{}", result);
Expand All @@ -471,7 +472,7 @@ void qualifiedName3() {
CompiledFEELExpression qualRef = parseCodegen(inputExpression, mapOf(entry("a date", dateType)));
LOG.debug("{}", qualRef);

EvaluationContext context = CodegenTestUtil.newEmptyEvaluationContext();
EvaluationContext context = EvaluationContextTestUtil.newEmptyEvaluationContext();
context.setValue("a date", LocalDate.of(2016, 8, 2));
Object result = qualRef.apply(context);
LOG.debug("{}", result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import java.util.Map;

import org.junit.jupiter.api.Test;
import org.kie.dmn.feel.codegen.feel11.CodegenTestUtil;
import org.kie.dmn.feel.util.EvaluationContextTestUtil;
import org.kie.dmn.feel.lang.Type;
import org.kie.dmn.feel.lang.types.BuiltInType;

Expand All @@ -39,7 +39,7 @@ void evaluateSimpleArray() {
IterationContextNode x = getIterationContextNode("x", getListNode("[ 1, 2, 3, 4 ]", Arrays.asList("1", "2", "3", "4")), "x in [ 1, 2, 3, 4 ]");
IterationContextNode y = getIterationContextNode("y", getNameRefNode(BuiltInType.UNKNOWN, "x"), "y in x");
ForExpressionNode forExpressionNode = new ForExpressionNode(Arrays.asList(x, y), getNameRefNode(BuiltInType.UNKNOWN, "y"), "for x in [ 1, 2, 3, 4 ], y in x return y");
Object retrieved = forExpressionNode.evaluate(CodegenTestUtil.newEmptyEvaluationContext());
Object retrieved = forExpressionNode.evaluate(EvaluationContextTestUtil.newEmptyEvaluationContext());
assertThat(retrieved).isInstanceOf(List.class).asList().
containsExactly(BigDecimal.ONE, BigDecimal.valueOf(2), BigDecimal.valueOf(3), BigDecimal.valueOf(4));
}
Expand All @@ -52,7 +52,7 @@ void evaluateNestedArray() {
IterationContextNode x = getIterationContextNode("x", getNestedListNode("[ [1, 2], [3, 4] ]", firstIterationContext), "x in [ [1, 2], [3, 4] ]");
IterationContextNode y = getIterationContextNode("y", getNameRefNode(BuiltInType.UNKNOWN, "x"), "y in x");
ForExpressionNode forExpressionNode = new ForExpressionNode(Arrays.asList(x, y), getNameRefNode(BuiltInType.UNKNOWN, "y"), "for x in [ [1, 2], [3, 4] ], y in x return y");
Object retrieved = forExpressionNode.evaluate(CodegenTestUtil.newEmptyEvaluationContext());
Object retrieved = forExpressionNode.evaluate(EvaluationContextTestUtil.newEmptyEvaluationContext());
assertThat(retrieved).isInstanceOf(List.class).asList().
containsExactly(BigDecimal.ONE, BigDecimal.valueOf(2), BigDecimal.valueOf(3), BigDecimal.valueOf(4));

Expand Down
Loading

0 comments on commit 9a52c69

Please sign in to comment.