use super::Engine;
use crate::arena::ContextStack;
use crate::operators::table::dispatch_builtin;
use crate::{CompiledNode, Error, Result};
#[inline(always)]
pub(super) fn dispatch_node_inner<'a>(
engine: &Engine,
node: &'a CompiledNode,
ctx: &mut ContextStack<'a>,
arena: &'a bumpalo::Bump,
) -> Result<&'a crate::arena::DataValue<'a>> {
match node {
CompiledNode::BuiltinOperator {
opcode,
args,
iter_arg_kind,
..
} => dispatch_builtin(*opcode, args, *iter_arg_kind, ctx, engine, arena),
CompiledNode::Var {
scope_level,
segments,
reduce_hint,
metadata_hint,
default_value,
binding,
..
} => crate::operators::variable::evaluate_val_compiled(
crate::operators::variable::CompiledVarSpec {
scope_level: *scope_level,
segments,
reduce_hint: *reduce_hint,
metadata_hint: *metadata_hint,
default_value: default_value.as_deref(),
binding: *binding,
},
ctx,
engine,
arena,
),
#[cfg(feature = "ext-control")]
CompiledNode::Exists(data) => crate::operators::variable::evaluate_exists_compiled(
data.scope_level,
&data.segments,
data.binding,
ctx,
),
CompiledNode::Value { value, .. } => Ok(super::literal_fallback(value, arena)),
CompiledNode::Missing(data) => {
crate::operators::missing::evaluate_compiled_missing(data, ctx, engine, arena)
}
CompiledNode::MissingSome(data) => {
crate::operators::missing::evaluate_compiled_missing_some(data, ctx, engine, arena)
}
CompiledNode::InvalidArgs { op_name, .. } => {
Err(crate::Error::invalid_args().with_operator(*op_name))
}
#[cfg(feature = "error-handling")]
CompiledNode::Throw(data) => {
if ctx.in_catch_scope() && !ctx.is_tracing() {
let av: &crate::arena::DataValue = arena.alloc(data.error.view_in(arena));
ctx.set_thrown_slot(av);
Err(Error::deferred_thrown())
} else {
Err(Error::thrown(data.error.clone()))
}
}
#[cfg(feature = "templating")]
CompiledNode::StructuredObject(data) => {
evaluate_structured_object(data, ctx, engine, arena)
}
CompiledNode::Array { nodes, .. } => evaluate_array_literal(nodes, ctx, engine, arena),
CompiledNode::CustomOperator(data) => evaluate_custom_operator(data, ctx, engine, arena),
CompiledNode::Cse(data) => dispatch_cse(engine, data, ctx, arena),
}
}
#[inline(never)]
fn dispatch_cse<'a>(
engine: &Engine,
data: &'a crate::node::CseData,
ctx: &mut ContextStack<'a>,
arena: &'a bumpalo::Bump,
) -> Result<&'a crate::arena::DataValue<'a>> {
debug_assert_eq!(
ctx.depth(),
0,
"Cse node dispatched under a pushed frame — the memo would leak across frames"
);
if !ctx.is_tracing() {
if let Some(hit) = ctx.cse_slot(data.slot) {
return Ok(hit);
}
let result = dispatch_node_inner(engine, &data.inner, ctx, arena);
if let Ok(value) = &result {
ctx.fill_cse_slot(data.slot, value);
}
result
} else {
dispatch_node_inner(engine, &data.inner, ctx, arena)
}
}
#[cfg(feature = "templating")]
#[inline(never)]
fn evaluate_structured_object<'a>(
data: &'a crate::node::StructuredObjectData,
ctx: &mut crate::arena::ContextStack<'a>,
engine: &super::Engine,
arena: &'a bumpalo::Bump,
) -> crate::Result<&'a crate::arena::DataValue<'a>> {
use crate::arena::DataValue;
if data.fields.is_empty() {
return Ok(crate::arena::singletons::singleton_empty_object());
}
let escape = data.escape;
let mut pairs: bumpalo::collections::Vec<'a, (&'a str, DataValue<'a>)> =
bumpalo::collections::Vec::with_capacity_in(data.fields.len(), arena);
for (key, n) in data.fields.iter() {
let val_av = engine.dispatch_node(n, ctx, arena)?;
let key: &'a str = match escape {
Some(c) => key.strip_prefix(c).unwrap_or(key),
None => key.as_str(),
};
pairs.push((key, *val_av));
}
Ok(arena.alloc(DataValue::Object(pairs.into_bump_slice())))
}
#[inline(never)]
fn evaluate_array_literal<'a>(
nodes: &'a [crate::CompiledNode],
ctx: &mut crate::arena::ContextStack<'a>,
engine: &super::Engine,
arena: &'a bumpalo::Bump,
) -> crate::Result<&'a crate::arena::DataValue<'a>> {
use crate::arena::DataValue;
if nodes.is_empty() {
return Ok(crate::arena::singletons::singleton_empty_array());
}
let mut items: bumpalo::collections::Vec<'a, DataValue<'a>> =
bumpalo::collections::Vec::with_capacity_in(nodes.len(), arena);
for n in nodes.iter() {
let av = engine.dispatch_node(n, ctx, arena)?;
items.push(*av);
}
Ok(arena.alloc(DataValue::Array(items.into_bump_slice())))
}
#[inline(never)]
fn evaluate_custom_operator<'a>(
data: &'a crate::node::CustomOperatorData,
ctx: &mut crate::arena::ContextStack<'a>,
engine: &super::Engine,
arena: &'a bumpalo::Bump,
) -> crate::Result<&'a crate::arena::DataValue<'a>> {
use crate::arena::DataValue;
let (op, info) = match data.slot {
Some((engine_id, slot)) if engine_id == engine.id => {
(engine.custom_operators.at(slot), data.info)
}
_ => {
let op = engine
.custom_operators
.get(&data.name)
.ok_or_else(|| Error::invalid_operator(data.name.clone()))?;
(op, op.info())
}
};
if !info.accepts(data.args.len()) {
return Err(Error::invalid_args().with_operator(data.name.clone()));
}
let mut args: bumpalo::collections::Vec<'a, &'a DataValue<'a>> =
bumpalo::collections::Vec::with_capacity_in(data.args.len(), arena);
for arg in data.args.iter() {
args.push(engine.dispatch_node(arg, ctx, arena)?);
}
let mut wrapped = crate::operator::EvalContext::new(ctx);
op.evaluate(&args, &mut wrapped, arena)
}