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.
| Java | BKIR · FEEL | What it is |
a && b | a and b | Logical AND (word) |
a || b | a or b | Logical OR (word) |
!a | not(a) | Negation (function) |
a == b | a = b | Equality: a single = |
a != b | a != b | Inequality (same) |
a < b · <= · > · >= | a < b · <= · > · >= | Relational (same) |
x == null | x = null | Comparison with null |
c ? x : y | if c then x else y | Conditional (ternary) |
x >= lo && x <= hi | x between lo and hi | Inclusive range |
set.contains(x) | x in [a, b, c] | Membership in a set |
list.stream().anyMatch(i -> p) | some i in list satisfies p | Does any exist? |
list.stream().allMatch(i -> p) | every i in list satisfies p | Do all hold? |
list.stream().map(..).reduce(..) | sum(for i in list return ..) | Map + aggregation |
obj.field | obj.field | Field access (same) |
list.get(0) | list[0] | Index / filter |
list.size() | count(list) | Size |
Math.pow(a, b) | a ** b | Power |
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
| Operator | Meaning | Example |
+ - * / | Add, subtract, multiply, divide | saldo - monto |
** | Power | base ** exponente |
- (unary) | Numeric negation | -importe |
Comparison
| Operator | Meaning | Example |
= | Equal (not ==!) | cliente = null |
!= | Not equal | estado != "ACTIVA" |
< <= > >= | Less, less-or-equal, greater, greater-or-equal | monto <= 0 |
between … and … | Inclusive range | edad between 18 and 65 |
in | Membership in a list or interval | tipo in ["visa", "mastercard"] |
instance of | Type check | saldo instance of number |
Logical
| Operator | Meaning | Example |
and | Conjunction | activo and saldo > 0 |
or | Disjunction | vip or antiguo |
not(…) | Negation | not(tarjeta.valida) |
Access and invocation
| Operator | Meaning | Example |
a.b | Field / path access | cuenta.saldo |
a[i] | List index or filter | stock[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.
| Form | What it does | Example |
if … then … else … | Conditional (always with else) | if activo then 1 else 0 |
for … in … return … | Comprehension: produces a list | for 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
| Type | Syntax | Example |
| Number | 42 · 3.14 | monto > 1000 |
| Text | "…" | estado = "ACTIVA" |
| Temporal | @"…" | fecha >= @"2024-01-01" |
| Boolean | true · false | cuenta.permiteSobregiro = true |
| Null | null | cliente = 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
| Rule | Detail |
names = identifiers | No spaces (saldoDisponible, not saldo disponible). The readable label goes in the term's label in the vocabulary. |
= is equality | A 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 resolution | Every 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.