GithubHelp home page GithubHelp logo

nikku / feelin Goto Github PK

View Code? Open in Web Editor NEW
37.0 4.0 8.0 1.16 MB

A DMN FEEL parser and interpreter written in JavaScript

License: MIT License

JavaScript 51.65% TypeScript 48.35%
feel dmn interpreter dmn-feel

feelin's Introduction

feelin

CI

A DMN FEEL parser and interpreter written in JavaScript. ➡️ Try it out.

Usage

import {
  unaryTest,
  evaluate
} from 'feelin';

unaryTest('1', { '?': 1 }); // true
unaryTest('[1..end]', { '?': 1, end: 10 }); // true

evaluate("Mike's dauther.name", {
  'Mike\'s dauther.name': 'Lisa'
}); // "Lisa"

evaluate('for a in [1, 2, 3] return a * 2'); // [ 2, 4, 6 ]

evaluate('every rate in rates() satisfies rate < 10', {
  rates() {
    return [ 10, 20 ];
  }
}); // false

Features

  • Recognizes full FEEL grammar
  • Context sensitive (incl. names with spaces)
  • Recovers on errors
  • Temporal types and operations
  • Built-in FEEL functions
  • Full DMN TCK compliance

Build and Run

# build the library and run all tests
npm run all

# spin up for local development
npm run dev

# execute FEEL tests in DMN TCK
npm run tck

Related

License

MIT

feelin's People

Contributors

m4r71n avatar nikku avatar renovate[bot] avatar skaiir avatar vsgoulart 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

Watchers

 avatar  avatar  avatar  avatar

feelin's Issues

Implement temporal ranges

What should we do?

Temporal types shall be comparable with each other.

Why should we do it?

Pass more DMN TCK tests.

in clause parsing and interpretation issue

Describe the Bug

if you try to run
3 in [1,2] or true
in feelin playground. It results in false.

Steps to Reproduce

1.visit link
https://nikku.github.io/feel-playground/?e=3+in+%5B1%2C2%5D+or+true&c=%7E3YCAkIC%2BgICAgICAgIC9AgCXnLILCNLEWVvD4A7RTE6e5VQHnL67RAf8ffGpOTFExGjxp2lIHt%2Fmy8diAzMQSsxIFwmdegBxI7oRrX98i6CA&t=expression&st=true

Expected Behavior

I think this is due to the parser not able to determine the proper ending of an unary test when "in" is involved in a comparison.
The disjunction in the above case should involve 3 in [1,2] and true
not [1,2] and true

Environment

  • Host (Browser/Node version), if applicable: [e.g. MS Edge 18, Chrome 69, Node 10 LTS]
  • OS: Windows 10
  • Library version: latest

You cannot use "type":"module" and "require" at the same time!

You cannot use "type":"module" and "require" at the same time!
In your package.json file you use "type":"module" and in "index.js" you use var lezer = require('lezer');. According to node js documentation it's not appropriate
Useful links:
https://nodejs.org/api/esm.html#esm_introduction
https://nodejs.org/docs/latest-v13.x/api/esm.html#esm_enabling

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: C:\work\Projects\BEACON-PROJECTS\forms-manager-server-ts\node_modules\feelin\dist\index.js
require() of ES modules is not supported.
require() of C:\work\Projects\BEACON-PROJECTS\forms-manager-server-ts\node_modules\feelin\dist\index.js from C:\work\Projects\BEACON-PROJECTS\forms-manager-server-ts\src\services\matrixServices\buildMatrixService.ts is an ES module file as it is a .js file whose ne
arest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from C:\work\Projects\BEACON-PROJECTS\forms-manager-server-ts\node_modules\feelin\package.json.

    at Module._extensions..js (internal/modules/cjs/loader.js:1080:13)
    at Object.require.extensions.<computed> [as .js] (C:\work\Projects\BEACON-PROJECTS\forms-manager-server-ts\node_modules\ts-node\src\index.ts:1045:43)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (C:\work\Projects\BEACON-PROJECTS\forms-manager-server-ts\src\services\matrixServices\buildMatrixService.ts:1:1)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Module.m._compile (C:\work\Projects\BEACON-PROJECTS\forms-manager-server-ts\node_modules\ts-node\src\index.ts:1056:23)
    at Module._extensions..js (internal/modules/cjs/loader.js:1092:10)

