Sidebar

Home



Expressions V4


Tutorials

How-To

Reference

  Lexical basics
  Type system
  Variables and assignment
  Operators
  Expression rules
  Control flow
  Functions
  Built-in functions
   None and NaN
   Arithmetic
   Algebra
   Logarithmic and Exponential
   Trigonometric
   Rounding and Centering
   Strings
   Output, Formatting, Clipboard, and Errors
   Dialog Functions
   Platform and Paths
  Methods and properties
  Built-in methods
   Common Value Methods
   Number Methods
   String Basic Methods
   String Slice Methods
   String Parsing Methods
   String Formatting Methods
   String Regex Methods
   String Trim and Case Methods
  Objects
  Built-in objects
   Global objects
    settings
    controller
    session
    python
   Collections
    array
    map
    bytes
   File Format I/O
    image_data
    image_stream
   Utils
    crypto
    timer
   Comm
    serial
   Dialogs
    file_open
    file_save
    msg_ok
    msg_ok_cancel
    msg_yes_no
    msg_password
   GUI objects
    window
    panel
    group
    splitpanel
    label
    textbutton
    drawablebutton
    togglebutton
    togglegroup
    textinput
    textedit
    numinput
    slider
    combobox
    listbox
    progressbar
    led
    separator
    menu
    image
    snake
  Classes and user-defined objects
  Include system
  Error model
  Execution model and sessions
  Host integration
  Limits and performance
  Formal reference
  Glossary

Cookbook

exprv4:reference:limits_and_performance

Limits and Performance

Expr is intended for configuration logic, custom dialogs, G-code expression evaluation, probing scripts, ATC scripts, file processing helpers, and other host automation tasks.

It is not a real-time motion-control language. Time-critical motion control, hardware safety, and low-level device scheduling belong to the host application and controller firmware.

This page describes practical limits and performance behavior for user scripts.


Implementation Guards

Expr has internal guards that stop scripts before runaway parsing, recursion, or loops consume too much stack or CPU time.

Current release guards are:

Limit Current value Notes
Parse depth `3800` Maximum nested expression/parser depth. Deeply nested parentheses, nested calls, and nested expressions contribute to this.
Include nesting depth `16` Include chains deeper than this are rejected. Recursive include chains are also rejected.
Evaluation depth `5000` Guard for recursive evaluation depth. Deep function recursion and deeply nested expression evaluation can reach this.
Iterative evaluation stack depth `7600` Guard for the evaluator's internal iterative stack.
`loop` iteration guard `1000000` A `loop` stops with an error after too many iterations.
`while` iteration guard `1000000` A `while` stops with an error after too many iterations.
`for` iteration guard `2000000` A `for` stops with an error after too many iterations.

These values are safety limits, not design targets. Scripts should finish normally well before reaching them.

The exact limits can differ between builds or future versions. Treat them as protection against mistakes, not as capacity guarantees.


Parse Depth

Parse depth is affected by nested expression structure.

Examples that increase parse depth include:

  • deeply nested parentheses
  • deeply nested function calls
  • deeply nested method chains
  • deeply nested `if`, `loop`, `while`, or `for` blocks
  • very large single expressions

Prefer readable intermediate variables over very large nested expressions:

// harder to read and easier to make deeply nested
result = clamp(round((a + b) * scale, 3), min_value, max_value);
 
// easier to inspect and usually easier to debug
raw = (a + b) * scale;
rounded = round(raw, 3);
result = clamp(rounded, min_value, max_value);

A parse-depth error means the script structure is too deeply nested for the parser guard.


Include Depth

Include files are parsed when the host parses the script.

Expr rejects:

  • direct recursive includes
  • indirect recursive includes
  • include nesting deeper than 16 levels

Example of an indirect recursive include:

// A.expr
include 'B.expr';
 
// B.expr
include 'A.expr';     // recursive include error

Keep include trees shallow. Use a small number of library files grouped by purpose instead of long include chains.

Good include files usually define functions and classes. Avoid expensive top-level side effects in reusable include files, because those effects run when the file is included.

See Include system.


Evaluation Depth and Recursion

Evaluation depth is affected by nested evaluation work at runtime.

Common causes include:

  • deep recursive function calls
  • mutual recursion
  • deeply nested expressions that must be evaluated
  • callbacks that recursively start more work

Small recursion is fine:

function SumTo(n)
{
    if(n <= 0)
    {
        return 0;
    }
 
    return n + SumTo(n - 1);
}
 
SumTo(10);

For large counts, prefer an iterative loop:

function SumTo(n)
{
    total = 0;
 
    for(i = 1; i <= n; i += 1)
    {
        total += i;
    }
 
    return total;
}
 
SumTo(10000);

Iterative code is usually easier to interrupt, inspect, and tune.


Loop Guards

`loop`, `while`, and `for` have iteration guards to catch accidental infinite loops.

A guard error looks like a runtime error. The exact message includes the loop type and iteration count.

Example of a loop that cannot finish normally:

position = 0;
 
