Skip to content

Commit

Permalink
Fixing not() to work both as a logical function and a unary test. Ren…
Browse files Browse the repository at this point in the history
…aming function invocation method name from apply() to invoke() to prevent class with the Function interface methods.
  • Loading branch information
etirelli committed Dec 9, 2016
1 parent 64d808e commit 193ef5a
Show file tree
Hide file tree
Showing 62 changed files with 238 additions and 188 deletions.
Expand Up @@ -61,7 +61,7 @@ public EvaluatorResult evaluate(InternalDMNRuntimeEventManager eventManager, DMN
params[i] = feel.evaluate( paramNames.get( i ), result.getContext().getAll() );
ctx.setValue( paramNames.get( i ), params[i] );
}
Object dtr = dt.apply( ctx, params ).cata(e -> { events.add(e); return null; }, Function.identity());
Object dtr = dt.invoke( ctx, params ).cata( e -> { events.add( e); return null; }, Function.identity());
r = processEvents( events, eventManager, result );
return new EvaluatorResult( dtr, r.hasErrors ? ResultType.FAILURE : ResultType.SUCCESS );
} finally {
Expand Down
Expand Up @@ -95,7 +95,7 @@ public DMNExpressionEvaluatorFunction(String name, List<FormalParameter> paramet
this.resultContext = result;
}

public Object apply(EvaluationContext ctx, Object[] params) {
public Object invoke(EvaluationContext ctx, Object[] params) {
DMNContext previousContext = resultContext.getContext();
try {
DMNContextImpl dmnContext = new DMNContextImpl();
Expand Down
Expand Up @@ -112,7 +112,7 @@ public EvaluatorResult evaluate(InternalDMNRuntimeEventManager eventManager, DMN
}

EvaluationContextImpl ctx = new EvaluationContextImpl( feel.getEventsManager() );
invocationResult = function.applyReflectively( ctx, namedParams );
invocationResult = function.invokeReflectively( ctx, namedParams );

boolean hasErrors = hasErrors( events, eventManager, result );
return new EvaluatorResult( invocationResult, hasErrors ? ResultType.FAILURE : ResultType.SUCCESS );
Expand Down
Expand Up @@ -31,7 +31,7 @@
</semantic:rule>
<semantic:rule id="_7000e1c6-a218-49c6-854b-e9df218aeeac">
<semantic:inputEntry id="_7000e1c6-a218-49c6-854b-e9df218aeeac-0">
<semantic:text>not(="Student")</semantic:text>
<semantic:text>not("Student")</semantic:text>
</semantic:inputEntry>
<semantic:outputEntry id="_7000e1c6-a218-49c6-854b-e9df218aeeac-1">
<semantic:text>"Is not a Student"</semantic:text>
Expand Down
Expand Up @@ -273,7 +273,7 @@ booleanLiteral
**************************/
// #13
simpleUnaryTests
: simpleUnaryTest ( ',' simpleUnaryTest )*
: (simpleUnaryTest|primary) ( ',' (simpleUnaryTest|primary) )*
;

// #7
Expand Down
Expand Up @@ -43,10 +43,6 @@ public static StringNode newStringNode(ParserRuleContext ctx) {
return new StringNode( ctx );
}

public static NotNode newNotNode(ParserRuleContext ctx, BaseNode expr) {
return new NotNode( ctx, expr );
}

public static InfixOpNode newInfixOpNode(ParserRuleContext ctx, BaseNode left, String op, BaseNode right) {
return new InfixOpNode( ctx, left, op, right );
}
Expand Down
Expand Up @@ -69,7 +69,7 @@ public Object evaluate(EvaluationContext ctx) {
function = (FEELFunction) value;
if ( function != null ) {
Object[] p = params.getElements().stream().map( e -> e.evaluate( ctx ) ).toArray( Object[]::new );
Object result = function.applyReflectively( ctx, p );
Object result = function.invokeReflectively( ctx, p );
return result;
} else {
logger.error( "Function not found: '" + name.getText() + "'" );
Expand Down
Expand Up @@ -63,6 +63,10 @@ public static InfixOperator determineOperator(String symbol) {
}
throw new IllegalArgumentException( "No operator found for symbol '" + symbol + "'" );
}

public boolean isBoolean() {
return this == LTE || this == LT || this == GT || this == GTE || this == EQ || this == NE || this == AND || this == OR;
}
}

private InfixOperator operator;
Expand All @@ -84,6 +88,10 @@ public void setOperator(InfixOperator operator) {
this.operator = operator;
}

public boolean isBoolean() {
return this.operator.isBoolean();
}

public BaseNode getLeft() {
return left;
}
Expand Down

This file was deleted.

Expand Up @@ -107,6 +107,11 @@ public UnaryTest evaluate(EvaluationContext ctx) {
if( ((Range)test).includes( (Comparable) o ) ) {
return false;
}
} else {
// test is a constant, so return false if it is equal to "o"
if( test.equals( o ) ) {
return false;
}
}
}
return true;
Expand Down
Expand Up @@ -25,7 +25,6 @@
import org.kie.dmn.feel.runtime.events.FEELEvent;
import org.kie.dmn.feel.runtime.functions.*;

import java.math.BigDecimal;
import java.time.*;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -62,12 +61,12 @@ public String getName() {
@Override
public Object fromString(String value) {
switch ( this ) {
case NUMBER: return BuiltInFunctions.getFunction( NumberFunction.class).apply( value, null, null ).cata(BuiltInType.justNull(), Function.identity());
case NUMBER: return BuiltInFunctions.getFunction( NumberFunction.class).invoke( value, null, null ).cata(BuiltInType.justNull(), Function.identity());
case STRING: return value;
case DATE: return BuiltInFunctions.getFunction( DateFunction.class ).apply( value ).cata(BuiltInType.justNull(), Function.identity());
case TIME: return BuiltInFunctions.getFunction( TimeFunction.class ).apply( value ).cata(BuiltInType.justNull(), Function.identity());
case DATE_TIME: return BuiltInFunctions.getFunction( DateTimeFunction.class ).apply( value ).cata(BuiltInType.justNull(), Function.identity());
case DURATION: return BuiltInFunctions.getFunction( DurationFunction.class ).apply( value ).cata(BuiltInType.justNull(), Function.identity());
case DATE: return BuiltInFunctions.getFunction( DateFunction.class ).invoke( value ).cata(BuiltInType.justNull(), Function.identity());
case TIME: return BuiltInFunctions.getFunction( TimeFunction.class ).invoke( value ).cata(BuiltInType.justNull(), Function.identity());
case DATE_TIME: return BuiltInFunctions.getFunction( DateTimeFunction.class ).invoke( value ).cata(BuiltInType.justNull(), Function.identity());
case DURATION: return BuiltInFunctions.getFunction( DurationFunction.class ).invoke( value ).cata(BuiltInType.justNull(), Function.identity());
case BOOLEAN: return Boolean.parseBoolean( value );
case RANGE:
case FUNCTION:
Expand All @@ -81,7 +80,7 @@ public Object fromString(String value) {

@Override
public String toString(Object value) {
return BuiltInFunctions.getFunction( StringFunction.class ).apply( value ).cata(BuiltInType.justNull(), Function.identity());
return BuiltInFunctions.getFunction( StringFunction.class ).invoke( value ).cata(BuiltInType.justNull(), Function.identity());
}

static <T> Function<FEELEvent, T> justNull() {
Expand Down
Expand Up @@ -16,11 +16,16 @@

package org.kie.dmn.feel.parser.feel11;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.kie.dmn.feel.lang.ast.*;
import org.kie.dmn.feel.runtime.UnaryTest;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;

public class ASTBuilderVisitor
extends FEEL_1_1BaseVisitor<BaseNode> {
Expand Down Expand Up @@ -58,8 +63,11 @@ public BaseNode visitPrimaryParens(FEEL_1_1Parser.PrimaryParensContext ctx) {

@Override
public BaseNode visitLogicalNegation(FEEL_1_1Parser.LogicalNegationContext ctx) {
BaseNode name = ASTBuilderFactory.newNameRefNode( ctx.not_key() );
BaseNode node = visit( ctx.unaryExpression() );
return ASTBuilderFactory.newNotNode( ctx, node );
ListNode params = ASTBuilderFactory.newListNode( ctx.unaryExpression(), Arrays.asList( node ) );

return buildFunctionCall( ctx, name, params );
}

@Override
Expand Down Expand Up @@ -132,7 +140,8 @@ public BaseNode visitPositiveUnaryTestIneq(FEEL_1_1Parser.PositiveUnaryTestIneqC
public BaseNode visitSimpleUnaryTests(FEEL_1_1Parser.SimpleUnaryTestsContext ctx) {
List<BaseNode> 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.SimpleUnaryTestContext ||
ctx.getChild( i ) instanceof FEEL_1_1Parser.PrimaryContext) {
tests.add( visit( ctx.getChild( i ) ) );
}
}
Expand Down Expand Up @@ -310,32 +319,11 @@ public BaseNode visitQuantExprEvery(FEEL_1_1Parser.QuantExprEveryContext ctx) {
return ASTBuilderFactory.newQuantifiedExpression( ctx, QuantifiedExpressionNode.Quantifier.EVERY, list, expr );
}

// @Override
// public BaseNode visitPathExpression(FEEL_1_1Parser.PathExpressionContext ctx) {
// BaseNode expr = visit( ctx.expr );
// BaseNode name = visit( ctx.name );
// return ASTBuilderFactory.newPathExpressionNode( ctx, expr, name );
// }

@Override
public BaseNode visitNameRef(FEEL_1_1Parser.NameRefContext ctx) {
return ASTBuilderFactory.newNameRefNode( ctx );
}

// @Override
// public BaseNode visitExpressionBoxed(FEEL_1_1Parser.ExpressionBoxedContext ctx) {
// BaseNode expr = visit( ctx.expr );
// if ( ctx.filter != null ) {
// BaseNode filter = visit( ctx.filter );
// expr = ASTBuilderFactory.newFilterExpressionNode( ctx, expr, filter );
// if( ctx.qualifiedName() != null ) {
// BaseNode path = visit( ctx.qualifiedName() );
// expr = ASTBuilderFactory.newPathExpressionNode( ctx, expr, path );
// }
// }
// return expr;
// }

@Override
public BaseNode visitPositionalParameters(FEEL_1_1Parser.PositionalParametersContext ctx) {
List<BaseNode> params = new ArrayList<>();
Expand Down Expand Up @@ -381,12 +369,67 @@ public BaseNode visitPrimaryName(FEEL_1_1Parser.PrimaryNameContext ctx) {
BaseNode name = visit( ctx.qualifiedName() );
if( ctx.parameters() != null ) {
ListNode params = (ListNode) visit( ctx.parameters() );
return ASTBuilderFactory.newFunctionInvocationNode( ctx, name, params );
return buildFunctionCall( ctx, name, params );
} else {
return name;
}
}

private String getFunctionName(BaseNode name) {
String functionName = null;
if ( name instanceof NameRefNode ) {
// simple name
functionName = name.getText();
} else {
QualifiedNameNode qn = (QualifiedNameNode) name;
functionName = qn.getParts().stream().map( p -> p.getText() ).collect( Collectors.joining( " ") );
}
return functionName;
}

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 );
}
}

private BaseNode buildNotCall(ParserRuleContext ctx, BaseNode name, ListNode params) {
// if a not() call is found, we have to differentiate between the boolean function
// and the unary tests operator
if( params.getElements().size() == 1 ) {
// if it is a single parameter, we need to check if the type is boolean
BaseNode param = params.getElements().get( 0 );
if( param instanceof UnaryTestNode ) {
return ASTBuilderFactory.newUnaryTestNode( ctx, "not", params );
} else if( param instanceof BooleanNode ) {
return ASTBuilderFactory.newFunctionInvocationNode( ctx, name, params );
} else if( param instanceof NameRefNode ) {
return ASTBuilderFactory.newFunctionInvocationNode( ctx, name, params );
} else if( param instanceof QuantifiedExpressionNode ) {
return ASTBuilderFactory.newFunctionInvocationNode( ctx, name, params );
} else if( param instanceof InstanceOfNode ) {
return ASTBuilderFactory.newFunctionInvocationNode( ctx, name, params );
} else if( param instanceof BetweenNode ) {
return ASTBuilderFactory.newFunctionInvocationNode( ctx, name, params );
} else if( param instanceof InNode ) {
return ASTBuilderFactory.newFunctionInvocationNode( ctx, name, params );
} else if( param instanceof InfixOpNode && ((InfixOpNode)param).isBoolean() ) {
return ASTBuilderFactory.newFunctionInvocationNode( ctx, name, params );
} else if( param instanceof RangeNode ) {
return ASTBuilderFactory.newUnaryTestNode( ctx, "not", params );
} else if( param instanceof DashNode ) {
return ASTBuilderFactory.newUnaryTestNode( ctx, "not", params );
} else {
return ASTBuilderFactory.newUnaryTestNode( ctx, "not", params );
}
} else {
return ASTBuilderFactory.newUnaryTestNode( ctx, "not", params );
}
}

@Override
public TypeNode visitType(FEEL_1_1Parser.TypeContext ctx) {
return ASTBuilderFactory.newTypeNode( ctx );
Expand Down Expand Up @@ -430,7 +473,8 @@ public BaseNode visitCompilation_unit(FEEL_1_1Parser.Compilation_unitContext ctx

@Override
public BaseNode visitNegatedUnaryTests(FEEL_1_1Parser.NegatedUnaryTestsContext ctx) {
BaseNode value = visit( ctx.simpleUnaryTests() );
return ASTBuilderFactory.newUnaryTestNode( ctx, "not", value );
BaseNode name = ASTBuilderFactory.newNameRefNode( ctx.not_key() );
ListNode value = (ListNode) visit( ctx.simpleUnaryTests() );
return buildFunctionCall( ctx, name, value );
}
}
Expand Up @@ -66,6 +66,6 @@ public interface FEELFunction {
* @param params
* @return
*/
Object applyReflectively(EvaluationContext ctx, Object[] params);
Object invokeReflectively(EvaluationContext ctx, Object[] params);

}
Expand Up @@ -34,7 +34,7 @@ public AppendFunction() {
super( "append" );
}

public FEELFnResult<List> apply( @ParameterName( "list" ) List list, @ParameterName( "item" ) Object[] items ) {
public FEELFnResult<List> invoke( @ParameterName( "list" ) List list, @ParameterName( "item" ) Object[] items ) {
if ( list == null ) {
return FEELFnResult.ofError(new InvalidParametersEvent(Severity.ERROR, "list", "cannot be null"));
}
Expand Down
Expand Up @@ -72,8 +72,8 @@ public Symbol getSymbol() {
}

@Override
public Object applyReflectively(EvaluationContext ctx, Object[] params) {
// use reflection to call the appropriate apply method
public Object invokeReflectively(EvaluationContext ctx, Object[] params) {
// use reflection to call the appropriate invoke method
try {
boolean isNamedParams = params.length > 0 && params[0] instanceof NamedParameter;
if ( !isCustomFunction() ) {
Expand Down Expand Up @@ -125,7 +125,7 @@ public Object applyReflectively(EvaluationContext ctx, Object[] params) {
if ( isNamedParams ) {
params = rearrangeParameters( params, this.getParameterNames().get( 0 ) );
}
Object result = apply( ctx, params );
Object result = invoke( ctx, params );
if ( result instanceof Either ) {
@SuppressWarnings("unchecked")
Either<FEELEvent, Object> either = (Either<FEELEvent, Object>) result;
Expand Down Expand Up @@ -165,7 +165,7 @@ public Object applyReflectively(EvaluationContext ctx, Object[] params) {
* @param params
* @return
*/
public Object apply(EvaluationContext ctx, Object[] params) {
public Object invoke(EvaluationContext ctx, Object[] params) {
throw new RuntimeException( "This method should be overriden by classes that implement custom feel functions" );
}

Expand All @@ -189,7 +189,7 @@ private CandidateMethod getCandidateMethod(Object[] params, boolean isNamedParam
CandidateMethod candidate = null;
// first, look for exact matches
for ( Method m : getClass().getDeclaredMethods() ) {
if ( !m.getName().equals( "apply" ) ) {
if ( !m.getName().equals( "invoke" ) ) {
continue;
}
CandidateMethod cm = new CandidateMethod( isNamedParams ? calculateActualParams( m, params, available ) : params );
Expand Down
Expand Up @@ -61,6 +61,7 @@ public class BuiltInFunctions {
new FloorFunction(),
new CeilingFunction(),
new DecisionTableFunction(),
new NotFunction(),

// additional functions not part of the spec version 1.1
new NowFunction()
Expand Down

0 comments on commit 193ef5a

Please sign in to comment.