Python-like virtual machine
Find a file
2025-11-03 12:43:30 +00:00
src initial commit 2025-11-03 12:43:30 +00:00
.envrc initial commit 2025-11-03 12:43:30 +00:00
.gitignore initial commit 2025-11-03 12:43:30 +00:00
build.zig initial commit 2025-11-03 12:43:30 +00:00
build.zig.zon initial commit 2025-11-03 12:43:30 +00:00
flake.lock initial commit 2025-11-03 12:43:30 +00:00
flake.nix initial commit 2025-11-03 12:43:30 +00:00
LICENSE initial commit 2025-11-03 12:43:30 +00:00
README.md initial commit 2025-11-03 12:43:30 +00:00

Owa

A small experimental virtual machine and interpreter for an interpreted language, written in Zig.

Owa provides a minimal bytecode format, a stack-based VM, and an object model with prototype inheritance and "magic" hooks. It is designed as a playground for building a compact interpreter, adding opcodes, and experimenting with native/built-in functions.

Features

  • Stack-based VM: executes a custom bytecode (OwaInstruction).
  • Interpreter/threads: OwaInterpreter manages OwaThread instances that run frames and bytecode.
  • Frames and locals: each call executes in an OwaFrame with its own instruction pointer and locals map.
  • Object model: OwaObject supports attributes, prototype chain, and a callable function slot.
  • Magic hooks: predefined magic names (__call, __str, etc.) enable dynamic behavior.
  • Builtins module: includes basic objects such as OWA_NULL and a native print function.

Getting Started

Prerequisites

  • Zig (0.12+ recommended)

Build and Run

zig build
zig build run

This builds the Owa executable and runs the demo program from src/main.zig.

How It Works

Bytecode and Instructions

Instructions are defined in src/vm/instruction.zig as a tagged union OwaInstruction with an underlying enum(u8) opcode. Examples include Nop, LoadConst, Call, Return, and simple stack/rotation ops. Programs are encoded to bytes and then decoded during execution.

const encoded = try OwaInstruction.encode_program(&.{
    OwaInstruction{ .LoadConst = 0 }, // push builtins.print
    OwaInstruction{ .LoadConst = 1 }, // push OWA_NULL
    OwaInstruction{ .Call = 1 },      // call print with 1 arg
    OwaInstruction{ .Return = {} },
}, allocator);

Interpreter and Threads

  • OwaInterpreter initializes memory, holds thread and module lists, and can spawn() new OwaThreads.
  • OwaThread manages a value stack and a current OwaFrame; it repeatedly decodes bytecode and dispatches instructions.
  • OwaFrame tracks the function being executed, the instruction pointer (ip), a link to the previous frame, and a locals map.

Object Model

OwaObject holds:

  • attrs: attribute map (StringHashMapUnmanaged)
  • prototype: optional prototype link
  • call_fn: optional function (either bytecode-backed or native)

The VM resolves attribute access via getAttr and prototype chaining. Callability is resolved through call_fn or the __call magic attribute.

Builtins

The modules/builtins module defines core objects and natives:

  • OWA_NULL: the null value
  • print: a native function printing its arguments

Example

src/main.zig demonstrates creating an interpreter and running a tiny program that calls the builtin print with OWA_NULL:

const std = @import("std");
const owa = @import("Owa");
const modules = @import("modules");

pub fn main() !void {
    const allocator = std.heap.page_allocator;
    var interpreter = try owa.OwaInterpreter.init(allocator);
    var thread1 = try interpreter.spawn();

    var func = owa.OwaFunction{
        .owa = .{
            .code = try owa.OwaInstruction.encode_program(&.{
                owa.OwaInstruction{ .LoadConst = 0 }, // builtins.print
                owa.OwaInstruction{ .LoadConst = 1 }, // OWA_NULL
                owa.OwaInstruction{ .Call = 1 },
                owa.OwaInstruction{ .Return = {} },
            }, allocator),
            .var_const = &.{
                &modules.builtins.print,
                &modules.builtins.OWA_NULL,
            },
        },
    };

    try thread1.execute(&func);
    interpreter.deinit();
}

Project Structure

  • src/mod.zig: re-exports core VM types (instructions, objects, interpreter, etc.).
  • src/main.zig: demo entry point.
  • src/vm/instruction.zig: opcodes and encode/decode utilities.
  • src/core/object.zig: object model and attribute logic.
  • src/core/function.zig: function union (bytecode vs native) and flags.
  • src/core/frame.zig: call frame and locals.
  • src/core/thread.zig: thread state, dispatch loop, and call mechanics.
  • src/core/interpreter.zig: interpreter lifecycle and thread management.
  • src/core/magic.zig: magic names like __call, __str, etc.
  • src/modules/builtins/: built-in objects and native functions.
  • build.zig: Zig build configuration (modules, executable, run step).

Roadmap

  • More opcodes (arithmetic, control flow, comparisons, etc.).
  • Exception handling and error objects.
  • Strings, numbers, and collections as first-class runtime objects.
  • Module system and loader.
  • Basic standard library.

License

See LICENSE for details.