Script semantics (what actually runs)
This is the interpreter you are targeting, not an abstract language spec. Match examples to this behavior.
Tokenization
- Skip spaces/tabs (and newlines unless indentation is marked significant).
- Skip line comments (
markers) and block comments (pairs). - Longest match among operators, terminators,
(),[], block delimiters, argument/member separators, decorator prefixes. - Numbers: optional
-, digits, optional fractional part if floats enabled. - Strings: each
literals.string.formsdelimiter, default". Escapes:n t r 0and “unknown escape is the next char.” - Words:
[A-Za-z_][A-Za-z0-9_]*. If the word is in any keyword list (including custom kinds and true/false/null), it is a keyword token.
Unicode identifiers are not fully implemented even if the JSON says they are.
Statements
Processed in order. Decorators may precede a statement.
- Function / custom function kind: name,
(params), block. Registered onloadand again onrun. - Type-first function:
ReturnType Name(Type param, ...) { ... }when the language JSON enablesc_type_first.voidreturns no value. - Variable / declaration keyword: name, optional
= expr. Onrun, stored in engine globals. - Typed declaration:
Type name [= expr]at file scope, in blocks, and in class bodies when type-first declarations are enabled. - Bare assignment
name = exprwhen the name is new (typical whendeclaration_requiredis false). - Custom type:
keyword Name { field = expr; ... }→ global map (fields only). - Class / actor:
keyword Name [: Base] { fields and methods }. One instance per declaration; methods are callable withengine.call("methodName")afterrun. Inside methods, names resolve to instance fields, then methods, then globals/host. importspec [;]: spec isident,a.b.c(imports.module_separator), or a string. Hostadd_library/add_library_filefirst, then a script file (importer dir,.,imports.search_directories,add_search_path). Script modules run in their own environment; binding followsimports.bind_as(namespace→ last segment is a map;globals→ flatten).ifexpr block [else if…] [elseblock]. Parentheses around expr are optional. Braces around the body are optional.whileexpr block-
forname [=] [inof] expr block- If expr is an array, iterate elements.
- If expr is a number
N, iterate0 .. N-1. - Otherwise the body does not run.
return[expr]. If statement terminators are optional,returndoes not steal the next declaration (returnthenType name = …on the following line is a bare return).break/continue- Otherwise: expression statement
Blocks: { ... } using configured delimiters, or a single statement if { is missing.
Statement terminators (often ;) are optional between many statements but recommended in generated examples.
Expressions (Pratt parser)
Prefix: unary -, !, not; parenthesized expr; [a, b] arrays; numbers; strings; true/false/null words; identifiers.
Infix / postfix:
- Call:
f(a, b) - Index:
a[i]— arrays, maps (keyto_string), strings (one character) - Member:
a.name— maps only - Assignment operators (right-assoc, low precedence), including compound
+= -= *= /=when configured - Binary operators from the language + defaults
&& / and and || / or short-circuit.
+ concatenates if either side is a string (to_string on the other).
Division by zero throws RuntimeError.
Assignment target must be an identifier (x = 1, not a[0] = 1 yet).
Scope
- Top-level
runwrites globals. Latercalland other scripts see those names. - Function parameters are a child environment. Method calls use the instance environment as parent (fields, then methods, then globals). Closures do not capture locals from an outer
fnunless those names were global. - Block
{ }is a child environment forletinside the block. Those names die at end of block.
Functions
Arity must match. Extra/missing args throw. Recursion is allowed. Nested fn is parsed if it appears as a statement inside a block; the inner function is still registered on globals by name (last one wins).
Load vs run vs unload
| Action | Functions | Top-level let / exprs / types / classes / imports |
|---|---|---|
load |
Registered (free functions on entry scripts). Imported script files are parsed as isolated modules | Not executed |
run |
Re-registered; class methods bound on the instance | Executed; class instances created; imports bind host packages or script exports |
unload |
Names removed | Names removed |
eval does all three.
Truthiness
Used by if / while: null, false, 0, "", empty array → false. Everything else → true.