Skip to content

Embedding ZephyrVM

Zephyr is designed to be embedded into C++ environments such as game engines. All built-in APIs are exposed through the include/zephyr/api.hpp header.

Minimal Example

Initialize a ZephyrVM, compile a script, and execute it:

cpp
#include <zephyr/api.hpp>

int main() {
    // 1. Initialize VM and create a runtime
    ZephyrVM vm;
    auto rt = vm.create_runtime();

    // 2. Script source to compile
    const char* source = R"(
        fn add(a: int, b: int) -> int { return a + b; }
        fn main() -> int { return add(10, 20); }
    )";

    // 3. Compile and execute at a specific entry point
    auto chunk = vm.compile_bytecode_function(source, "main");
    auto result = rt.execute(chunk);

    // 4. Extract the result value
    printf("Result: %lld\n", result.as_int()); // 30

    return 0;
}

Loading Files and Calling Functions Repeatedly

Set a package root directory and load script files from disk:

cpp
ZephyrVM vm;
vm.set_package_root("game/scripts/");
auto rt = vm.create_runtime();

// Load and evaluate immediately
rt.run_file("game/scripts/main.zph");

To call a specific function inside a loaded module from the engine loop, use global symbol reflection:

cpp
auto chunk = vm.compile_module_bytecode(source, "game");
rt.load_module(chunk, "game");

// Retrieve the 'update' function and call it each frame
ZephyrValue fn_val = rt.get_value("update");
ZephyrValue arg = ZephyrValue::from_float(0.016f);  // delta time
rt.call(fn_val, {arg});

Coroutine Control from C++

Zephyr provides APIs to spawn and drive coroutines directly from the host, integrating naturally with the engine update loop:

cpp
auto co = rt.spawn_coroutine("patrol_ai");
rt.pass_arg(co, entity_handle);

// Inside the engine update loop:
while (!rt.query_coroutine(co).done) {
    rt.resume(co, {});
    engine.step();
}
rt.cancel_coroutine(co);