Steps to Reproduce

  1. Download "0.24.0" version of "feelin" library
  2. Import and use it in express application with TS like import * as feelin from 'feelin' or import { unaryTest, evaluate } from 'feelin';
  3. Build it

Expected Behavior

Should work without errors as in "0.23.0" version

Environment

  • node 14.16.0
  • OS: windows 10
  • Library version: 0.24.0

Add `duration` ranges

Is your feature request related to a problem? Please describe

Currently not implemented are duration ranges and related operations, i.e. in comparisons:

duration("P10Y") in > duration("P10Y")
duration("P5Y") in [duration("P2Y")..duration("P4Y")]

Describe the solution you'd like

Add duration ranges.

Describe alternatives you've considered

None.

Additional context

Required to pass 30+ additional DMN TCK tests.

Round, half even

In DMN FEEL rounding is half-even not up as it is in our current implementation.

round(1.5) = 2
round(2.5) = 2

Path shorthand filtering returns `null` values

Describe the Bug

Path shorthand filtering (list.attribute) should filter null values.

Steps to Reproduce

Cf. playground.

Expected Behavior

[ { x: 1, y: 2 }, { x: null, y: 3 } ].x = [ { x: 1, y: 2 } ]

Environment

  • Host (Browser/Node version), if applicable: any
  • OS: any
  • Library version: v0.43.0

Implement `number` built-in

Is your feature request related to a problem? Please describe

number is a DMN built-in.

Describe the solution you'd like

  • number is available.

Describe alternatives you've considered

None.

Additional context

None.

Support FEEL compliant instance of

Describe the solution

Null safety

null instance of Any = false
null instance of boolean = false

Recognize all built-in types:

date("FOO") instance of date = true

Additional Context

Required to pass 50+ additional DMN TCK tests.

Comparison with `null` is not properly handled

Describe the Bug

Comparison with null values should yield false but yields true in some cases:

Steps to Reproduce

Cf. playground

Expected Behavior

(Cf. also DMN spec):

[ { x: 1, y: 2 }, { x: null, y: 3 } ][ x < 2 ] = [ { x: 1, y: 2 } ]

Environment

  • Host (Browser/Node version), if applicable: any
  • OS: any
  • Library version: v0.43.0

Support evaluation of async functions

This question is purely a matter of interest - no expectation either way! The https://github.com/EdgeVerve/feel project has a very convenient feature in that it allows context functions to be async, for example:

const expression = `{ promiseTrue: promise(true), constantTrue: true }`;
const context = {
    promise: (v) => Promise.resolve(v)
};
feel.parse(expression).build(context).then(console.log);
// { promiseTrue: true, constantTrue: true }

For comparison (and completely expected at the moment) would result in a promise still being in the result. It's easy enough to traverse the results to collate all the leaf nodes and wait for them with a Promise.all, but I just thought I'd ask if something like the above would have a place in the feelin project or not?

const expression = `{ promiseTrue: promise(true), constantTrue: true }`;
const context = {
    promise: (v) => Promise.resolve(v)
};
const result = feelin.evaluate(expression, context);
console.log(result)
// { promiseTrue: Promise { true }, constantTrue: true }

Implement temporal arithmentics

What should we do?

Temporal types can be added and substracted from one another. Implement this.

Why should we do it?

Pass the DMN TCK spec.

Filtering for variable names is not covered by spec

Describe the Bug

As a user I want to extract a value from a list. While [ { a: 1 } ][a] seems to be the intuitive way to do this and supported in feelin it is not covered by the spec:

image (2)

Steps to Reproduce

  1. Evaluate [ { a: 1 } ][a]
  2. Variable extraction happens

Expected Behavior

  • Error is thrown indicating miss-use

Environment

  • Host (Browser/Node version), if applicable: [e.g. MS Edge 18, Chrome 69, Node 10 LTS]
  • OS: [e.g. Windows 7]
  • Library version: [e.g. 2.0.0]

evaluate returns wrown result for expression and context

Describe the Bug

evaluate("string", {}) // return function()

while "string" is determined as a VariableName by the parser

Steps to Reproduce

execute provided example

Expected Behavior

returns null

Environment

  • Host (Browser/Node version), if applicable: Firefox latest
  • OS: MacOS
  • Library version: 3.0.1

Decimal cast rounding behaves incorrectly

Describe the Bug

