Ix Dev 10 - Ix Script Internals

Overview

IxScript is compiled from plain text to JS objects in a single-pass, using Ohm to represent grammar, handle tokenization, and provide a base (recursive) visitor for parsing. If parsing fails (either due to a user syntax error or bug in the mapping), the error's coerced to a standard format for return. If successful, the root program node is returned, ready to run.

After checking for success, the user constructs a new ScriptContext, adds their custom variables and commands, and creates a new ScriptRunner. This allows running the program statement-by-statement using generators, or in a single function call via a helper to exhaust the generator.

The result of the last statement is returned directly to the caller as a wrapped ValueSource.

Data Structures

IxNode

The result of the Ohm parse operation is a tree of IxNode objects - generally class instances, but with minimal logic beyond construction and type conversion. These are a mix of functional components (programs, statements, blocks) and data (variables, literals).

Loading syntax highlighter...

ValueSources

ValueSources are the core data wrapper objects in IxScript, wrapping a raw value with metadata and providing the means to bind message handlers. All literals, variables, arguments, and language constructs (expressions, programs) can and are wrapped in ValueSources to provide a consistent, generic interface.

The ValueSource class itself is fairly simple, with most code devoted to factory methods and type inference.

Loading syntax highlighter...

Tuples

Tuples are the basic array type in IxScript. They are ordered collections that wrap underlying arrays of IxArgument, which maintains position and optional label. Labeled array values can be retrieved in the same way as numeric, similar to JS arrays. When converting to a JSON representation, the last element is an object of labels and values (ignoring position-only values).

As they use ValueSources, tuples can encode complex types, values, and control flows as their elements. Producers and consumers can choose to use either the "raw" tuple values (variables, commands, and expressions as data), or their fully-realized forms - or both, as needed.

Tuples provide built-in message handlers for common array operations, mainly mirrored from Array.prototype.

Loading syntax highlighter...

Dicts

Dicts are the equivalent to objects in IxScript - a map of keys to ValueSource. They wrap a simple PartialRecord<string, IxValueSource>, and serialize to the expected equivalent in JSON (ValueSources recursively serialized).

Loading syntax highlighter...

Scopes

Variables and commands are scoped, with each block creating a new child scope of the surrounding scope. When variables or commands are looked up, the runner starts in the current scope, then follows its parent upwards recursively, until it either finds the value or returns an error.

Loading syntax highlighter...

Variables

Variables references are ValueSources, too:

Loading syntax highlighter...

Variable references don't store the concrete value - instead the StackVar has a ValueSource that maintains the same reference identity and ensures there's only a single instance of that value.

Commands

Commands (also referred to as functions) are also stored in the same scoped setup. There are three types of commands:

  1. IxScript commands, created using :def "commandName" @[] {}
  2. Simple JS functions
  3. Generator functions
Loading syntax highlighter...

Both JS and Script commands can use a mix of pre-defined and arbitrary arguments: parameters, if populated, can contain positional and named params with optional default values. When executed, arguments are mapped by their label and position :log 1 level="info" -> $0 := 1, $1 := "info", $label := "info"; if parameters are specified, it will try to match positional values to their names, and will fill in defaults if needed.

Traits

Parse

VM

Run