Skip to content

nickzinn/zmanLang

Repository files navigation

ZmanLang

Inspired over the holidays by reading Crafting Interpreters by Robert Nystrom, I designed a custom high level language and assembly language and built a compiler, assembler, virtual machine, and disassembler. Compatible with WebAssembly (WASM) so it runs in the browser (VM compiled to WASM). All implemented in low level C. The goal of this project was to do a very difficult software engineering task using GitHub Copilot Agent Mode.

I say inspired by Nystom's book, but of course I am old school and learned compiler design from the Red Dragon book. The assembler code I learned back in the day on Z80 and Motorola 68k. Implementing a VM with byte-code was new experience for me, and actually the VM is considerably more sophisticated than the high level language.

Live demo using Web Assembly

https://nickzinn.github.io/zmanLang/

Language, ZmanLang

A C-style, implicitly typed, sort-of toy language but relatively complete with functions, arrays, and strings. Canonical Example:

print("Hello, World!\n");

Sort Example:

func bubbleSort(arr[]) {
  let n := length(arr);
  let i := 0;
  while (i < n) {
    let j := 0;
    while (j < n - 1) {
      if (arr[j] > arr[j + 1]) {
        let tmp := arr[j];
        arr[j] := arr[j +1];
        arr[j+1] := tmp;
      }
      j := j + 1;
    }
    i := i + 1;
  }
}

let data := {-1,5,4, -3, 8, 7};
println(data);
bubbleSort(data);
println(data);

I classify it as a toy language because it doesn't implement floating point math (only integer), data structures and garbage collection. Currently dynamic memory allocation just grows the heap via a bump allocator, which is fast and suitable for small scripts.

Assembly Language, StackVM-32 ASM

32-bit, stack-based assembly language with support for directives and labels. Picked a small instruction set of 50 opcodes (and one for tail recursion). Easy to handcode and very capable. Also added 9 syscalls for IO and dynamic memory. Example:

.code
.entry main

main:
  PUSHI str_lit_0
  PUSHI 13
  SYSCALL 4
  HALT

.data

str_lit_0:
  .ascii "Hello world!\n"

.end

Compiler

The v0 reference compiler, zmc, compiles ZmanLang source (.zm) to StackVM-32 assembly (.asm) (see spec-lang.md and examples/web/README.md).

Assembler

The assembler parses StackVM-32 assembly (.asm) and produces a runnable ZVM container (.zvm) (see spec-assembler.md and spec-vm.md).

Virtual Machine

The StackVM-32 virtual machine loads and executes .zvm containers (instruction set + container format: spec-vm.md).

Disassembler

The disassembler turns a .zvm container back into readable assembly and auto-labels branch/call targets (see spec-vm.md).

Build / Dependencies

  • Required: a C compiler (cc, clang, or gcc) and make.
  • Optional (static analysis): clang-tidy (preferred) or clang for make lint.
  • Optional (web/WASM): Emscripten (emcc) for make web.
  • Optional (web harness run): Python 3 for python3 -m http.server.

Common commands:

make release      # build optimized binaries into bin/
make debug        # debug build
make test         # run golden tests (also builds release)

make run PROG=examples/test1.asm
make disasm ZVM=program.zvm

make web          # build WebAssembly bundles (requires emcc)

Deploy to GitHub Pages

This repo deploys the web harness in examples/web to GitHub Pages using a GitHub Actions workflow.

  • One-time setup (repo settings): Settings → Pages → Source: GitHub Actions
  • Deploys automatically on push to main (or you can manually run the workflow from the Actions tab)
  • The workflow builds the WASM bundle (Emscripten) and publishes the contents of examples/web

Local build (optional):

make web
cd examples/web && python3 -m http.server 8000

About

A little programming language, full virtual machine, assembler and disassembler.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published