A compact embeddable scripting core for systems that need control where it matters.
Core
- One static library, dropped in, that's the integration
- No global state, no dependencies beyond the C standard library, by design
- Designed for real systems — not research, not opinion
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.
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.
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");
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.
#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; }
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.
cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build
git submodule add https://github.com/zym-lang/zym_core.git zym_core
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.