Giter Club home page Giter Club logo

feel-scala's Introduction

FEEL Scala

Maven Central

A parser and interpreter for FEEL that is written in Scala (see What is FEEL?).

The FEEL engine started as a slack time project, grown into a community-driven project, and is now officially maintained by Camunda. 🚀

It is integrated in the following projects:

Features:

  • Full support for unary-tests and expressions
  • Including built-in functions
  • Extensible by custom functions and object mappers

Usage

Please have a look at the documentation. It describes how to write FEEL expressions (e.g. data types, language constructs, builtin-functions, etc.). Or, check out examples and learning resources here.

Want to try it out? Use the Playground to evaluate FEEL expressions.

Install

Please have a look at the developer documentation. It describes how to integrate the engine into your application, and how to extend/customize it.

Contribution

Contributions are welcome 🎉 Please have a look at the Contribution Guide.

The following resources can help to understand some general concepts behind the implementation:

License

Apache License, Version 2.0

feel-scala's People

Contributors

berkaycanbc avatar bigpuritz avatar dependabot-preview[bot] avatar dependabot[bot] avatar gmgamez avatar hannes-o-brunner avatar ingorichtsmeier avatar kaaquist avatar koevskinikola avatar korthout avatar lzgabel avatar mboskamp avatar mustafadagher avatar nicpuppa avatar p3trur0 avatar psavidis avatar remcowesterhoud avatar richardmward avatar s-frick avatar saig0 avatar sbuettner avatar sccalabr avatar skayliu avatar tasso94 avatar thorbenlindhauer avatar till-stadtler avatar ubaldop avatar upgradingdave avatar vicmosin avatar yanavasileva avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

feel-scala's Issues

Support for Scala's Enumeration

AT:

  • I can access a Scala enumeration field as String
it should "access enumeration field" in {
    
    object WeekDay extends Enumeration {
      type WeekDay = Value
      val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
    }
    
    import WeekDay._
    class A(val b: WeekDay)
    
    eval("a.b", Map("a" -> new A(WeekDay.Mon))) should be(ValString("Mon"))
    eval("a.b", Map("a" -> new A(WeekDay.Fri))) should be(ValString("Fri"))
  }

I can use an expression as positive unary test

AT:

  • I can use an expression as a positive unary test
  • the expression must return a boolean result
  • the input value can be access via variable ?

Example:

"911", starts with( ?, "1800"), starts with(?, "1866")
list contains(?, "@camunda.com")

I can access date, time and duration properties

AT:

  • I can access the properties of a date, time, date-time and duration variable
date("2017-03-10").year                   => 2017
date("2017-03-10").month                => 3
date("2017-03-10").day                    => 10

time("11:45:30+02:00").hour            => 11
time("11:45:30+02:00").minute         => 45
time("11:45:30+02:00").second        => 30
time("11:45:30+02:00")time_offset   => "PT2H"
time("11:45:30+02:00").timezone     => (need to create the time with timezone)

date and time("2017-03-10T11:45:30+02:00")    // all properties of date and time

duration("P2Y3M").years                  => 2
duration("P2Y3M").months               => 3

duration("P1DT2H10M30S").days      => 1
duration("P1DT2H10M30S").hours     => 2
duration("P1DT2H10M30S").minutes  =>10
duration("P1DT2H10M30S").seconds => 30

Support null equality checks

The interpreter crashes for equality checks containing null values on either the left hand or right hand side.
F.ex. for a domain object with field language, which can be null, both person.language = "German" (if current person.language is null) and person.language = null crash

Quickfix in FeelInterpreter.dualOpAny as follows

if (x == ValNull || y == ValNull) {
  ValBoolean(x == y)
} else {
  // existing logic
}

IllegalArgumentException when using java.util.Date as value for input column

Hi,

I have a simple decision-table with one input-column with type "date". Without this plugin it workes fine. Now, when I activate this plugin I got an IllegalArgumentException from DateDataTypeTransformer. It will try to convert a value with type LocalDateTime to java.util.Date but this fails.

At some point this plugin converts my input from type java.util.Date to LocalDateTime.

Greets
Jan

PS: Version of DmnEngine: 7.7.3

Error in Exponentiation function when involved decimal

Exponentiation calculation does not give a correct result when decimal is involved, and it seems to round to integer when performing the calculation
e.g. If i evaluate the following FEEL expression
2 ** 3.1
and the result is
8 instead of 8.574188

I suspect it is caused by the integer rounding toInt at the following file
FeelInterpreter.scala line 65

    case Exponentiation(x, y) =>
      withValOrNull(dualNumericOp(eval(x), eval(y), _ pow _.toInt, ValNumber))

