YatsuScript

A bytecode interpreter built from absolute scratch. No dependencies, no existing code — just me and the Rust compiler.

What it is

YatsuScript is a bytecode interpreter I wrote from absolute zero. No dependencies. No reading an existing interpreter's source. Just the Rust compiler, a text editor, and a lot of terminal print debugging.

It started as “I wonder how these work” and ended as the hardest thing I’ve built.

Architecture

The project is structured as a single Rust crate with three clear phases:

  • Lexer — Tokenizes source code into a flat token stream. Handles keywords, identifiers, literals, and operators.
  • Compiler — Walks the token stream and emits bytecode instructions. Register-based, with a fixed instruction set.
  • VM — Executes bytecode. Register allocation, memory mapping, instruction dispatch.

The instruction set is small by design — about 20 opcodes covering load/store, arithmetic, control flow, and I/O. Enough to be interesting, not so many that I'd never finish.

The hard parts

Register allocation. Deciding which values live in registers vs. memory is a genuinely hard problem. My first pass was naive — spill everything — and the interpreter was unusably slow. The second pass used a linear scan allocator. It's still not production-grade, but it works, and I understand exactly where every cycle goes.

Self-hosting the debugger. When a bytecode program crashes, you get a register dump and a memory map. Building a disassembler that could map bytecode offsets back to source lines was a week of work by itself. Worth every hour — trying to debug raw bytecode without it is like reading assembly without a symbol table.

What's next

A garbage collector. The interpreter currently leaks memory — values are allocated but never freed. I've been reading the GC literature (Baker's algorithm, generational collection, the Cheney copy collector) and prototyping a simple mark-sweep in a branch. After that: maybe a JIT, but let's be realistic about scope.

What I learned

More than any other project: that you don't need to understand everything before you start. I began with zero knowledge of register allocation, zero knowledge of VM design. The Rust compiler catches enough mistakes that you can learn by doing — each compiler error teaches you something real about how computers work at the low level.

If you like compilers, we should talk.