Architecture

The crate is a library (src/lib.rs) plus a thin command-line front end (src/main.rs). This page is an overview; the doc comments in each module go into detail.

Compiler passes

  1. Lower the checked AST to IR: blocks of instructions over virtual registers, one per local.

  2. For each function, compute live intervals and assign registers by linear scan, spilling the least-used values to the stack.

  3. Emit x86-64 assembly from the allocated IR, then assemble and link it with gcc.

Pipelines

src/driver.rs wires the stages together, and src/main.rs picks a pipeline from the command line.

  • stone run: lexer -> parser -> checker -> interpreter

  • stone build: lexer -> parser -> checker -> codegen::ir (lower to IR) -> codegen::regalloc (linear scan) -> codegen::x64 (emit, then gcc)

  • stone check: lexer -> parser -> checker, printing every diagnostic without running

Source map

module

role

span

source positions (Pos) and ranges (Span) carried by tokens and AST nodes

diagnostic

errors and warnings with a span, and their terminal rendering

token

Token, TokenType, and reserved keywords

lexer

source text to tokens, including Python-style Indent/Dedent

ast

syntax tree nodes, modeled on docs/grammar/stone.asdl

parser

recursive-descent PEG parser; parser/expressions.rs and parser/statements.rs mirror the rules in docs/grammar/stone.gram

checker

type inference and name resolution, producing diagnostics, expression types, and symbols with their references

interpreter

tree-walking evaluator

codegen

the AssemblyGenerator trait and toolchain discovery

codegen/ir

the IR the backend compiles through: lowering from the AST (codegen/ir/lower.rs) and liveness intervals (codegen/ir/liveness.rs)

codegen/regalloc

target-independent linear-scan register allocation and parallel-move ordering

codegen/x64

the x86-64 backend: instruction selection from allocated IR (codegen/x64/emit.rs) and its hand-written builtins (codegen/x64/builtins.rs)

stdlib

names of the builtins shared by both backends (print, len, range, append, int, and float), and format_float, which defines how both print floats

driver

the run/build/check pipelines, and analyze for editor tooling

Language server

lsp/ is the stone-lsp crate, a language server built on lsp-server and lsp-types, kept out of the main crate so stone itself still depends only on clap. It reanalyzes a document on every change with driver::analyze and answers requests from the resulting checker::Analysis: its diagnostics, the type of every expression, and every symbol with all of its references.

request

answered from

diagnostics

Analysis::diagnostics, with a syntax error for every statement that fails to parse, in blocks too

hover

the symbol’s signature, a builtin’s documentation, or the innermost expression’s type

definition, references, rename

Analysis::reference_at and Analysis::references_to; a builtin’s definition is its line in a generated builtins.st reference

document symbols

globals and functions, with each function’s parameters and locals

completion

Analysis::visible_at, builtins, and keywords

editors/vscode/ is a VS Code extension that provides highlighting and indentation rules and starts stone-lsp for .st files.

Fuzzing

The fuzz/ crate uses cargo-fuzz (libFuzzer, nightly Rust). It is a separate crate, so the main crate still depends only on clap.

target

input

checks

lex, parse

arbitrary text

the front end returns tokens, a module, or an error, and never panics, overflows the stack, or takes exponential time

interpret

arbitrary text

the interpreter finishes under fuzz::LIMITS without panicking

codegen

arbitrary text that parses

X64Generator::assemble succeeds or returns an error, without gcc

structured

programs from stone_fuzz::generate

the same stages on deep, valid programs

differential

programs from stone_fuzz::generate

stone run and stone build print the same output

stone_fuzz::generate writes source text rule by rule from the grammar, tracking scope so every name and call is defined and every program passes the checker. It covers int arithmetic, comparisons, functions with up to eight parameters, if/while/for with break and cont, multi-argument print with strings and booleans, and top-level lists. Every loop is bounded, and functions never read globals, which may not be assigned yet when they run. differential skips a program if the interpreter rejects it (out of fuel, division by zero, an unassigned variable).

Seeds for the text targets are the .st programs in fuzz/corpus/<target>/. fuzz/stone.dict lists stone’s tokens.

Benchmarks

The bench/ crate compares compiled stone with C (gcc -O2 and gcc -O0) and Python. It is a separate crate, like fuzz/, and depends on stone only for its tests. Each benchmark in bench/programs/ is written three times, as .st, .c, and .py, and the runner refuses to time a program unless all four builds print the same .out file. See bench/README.md for the programs and a baseline.