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
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.
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"); }
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.
#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; }
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 write, compile, and ship Zym programs.