FEEL
Friendly enough expression language (FEEL) for the purpose of giving standard executable semantics to many kinds of expressions in decision model has the following features;
- Side-effect free
- Simple data model with numbers, dates, strings, lists and contexts
Simple syntax designed for a wide audience
Three-valued logic (true, false, null)
Data Types
FEEL supports the following datatypes:
number
string
boolean
days and time duration
years and months duration
time
date and time
list
context
Contexts, Lists, Qualified Names
A context is a map of key-value pairs called context entries, and is written using curly braces to delimit the context, commas to separate the entries, and a colon to separate key and value. The key can be a string or a name. The value is an expression.
A list is written using square brackets to delimit the list, and commas to separate the list items. A singleton list is equal to its single item, Example "[e]=e" for all FEEL expressions.
Contexts and lists can reference other contexts and lists, giving rise to a directed acyclic graph. Naming is path based. The qualified name (QN) of a context entry is of the form "N1.N2 ... Nn" where N1 is the name of an in-scope context.
Nested lists encountered in the interpretation of N1.N2 ... Nn are preserved. For example,
[{a: [{b: 1}, {b: [2.1, 2.2]}]}, {a: [{b: 3}, {b: 4}, {b: 5}]}].a.b =
[[{b: 1}, {b: [2.1, 2.2]}], [{b: 3}, {b: 4}, {b: 5}]].b =
[[1, [2.1, 2.1]],[ 3, 4, 5]]
Nested lists can be flattened using the flatten() built-in function.
Literal and Data Type Semantics
Equality
In general, the values to be compared must be of the same kind. For example; FEEL("1" = 1) = null
data type | e1 = e2 |
---|---|
list | lists must be same length, and e1[n] = e2[n] must be equal |
context | context must be same set of keys and every e1.key = e2.key must be equal |
range | range start and end points must be same |
number | value of numbers must be equal |
string | value of string must be equals with case sensitivity |
date | all date components must be equal |
date and time | all date and time components must be equal |
time | all time components must be equal |
days and time duration | value of duration must be equal |
years and months duration | value of duration must be equal |
boolean | both e1 and e2 must be true or false |
number
Feel numbers based on decimal format, there is no integer or float data types.
Literals consist of base 10 digits and an optional decimal point. –INF, +INF, and NaN literals are not supported. There is no distinction between -0 and 0. The number(from, grouping separator, decimal separator) built-in function supports a richer literal format. For example, FEEL(number("1.000.000,01", ".", ",")) = 1000000.01
There is no value for NaN, positiveInfinity, or negativeInfinity. Use null instead.
FEEL Expression | Value |
---|---|
decimal(1,2) | 1.00 |
.25 + 0.2 | 0.45 |
0.10 * 30.00 | 3.0000 |
1 + 3/2*2 - 2 ** 3 | -4.0 |
1/3 | 0.333333333 |
decimal(1/3, 2) | 0.22 |
1 = 1.0000 | true |
1.01 / 2 | 0.505 |
decimal(0.502, 2) | 0.50 |
decimal(0.515, 2) | 0.52 |
1.0*10**3 | 1000.0 |
string
Literal strings can be double-quoted or single quoted sequence of characters. e.g. "abc" All strings compared as UTF-8 encoding.
boolean
Boolean literals are true and false.
time
Feel does not have time literals although time values can be expressed using a string literal and time built-in function.
Time values also have optional time zone offset. If no time zone offset specified, time is interpreted as local time of day.
Time data type has also provides a context of sequence of numbers for the hour, minute, second, and an optional time offset.
date
Feel does not have time literals although time values can be expressed using a string literal and date built-in function.
Date values has no time zone offset and interpreted as UTC time zone.
Date data type has also provides a context of sequence of numbers for the year, month, day of month.
date time
Feel does not have time literals although time values can be expressed using a string literal and date time built-in function.
Date time also have optional time zone offset. If no time zone offset specified, time is interpreted as UTC time zone.
Date time data type has also provides a context of sequence of numbers for the year, month, day, hour, minute, second, and optional time offset.
days and time duration
Feel does not have time literals although time values can be expressed using a string literal and days and time duration built-in function. Duration format expressed in ISO 8601 duration syntax.
Days and time duration data type has also provides a context of sequence of numbers for the days, hours, minutes, and seconds of duration and normalized such that the sum of these numbers is minimized. For example; FEEL(duration("P0DT25H")) = P1DT1H
years and months duration
Feel does not have time literals although time values can be expressed using a string literal and years and months duration built-in function. Duration format expressed in ISO 8601 duration syntax.
Days and time duration data type has also provides a context of sequence of numbers for the years and months of duration and normalized such that the sum of these numbers is minimized. For example; FEEL(duration("P0Y13M")) = P1Y1M
Lists and filters
Lists are immutable and may be nested. First element of list can be accessed using L[1] and last element accessed using L[-1]. If no element exists in list then L[n] = null.
Lists can be filtered with a boolean expression in square brackets. The expression in square brackets can reference a list element using name "item". If list entries is a context, item entries may be referenced within filter expression without item prefix. For example;
[1, 2, 3, 4][item > 2] = [3, 4]
[{x:1, y:2}, {x:2, y:3}][x=1] = {x:1, y:2}
For convenience a selection using "." operator with a list of contexts on its left hand side returns a list of selections. For example;
[{x:1, y:2}, {x:2, y:3}].y = [2,3]
Context
Context is a collection of key and expression pairs called context entries. Keys are mapped to string values. The syntax for selecting value of context entry named key1 from context1 is "context1.key1"
To retrieve a list of key,value pairs from a context m, the following built-in function may be used: get entries(m).
Range
Feel supports the compact syntax for range of values.
FEEL Expression | Value |
---|---|
5 in ( <=5 ) | true |
5 in ( (5..10] ) | false |
5 in ( [5..10] ) | true |
5 in ( 4, 5, 6 ) | true |
5 in ( <5, >5 ) | false |
2012-12-31 in ( (2012-12-25..2013-02-14) ) | true |
Negation
Negation is performed with builtin not function. Semantics of negation is;
a | not(a) |
---|---|
true | false |
false | true |
otherwise | null |
Binary logic
Binary logic can be expressed with "and" and "or" keywords. Binary logic semantics;
a | b | a and b | a or b |
---|---|---|---|
true | true | true | true |
true | false | false | true |
true | otherwise | null | true |
false | true | false | true |
false | false | false | false |
false | otherwise | false | null |
otherwise | otherwise | null | null |
Xml Data Mapping
FEEL supports the xml data context in contexts. Xml namespaces are not supported but elements can be accessed with prefix. All xml values are interpreted as string values.
XML | context entry | Remarks |
---|---|---|
<e /> | e : null | empty element mapped to null value. |
<n:e /> | n$e : null | namespaces are ignored and prefix can be used to access entry. |
<e>v</e> | e : "v" | single element with value |
<e>v1</e><e>v2</e> | e : ["v1","v2"] | repeating element with value |
<e a="v">v1</e> | e$a : "v" e : "v1" | attributes are prefixed with @ element value accessed with name |
<e a="v">v1 <c>v1</c> </e> | e$a : "v", e.c : "v1" | attributes are prefixed with @ childs accessed with name |
Builtin Functions
To promote interoperability, FEEL includes a library of built-in functions. The syntax and semantics of the built-ins are required for a conformant FEEL implementation.
Conversion Functions
FEEL supports many conversions between values of different types. Of particular importance is the conversion from strings to dates, times, and durations. There is no literal representation for date, time, or duration. Also, formatted numbers such as 1,000.00 must be converted from a string by specifying the grouping separator and the decimal separator.
Name and Parameters | Parameter | Description | Example |
---|---|---|---|
date(from) | date string | convert from to a date | date("2012-12-25") – date("2012-12-24") = duration("P1D") |
date(from) | date and time | convert from to a date (set time components to null) | date(date and time("2012-12-25T11:00:00Z")) = date("2012-12-25") |
date(year, month, day) | year, month, day are numbers | creates a date from year, month, day component values | date(2012, 12, 25) = date("2012-12-25") |
date and time(date, time) | date is a date or date time; time is a time | creates a date time from the given date (ignoring any time component) and the given time | date and time ("2012-12-24T23:59:00") = date and time (date("2012-12-24”), time(“T23:59:00")) |
date and time(from) | date time string | convert from to a date and time | date and time("2012-12-24T23:59:00") + duration("PT1M") = date and time("2012-12- 25T00:00:00") |
time(from) | time string | convert from to time | time("23:59:00z") + duration("PT2M") = time("00:01:00@Etc/UTC") |
time(from) | time, date and time | convert from to time (ignoring date components) | time(date and time("2012-12-25T11:00:00Z")) = time("11:00:00Z") |
time(hour, minute, second, offset) | hour, minute, second, are numbers, offset is a days and time duration, or null | creates a time from the given component values | time(“T23:59:00z") = time(23, 59, 0, duration(“PT0H”)) |
number(from, grouping separator, decimal separator) | string, string, string | convert from to a number | number("1 000,0", " ", ",") = number("1,000.0", ",", ".") |
string(from) | non-null | convert from to a string | string(1.1) = "1.1" string(null) = null |
duration(from) | duration string | convert from to a days and time or years and months duration | date and time("2012-12-24T23:59:00") - date and time("2012-12-22T03:45:00") = duration("P2DT20H14M") duration("P2Y2M") = duration("P26M") |
years and months duration(from, to) | both are date and time | return years and months duration between from and to | years and months duration( date("2011-12-22"), date("2013-08-24") ) = duration("P1Y8M") |
Boolean Functions
Name and Parameters | Parameter | Description | Example |
---|---|---|---|
boolean(from) | string | boolean conversion | boolean("true") = true boolean("false") = false boolean(true) = true |
not(negand) | boolean | logical negation | not(true) = false not(null) = null |
String Functions
Name and Parameters | Parameter | Description | Example |
---|---|---|---|
substring(string, start position, length?) | string, number | return length (or all) characters in string, starting at start position. 1st position is 1, last position is -1 | substring("foobar",3) = "obar" |
string length(string) | string | return length of string | string length("foo") = 3 |
upper case(string) | string | return uppercased string | upper case("aBc4") = "ABC4" |
lower case(string) | string | return lowercased string | lower case("aBc4") = "abc4" |
substring before (string, match) | string, string | return substring of string before the match in string | substring before("foobar", "bar") = "foo" |
substring after (string, match) | string, string | return substring of string after the match in string | substring after("foobar", "ob") = "ar" |
replace(input, pattern, replacement, flags?) | string | regular expression pattern matching and replacement | replace("abcd", "(ab)|(a)", "[1=$1][2=$2]") = "[1=ab][2=]cd" |
contains(string, match) | string | does the string contain the match ? | contains("foobar", "of") = false |
starts with(string, match) | string | does the string start with the match ? | starts with("foobar", "fo") = true |
ends with(string, match) | string | does the string end with the match ? | ends with("foobar", "r") = true |
matches(input, pattern, flags?) | string | does the input match the regexp pattern ? | matches("foobar", "^fo*b") = true |
List Functions
Name and Parameters | Parameter | Description | Example |
---|---|---|---|
list contains(list, element) | list, any element of the semantic domain including null | does the list contain the element ? | list contains([1,2,3], 2) = true |
count(list) | list | return size of list | count([1,2,3]) = 3 |
min(list) | (list of) comparable items | return minimum item | min([1,2,3]) = 1 |
sum(list) | (list of) numbers | return sum of numbers | sum([1,2,3]) = 6 |
mean(list) | (list of) numbers | return arithmetic mean (average) of numbers | mean([1,2,3]) = 2 |
and(list) | (list of) Boolean items | return false if any item is false, else true if all items are true, else null | and([false,null,true]) = false and(false,null,true) = false |
or(list) | (list of) Boolean items | return true if any item is true, else false if all items are false, else null | or([false,null,true]) = true |
sublist(list, start position, length?) | list, number1, number2 | return list of length (or all) elements of list, starting with list[start position]. 1st position is 1, last position is -1 | sublist([1,2,3], 1, 2) = [2] |
append(list, item...) | list, any element including null | return new list with items appended | append([1], 2, 3) = [1,2,3] |
concatenate(list...) | list | return new list that is a concatenation of the arguments | concatenate([1,2],[3]) = [1,2,3] |
insert before(list, position, newItem) | list, number1, any element including null | return new list with newItem inserted at position | insert before([1,3],1,2) = [1,2,3] |
remove(list, position) | list, number1 | list with item at position removed | remove([1,2,3], 2) = [1,3] |
reverse(list) | list | reverse the list | reverse([1,2,3]) = [3,2,1] |
index of(list, match) | list, any element including null | return ascending list of list positions containing match | index of([1,2,3,2],2) = [2,4] |
union(list...) | list | concatenate with duplicate removal | union([1,2],[2,3]) = [1,2,3] |
distinct values(list) | list | duplicate removal | distinct values([1,2,3,2,1] = [1,2,3] |
flatten(list) | list | flatten nested lists | flatten([[1,2],[[3]], 4]) = [1,2,3,4] |
Numeric functions
Name and Parameters | Parameter | Description | Example |
---|---|---|---|
decimal(n, scale) | number, number1 | return n with given scale | decimal(1/3, 2) = .33 |
floor(n) | number | return greatest integer <= n | floor(1.5) = 1 |
ceiling(n) | number | return smallest integer >= n | ceiling(1.5) = 2 |
Copyright © 2010 - 2023 Emakin. All rights reserved.