use bumpalo::Bump;
use crate::arena::{ContextStack, DataValue};
use crate::node::{MetadataHint, PathSegment, ReduceHint, ScopeBinding};
use crate::{CompiledNode, Result};
#[cfg(feature = "ext-control")]
mod exists;
mod val;
#[cfg(feature = "ext-control")]
pub(crate) use exists::{evaluate_exists, evaluate_exists_compiled};
pub(crate) use val::{evaluate_val, evaluate_val_compiled};
#[inline]
fn metadata_hint_lookup<'a>(
ctx: &ContextStack<'a>,
path: &str,
arena: &'a Bump,
) -> Option<&'a DataValue<'a>> {
if path == "index" {
let idx = ctx.current().get_index()?;
let i = idx as i64;
return Some(
crate::arena::singletons::singleton_small_int(i).unwrap_or_else(|| {
arena.alloc(DataValue::Number(datavalue::NumberValue::Integer(i)))
}),
);
}
if path == "key" {
let key = ctx.current().get_key()?;
return Some(arena.alloc(DataValue::String(key)));
}
None
}
#[inline(always)]
fn current_data<'a>(ctx: &ContextStack<'a>) -> &'a DataValue<'a> {
ctx.current().data()
}
#[inline]
fn frame_data_at_level<'a>(ctx: &ContextStack<'a>, level: isize) -> Option<&'a DataValue<'a>> {
Some(ctx.get_at_level(level)?.data())
}
#[rustfmt::skip]
static SMALL_INT_STRS: [&str; 100] = [
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
];
#[inline]
pub(super) fn small_int_str(i: i64) -> Option<&'static str> {
if (0..100).contains(&i) {
Some(SMALL_INT_STRS[i as usize])
} else {
None
}
}
#[inline]
fn path_str_from_data<'a>(av: &'a DataValue<'a>, arena: &'a Bump) -> &'a str {
if let Some(s) = av.as_str() {
return s;
}
if let DataValue::Number(n) = av {
if let Some(i) = n.as_i64()
&& let Some(s) = small_int_str(i)
{
return s;
}
return arena.alloc_str(&n.to_string());
}
""
}
pub(crate) struct CompiledVarSpec<'n> {
pub scope_level: u32,
pub segments: &'n [PathSegment],
pub reduce_hint: ReduceHint,
pub metadata_hint: MetadataHint,
pub default_value: Option<&'n CompiledNode>,
pub binding: ScopeBinding,
}
#[inline]
fn level_marker_from_array(av: &DataValue<'_>) -> Option<i64> {
match av {
DataValue::Array(items) if !items.is_empty() => items[0].as_i64(),
_ => None,
}
}
#[inline]
fn array_len(av: &DataValue<'_>) -> Option<usize> {
match av {
DataValue::Array(items) => Some(items.len()),
_ => None,
}
}
#[inline]
fn array_get<'a>(av: &'a DataValue<'a>, i: usize) -> Option<&'a DataValue<'a>> {
let DataValue::Array(items) = av else {
return None;
};
items.get(i)
}
#[inline]
fn default_or_null<'a>(
default_value: Option<&'a CompiledNode>,
ctx: &mut ContextStack<'a>,
engine: &crate::Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
match default_value {
Some(node) => engine.dispatch_node(node, ctx, arena),
None => Ok(crate::arena::singletons::singleton_null()),
}
}
#[inline]
pub(super) fn debug_check_binding<'a>(
binding: ScopeBinding,
scope_level: u32,
ctx: &ContextStack<'a>,
) {
if !cfg!(debug_assertions) {
return;
}
let predicted: *const DataValue<'a> = match binding {
ScopeBinding::Unresolved => return,
ScopeBinding::Root => ctx.root_input(),
ScopeBinding::Current => ctx.current().data(),
ScopeBinding::Ancestor => {
debug_assert!(
scope_level >= 2 && (scope_level as usize) < ctx.depth(),
"Ancestor binding (level {scope_level}) at depth {} is not a strict \
interior frame",
ctx.depth()
);
return;
}
};
let actual: *const DataValue<'a> = if scope_level == 0 {
ctx.current().data()
} else {
match ctx.get_at_level(scope_level as isize) {
Some(r) => r.data(),
None => return,
}
};
debug_assert!(
std::ptr::eq(predicted, actual),
"static scope binding {binding:?} (level {scope_level}) diverged from \
the runtime walk at depth {}",
ctx.depth()
);
}