Expression Language
Expressions compute a value from other values — ctx.price * ctx.qty, coalesce(ctx.region, "US"), len(ctx.name) > 30. They are small, typed, and deterministic, and you can use one anywhere the engine reads a value.
Where you can use an expression
An expression appears in two places:
- As a value source — a fourth option (alongside Context, Env Var, and Constant) anywhere a value input appears: a condition compare value, a derived fact value, an Add to Context entry, and action inputs.
- As the left side of a condition clause — a clause offers a Key / Expression toggle, so instead of “Key to evaluate” you can compare a computed value:
len(ctx.name) > 30as a clause on its own.
The editor shows the hint “Read context with ctx.key, variables with $name” and a placeholder like “e.g. ctx.price * ctx.qty”.
Reading data
| Form | Reads |
|---|---|
ctx.key | A context value (the canonical form). |
context.key | The same thing — context is an accepted alias for ctx. |
ctx.[key with spaces] | Bracket form, for keys that are not simple identifiers (spaces, dots, punctuation). |
$name | An environment variable. |
No bare identifiers A name always has to say where it comes from. Write ctx.price or $rate, never a bare price — the same qualified ctx.key / $name shorthand the decision table accepts when you reference context in a cell.
Literals
Numbers are written plainly (42, 3.14), strings in double quotes ("gold"), and booleans as true / false.
Operators
| Group | Operators |
|---|---|
| Arithmetic | +-*/% — + also concatenates strings. |
| Comparison | ==!=<<=>>= |
| Logical | andornot (aliases &&||!) |
| Grouping | Parentheses ( ) |
| Conditional | Ternary condition ? whenTrue : whenFalse |
Function reference
The function library is fixed (you cannot add your own). Every function takes typed arguments and returns a typed value.
String
| Function | Does | Example |
|---|---|---|
len(x) | Length of a string, or number of items in an array. | len(ctx.name) |
lower(x) | Lowercase a string. | lower(ctx.code) |
upper(x) | Uppercase a string. | upper(ctx.code) |
trim(x) | Remove surrounding whitespace. | trim(ctx.input) |
contains(x, sub) | True if a string contains sub (or an array contains the value). | contains(ctx.email, "@") |
startsWith(s, prefix) | True if s starts with prefix. | startsWith(ctx.sku, "AB") |
endsWith(s, suffix) | True if s ends with suffix. | endsWith(ctx.file, ".pdf") |
replace(s, find, with) | Replace every occurrence of find. | replace(ctx.phone, "-", "") |
substring(s, start, length?) | Slice from start (0-based); length is optional. | substring(ctx.code, 0, 3) |
concat(a, b, …) | Join values into one string; null arguments are skipped. | concat(ctx.first, " ", ctx.last) |
Math
| Function | Does | Example |
|---|---|---|
abs(n) | Absolute value. | abs(ctx.delta) |
round(n, digits?) | Round to digits (default 0). | round(ctx.price, 2) |
floor(n) | Round down to a whole number. | floor(ctx.score) |
ceil(n) | Round up to a whole number. | ceil(ctx.score) |
min(a, b, …) | Smallest of the arguments (or of a single array). | min(ctx.a, ctx.b) |
max(a, b, …) | Largest of the arguments (or of a single array). | max(ctx.a, 0) |
coalesce(a, b, …) | The first argument that is not null. | coalesce(ctx.nickname, ctx.name) |
Conversion
| Function | Does | Example |
|---|---|---|
number(x) | Parse to a number, or null if it cannot. | number(ctx.qtyText) |
string(x) | Format a value as a string. | string(ctx.count) |
bool(x) | Parse to a boolean (true/false, 1/0, yes/no). | bool(ctx.flag) |
date(x) | Parse to a date/time, or null. | date(ctx.dob) |
Date
| Function | Does | Example |
|---|---|---|
dateAdd(d, amount, unit) | Add amount units to a date (day, week, month, year, hour, minute, second). | dateAdd(ctx.start, 30, "day") |
dateDiff(a, b, unit) | a minus b, measured in unit (day, hour, minute, second). | dateDiff(ctx.end, ctx.start, "day") |
year(d) | The year of a date. | year(ctx.dob) |
month(d) | The month of a date (1–12). | month(ctx.dob) |
day(d) | The day of the month. | day(ctx.dob) |
Array
| Function | Does | Example |
|---|---|---|
count(array) | Number of elements. | count(ctx.items) |
sum(array) | Total of the numeric elements. | sum(ctx.amounts) |
avg(array) | Mean of the numeric elements. | avg(ctx.scores) |
first(array) | First element. | first(ctx.items) |
last(array) | Last element. | last(ctx.items) |
join(array, separator) | Join elements into a string. | join(ctx.tags, ", ") |
Nulls and typing
Null propagates: if any operand is null, an arithmetic or comparison result is null too, and most functions return null when given a null or mistyped argument. The two deliberate exceptions are coalesce (returns the first non-null argument — the escape hatch) and concat (skips null arguments). Numeric comparisons follow the same coercion the engine's conditions use, and the result of an expression is a typed value — string, number, boolean, date, or an array element type — carried through wherever it is used.
Deterministic by design There is deliberately no now() or random(). Expressions depend only on their inputs, so a run is repeatable, an inference rule's re-fire behavior stays stable, and a what-if comparison reproduces exactly. Read the clock or a random seed by putting it in the context as an input instead.
Validation
Expressions are checked as you type: a parse error is reported with the character position of the problem, so you can fix it in place. A malformed expression also fails when you save, so a bad expression never reaches a run.
Worked examples
ctx.price * ctx.qty // multiply two context numbers
coalesce(ctx.[customer region], "US") // default a missing value
len(ctx.name) > 30 and $ENV == "prod" // a boolean clause expression
ctx.tier == "gold" ? 0.2 : 0.1 // ternary: pick a discount by tier