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`.
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.
Expr resolves variable names through scopes.
The main categories are:
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 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.
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.
Assignment chooses where to store a value using the current context.
For normal assignment `name = value`:
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` 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.
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
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 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
++ 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
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()`.
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.
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; } }
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.
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