Table of Contents

Include System

The include system imports Expr code from another file at parse time.

Use includes for reusable functions, classes, constants, and shared script libraries.


Basic Include

Syntax:

include 'MyMath.expr'

The included file is parsed and inserted into the current program.

Example library file:

// MyMath.expr
function Add(a, b)
{
    return a + b;
}
 
function Sub(a, b)
{
    return a - b;
}

Using it:

include 'MyMath.expr'
 
print(Add(2, 3));
print(Sub(8, 5));

An include statement can be followed by a semicolon, but it does not require one:

include 'MyMath.expr';
include 'Other.expr'

Where Include Is Allowed

`include` is a top-level statement.

It is valid at the top level of a script:

include 'Library.expr'
 
result = LibraryValue();

It is not valid inside blocks, functions, or classes:

if (1)
{
    include 'Library.expr'     // error
}

An include statement must start a statement. It cannot be used as part of an expression.


Included Content

Included code is parsed as if it appeared in the including script.

This means an included file can contain:

For library files, prefer function and class declarations only. Top-level statements in included files run each time the file is included.


Namespaced Include

Use `as` to import a file under a namespace alias.

include 'MyMath.expr' as MathLib
 
print(MathLib::Add(2, 3));

With a namespaced include, top-level function and class declarations from the included file are placed into the alias namespace.

// MyMath.expr
function Add(a, b)
{
    return a + b;
}
include 'MyMath.expr' as MathLib
 
MathLib::Add(2, 3);      // OK
Add(2, 3);               // error unless another Add exists

The alias after `as` is a namespace name, such as `MathLib`.


Namespaced Classes

Namespaced includes also apply to class declarations.

// Tools.expr
class ToolInfo(number, diameter)
{
    Number = number;
    Diameter = diameter;
 
    function Radius()
    {
        return Diameter / 2;
    }
}
include 'Tools.expr' as Tools
 
tool = Tools::ToolInfo(3, 6.0);
print(tool.Radius());

If an included class or function already has a namespace, the include alias is the namespace used for the imported definition.


Top-level Statements With Namespace Alias

The namespace alias applies to imported top-level function and class declarations.

It does not rewrite arbitrary top-level statements.

For example, this file is not a good candidate for aliased include:

// Init.expr
value = Add(2, 3);

If it is included with an alias, the call expression is not rewritten to `Alias::Add(…)`.

Keep aliased include files definition-only unless you are deliberately executing top-level code.


Duplicate Includes

Includes are not treated as `pragma once`.

Including the same file more than once is allowed.

Effects of duplicate include:

Example:

include 'MyMath.expr' as A
include 'MyMath.expr' as B
 
A::Add(1, 2);
B::Add(1, 2);

Recursive Includes

Expr detects active recursive include chains.

If a file includes itself directly or indirectly while it is already being parsed, parsing fails with a recursive include error.

Example:

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

Expr also limits include nesting depth. Include nesting deeper than 16 levels is rejected.


Path Resolution

Include file names are strings.

include './lib/MyMath.expr'
include 'LocalHelper.expr'
include 'C:/PlanetCNC/scripts/MyMath.expr'

Path resolution is handled by the host path resolver.

General rules:

Use profile-relative include paths for shared profile libraries:

include './lib/probing/probe_job.expr' as Probe
include './lib/atc/tool_change.expr' as ATC

Use source-relative include paths for helper files beside the current source script:

include 'probe_steps.expr' as Steps
include '~/local_report.expr' as Report

Do not use `../lib/…` when you mean a library inside the profile folder. Because it starts with `.`, it resolves from the profile path and goes outside the profile folder.

See How relative paths work.


Runtime Path Context

Runtime path resolution uses the current source path and profile path from the host evaluation context.

A function or class method should use explicit profile-relative paths for shared profile assets:

function LoadLogo()
{
    return image_data().load('./assets/images/logo.png');
}

Use paths without a leading dot for files that belong beside the current source script:

function LoadJobData()
{
    return bytes().load('job_data.csv');
}

Parsing Options

Included files are parsed with strict default Expr parsing options.

They do not inherit caller compatibility options enabled by a host context, such as:

Write include files using normal Expr syntax.


For reusable libraries:

Example:

include './lib/Geometry.expr' as Geometry
 
area = Geometry::CircleArea(10);

Previous: Classes and user-defined objects

Next: Error model