Expression language reference

FEEL · the catalog of what's allowed

Conditions, computations and guards are written in FEEL (DMN's expression language), not in Java expressions. Here is what the BKIR profile accepts: operators, reserved words, functions and literals. The authoritative source is the checker schema/feel_check.py; this is a practical subset that evolves toward conformant FEEL.

The first thing that changes from Java: AND/OR/NOT are words (and, or, not(...)), not && / || / !; and equality is a single =, not ==.

From Java to FEEL

The direct mapping of the most common operators and idioms. If you come from Java, this is the table that resolves 90% of the doubts.

JavaBKIR · FEELWhat it is
a && ba and bLogical AND (word)
a || ba or bLogical OR (word)
!anot(a)Negation (function)
a == ba = bEquality: a single =
a != ba != bInequality (same)
a < b · <= · > · >=a < b · <= · > · >=Relational (same)
x == nullx = nullComparison with null
c ? x : yif c then x else yConditional (ternary)
x >= lo && x <= hix between lo and hiInclusive range
set.contains(x)x in [a, b, c]Membership in a set
list.stream().anyMatch(i -> p)some i in list satisfies pDoes any exist?
list.stream().allMatch(i -> p)every i in list satisfies pDo all hold?
list.stream().map(..).reduce(..)sum(for i in list return ..)Map + aggregation
obj.fieldobj.fieldField access (same)
list.get(0)list[0]Index / filter
list.size()count(list)Size
Math.pow(a, b)a ** bPower
What about string concatenation? User-facing text is not built in FEEL: it goes in the message templates ({ template, args }) of the output catalog. FEEL is reserved for business logic (conditions and computations).

Reserved words

These identifiers are keywords of the language: they cannot be used as names of variables, fields or terms. They are all the control syntax and the word-operators.

andornot ifthenelse forinreturn someeverysatisfies betweeninstanceof truefalsenull
Deeper: not, and, or are logical operators; between … and …, in and instance of … are comparison operators; the rest builds the control structures (if/then/else, for/return, some/every … satisfies). true/false/null are literals.

Operators

Full catalog of the profile's operators, by family.

Arithmetic

OperatorMeaningExample
+ - * /Add, subtract, multiply, dividesaldo - monto
**Powerbase ** exponente
- (unary)Numeric negation-importe

Comparison

OperatorMeaningExample
=Equal (not ==!)cliente = null
!=Not equalestado != "ACTIVA"
< <= > >=Less, less-or-equal, greater, greater-or-equalmonto <= 0
between … and …Inclusive rangeedad between 18 and 65
inMembership in a list or intervaltipo in ["visa", "mastercard"]
instance ofType checksaldo instance of number

Logical

OperatorMeaningExample
andConjunctionactivo and saldo > 0
orDisjunctionvip or antiguo
not(…)Negationnot(tarjeta.valida)

Access and invocation

OperatorMeaningExample
a.bField / path accesscuenta.saldo
a[i]List index or filterstock[i.sku] · pax[usuario != null]
f(args)Function call (library or your own FN_)FN_VALIDAR_TARJETA(numero)

Control and quantifiers

FEEL's only control structures: conditional, comprehension and quantifiers. They are expressions (they return a value), not statements.

FormWhat it doesExample
if … then … else …Conditional (always with else)if activo then 1 else 0
for … in … return …Comprehension: produces a listfor i in items return i.precio * i.cantidad
some … in … satisfies …Does any element hold?some i in items satisfies stock[i.sku] < i.cantidad
every … in … satisfies …Do all elements hold?every i in items satisfies i.cantidad > 0
The variable of for/some/every is bound only inside the expression; the validator recognizes it and does not require declaring it in scope.

Literals

TypeSyntaxExample
Number42 · 3.14monto > 1000
Text"…"estado = "ACTIVA"
Temporal@"…"fecha >= @"2024-01-01"
Booleantrue · falsecuenta.permiteSobregiro = true
Nullnullcliente = null
List[a, b, c]tipo in ["visa", "amex"]
Context{ key: value }{ item: i, costo: precio }
Interval[a..b] · (a..b) · [a..b)x in [0..100]
Intervals: the bracket [/] is inclusive and the parenthesis (/) is exclusive, mixable: [0..100) includes 0 and excludes 100.

Library functions

Functions available in the current subset. In addition, any computation of your own (FN_…) is callable as a function, declaring the dependency in uses.

Numeric and list

sum(list)count(list)min(list)max(list) abs(n)ceiling(n)floor(n)modulo(a, b)number(s)

Text

string(x)substring(s, start)contains(s, sub)matches(s, pattern)

Temporal (environment)

date(s)time(s)duration(s)today()now()
today() and now() are dependencies on the ambient clock: the Unit that uses them must declare env: [clock] (so equivalence tests can freeze the clock). The validator enforces it in both directions.

BKIR profile rules

RuleDetail
names = identifiersNo spaces (saldoDisponible, not saldo disponible). The readable label goes in the term's label in the vocabulary.
= is equalityA single sign, not ==. Assignment does not exist (FEEL is pure expressions).
FN_X(args)Computations are called as functions; the dependency must be declared in uses. It enables self-contained conditions over foreign logic.
env: [clock]Required if the expression uses today()/now().
Name resolutionEvery free root must exist in scope (inputs of the condition/computation; inputs + binds + forEach variable + principal in an operation). The validator checks it.

Known limits

The current subset (schema/feel_check.py) is practical but not yet conformant FEEL. Each limit that shows up while modeling is a candidate to extend the checker.

Not supported (yet)Use instead
date and time(…) (multi-word functions)single-name functions (date(…), time(…))
x in (1, 2, 3) (list with parentheses)x in [1, 2, 3] (brackets)
]a..b[ (reversed open interval)(a..b) (parentheses)
The authoritative source of what's allowed is schema/feel_check.py (grammar) and the function list in schema/validate_bkir.py. If something validates there, it is valid; if not, it is a limit to record.
See the Java → BKIR patterns →