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

Memory Semantics

ref, slot, and val modifiers control how values move between caller and callee, the distinction is observable.

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, smart, and off modes, stack behavior is predictable.

Bytecode Serialization

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

memory semantics
func mix(slot ext, ref mirror, val snap) {
    ext = ext + 1       // writes back to caller's variable
    mirror = ext         // writes through to caller's ref
    snap[0] = 999       // local copy only — caller unchanged
}
var x = 10;
var y = 0;
var arr = [5, 6, 7];
mix(x, y, arr);
assert(x == 11,      "slot wrote back");
assert(y == 11,      "ref wrote through");
assert(arr[0] == 5,  "val kept caller safe");
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");
}

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();
    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 integrate Zym into your project, understanding is assumed.