When casting a number to decimal with a fixed set decimals, several issues can be observed.

  • Negative numbers ALWAYS round up, even in extreme cases like -5.5999999999 -> -5.5
  • Both negative and positive numbers round towards zero when it's ambiguous, which is the opposite behavior from zeebe, which favors rounding towards the nearest even 0.5 -> 0, and -1.5 -> -2, which is also what the DMN spec defines.

Steps to Reproduce

See the playground example for evaluations

Expected Behavior

The rounding behavior should be consistent with the zeebe feel engine.

Support type accessors

Is your feature request related to a problem? Please describe

The DMN spec mandates that temporal and range type expose certain meta-data via properties:

image

Describe the solution you'd like

Make these types available via feelin.

Describe alternatives you've considered

None.

Additional context

None.

Incorrect bundling

Describe the Bug

The package.json file specifies "type": "module", but entry point uses commonjs (require Syntax).
This causes errors when you try to test with vitest, see also: vitest-dev/vitest#4007 (comment)

Steps to Reproduce

  1. Take a look at type definition in package.json
  2. See wrong Syntax in index.json https://www.npmjs.com/package/feelin?activeTab=code

Expected Behavior

The package.json should state "type": "commonjs" or the index.json file should be ESM Syntax.
See also: https://nodejs.org/api/packages.html

Environment

  • Library version: 1.0.0

Support new built-in functions shipped via DMN 1.4 + 1.5

Is your feature request related to a problem? Please describe

New built-in functions, including string join are not supported yet.

Describe the solution you'd like