I can invoke an external Java function with array parameter

  • declare an external function to invoke a Java function with array parameter, for example java.lang.String.format(java.lang.String,java.lang.Object[])
function(template,text) external { java: {
  class: “java.lang.String”, 
  method signature: “format(string, object[])”
  }
}
  • change method_signature to method signaturein the function declaration to be align with the spec

Performance issue when using literal expressions

Hi Philipp,

in my setup (camunda 7.7 BPM-Platform CE, feel-scala as engine plugin 1.3.0-SNAPSHOT) I am experiencing performance issues when using literal expressions. If I evaluate an expression as input expression of a decision table, the evaluation of it is much faster in comparison to the evaluation of the same expression as literal expression (e.g. as input expressions in a table: 100 ms; as literal expression in a context object: 1000ms).

Thank you so much for this feel engine!

Kind regards
Arne

Problem implementing Function Provider

I'm trying to implement a custom function so I can use it in FEEL expresions in a DMN decision table.
To do so I'm following the wiki page Function Provider SPI.
The resulting Java class that implements my class looks like this:

public class CoalesceFunctionProvider extends JavaFunctionProvider {

    @Override
    public List<JavaFunction> resolveFunctions(String functionName) {
        if (functionName.equals("coalesce")) {
            Function<List<Val>, Val> function =
                    arguments -> arguments.stream()
                            .filter(Objects::nonNull)
                            .findFirst()
                            .orElse(null);

            List<String> params = Lists.newArrayList("args");
            return Collections.singletonList(new JavaFunction(params, function, true));
        } else {
            return Collections.emptyList();
        }
    }
}

in addition, I created a file named org.camunda.feel.spi.CustomFunctionProvider under the META-INF/services folder, as described in that page.

But when trying to use the function in a DMN decision table, inside a literal expression like:

coalesce(input1, input2, input3)

I get

org.camunda.bpm.dmn.feel.impl.FeelException: failed to evaluate expression 'coalesce(input1, input2, input3)':
no function found with name 'coalesce' and 3 parameters
  1. What am I missing here?
  2. Is the information shown in this other wiki page about defining new functions, meaningful for this purpose too?
    It is not described how where and how to import that function definition into the engine.

Handle variable of type SpinXmlNode as context

Handle XML as context variable according to the spec (DMN 10.3.3).

Example:

<customer name="Kermit">
  <address city="Berlin" zipCode="10961" />
</customer>

is transformed to a context

{
  "customer" : {
    "@name" : "Kermit",
    "address" : {
      "@city" : "Berlin",
      "@zipCode" : "10961"
    }
  }
}

Propagate error in list/context

If a list or context contains an error then this error is currently not propagated. Instead, the list/context contains the error object.

To Reproduce
[notExistingVariable]

Expected behavior
The error is propagated. I.e. an exception is thrown because of the error.

Environment
Feel Engine.

Make it possible to use a local-date-time expression

I want to use the following date-time expression with the Camunda DMN engine:

< date and time("2000-01-01T00:00:00")

Currently, the Camunda value mapper transforms the date-time value of the input expression to date-time (with timezone).

Add context built-in functions

Add the following built-in functions for context from the spec:

get entries({key 1 : "value 1 "})[key="key 1 "].value = "value 1 "

get value({key 1 : "value 1 "}, "key 1 ") = "value 1 "

External function definition with argument Map or List

My external function returns all Values from a context(java.util.Map) as List. This fails with error: fail to invoke method with name 'getValues' and arguments 'List(java.util.Map)' from class 'dmn.externalfunctions.Context').

FEEL Literal Expression
{
  "get_values": function(object) external { 
    java: { 
        class: "dmn.externalfunctions.Context", 
        method_signature: "getValues(java.util.Map)" 
          } 
     },
"my_object": {
   "a": input.a instance of string or input.a = null,
   "b": input.b instance of number,
   "c": input.c instance of list,
   "c1": every c1 in input.c satisfies c1 instance of boolean,
   "d": input.d instance of context
    },
    "valid": every value in get_values(my_object) satisfies value
}
Java External Function 
public static ArrayList getValues(Map<String, Object> myMap) {
   	   return new ArrayList(myMap.values());
     }

How to define external functions with complex types like Maps or Lists?

Arne

I can access a list via index

AT:

  • I can access a list via index
  • the first index is 1 and the last index is -1
  • if index is greater than the size then the result is null
[1,2,3,4,5][1] = [1]
[1,2,3,4,5][5] = [5]
[1,2,3,4,5][-1] = [5]
[1,2,3,4,5][-2] = [4]
[1,2,3,4,5][6] = null

