Architecture

Pipeline Overview

rill processes scripts through a linear pipeline with 4 stages:

Source Text → Lexer → Parser → Runtime → Result
               ↓         ↓
            Tokens      AST

Each stage transforms data and passes it forward. Errors at any stage halt execution with a structured error code (see Error Reference).

Stage 1: Lexer

The lexer converts source text into a flat sequence of tokens. Each token carries a type, value, and source position.

InputToken TypeValue
"hello"STRINGhello
42NUMBER42
->PIPE->
$nameVARIABLEname
.lenDOT_ACCESSlen

The lexer handles string interpolation by splitting "Hello {$name}" into string parts and expression tokens. Triple-quoted strings ("""...""") and escape sequences are resolved at this stage.

Error category: RILL-L001 through RILL-L005 (unterminated strings, invalid characters, malformed numbers).

Stage 2: Parser

The parser converts the token stream into an Abstract Syntax Tree (AST). The AST represents the program structure as nested nodes.

A pipe chain like:

"hello" -> .upper -> .len

Becomes an AST where each -> creates a pipe node connecting its left operand to its right operand.

The parser resolves operator precedence, groups blocks ({ }), and validates syntax (balanced braces, correct operator usage). It does not check types or variable existence.

Error category: RILL-P001 through RILL-P010 (unexpected tokens, unclosed blocks, invalid expressions).

Stage 3: Runtime

The runtime walks the AST and evaluates each node. It manages:

ResponsibilityDescription
Variable scope$ pipe value, $name captures, $ENV environment
Type checkingValidates operand types for every operation
Pipe threadingPasses each result as $ to the next pipe stage
Built-in methods.len, .trim, .keys, collection operators
Host functionsCalls registered app:: functions via RuntimeContext
Control flowConditionals (?), loops (@), break, return
LimitsIteration caps, timeouts, abort signals

The runtime enforces rill’s type safety rules: no implicit coercion, no null values, no truthiness. Every type mismatch produces a RuntimeError with a specific error code.

Error category: RILL-R001 through RILL-R061 (type errors, undefined variables, limit violations).

Host Integration Point

The runtime receives its configuration through createRuntimeContext():

import { parse, execute, createRuntimeContext } from '@rcrsr/rill';

const ast = parse(sourceText);
const ctx = createRuntimeContext({
  functions: { /* app:: functions */ },
  variables: { /* initial $variables */ },
  callbacks: { onLog: console.log },
});
const result = await execute(ast, ctx);

The host controls what domain functions the script can call. The runtime provides the language; the host provides the capabilities. See Host Integration for the full API.

Checker Mode

rill supports a static analysis mode (rill-check) that validates scripts without executing them. The checker walks the same AST but validates types, variable usage, and function signatures without calling host functions.

rill-check script.rill    # Type-check only, no execution

See CLI Tools for checker usage.

See Also

DocumentDescription
Error ReferenceAll error codes with causes and resolutions
Host IntegrationEmbedding rill in applications
ReferenceComplete language specification
GrammarFormal EBNF grammar