Latest DMN standard built-ins are supported:

  • string join (#35)
  • context put
  • context merge
  • context (43c0d0f)
  • now (#36)
  • today (#36)
  • round up (#40)
  • round down (#40)
  • round half up (#40)
  • round half down (#40)
  • list replace (DMN 1.5) (#39)

Describe alternatives you've considered

None.

Additional context

None.

Support `round *` built-ins

Is your feature request related to a problem? Please describe

Additional rounding built-ins have been added with DMN 1.4/1.5: round up, round down, round half up, round half down:

image

Describe the solution you'd like

Make these functions available.

Describe alternatives you've considered

None.

Additional context

Added in DMN 1.4 / 1.5.

List indexing with variables

Describe the Bug

When attempting to index a list using a variable, the expression A[R] does not produce the expected output. Despite R being set to 3 and A being a list with the values ["Yes", "No", "Yes"], the expected output should be the third element of list A twice, as in "Yes". However, the output is[ 3, 3, 3 ].

FEEL Playground Issue

Steps to Reproduce

evaluate('A[R]',{
"R": 3,
"A": ["Yes","No","Yes"]
})

Expected Behavior

The expression returns the value of the list on the index which matches the variable value; ie, "Yes"

Environment

  • Host (Browser/Node version), if applicable: Node v21.5.0
  • OS: macOS 14.2.1
  • Library version: 3.0.0

Additionally, this can be replicated in the playground -> FEEL Playground Issue

image image

Support range properties

Be able to access range special properties such as end included:

(<= 10).end included

Available on intervals, ranges and tests.

Does evaluate() cache a parsed expression?

I execute expressions with evaluate() in a loop with up to a thousand iterations.
Usually template or epression languages have separate parse and evaluate methods, the first is needs to execute only once, and the second only evaluated already parse epression with current context (variables).
Is this possible with feelin or does it persorm any internal AST cahing per expression?

`date and time` does not properly parse as a variable

Describe the Bug

FEEL is context sensitive and as such shall allow users, though not encouraged, to override special names such as date and date and time.

This seems to not be properly supported by feelin at this stage (playground ref)

Steps to Reproduce

Cf. playground ref.

Expected Behavior

With the following context:

{
  "date and time": "DATE AND TIME",
  "date": "DATE"
}

Evaluating the following expression:

{
  foo: date and time,
  bar: date,
  date and time: 10,
  date: 100,
  sum: date and time + date
}

Shall yield the following result:

{
  "foo": "DATE AND TIME",
  "bar": "DATE",
  "date and time": 10,
  "date": 100,
  "sum": 110
}

Environment

  • Host (Browser/Node version), if applicable: [e.g. MS Edge 18, Chrome 69, Node 10 LTS]
  • OS: [e.g. Windows 7]
  • Library version: [e.g. 2.0.0]

Context overrides should not override functions?

Describe the Bug

This is a follow-up to #62.

Uncertain if this is 100% a bug, but see: https://nikku.github.io/feel-playground/?e=sum%28%5B1%2C2%5D%29&c=%7B%0A++%22sum%22+%3A+20%0A%7D%0A&t=expression&st=true.

Basically, context variables are overriding base functions now following the change we made to allow variables like "date" to be used, but this causes unexpected behaviors for a lot of people in forms who define variables like sum, which basically stops the sum function from working. I'm wondering where we should go with this.

Steps to Reproduce

  1. See attached playground link

Expected Behavior

Uncertain at the moment, @nikku what do you think?

Different result with FEEL Scala on evaluating the same expression

Describe the Bug

Case: comparing date with datetime.

Javascript(feelin) only have one date object that includes both date and time,
meanwhile FEEL Scala is more strict that date and datetime are two different type and not comparable.
This is causing different behavior issue where the FEEL expression is evaluable in feelin, but not in Scala.
Example: now() > @"2024-01-12" is valid expression in feelin and no problems for frontend.
Meanwhile backend (in this case not built in Javascript) can’t evaluate the expression since the operands are presented in different data types and always return null.

Steps to Reproduce

  1. Evaluate expression that comparing different data type in FEEL Scala.
    e.g. now() > @"2024-01-12", can be tested in playground.
  2. It's not able to evaluate the expression with warning:
    [NOT_COMPARABLE] Can't compare '2024-01-12T09:14:16.36159894@Etc/UTC' with '2024-01-12'
  3. Evaluate the same expression in feelin, can be tested in feelin playground.
  4. The engine is able to evaluate and return the result.

Expected Behavior

It should be at least returning the same output/behavior with camunda FEEL Scala.

Environment

  • Chrome 120.0.6099.199 (Official Build) (arm64)
  • OS: macos sonoma 14.0
  • Library version: ^1.0.0

Support character range based intervals

Both low to high as well as high to low character ranges are supported in DMN FEEL:

"c" in ["b".."d") = true

"e" in ("f"..."a") = true

"c" between "b" and "d" = true

Implement temporal behaviors

What should we do?

  • AtLiteral
  • Temporal helpers
  • Temporal equality checks

Why should we do it?

Pass the DMN TCK tests.

Using the builtin function "get value" returns null instead of false on "false" properties.

Describe the Bug

Using the builtin function "get value" fails on "false" boolean value. It will behave as if the property doesn't exist and returns null.
It works when the value is "true"

Check the playground:
https://nikku.github.io/feel-playground/?e=get+value%28%7B+something%3A+false+%7D%2C+%22something%22%29&c=%7B%7D&t=expression&st=true

Steps to Reproduce

  1. Pass an object into "get value" with a boolean property that is set false
  2. Invoke "get value(object, 'saidProperty')
  3. You get "null" instead of false

Expected Behavior

I should get the exact value of the boolean when I try to retrieve it with "get value".

Adding time to duration doesn't create the same result as adding duration to time

Describe the Bug

Addition of time and duration types should create a time type (See Camunda docs). If the first and second argument of addition are time and duration types, the result is as expected. But if the first and second argument of addition are duration and time (the reverse order of the previous situation), the result becomes a number.

Steps to Reproduce

See playground

Expected Behavior

If the first and second argument of addition are duration and time, the result should be a time.

String comparison does not appear to work for multi-character strings

I tried tracking down in DMN-TCK if there's a test case for this, but couldn't easily find one. I also couldn't find an explicit case in the DMN 1.3 FEEL standard PDF for this, so this may be a wrong expectation of me... However, the Camunda playground (https://camunda.github.io/feel-scala/docs/playground/) does support it.

Describe the Bug

A range expression on strings doesn't work. Tested examples:

  • x >= "1015CJ" and x <= "1020ZZ"
  • x in ["1015CJ".."1020ZZ"]

The reported error is "Failed to evaluate FEEL expression: illegal range start: 1015CJ".

Single character ranges (like x in ["A".."C"]) do work though!

The syntax tree on the playground looks correct however.

Steps to Reproduce

  1. Define a string range expression: ["AA".."ZZ"]
  2. Try to parse or evaluate the expression
  3. The expression fails to evaluate.

Playground: https://nikku.github.io/feel-playground/?e=%5B%22AA%22..%22ZZ%22%5D&c=%7B%0A++%22%3F%22%3A+%22CC%22%0A%7D&t=expression&st=true

Expected Behavior

Multi-character strings can be used in ranges.

Environment

  • Brave (Chromium 124)
  • OS: Linux
  • Library version: unknown - whatever the playground is using :)

Support `list replace`

Is your feature request related to a problem? Please describe

list replace is a new function added with DMN 1.5:

image

Describe the solution you'd like

list replace is available.

Describe alternatives you've considered

None.

Additional context

Available in DMN 1.5.

Null conversion with cause

Is your feature request related to a problem? Please describe

As a user I want to know why null conversion takes place, and get notified accordingly.

Describe the solution you'd like

The tooling allows me to back trace null conversions and their cause.

Describe alternatives you've considered

None.

Additional context

This is required to implement sensible tooling.

npm run tck: TypeError: Cannot read properties of undefined (reading 'text')

Describe the Bug

Hi, I've tried to run the DMN-TCK tests with npm run tck but there is an error:

TypeError: Cannot read properties of undefined (reading 'text')
    at Suite.<anonymous> (file:///home/martin/feelin/test/tck/tck-spec.js:52:38)
    at Object.create (/home/martin/feelin/node_modules/mocha/lib/interfaces/common.js:148:19)
    at context.describe.context.context (/home/martin/feelin/node_modules/mocha/lib/interfaces/bdd.js:42:27)
    at Suite.<anonymous> (file:///home/martin/feelin/test/tck/tck-spec.js:33:9)
    at Object.create (/home/martin/feelin/node_modules/mocha/lib/interfaces/common.js:148:19)
    at context.describe.context.context (/home/martin/feelin/node_modules/mocha/lib/interfaces/bdd.js:42:27)
    at Suite.<anonymous> (file:///home/martin/feelin/test/tck/tck-spec.js:27:5)
    at Object.create (/home/martin/feelin/node_modules/mocha/lib/interfaces/common.js:148:19)
    at context.describe.context.context (/home/martin/feelin/node_modules/mocha/lib/interfaces/bdd.js:42:27)
    at file:///home/martin/feelin/test/tck/tck-spec.js:17:1
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)
ERROR: "tck:test" exited with 1.

Steps to Reproduce

git clone https://github.com/nikku/feelin.git
git clone https://github.com/dmn-tck/tck.git dmn-tck
cd feelin
npm i
npm run tck

Expected Behavior

No error

Environment

  • Host (Browser/Node version), if applicable: Node 18.17.1
  • OS: Windows 10 WSL2 Ubuntu 22.04

A list nested in a list with too many tokens cannot be evaluated

Describe the Bug

The following expression throws an error:

[[substring before(string(now()), "@"),
  substring before(string(now()), "@"),
 substring before(string(now()), "@"),
 substring before(string(now()), "@"),
 substring before(string(now()), "@"),
 substring before(string(now()), "@")]]

"Property <source> is not a valid FEEL expression"

This expression is interpreted to be a list nested in an interval. The evaluation of this expression fails.
When adding "..1" as the "rest" of the interval, the expression is deemed valid again:

[[substring before(string(now()), "@"),
  substring before(string(now()), "@"),
 substring before(string(now()), "@"),
 substring before(string(now()), "@"),
 substring before(string(now()), "@"),
 substring before(string(now()), "@")]..1]

Removing entries from the nested list (reducing the complexity, the number of tokens) leads to a successful evaluation as a list nested in a list:

[[substring before(string(now()), "@"),
  substring before(string(now()), "@"),
 substring before(string(now()), "@"),
 substring before(string(now()), "@"),
 substring before(string(now()), "@")]]

Steps to Reproduce

  1. copy the expressions above into a FEEL editor with the latest FEEL-JS engine

Expected Behavior

I expect the list nested in a list to always be interpreted as such, independent of the complexity of its entries.

Environment

Desktop Modeler 5.16.0, Web Modeler (8.3?) or https://nikku.github.io/feel-playground

Standards compliant equality check

Implement deep comparison by value as mandated by the FEEL specification / DMN TCK.

[{b: [1]}, {b: [2.1,2.2]}, {b: [3]}, {b: [4, 5]}].b = 
[[1], [2.1, 2.2], [3], [4, 5]]  

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.