Pass the input variables via context

AT:

  • I can pass the input variables as a context instead of a Map[String, Any]
  • Use Case: lazy loading, more flexibility to provide the variables
trait VariableContext {
  def apply(key: String): Option[Any]
}

class FeelEngine(...) {

  def evalExpression(expression: String, context: VariableContext): EvalResult = {
    eval(FeelParser.parseExpression, expression, context)
  }

Parser issue

This expression doesn't parse:

function(rate, term, amount) (amount * rate/12) / (1 – (1 + rate/12)**-term)

(source: the PMT function, section 10.6.1, the DMN v1.1 spec document).

Test:
org.camunda.feel.parser.FeelParser.parseExpression("function(rate, term, amount) (amount * rate/12) / (1 – (1 + rate/12)**-term)");

{[1.54] error: ')' expected but '–' found

function(rate, term, amount) (amount * rate/12) / (1 – (1 + rate/12)**-term)

Interface to get list of Feel built-in function

I would like to create a REST API for built-in FEEL expression and expected to return list of built in FEEL function (and if it is possible the custom function created by JavaFunctionProvider)

is there any way to return list of all the function ?

min/max function don't work for date types

Describe the bug
The functions min and max doesn't work for date types.

To Reproduce
Steps to reproduce the behavior:

  1. evaluate expression min(list) with list of dates
  2. evaluate expression max(list) with list of dates

Expected behavior
min and max works with all comparable types. Same result as sort(list, function(x,y) x < y)

Environment
FEEL engine 1.6.x

Exception with for* names of variables

Expression:
if forename = "ALA" and birthDate > date("2000-01-01") then true else false
produces exception:

javax.script.ScriptException: failed to parse expression 'if forename = "ALA" and birthDate > date("2000-01-01") then true else false':
[1.13] failure: in' expected but =' found

if forename = "ALA" and birthDate > date("2000-01-01") then true else false
^

Expression:
if surname = "ALA" and birthDate > date("2000-01-01") then true else false
works fine.

I've found that variables with names beginning with "for" cause that problem.
Tested with version 1.2 in Java Spring application.

I can use quoted names as identifiers

AT:

  • allow identifiers with single quotes
  • the name within the single quotes can have any char (e.g. whitespaces, operators etc.) but no single or double quotes

Example:

'my var' 
'foo-bar'
'my readable variable'

Support loop with range and partial result

In a 'for' loop, I can iterate over a given range using '..' notation. And I can access the previous result which are stored in a list variable 'partial'.

for x in 1..10 return if (x=1) then 1 else x+sum(partial)

Provide a Java Value Mapper

AT:

  • provide a value mapper which unpack list and context to Java collections
  • use the Java value mapper as default for factory + plugin
  • allow to set the value mapper via service loader (in case it is used as script engine)

failed to parse expression '-'

08:32:47.953 [main] INFO  o.o.d.t.r.camunda.CamundaTCKTest - Executing test '0004-simpletable-U-test-01 / 003'

08:32:47.955 [main] ERROR o.o.d.t.r.camunda.CamundaTCKTest - FAILURE: unnexpected exception executing test case '0004-simpletable-U-test-01 / 003'
org.camunda.bpm.dmn.engine.impl.DmnEvaluationException: DMN-01002 Unable to evaluate expression for language 'feel': '-'
	at org.camunda.bpm.dmn.engine.impl.DmnEngineLogger.unableToEvaluateExpression(DmnEngineLogger.java:35) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.ExpressionEvaluationHandler.evaluateScriptExpression(ExpressionEvaluationHandler.java:100) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.ExpressionEvaluationHandler.evaluateExpression(ExpressionEvaluationHandler.java:62) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.DecisionTableEvaluationHandler.evaluateInputEntry(DecisionTableEvaluationHandler.java:198) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.DecisionTableEvaluationHandler.isConditionApplicable(DecisionTableEvaluationHandler.java:142) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.DecisionTableEvaluationHandler.evaluateInputForAvailableRules(DecisionTableEvaluationHandler.java:134) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.DecisionTableEvaluationHandler.evaluateDecisionTable(DecisionTableEvaluationHandler.java:108) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.DecisionTableEvaluationHandler.evaluate(DecisionTableEvaluationHandler.java:78) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.DefaultDmnDecisionContext.evaluateDecision(DefaultDmnDecisionContext.java:82) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.DefaultDmnEngine.evaluateDecision(DefaultDmnEngine.java:164) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.DefaultDmnEngine.evaluateDecision(DefaultDmnEngine.java:155) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.omg.dmn.tck.runner.camunda.CamundaTCKTest.executeTest(CamundaTCKTest.java:132) ~[test-classes/:na]
	at org.omg.dmn.tck.runner.junit4.DmnTckRunner.runChild(DmnTckRunner.java:143) [classes/:na]
	at org.omg.dmn.tck.runner.junit4.DmnTckRunner.runChild(DmnTckRunner.java:1) [classes/:na]
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
	at org.omg.dmn.tck.runner.junit4.DmnTckRunner.run(DmnTckRunner.java:112) [classes/:na]
	at org.junit.runners.Suite.runChild(Suite.java:128) [junit-4.12.jar:4.12]
	at org.junit.runners.Suite.runChild(Suite.java:27) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) [.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) [.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) [.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) [.cp/:na]
Caused by: javax.script.ScriptException: failed to parse expression '-':
[1.2] error: `{' expected but end of source found

-
 ^
	at org.camunda.feel.script.FeelScriptEngine$class.compile(FeelScriptEngine.scala:81) ~[feel-engine-1.1.0-SNAPSHOT.jar:1.1.0-SNAPSHOT]
	at org.camunda.feel.script.FeelExpressionScriptEngine.compile(FeelExpressionScriptEngine.scala:14) ~[feel-engine-1.1.0-SNAPSHOT.jar:1.1.0-SNAPSHOT]
	at org.camunda.bpm.dmn.engine.impl.evaluation.ExpressionEvaluationHandler.evaluateScriptExpression(ExpressionEvaluationHandler.java:86) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	... 33 common frames omitted

failed to parse expression '"Medium","Low"'

08:37:44.888 [main] INFO  o.o.d.t.r.camunda.CamundaTCKTest - Executing test '0004-simpletable-U-test-01 / 003'

08:37:44.891 [main] ERROR o.o.d.t.r.camunda.CamundaTCKTest - FAILURE: unnexpected exception executing test case '0004-simpletable-U-test-01 / 003'
org.camunda.bpm.dmn.engine.impl.DmnEvaluationException: DMN-01002 Unable to evaluate expression for language 'feel': '"Medium","Low"'
	at org.camunda.bpm.dmn.engine.impl.DmnEngineLogger.unableToEvaluateExpression(DmnEngineLogger.java:35) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.ExpressionEvaluationHandler.evaluateScriptExpression(ExpressionEvaluationHandler.java:100) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.ExpressionEvaluationHandler.evaluateExpression(ExpressionEvaluationHandler.java:62) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.DecisionTableEvaluationHandler.evaluateInputEntry(DecisionTableEvaluationHandler.java:198) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.DecisionTableEvaluationHandler.isConditionApplicable(DecisionTableEvaluationHandler.java:142) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.DecisionTableEvaluationHandler.evaluateInputForAvailableRules(DecisionTableEvaluationHandler.java:134) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.DecisionTableEvaluationHandler.evaluateDecisionTable(DecisionTableEvaluationHandler.java:108) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.evaluation.DecisionTableEvaluationHandler.evaluate(DecisionTableEvaluationHandler.java:78) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.DefaultDmnDecisionContext.evaluateDecision(DefaultDmnDecisionContext.java:82) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.DefaultDmnEngine.evaluateDecision(DefaultDmnEngine.java:164) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.camunda.bpm.dmn.engine.impl.DefaultDmnEngine.evaluateDecision(DefaultDmnEngine.java:155) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	at org.omg.dmn.tck.runner.camunda.CamundaTCKTest.executeTest(CamundaTCKTest.java:132) ~[test-classes/:na]
	at org.omg.dmn.tck.runner.junit4.DmnTckRunner.runChild(DmnTckRunner.java:143) [classes/:na]
	at org.omg.dmn.tck.runner.junit4.DmnTckRunner.runChild(DmnTckRunner.java:1) [classes/:na]
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
	at org.omg.dmn.tck.runner.junit4.DmnTckRunner.run(DmnTckRunner.java:112) [classes/:na]
	at org.junit.runners.Suite.runChild(Suite.java:128) [junit-4.12.jar:4.12]
	at org.junit.runners.Suite.runChild(Suite.java:27) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) [.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) [.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) [.cp/:na]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) [.cp/:na]
Caused by: javax.script.ScriptException: failed to parse expression '"Medium","Low"':
[1.9] failure: `or' expected but `,' found

"Medium","Low"
        ^
	at org.camunda.feel.script.FeelScriptEngine$class.compile(FeelScriptEngine.scala:81) ~[feel-engine-1.1.0-SNAPSHOT.jar:1.1.0-SNAPSHOT]
	at org.camunda.feel.script.FeelExpressionScriptEngine.compile(FeelExpressionScriptEngine.scala:14) ~[feel-engine-1.1.0-SNAPSHOT.jar:1.1.0-SNAPSHOT]
	at org.camunda.bpm.dmn.engine.impl.evaluation.ExpressionEvaluationHandler.evaluateScriptExpression(ExpressionEvaluationHandler.java:86) ~[camunda-engine-dmn-7.6.0.jar:7.6.0]
	... 33 common frames omitted

Custom context provider

First thanks for the nice library!

I am using feel-scala as an embedded expression interpreter from within a Scala program. There is some impedance mismatch between Java and Scala classes / objects which impedes direct access of given Scala domain objects and their properties with the supported Java context implementation.
F.ex. to specify a nullable property, in Scala it would be defined as name: Option[String], another example is enumerations whose value are implemented as Scala case classes and their corresponding objects.
In order to seamlessly access the Scala domain objects, it would be helpful to be able to specify corresponding custom context providers, which - given an object of a certain base class / interface - could enumerate its properties (in a first step I only need properties, not methods).
Something like contextProvider: (Superclass/MarkerInterface) -> Map[String, <FeelProperty>]

My current workaround is to replace all the domain objects (and their nested entities, where necessary) into a Map[String, Any] before handing over the context to the feel interpreter. While this works, it is probably not that efficient, since I have to map the full domain model, as I do not know what is being accessed in the expressions (rather than having the interpreter doing context resolution only when necessary).

Date conversion function

Hi,

I am getting to grips with FEEL, and so I may be doing something wrong rather than this being an issue - so I apologise up front if that is the case!

If I try to run date("2012-12-25") - date("2012-12-24") = duration("P1D"), then I get a warning WARN org.camunda.feel.FeelEngine - Suppressed failure: expected Number, Date, Time or Duration but found 'ValDate(2012-12-25)' and returns false rather than true.

Interestingly if I change the expresssion to be date and time("2012-12-25T00:00:00") - date and time("2012-12-24T00:00:00") = duration("P1D"), then it does not cause a warning and returns true as expected.

I am executing the expressions via the ScriptEngineManager and am calling it using Java rather than Scala (which I have never experienced before this - and as such I have attempted to debug into it, but I have been unable work out how to!)

Not-A-Number should be Null

AT:

