exprv4:reference:expression_rules



Expression Rules

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 and Associativity

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

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 and Unary Operators

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 Grouping

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.


Conditional Operator Grouping

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.


Evaluation Order

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.


Assignment Evaluation

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 Semantics

Short-circuit operators skip unnecessary evaluation.

Logical AND

For A && B:

  • `A` is evaluated first.
  • If `A` is false, `B` is not evaluated.
  • If `A` is true, `B` is evaluated.
0 && fail();      // fail() is not called

This is useful for guards:

if(value.is_num_notnan() && value > 0)
{
    print('valid');
};

Logical OR

For A || B:

  • `A` is evaluated first.
  • If `A` is true, `B` is not evaluated.
  • If `A` is false, `B` is evaluated.
1 || fail();      // fail() is not called

Conditional Operator

For C ? X : Y:

  • `C` is evaluated first.
  • If `C` is true, only `X` is evaluated.
  • If `C` is false, only `Y` is evaluated.
ready ? UseReadyValue() : UseFallbackValue();

Only one branch is called.

Logical XOR Does Not Short-Circuit

^^ does not short-circuit. Both sides are evaluated.

A() ^^ B();      // both A() and B() are evaluated

Do not use ^^ to guard unsafe operations.


Chained Comparisons

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

Named operators work only when the host enables named-operator compatibility mode.

Examples include:

  • AND, OR, XOR, NOT
  • DIV, MOD
  • EQ, NE, GT, GE, LT, LE

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.


Practical Guidance

For reliable scripts:

  • Use parentheses when mixing logical operators, conditional expressions, and assignment.
  • Avoid complex side effects inside one expression.
  • Use temporary variables when the order of operations matters.
  • Use && and || to guard unsafe operations.
  • Do not use ^^ as a guard; it evaluates both sides.
  • Write range checks explicitly instead of using chained comparisons.

Example:

if(value.is_num_notnan())
{
    set inRange = (value >= low) && (value <= high);
 
    if(inRange)
    {
        print('value is in range');
    };
};

Common Pitfalls

Relying On Chained Comparison

1 < value < 10;      // not a mathematical chained comparison

Use:

(1 < value) && (value < 10);

Mixing Assignment and Conditional Expressions Without Parentheses

Assignment has very low precedence. Use parentheses when assigning a conditional result.

result = (value > 0) ? 'positive' : 'not positive';

Expecting Logical Compound Assignment

These forms are not supported:

a &&= b;
a ||= b;
a ^^= b;

Use normal assignment with an explicit expression instead.

Previous: Operators

Next: Control flow

exprv4/reference/expression_rules.txt · Last modified: by 127.0.0.1

Page Tools