operators-and-precedence
title: Operators & Precedence sidebar_position: 7
Operators and precedence
Part of the function and operator catalog. Every example on the catalog
is executed as an assertion by the engine's own test suite (examples.test.ts).
Operators
Every condition operator ENG-08 enumerates. An operator with a symbol is callable either way —
by name (add(1, 2)) or by its infix/prefix symbol (1 + 2) — and both forms dispatch through
the exact same implementation.
| Operator | Symbol | Signature | Description |
|---|---|---|---|
equal | == | equal(left: any value, right: any value) -> true/false | Answers whether two values are equal. Cross-type-tolerant: a number and text are simply not equal — never an error. |
notEqual | != | notEqual(left: any value, right: any value) -> true/false | The negation of equal — cross-type-tolerant in the same way. |
approxEqual | — | approxEqual(left: a number, right: a number, tolerance: a number) -> true/false | Answers whether two numbers are equal within an explicit tolerance. There is no default tolerance — omitting it is a wrong-arity error, not a fallback. A difference exactly equal to the tolerance is equal. |
lessThan | < | lessThan(left: any value, right: any value) -> true/false | Answers whether the left value orders before the right value. Strings order by exact UTF-16 code-unit sequence, not locale collation, so an uppercase letter sorts before a lowercase one. Ordering across incomparable types (e.g. text against a number) is a coded error, never a silent false. |
lessThanInclusive | <= | lessThanInclusive(left: any value, right: any value) -> true/false | Like lessThan, inclusive of equality. Same code-unit ordering rule and the same incomparable-type error. |
greaterThan | > | greaterThan(left: any value, right: any value) -> true/false | Answers whether the left value orders after the right value. Same code-unit ordering rule and the same incomparable-type error. |
greaterThanInclusive | >= | greaterThanInclusive(left: any value, right: any value) -> true/false | Like greaterThan, inclusive of equality. Same code-unit ordering rule and the same incomparable-type error. |
and | — | and(operand: any value, ...) -> true/false | Answers true only if every operand is true. Short-circuits left to right: stops at the first operand that resolves false, and never evaluates the operands after it. Each operand must be a boolean or null (null counts as false); a string or a number is a coded error. |
or | — | or(operand: any value, ...) -> true/false | Answers true if any operand is true. Short-circuits left to right: stops at the first operand that resolves true, and never evaluates the operands after it. Each operand must be a boolean or null (null counts as false); a string or a number is a coded error. |
not | — | not(operand: any value) -> true/false | Negates a boolean or null (null counts as false). A string or a number is a coded error — there is nothing to skip, so this parameter is eager. |
add | + | add(left: a number, right: a number) -> a number | Adds two numbers. The '+' operator only adds numbers in rule-line — it never joins strings; use CONCAT for that. |
sub | - | sub(left: a number, right: a number) -> a number | Subtracts the right number from the left number. |
mul | * | mul(left: a number, right: a number) -> a number | Multiplies two numbers. |
div | / | div(left: a number, right: a number) -> a number | Divides the left number by the right number. Division by zero is a coded error, never Infinity. |
contains | — | contains(subject: text, needle: text) -> true/false | Answers whether the subject text contains the needle text, anywhere. Exact UTF-16 code-unit comparison: no case folding, no trimming, no Unicode normalization. An empty needle is always found, so this is true. |
doesNotContain | — | doesNotContain(subject: text, needle: text) -> true/false | The negation of contains. An empty needle is always found, so this is false. |
startsWith | — | startsWith(subject: text, needle: text) -> true/false | Answers whether the subject text begins with the needle text. Exact UTF-16 code-unit comparison: no case folding, no trimming, no Unicode normalization. An empty needle is always true. |
endsWith | — | endsWith(subject: text, needle: text) -> true/false | Answers whether the subject text ends with the needle text. Exact UTF-16 code-unit comparison: no case folding, no trimming, no Unicode normalization. An empty needle is always true. |
in | — | in(value: any value, set: an array of any value) -> true/false | Answers whether a value is a member of an array, deciding membership through the one equality rule — a decimal 2 is not in an array holding the text "2". An empty array is always false. A second argument that is not an array is a coded type error, not false. |
notIn | — | notIn(value: any value, set: an array of any value) -> true/false | The negation of in. An empty array is always true. |
matches | — | matches(subject: text, pattern: text, flags?: text) -> true/false | Answers whether the subject text matches the pattern over its WHOLE length. The pattern is checked against rule-line's permitted regular-expression subset before it is ever matched. A pattern that already anchors itself with ^ or $ is refused — rule-line cannot safely add its own whole-subject anchoring around one — use regexMatch instead for match-anywhere semantics. |
regexMatch | — | regexMatch(subject: text, pattern: text, flags?: text) -> true/false | Answers whether the subject text matches the pattern ANYWHERE in it — unlike matches, which requires the whole subject to match. Same permitted-subset check and the same input cap. |
exists | — | exists(operand: any value) -> true/false | Answers whether a data-access path is present, whether or not its value is null: true for a present value (including an explicit null — the key is there), false for an absent path. Only valid over a path access; asking whether a call failed is not what this operator does — use IF or COALESCE for that. |
notExists | — | notExists(operand: any value) -> true/false | The exact negation of exists on every input: false for a present value (including explicit null), true for an absent path. |
isNull | — | isNull(operand: any value) -> true/false | Answers whether a data-access path is present AND explicitly null. True only for a present null value; false for a present non-null value; false for an absent path — absence is not null. |
Operator precedence
The parser's one declared precedence and associativity table, loosest to tightest. This table is
generated directly from PRECEDENCE_TABLE — the hand-written Expression Language
page explains why the levels are ordered this way and links back here rather than restating this
table in prose.
| Precedence | Operator | Associativity |
|---|---|---|
| 1 | or | left |
| 2 | and | left |
| 3 | not | right |
| 4 | == | left |
| 4 | != | left |
| 5 | < | left |
| 5 | <= | left |
| 5 | > | left |
| 5 | >= | left |
| 6 | + | left |
| 6 | - | left |
| 7 | * | left |
| 7 | / | left |