  • if number is nAN or pos/neg infinity then it is null
  • if a number is add/sub/mul/div to nAn then the result is null
  • if a number is divided by zero then the result is null
  • create log entry for this failures

Support for Scala's Option

AT:

  • I can access a Scala option field which is either null or the inner value
it should "access option field" in {
 
   class A(val b: Option[Int])
    
    eval("a.b", Map("a" -> new A(Some(2)))) should be(ValNumber(2))
    eval("a.b", Map("a" -> new A(None))) should be(ValNull)
  }

Support var args for built-in functions

AT:

  • I can invoke built-in functions with var args, e.g. sum(1,2,3) == sum([1,2,3])
  • if a built-in function is invoked with invalid arguments then it returns null and log the failure
  • if a named parameter is not set on invocation then the parameter should be set to null

Use ` instead of ' to escape names

If the name contains a special character (e.g. whitespace) then it needs to be escaped. Currently, it can be escaped by wrapping it into single quotes '.

Describe the solution you'd like
It is more common to wrap the name by ` instead.

Support async functions

Thanks for awesome project.

I have some function that I'd like to be invoked asynchronously.
A way, that induce a breaking change, would be modifying ValFunction to something like:

case class ValFunction(
    params: List[String],
    invoke: Either[List[Val] => Val, Async]
    hasVarArgs: Boolean = false,
    requireInputVariable: Boolean = false) extends Val {

    val paramSet: Set[String] = params.toSet
}
trait Async {
  def apply(args: List[Val])(implicit ec: ExecutionContext): Future[Val]
}

alternatively there could be a new ValAsyncFunction.

And then there would be new method in FeelInterpreter:

def evalAsync(expression: Exp)(implicit context: Context, ec: ExecutionContext): Future[Val]

to avoid duplication the current eval could be implemented as such:

def eval(expression: Exp)(implicit context: Context): Val = {
  implicit val sameThreadExecutionContext = ExecutionContext.fromExecutor(_.run())
  Await.result(evalAsync(expression), Duration.Inf)
}

Would you be open to such a feature?

Handle variable of type SpinJsonNode as context

When a decision is evaluated by the Camunda Rest API and a variable is submitted as JSON then the FEEL engine gets a variable of type SpinJsonNode. It should transform the JSON variable into a context.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.