Embedding Candela¶
Warning
This API is subject to change, particularly once instruction and memory limits are implemented.
Candela is built to embed in other programs. There are two ways to do it: a
one-shot C ABI for running a script and collecting its output, and a Rust
Engine/Program API for registering host functions and calling script
functions.
One-shot C ABI¶
Download the libcandela-* artifact from
the latest release, or
build it from source as a dynamic library:
Two functions are exposed:
// Compile and run the code, returning its captured output.
extern char* candela_run(const char* code);
// Free a string returned by candela_run.
extern void candela_free_output(char* output);
candela_run compiles the source, runs main, and returns whatever the script
printed. Errors come back in the output string rather than crashing the host.
Host functions and the Engine/Program API¶
A Rust host embeds Candela through the Engine/Program API (see
src/engine.rs). Unlike the one-shot C ABI, it keeps compiler and VM state
resident, so you register typed host functions, compile a script once, and call
its functions repeatedly with state persisting between calls.
Declare the functions your host provides in a host block, then call them
through the namespace you gave the block:
On the Rust side, register a closure for each declared function, compile the script, and call into it by name:
let mut engine = candela::Engine::new();
engine.register_host_fn("app", "rows", |id: &str| id.len() as i64);
let mut program = engine.compile(src, "main.cdl")?;
let rows = program.call("count", &["board".into()])?;
assert_eq!(rows, candela::Value::Int(5));
compile binds each host function to its registered closure and checks the
declared signature against the closure. A missing registration, a signature
mismatch, or a parse or type error is returned as a structured Diagnostic
rather than aborting the process. Values crossing the boundary (int, float,
bool, string, arrays, and string-keyed maps) marshal through the Value
type.
A closure that takes every argument as a slice can be registered with
register_host_fn_variadic, matched by a ... argument list in the host
block:
Because host functions are supplied by the embedding runtime, a program that
uses a host block cannot run through the standalone candela-vm; run it
through the Engine/Program API, which provides the bindings.