Small programming language in Go with register-based VM — feedback welcome

I’ve been working on a small programming language called Kairo to better understand how language runtimes and VMs work.

It started as a simple interpreter, but over time I extended it into a full pipeline:

source -> AST -> bytecode -> virtual machine

The VM uses a register-based design (instead of stack-based), and the language supports:

  • functions, closures, and upvalues
  • arrays and map/object-like structures
  • method dispatch on core types
  • control flow (if/while/for/switch with no fallthrough)
  • try/catch/finally with proper control flow handling (including break/continue/return inside try blocks)
  • bytecode serialization/deserialization (custom .kbc format with magic number + little-endian encoding)
  • REPL + ability to compile and run bytecode separately

One area I found particularly interesting was error handling — control flow is now handled entirely in the VM rather than relying on Go’s error propagation, including stack trace generation and semi-explicit unwinding.

At this stage, I’m not trying to build a production language — the goal is to understand tradeoffs in language and runtime design by building something end-to-end.

I’d really appreciate feedback on:

  • register-based vs stack-based VM tradeoffs in a project of this scale
  • how I’m handling closures/upvalues and runtime values
  • error handling and control-flow design (try/catch/finally semantics)
  • bytecode format / VM structure
  • anything that looks obviously wrong or worth rethinking

Repo: https://github.com/AntarMukhopadhyaya/Kairo

Any feedback or critique is welcome.

Posted Apr 5, 2026, 10:20 AM UTC

20
Loading comments…