Skip to main content

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.

OperatorSymbolSignatureDescription
equal==equal(left: any value, right: any value) -> true/falseAnswers 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/falseThe negation of equal — cross-type-tolerant in the same way.
approxEqualapproxEqual(left: a number, right: a number, tolerance: a number) -> true/falseAnswers 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/falseAnswers 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/falseLike lessThan, inclusive of equality. Same code-unit ordering rule and the same incomparable-type error.
greaterThan>greaterThan(left: any value, right: any value) -> true/falseAnswers 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/falseLike greaterThan, inclusive of equality. Same code-unit ordering rule and the same incomparable-type error.
andand(operand: any value, ...) -> true/falseAnswers 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.
oror(operand: any value, ...) -> true/falseAnswers 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.
notnot(operand: any value) -> true/falseNegates 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 numberAdds 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 numberSubtracts the right number from the left number.
mul*mul(left: a number, right: a number) -> a numberMultiplies two numbers.
div/div(left: a number, right: a number) -> a numberDivides the left number by the right number. Division by zero is a coded error, never Infinity.
containscontains(subject: text, needle: text) -> true/falseAnswers 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.
doesNotContaindoesNotContain(subject: text, needle: text) -> true/falseThe negation of contains. An empty needle is always found, so this is false.
startsWithstartsWith(subject: text, needle: text) -> true/falseAnswers 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.
endsWithendsWith(subject: text, needle: text) -> true/falseAnswers 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.
inin(value: any value, set: an array of any value) -> true/falseAnswers 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.
notInnotIn(value: any value, set: an array of any value) -> true/falseThe negation of in. An empty array is always true.
matchesmatches(subject: text, pattern: text, flags?: text) -> true/falseAnswers 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.
regexMatchregexMatch(subject: text, pattern: text, flags?: text) -> true/falseAnswers 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.
existsexists(operand: any value) -> true/falseAnswers 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.
notExistsnotExists(operand: any value) -> true/falseThe exact negation of exists on every input: false for a present value (including explicit null), true for an absent path.
isNullisNull(operand: any value) -> true/falseAnswers 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.

PrecedenceOperatorAssociativity
1orleft
2andleft
3notright
4==left
4!=left
5<left
5<=left
5>left
5>=left
6+left
6-left
7*left
7/left