while(position < 10)
{
    print(position);
    // position is never changed
}

Fix the loop by changing the condition state, using `break`, or using `loop` when the count is known:

position = 0;
 
while(position < 10)
{
    print(position);
    position += 1;
}

Use loop guards as error protection only. Do not write scripts that intentionally rely on the guard to stop execution.


Numeric Limits

Expr numbers are 64-bit floating-point values.

This gives a wide numeric range, but not unlimited precision. Very large integer-like values can lose exact integer precision, because they are still stored as floating-point numbers.

For ordinary CNC coordinates, feed rates, counters, and calculations this is usually appropriate.

Use `.is_num_notnan()` before important numeric calculations:

function RequireNumber(value)
{
    if(!value.is_num_notnan())
    {
        error('Expected a valid number');
    }
 
    return value;
}

Some numeric functions reject values outside their valid mathematical domain. For example, logarithms require positive input, some inverse trigonometric functions require a limited range, and exponent functions reject values that would overflow.

Bitwise and shift operations convert numeric values to integer-like values internally and require finite numbers. Shift counts must be in the valid range.

See Type system, none(), nan(), and errors, none and nan functions, and Functions.


Strings, Arrays, Maps, and Data Buffers

String, array, map, and data buffer performance depends mostly on size and repeated allocation.

Avoid repeatedly building large strings inside tight loops when a smaller structure would work.

// avoid unnecessary repeated formatting in tight loops
text = '';
for(i = 0; i < 10000; i += 1)
{
    text += format('%d,', i);
}

When possible:

  • collect values in an array and format output at the end
  • keep large binary data in `bytes` instead of strings
  • avoid cloning large objects unless a copy is really needed
  • reuse objects when an object-specific method supports reuse

See Collections and bytes.


Built-In Objects and Host Operations

Host operations are usually much slower than pure numeric expressions.

Examples include:

  • file loading and saving
  • dialogs and GUI updates
  • image loading and conversion
  • serial communication
  • Python execution
  • clipboard access
  • settings reads and writes

Avoid placing slow host operations inside tight loops unless the operation really must happen each iteration.

// usually better: read setting once
safe_z = settings.get('safeZ');
 
for(i = 0; i < count; i += 1)
{
    MoveToSafeZ(safe_z);
}

For GUI scripts, prefer updating visible controls only when the displayed value changes. For serial or timer callbacks, keep the callback short and move larger work into explicit script steps when possible.

See Host integration and Built-in objects.


Sessions and Repeated Evaluation

A session can persist variables, user functions, and user classes across evaluations.

For interactive workflows, reusing a named session can avoid redefining the same functions and classes manually in every command.

// first evaluation in a named session
function Scale(value)
{
    return value * 25.4;
}
 
// later evaluation in the same session
Scale(2);

However, each submitted source text still has to be parsed. Large scripts that repeatedly include the same large library files can spend noticeable time in parsing and initialization.

Practical guidelines:

  • Put reusable functions and classes in include files.
  • Keep include files focused and shallow.
  • Avoid top-level work in library include files.
  • Keep long-lived state in named sessions only when that persistence is useful.
  • Clear or delete sessions when long-lived state is no longer needed.

See Execution model and sessions.


Callbacks and Responsiveness

Callbacks can run later, sometimes from host-managed asynchronous work.

Keep callbacks short:

  • validate input
  • update a small amount of state
  • print or report concise status
  • schedule or trigger larger work elsewhere when needed

Long-running callbacks can make timers, dialogs, serial monitoring, or other host features feel unresponsive.

A callback that raises an error may stop the object that invoked it, depending on the object.

See timer, serial, and Error model.


Practical Optimization Checklist

When a script is slow, check these first:

  • Move constant calculations outside loops.
  • Replace deep recursion with loops.
  • Avoid repeated file, settings, dialog, Python, serial, or image operations in tight loops.
  • Reduce repeated string concatenation and formatting.
  • Keep include trees shallow.
  • Split very large expressions into named intermediate variables.
  • Use arrays, maps, and classes to keep state organized instead of rebuilding it repeatedly.
  • Prefer named sessions for interactive state that must persist.
  • Remove unnecessary `print()` calls from high-frequency loops.

Example:

// slower
for(i = 0; i < count; i += 1)
{
    scale = settings.get('unitScale');
    print(format('item %d', i));
    values[i] = values[i] * scale;
}
 
// faster and quieter
scale = settings.get('unitScale');
 
for(i = 0; i < count; i += 1)
{
    values[i] *= scale;
}
 
print(format('processed %d items', count));

When to Move Work Out of Expr

Use host code, Python, or a purpose-built importer/exporter when the task needs:

  • real-time behavior
  • very large file parsing
  • heavy image processing
  • long-running numerical computation
  • direct hardware scheduling
  • strict memory control
  • complex libraries not exposed through Expr objects

Expr is best used to coordinate host features, validate values, make decisions, and express automation logic.


Previous: Host integration

Next: Formal reference

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

Page Tools