This page defines how Expr groups, orders, and evaluates expressions.
Use this page when an expression contains several operators, assignments, function calls, or conditional branches and you need to know exactly what happens first.
Operator behavior is listed in Operators. This chapter focuses on precedence, associativity, evaluation order, and short-circuit behavior.
Precedence decides which operators bind first.
Associativity decides how operators of the same precedence group when they appear next to each other.
Higher precedence rows bind before lower precedence rows.
| Level | Operators | Associativity |
|---|---|---|
| 1 highest | parentheses (...), function call f(...), method call obj.m(...), property access obj.x | left-to-right |
| 2 | postfix x++, x-- | left-to-right |
| 3 | prefix ++x, --x | right-to-left |
| 4 | unary +x, -x, !x, ~x, NOT x | right-to-left |
| 5 | power ** | right-to-left |
| 6 | multiply/divide/modulo *, /, %, DIV, MOD | left-to-right |
| 7 | add/subtract +, - | left-to-right |
| 8 | shifts <<, >> | left-to-right |
| 9 | relational <, <=, >, >=, LT, LE, GT, GE | left-to-right |
| 10 | equality ==, !=, EQ, NE | left-to-right |
| 11 | bitwise AND & | left-to-right |
| 12 | bitwise XOR ^ | left-to-right |
| 13 | bitwise OR | | left-to-right |
| 14 | logical AND &&, AND | left-to-right |
| 15 | logical XOR ^^, XOR | left-to-right |
| 16 | logical OR ||, OR | left-to-right |
| 17 | conditional ?: | right-to-left |
| 18 lowest | assignment =, +=, -=, *=, /=, %=, **=, &=, |=, ^=, <<=, >>= | right-to-left |
Parentheses override normal precedence.
1 + 2 * 3; // 7 (1 + 2) * 3; // 9
Use parentheses when they make intent clearer, even when precedence would already produce the same result.
if((speed > 0) && (speed <= maxSpeed)) { print('valid'); };
Power has higher precedence than unary minus.
-2 ** 3;
This is grouped as:
-(2 ** 3); // -8
Use parentheses if the negative value should be the base:
(-2) ** 3; // -8 (-2) ** 2; // 4
Power is right-associative.
2 ** 3 ** 2;
This is grouped as:
2 ** (3 ** 2);
Assignment has the lowest precedence and is right-associative.
a = b = 3;
This is grouped as:
a = (b = 3);
Both `a` and `b` become `3`.
Compound assignment is also right-associative.
a = 1; b = 2; a += b += 2;
This is grouped as:
a += (b += 2);
`b` becomes `4`, then `a` becomes `5`.
Assignment returns the assigned value, so assignment can be used inside larger expressions.
a = 1; b = (a = 5) + 2;
After this script, `a` is `5` and `b` is `7`.
For readability, avoid using assignment inside larger expressions unless the intent is very clear.
The conditional operator has this form:
condition ? whenTrue : whenFalse
It has lower precedence than logical operators and higher precedence than assignment.
1 || 0 ? 2 : 3;
This is grouped as:
(1 || 0) ? 2 : 3;
The result is `2`.
The conditional operator is right-associative.
a ? b : c ? d : e;
This is grouped as:
a ? b : (c ? d : e);
Use parentheses for nested conditional expressions. They are easier to read and less error-prone.
For normal binary operators, Expr evaluates the left side first, then the right side.
function Left() { print('left'); return 1; } function Right() { print('right'); return 2; } Left() + Right();
`Left()` is evaluated before `Right()`.
Parentheses affect grouping, but they do not reverse normal left-to-right evaluation inside a grouped expression.
Normal assignment evaluates the right side, then stores the result.
a = 1; a = a + 1;
After this script, `a` is `2`.
Compound assignment reads the target value before evaluating the right side, then stores the computed result.
a = 10; function ChangeA() { a = 100; return 5; } a += ChangeA();
The compound assignment uses the original target value `10` and the right-side result `5`, so `a` becomes `15`.
This rule matters when the right side has side effects. Prefer simple right sides for compound assignment.
Short-circuit operators skip unnecessary evaluation.
For A && B:
0 && fail(); // fail() is not called
This is useful for guards:
if(value.is_num_notnan() && value > 0) { print('valid'); };
For A || B:
1 || fail(); // fail() is not called
For C ? X : Y:
ready ? UseReadyValue() : UseFallbackValue();
Only one branch is called.
^^ does not short-circuit. Both sides are evaluated.
A() ^^ B(); // both A() and B() are evaluated
Do not use ^^ to guard unsafe operations.
Expr does not have mathematical chained comparison syntax.
1 < 2 < 3;
This is parsed left-to-right:
(1 < 2) < 3;
The first comparison produces a boolean-style numeric value, then that value is compared with `3`.
Write chained range checks explicitly:
value > 1 && value < 3;
For clarity, use parentheses:
(value > 1) && (value < 3);
Named operators work only when the host enables named-operator compatibility mode.
Examples include:
When enabled, named operators follow the same precedence as their symbolic equivalents.
Examples:
a AND b; x DIV y; value GE 10;
Named operators are compatibility syntax. Prefer symbolic operators in normal Expr scripts.
For reliable scripts:
Example:
if(value.is_num_notnan()) { set inRange = (value >= low) && (value <= high); if(inRange) { print('value is in range'); }; };
1 < value < 10; // not a mathematical chained comparison
Use:
(1 < value) && (value < 10);
Assignment has very low precedence. Use parentheses when assigning a conditional result.
result = (value > 0) ? 'positive' : 'not positive';
These forms are not supported:
a &&= b; a ||= b; a ^^= b;
Use normal assignment with an explicit expression instead.
Previous: Operators
Next: Control flow