rhai_eval.rs wraps the embedded Rhai scripting engine. monty_eval.rs
wraps the monty crate, which compiles a Python function body to bytecode
and runs it in a pure-Rust interpreter. Both are called from core.rs after
the macro body has been expanded to a string.
Design rationale
Rhai: embedded, zero-process, type-preserving store
Rhai is embedded at compile time — no external process, no FFI. The engine is
allocated once in RhaiEvaluator::new() and reused across all %rhaidef
calls.
Variable injection order: weaveback string variables are added to the Rhai
Scope first (lower priority); persistent store entries are added on top and
override any same-named weaveback variable. This ensures scripts always see
the typed, persistent value in the store rather than the string snapshot from
weaveback’s scope.
Store write-back: after the script runs, any store key whose scope value
was touched by the script is written back into the HashMap<String, Dynamic>.
Only keys that were already present in the store participate — new variables
created inside the script are not auto-persisted. Use %rhaiset /
%rhaiexpr to pre-initialise a key before first use.
Numeric auto-promotion: rhaistore_set_str parses numeric strings to
i64 or f64 before storing them. This means arithmetic operators work
directly on store values inside Rhai scripts without parse_int(x).
Operations limit: set_max_operations(100_000) prevents infinite loops
from hanging the process.
Registered helpers available in every %rhaidef body:
-
parse_int(s)→i64 -
parse_float(s)→f64 -
to_hex(n)→"0xXX"
Python (monty): compile-once, run-no-limits
monty compiles the function body to bytecode once per call and runs it in a pure-Rust interpreter. This avoids PyO3 and CPython entirely — no dynamic linking, no Python installation required at runtime.
Parameter injection: declared parameters are passed as positional arguments. Store entries are prepended as additional parameters (store keys not in the declared parameter set), so they are visible inside the script as plain variables. Declared params shadow any store key with the same name.
No automatic store write-back: unlike Rhai, the Python store is not
written back automatically after the script runs. Use %pyset to persist
values explicitly.
Rhai evaluator
File structure
// <<@file weaveback-macro/src/evaluator/rhai_eval.rs>>=
// <<rhai eval preamble>>
// <<rhai evaluator struct>>
// <<rhai evaluator impl>>
// <<dynamic to string>>
// @
// <<@file weaveback-macro/src/evaluator/monty_eval.rs>>=
// <<monty eval preamble>>
// <<monty evaluator struct>>
// <<monty evaluator impl>>
// <<monty object to string>>
// @
Preamble
// <<rhai eval preamble>>=
// crates/weaveback-macro/src/evaluator/rhai_eval.rs
use ;
use HashMap;
// @
RhaiEvaluator struct
// <<rhai evaluator struct>>=
// @
RhaiEvaluator implementation
// <<rhai evaluator impl>>=
// @
dynamic_to_string
String Dynamic values are returned as-is. Unit (Rhai’s ()) maps to an
empty string. All other types use their to_string() representation.
// <<dynamic to string>>=
// @
Monty evaluator (Python)
Preamble
// <<monty eval preamble>>=
// crates/weaveback-macro/src/evaluator/monty_eval.rs
use ;
use ;
// @
MontyEvaluator struct
// <<monty evaluator struct>>=
;
// @
MontyEvaluator implementation
Store keys are prepended before declared params so that prefix + name works
inside Python without explicit parameter declaration. Declared params always
shadow same-named store keys because params are appended last and positional
binding is left-to-right.
// <<monty evaluator impl>>=
// @
monty_object_to_string
MontyObject::List items are concatenated without a separator — this matches
the behaviour of returning a list of strings from a Python function as if they
were a single concatenated string.
// <<monty object to string>>=
// @