Skip to main content

Expression syntax

An expression is a single line that reads from the context, transforms values through methods and functions, and produces one value.

Literals

KindExamples
String"hello", 'hello' — single or double quotes are equivalent
Integer42, 0
Float3.14, 0.5
Booleantrue, false
Nullnull

Inside a string, the escapes \n, \t, \r, and \\ are recognised; \ before any other character yields that character (so \" is a literal quote).

Paths

An identifier starts a path; further segments follow with .. Segments are context keys or numeric indexes.

trigger
trigger.user.email
trigger.items.0.name
nodes.step_2.output

Identifiers match [A-Za-z_][A-Za-z0-9_]*. A path that can't be resolved is null, or throws in strict mode.

Property and method chaining

trigger.user.name.trim().upper()
trigger.items.filter(current.active).first().name
  • .name after a value reads a property (array key).
  • .name(args) calls a method on the value.
  • ?. before either makes it safe: a null left-hand side short-circuits the rest of the chain to null instead of erroring.

Function calls

An identifier immediately followed by ( is a function call, not a path:

now()
uuid()
coalesce(trigger.a, trigger.b, "fallback")

Operators

Arithmetic — + - * / %

trigger.price * trigger.qty
(trigger.subtotal + trigger.tax) / 2
10 % 3 → 1
10.5 % 3 → 1.5 (float operands keep precision)

/ and % by zero throw EvaluatorException.

+ is overloaded. If either side is a genuine (non-numeric) string, + concatenates; otherwise it adds. Numeric strings (e.g. "10" from a form field) still add arithmetically, matching -, *, /.

"Hello " + trigger.user.name → "Hello Ada"
trigger.price + 5 → 15 (even if price is the string "10")

Comparison — == != > >= < <=

Return a bool. == and != are loose comparisons.

trigger.user.age >= 18
trigger.order.status == "paid"

Logical — && || !

&& and || short-circuit and evaluate to a bool. ! is prefix negation.

trigger.user.active && trigger.user.age >= 18
!trigger.order.cancelled

Null-coalescing — ??

Evaluates to the left side unless it is null, in which case the right side.

trigger.user.nickname ?? trigger.user.name ?? "friend"

Ternary — ? :

trigger.user.age >= 18 ? "adult" : "minor"

Unary minus — -

-trigger.balance

Applying - to a non-numeric string throws EvaluatorException.

Grouping

Parentheses override precedence:

(1 + 2) * 3 → 9

Precedence

Lowest to highest — lower binds looser:

  1. ? : (ternary)
  2. ??
  3. ||
  4. &&
  5. == != > >= < <=
  6. + -
  7. * / %
  8. unary ! -
  9. . ?. () (property / method / call chain)
a && b ?? c → (a && b) ?? c
1 + 2 * 3 → 1 + (2 * 3)
trigger.age > 18 && trigger.active → (trigger.age > 18) && trigger.active

What the language does not have

No variable assignment (outside set()/get()), no loops, no function definitions, no object instantiation, no access to PHP classes or globals. Expressions are read-and-transform only.

Errors

  • Invalid characters or an unterminated string → LexerException
  • Malformed structure (unbalanced parens, a trailing operator, = instead of ==) → ParserException

Both carry a position N in the message. See Error handling.