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 supported regular-expression subset before it is ever matched, and is then run on a linear-time matcher, so a pattern cannot make a match take long on long text. 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 supported-subset check, the same linear-time matcher 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. |
Text pattern limits
matches and regexMatch hold every pattern to the limits below. The
Text Patterns page explains the pattern language and what
each refusal means. These values are read from the engine itself, so they are the values it enforces.
| Limit | Value | When it is exceeded |
|---|---|---|
| Permitted flags | i, m, s | Any other flag, or a flags value longer than 8 characters, is refused with RE_DISALLOWED_FLAG. |
| Longest pattern | 1024 characters | The pattern is refused with RE_PATTERN_TOO_LONG before it is read. |
| Largest compiled pattern | 4096 matcher instructions | The pattern is refused with RE_PATTERN_TOO_LONG. Repeat counts expand a short pattern into many instructions, so a pattern of a few hundred characters can reach it. |
| Largest pattern written out in full | 65536 matcher instructions | A pattern whose alternatives and repeats, written out in full without sharing their common beginnings, are larger is refused with RE_PATTERN_TOO_LONG before it is built, even when sharing would bring the compiled pattern under the limit above. Patterns of ordinary shape are far below it. |
| Most characters compared for upper and lower case | 16384 characters | Under the i flag the matcher compares every character a character class covers, one by one, for its upper and lower case. A pattern whose classes cover more than this many such characters in total is refused with RE_PATTERN_TOO_LONG before it is built. ., [\s\S] and [^] cost nothing, [A-Za-z] costs 52 and \w costs 53, so patterns of ordinary shape are far below it. A very wide range, such as a whole script block, is refused under i; removing the i flag or narrowing the range accepts it. |
| Largest repeat count, alone or nested | 1000 | A count such as {1001}, or counts that multiply past it such as (?:a{100}){11}, is refused with RE_DISALLOWED_CONSTRUCT. |
| Longest text a pattern is applied to | 4096 UTF-16 code units | The match is not attempted and the step fails with STEP_EXECUTION_FAILED, and its details carry RE_INPUT_TOO_LONG. This is data, so it is checked when the run reaches the pattern, never when the ruleset is saved. |
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 |