Source editor
Tokens
AST
Bytecode
Run / Result
Step: value stack
VM not started.
Kindling is a small, dynamically typed language with a real bytecode compiler, a stack based virtual machine, and a mark and sweep garbage collector, written in pure Rust with zero dependencies. Type a program and watch the whole pipeline run in the page: source to tokens to a syntax tree to bytecode, then step the VM one instruction at a time while the value stack, call frames, and collector update live.
A quick tour. Kindling turns your text into tokens, then into a tree, then into a stack machine program. Run gives you the fast answer. Step is the slow motion view where you can see each push and pop as the machine works. Run GC shows how a mark and sweep collector keeps live objects and frees the rest.
VM not started.
The same compiler the playground runs also backs the CLI. Point kindling disasm at a file and it prints the constant pool and the instruction stream, offset by offset, in a text form that assembles straight back into the identical program.
Kindling's correctness rests on a differential gate. For hundreds of randomly generated programs, the bytecode VM and a completely independent tree walking interpreter must agree on the whole outcome, the same value on success or the same error on a trap such as division by zero or exceeding the call depth. Two independent evaluators agreeing is the oracle.
# round trip integrity, enforced on every build assemble(disassemble(program)) == program deserialize(serialize(program)) == program run(deserialize(serialize(program))) == run(program) # and the collector: a collection frees exactly the unreachable objects, # keeps every reachable one, and never frees a live object mid execution gc: mark from roots, sweep the rest, checked on a known reachability graph
Kindling lives in the space between a teaching tree walker and a production runtime you cannot read.
The C bytecode VM from the book. Kindling follows the same clox shape, a single pass compiler, a stack machine, closures with upvalues, and a mark and sweep collector, rewritten in safe Rust with zero dependencies.
Small, mature, embeddable bytecode languages written in C and tuned for speed. Kindling is smaller and slower, and spends that budget on staying readable end to end and on a machine checkable correctness gate instead.
A genuine bytecode runtime kept small enough to read in an afternoon: two independent evaluators checked against each other, a lossless text and binary bytecode format, and a sandbox with no file, network, or system access from inside a program.
A hand written scanner that turns source text into a token stream.
Recursive descent from tokens to an AST, with source nesting and expression depth capped so pathological input is a clean error, not a crash.
Walks the AST once and emits Kindling's own opcodes, resolving globals, locals, and upvalues as it goes.
Thirty three opcodes covering constants, globals, locals, upvalues, jumps, calls, closures, and print.
A clox style machine with call frames, globals, locals, and closures that capture the variable by reference, not a snapshot.
A completely independent tree walker, run against the VM on every build so any disagreement fails the gate.
A precise collector: mark from the roots, sweep the rest, verified on a known reachability graph and under allocation stress.
Disassemble to reassemblable text and serialize to a compact blob, both proven lossless round trips.
Three ways in: the command line tool, the crate API, and the correctness gate you can run yourself.
kindling run compiles and executes a program, kindling disasm prints the compiled bytecode, and kindling repl starts an interactive session.
run_source and compile_source drive the pipeline, run_program executes a compiled program, eval_reference runs the independent interpreter, and disassemble / assemble plus serialize / deserialize move bytecode to text or bytes and back.
Differential testing, round trip integrity, and garbage collector correctness all run as tests, with the fuzzing workload scaled by KINDLING_FUZZ_OPS.
# build the runtime and CLI (pure Rust, zero dependencies) cargo build --release # run a program, disassemble it, or start a REPL ./target/release/kindling run examples/factorial.kdl ./target/release/kindling disasm examples/factorial.kdl ./target/release/kindling repl # run the tests, including the differential correctness gate cargo test KINDLING_FUZZ_OPS=4000 cargo test --release