Table of Contents



Variables and Assignment

Variables store values. Expr variables are created dynamically; there is no separate declaration syntax for normal variables.

speed = 1200;
depth = 2.5;
name = 'Tool 1';

Assignment stores a value and also produces the assigned value.

a = 10;
b = a = 5;

After this script, both `a` and `b` are `5`.


Variable Creation

Plain assignment creates a variable if it does not already exist.

speed = 1200;

Reading an unknown variable returns `none()`.

missingValue;       // none()

This allows scripts to check for missing values explicitly:

if(missingValue.is_none())
{
    print('missing');
};

The `set` keyword creates a local variable in the current scope.

if(true)
{
    set temp = 42;
};

`set` is for local temporary values. Use normal assignment at top level.

speed = 1200;       // top-level variable

Do not use `set` for normal top-level variables.


Scope Categories

Expr resolves variable names through scopes.

The main categories are:

Local Scope

Local variables are created with `set` or by function/method parameters.

function Add(a, b)
{
    set sum = a + b;
    return sum;
}

Here, `a`, `b`, and `sum` are local to the function call.

A local variable exists only inside its current block, function, or method scope.

if(true)
{
    set temp = 5;
};
 
temp;       // none()

Root / Session Scope

Root variables are stored in the active Expr session.

count = 1;
count = count + 1;

Root/session variables can persist across evaluations when the host evaluates scripts in the same named session.

Different sessions are isolated from each other.

If evaluation runs without a persistent session, variables exist only during that evaluation.

Object Scope

Inside class constructors and methods, plain assignment can create or update object fields.

class Counter()
{
    value = 0;
 
    function Inc()
    {
        value = value + 1;
        return value;
    }
}
 
c = Counter();
c.Inc();
c.value;        // 1

Inside object context, assignment is object-first after local variables are considered.


Name Resolution For Assignment

Assignment chooses where to store a value using the current context.

For normal assignment `name = value`:

  1. If a matching local variable exists, it is updated.
  2. Otherwise, inside object context, a matching object field is updated or a new object field is created.
  3. Otherwise, a root/session variable is updated or created.

Example with local shadowing:

a = 5;
 
if(true)
{
    set a = 10;
    a;          // 10
};
 
a;              // 5

The local `a` hides the outer `a` only inside the block.

Example with object fields:

value = 100;
 
class Counter()
{
    value = 0;
 
    function Inc()
    {
        value = value + 1;
    }
}
 
c = Counter();
c.Inc();
 
value;      // 100
c.value;    // 1

The assignment inside `Inc()` updates the object field. It does not update the same-named root variable.


set

`set` creates a local variable in the current scope.

set localName = value;

Use `set` for temporary helper values:

function ToolArea(diameter)
{
    set radius = diameter / 2;
    return pi() * radius ** 2;
}

After a local variable has been created, normal assignment updates it while it is in scope.

a = 1;
 
if(true)
{
    set a = 2;
    a = a + 3;
    a;          // 5
};
 
a;              // 1

`set` always creates a local variable. It does not update an outer variable or object field.

Inside a method, this creates a local variable named `value`:

class Counter()
{
    value = 10;
 
    function Preview()
    {
        set value = this.value + 1;
        return value;
    }
}

The object field remains unchanged.


Assignment Operators

Expr supports these assignment and update forms:

Form Meaning
a = b normal assignment
a += b add and assign
a -= b subtract and assign
a *= b multiply and assign
a /= b divide and assign
a %= b modulo and assign
a **= b power and assign
a &= b bitwise AND and assign
a |= b bitwise OR and assign
a ^= b bitwise XOR and assign
a <<= b shift left and assign
a >>= b shift right and assign
++a pre-increment
a++ post-increment
--a pre-decrement
a-- post-decrement

Logical compound assignment operators are not part of Expr syntax:

a &&= b;     // not supported
a ||= b;     // not supported
a ^^= b;     // not supported

Assignment Associativity

Normal assignment is right-associative.

a = b = 3;

This behaves like:

a = (b = 3);

Both `a` and `b` become `3`.

Compound assignment is also right-associative.

a = 1;
b = 2;
a += b += 3;

This behaves like:

a += (b += 3);

`b` becomes `5`, then `a` becomes `6`.


Compound Assignment

Compound assignment updates a variable using its current value.

a = 10;
a += 5;      // same result as: a = a + 5

Compound assignment returns the assigned value.

a = 1;
b = (a += 4);

After this script, both `a` and `b` are `5`.

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 operation uses the original target value `10` and the right-side result `5`, so `a` becomes `15`.

The operator part keeps the same operand rules as the normal operator:

Example:

a = 10;
a += none();      // error

Increment and Decrement

++ and -- require a modifiable numeric variable.

Prefix form updates first and returns the new value:

a = 1;
b = ++a;

After this script, `a` is `2` and `b` is `2`.

Postfix form returns the old value and then updates:

a = 1;
b = a++;

After this script, `a` is `2` and `b` is `1`.

The same rule applies to decrement:

a = 3;
b = --a;     // a is 2, b is 2
c = a--;     // c is 2, a becomes 1

These forms are invalid because the target is not a modifiable variable:

++1;         // error
(1 + 2)++;   // error

Assignment and none()

Explicitly assigning `none()` clears or unsets a variable.

a = 10;
a = none();
a;              // none()

This applies to local variables too:

{
    set temp = 1;
    temp = none();
    temp;       // none()
}

There is one special rule for normal assignment from block-style keyword expressions.

If the right side is a block-style keyword expression that produces no value, normal assignment skips the update.

a = 10;
a = if(false)
{
    20;
};
 
a;              // 10

The `if` expression produced no value, so `a` was not changed.

Compound assignment does not use this skip behavior.

a = 10;
a += if(false)
{
    20;
};

This fails because compound assignment tries to calculate `10 + none()`.


Protected System Object Names

Some root names are reserved for system-provided objects.

These names cannot be overwritten by assignment:

state = 1;       // error
settings = 1;    // error
gcode = 1;       // error

Use the methods or properties provided by those objects instead of replacing the object itself.


Property Assignment

Reading object properties from outside the object is supported when the object exposes the property:

tool.Number;

Direct property assignment from outside the object is not supported:

tool.Number = 2;     // not supported

Use an object method to change object state:

tool.SetNumber(2);

Inside a class constructor or method, assign the field by name:

class Tool(number)
{
    Number = number;
 
    function SetNumber(number)
    {
        Number = number;
    }
}

Lifetime and Persistence

Local variables live only for their scope.

if(true)
{
    set temp = 5;
};
 
temp;       // none()

Root/session variables persist across evaluations within the same named session.

count = 1;

A later evaluation in the same session can read and update `count`.

Different sessions are isolated. A variable created in one session is not visible in another session.

If the host evaluates Expr without a persistent session name, variables are effectively non-persistent between separate evaluations.

Functions and classes can also persist in named sessions. Their details are covered in later reference chapters.


Practical Rules

Use normal assignment `=` when:

Use `set` when:

Use compound assignment when the update is simple and obvious:

count += 1;
total += value;

Avoid compound assignment when the right side has important side effects or may produce `none()`.

Previous: Type system

Next: Operators