///|
/// A reusable interpreter with explicit configuration and import-cache lifetime.
/// Ext/native changes invalidate evaluated imports; TLA and trace changes do not.
/// Replacing the importer clears both sources and values. Each execution receives
/// fresh resource counters. Instances cannot be reentered or changed by callbacks.
pub struct Evaluator {
priv ext_values : Map[String, Json]
priv ext_codes : Map[String, String]
priv ext_nodes : Map[String, Program]
priv tla_values : Map[String, Json]
priv tla_codes : Map[String, String]
priv tla_nodes : Map[String, Program]
priv natives : Map[String, NativeFunction]
priv sources : Map[(String, String), Source]
priv binary_sources : Map[(String, String), BinarySource]
priv parsed_imports : Map[String, Expr]
priv failed_import_asts : Map[String, Unit]
priv mut trace : (Location, String) -> Unit
priv mut eval_hook : EvalHook?
priv mut diagnostic_formatter : DiagnosticFormatter?
priv mut resolver : ((String, String) -> Source raise)?
priv mut binary_resolver : ((String, String) -> BinarySource raise)?
priv mut limits : Limits
priv mut string_output : Bool
priv mut state : Vm?
priv mut busy : Bool
}
///|
/// Create a reusable evaluator. Imports start disabled.
///
/// ```mbt check
/// test {
/// let evaluator = @moonjsonnet.Evaluator::new()
/// let program = @moonjsonnet.Program::parse("std.extVar('x')")
/// evaluator.ext_var("x", 42)
/// assert_eq(evaluator.evaluate(program), 42)
/// evaluator.ext_var("x", 7)
/// assert_eq(evaluator.evaluate(program), 7)
/// }
/// ```
pub fn Evaluator::new() -> Evaluator {
{
ext_values: Map([]),
ext_codes: Map([]),
ext_nodes: Map([]),
tla_values: Map([]),
tla_codes: Map([]),
tla_nodes: Map([]),
natives: Map([]),
sources: Map([]),
binary_sources: Map([]),
parsed_imports: Map([]),
failed_import_asts: Map([]),
trace: default_trace,
eval_hook: None,
diagnostic_formatter: None,
resolver: None,
binary_resolver: None,
limits: Limits::default(),
string_output: false,
state: None,
busy: false,
}
}
///|
fn Evaluator::idle(self : Evaluator) -> Unit raise JsonnetError {
if self.busy {
raise problem(
Runtime,
{ file: "", line: 1, column: 1, },
"evaluator is already executing",
)
}
}
///|
fn Evaluator::invalidate_values(self : Evaluator) -> Unit {
if self.state is Some(vm) {
vm.thunks.clear()
vm.objects.clear()
vm.free_thunks.clear()
vm.free_objects.clear()
vm.imports.clear()
vm.binary_imports.clear()
}
}
///|
/// Replace import callbacks and invalidate all imported sources, ASTs and values.
/// A binary callback is authoritative for all forms; otherwise the text callback
/// supplies UTF-8 bytes. Omitting both callbacks disables imports.
pub fn Evaluator::set_importer(
self : Evaluator,
resolver? : (String, String) -> Source raise,
binary_resolver? : (String, String) -> BinarySource raise,
) -> Unit raise JsonnetError {
self.idle()
self.resolver = resolver
self.binary_resolver = binary_resolver
self.state = None
self.sources.clear()
self.binary_sources.clear()
self.parsed_imports.clear()
self.failed_import_asts.clear()
}
///|
pub fn Evaluator::set_trace(
self : Evaluator,
trace : (Location, String) -> Unit,
) -> Unit raise JsonnetError {
self.idle()
self.trace = trace
}
///|
pub fn Evaluator::set_limits(
self : Evaluator,
limits : Limits,
) -> Unit raise JsonnetError {
self.idle()
check_limits(limits, { file: "", line: 1, column: 1, })
self.limits = limits
}
///|
/// Select raw string output for text/multi methods. JSON and stream ignore this.
pub fn Evaluator::set_string_output(
self : Evaluator,
enabled : Bool,
) -> Unit raise JsonnetError {
self.idle()
self.string_output = enabled
}
///|
pub fn Evaluator::native_function(
self : Evaluator,
name : String,
native : NativeFunction,
) -> Unit raise JsonnetError {
self.idle()
self.natives[name] = native
self.invalidate_values()
}
///|
fn[T] Evaluator::run(
self : Evaluator,
program : Program,
output : (Vm, Value, Location) -> T raise,
) -> T raise JsonnetError {
self.idle()
self.busy = true
defer {
self.busy = false
}
let reader = self.reader()
evaluate_with_output(
program,
output,
ext_vars=self.ext_values,
ext_code=self.ext_codes,
ext_programs=self.ext_nodes,
tla_vars=self.tla_values,
tla_code=self.tla_codes,
tla_programs=self.tla_nodes,
native_functions=self.natives,
trace=self.trace,
resolver=(from, path) => reader.load_text(from, path),
binary_resolver=(from, path) => reader.load_data(from, path),
limits=self.limits,
session=self,
)
}
///|
pub fn Evaluator::evaluate(
self : Evaluator,
program : Program,
) -> Json raise JsonnetError {
self.run(program, (vm, value, loc) => vm.manifest(value, loc, 0))
}
///|
pub fn Evaluator::evaluate_text(
self : Evaluator,
program : Program,
) -> String raise JsonnetError {
self.run(program, (vm, value, loc) => {
vm.render_text(value, loc, self.string_output)
})
}
///|
pub fn Evaluator::evaluate_stream(
self : Evaluator,
program : Program,
) -> Array[String] raise JsonnetError {
self.run(program, (vm, value, loc) => vm.render_stream(value, loc))
}
///|
pub fn Evaluator::evaluate_multi(
self : Evaluator,
program : Program,
) -> Map[String, String] raise JsonnetError {
self.run(program, (vm, value, loc) => {
vm.render_multi(value, loc, self.string_output)
})
}