ZYM
Control without the ceremony.
Fast. Simple. Powerful.

A compact embeddable scripting core for systems that need control where it matters.

Core

What Zym Is

Register-based VM. Tracing GC. Two-way C FFI. Rich built-in types — strings, lists, maps, structs, enums, and references — all first-class, all doing what you'd expect.

zym
func fibonacci(n) {
    if (n < 2) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}
print(fibonacci(30));

Why Zym Exists

Zym was built for real embedded projects. It exists because control sometimes needs to go deeper than conventional scripting allows.

It started as a replacement for a private embedded scripting core — built out of practical need, not research ambition. Every design decision reflects that origin, intentionally.

Technical Pillars

Delimited Continuations

Fibers, coroutines, generators, async/await, algebraic effects, all from a small set of primitives.

Preemptive Scheduling

Instruction-count-based time slicing at the VM level, build fair schedulers without cooperative yields, correctness is yours.

Thread-safe VM

Each VM instance owns its heap, globals, and execution state, nothing shared.

Tail-Call Optimization

@tco directive with aggressive, safe, and off modes, stack behavior is predictable.

Bytecode Serialization

Compile once, distribute bytecode, run anywhere, this is efficient.

continuations
var tag = Cont.newPrompt("fiber");

func yield() {
    return Cont.capture(tag);
}

func worker(name) {
    print(name + ": step 1");
    yield();
    print(name + ": step 2");
    yield();
    print(name + ": done");
}
preemption
var targetTicks = 80
var tickCount = 0
var done = false

func onPreempt() {
    tickCount = tickCount + 1
    print("[tick] %v", tickCount)

    if (tickCount >= targetTicks) {
        done = true
        Preempt.disable()
    }
}

Preempt.setCallback(onPreempt)
Preempt.setTimeslice(500)
Preempt.enable()

while (!done) {
    var j = 0
    while (j < 1000) {
        j = j + 1
    }
}

Embedding

Zym is designed to live inside your system — not replace it.

embed in C
#include "zym/zym.h"

int main() {
    ZymVM* vm = zym_newVM(NULL);
    ZymChunk* chunk = zym_newChunk(vm);
    ZymLineMap* map = zym_newLineMap(vm);

    ZymCompilerConfig config = { .include_line_info = true };
    zym_compile(vm, "print(\"Hello from Zym!\")",
                chunk, map, "main", config);
    zym_runChunk(vm, chunk);

    zym_freeChunk(vm, chunk);
    zym_freeLineMap(vm, map);
    zym_freeVM(vm);
    return 0;
}
native functions
ZymValue myAdd(ZymVM* vm, ZymValue a, ZymValue b) {
    return zym_newNumber(zym_asNumber(a) + zym_asNumber(b));
}

zym_defineNative(vm, "add(a, b)", myAdd);
// Scripts can now call add(10, 20) → 30

Building

Standalone Library

Produces libzym_core.a or zym_core.lib, one artifact, link it.

Git Submodule

Add as a submodule, link with CMake, #include "zym/zym.h", configuration is minimal.

standalone
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
as submodule
git submodule add https://github.com/zym-lang/zym_core.git zym_core
CMakeLists.txt
add_subdirectory(zym_core)
add_executable(my_app main.c)
target_link_libraries(my_app PRIVATE zym_core)

Getting Started

Everything you need to write, compile, and ship Zym programs.