use crate::arena::singletons::singleton_bool;
use crate::arena::{ContextStack, DataValue};
use crate::{CompiledNode, Engine, Result};
use bumpalo::Bump;
use std::ops::ControlFlow;
use super::helpers::{
FastPredicate, IterArgKind, ResolvedInput, for_each_iter_array, for_each_iter_object,
resolve_iter_input,
};
#[derive(Clone, Copy)]
pub(super) struct QuantifierShape {
pub(super) short_circuit_on: bool,
pub(super) invert_final: bool,
pub(super) empty_result: bool,
}
impl QuantifierShape {
#[inline]
fn finalize(self, found_short: bool) -> bool {
if found_short {
if self.invert_final {
!self.short_circuit_on
} else {
self.short_circuit_on
}
} else if self.invert_final {
self.short_circuit_on
} else {
!self.short_circuit_on
}
}
}
#[inline]
fn evaluate_quantifier<'a>(
args: &'a [CompiledNode],
iter_arg_kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
shape: QuantifierShape,
) -> Result<&'a DataValue<'a>> {
if args.len() != 2 {
return Err(crate::Error::invalid_args());
}
let predicate = &args[1];
let src = match resolve_iter_input(&args[0], iter_arg_kind, ctx, engine, arena)? {
ResolvedInput::Iterable(s) => s,
ResolvedInput::Empty => return Ok(singleton_bool(shape.empty_result)),
ResolvedInput::Bridge(av) => {
return quantifier_arena_bridge(av, predicate, shape, ctx, engine, arena);
}
};
if src.is_empty() {
return Ok(singleton_bool(shape.empty_result));
}
if !ctx.is_tracing() {
if let Some(fast_pred) = FastPredicate::from_node(predicate) {
let len = src.len();
for i in 0..len {
if fast_pred.evaluate(src.get(i)) == shape.short_circuit_on {
return Ok(singleton_bool(shape.finalize(true)));
}
}
return Ok(singleton_bool(shape.finalize(false)));
}
}
let mut found_short = false;
for_each_iter_array(src.0, predicate, ctx, engine, arena, |_, _item, av| {
if crate::arena::truthy_arena(av, engine) == shape.short_circuit_on {
found_short = true;
return Ok(ControlFlow::Break(()));
}
Ok(ControlFlow::Continue(()))
})?;
Ok(singleton_bool(shape.finalize(found_short)))
}
#[inline]
fn quantifier_arena_bridge<'a>(
input: &'a DataValue<'a>,
predicate: &'a CompiledNode,
shape: QuantifierShape,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
match input {
DataValue::Object(pairs) => {
if pairs.is_empty() {
return Ok(singleton_bool(shape.empty_result));
}
let mut found_short = false;
for_each_iter_object(
pairs,
predicate,
ctx,
engine,
arena,
|_, _item, _key, av| {
if crate::arena::truthy_arena(av, engine) == shape.short_circuit_on {
found_short = true;
return Ok(ControlFlow::Break(()));
}
Ok(ControlFlow::Continue(()))
},
)?;
Ok(singleton_bool(shape.finalize(found_short)))
}
DataValue::Array(items) => {
if items.is_empty() {
return Ok(singleton_bool(shape.empty_result));
}
let mut found_short = false;
for_each_iter_array(items, predicate, ctx, engine, arena, |_, _item, av| {
if crate::arena::truthy_arena(av, engine) == shape.short_circuit_on {
found_short = true;
return Ok(ControlFlow::Break(()));
}
Ok(ControlFlow::Continue(()))
})?;
Ok(singleton_bool(shape.finalize(found_short)))
}
_ => Ok(singleton_bool(shape.empty_result)),
}
}
#[inline]
pub(crate) fn evaluate_all<'a>(
args: &'a [CompiledNode],
iter_arg_kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
evaluate_quantifier(
args,
iter_arg_kind,
ctx,
engine,
arena,
QuantifierShape {
short_circuit_on: false,
invert_final: false,
empty_result: false,
},
)
}
#[inline]
pub(crate) fn evaluate_some<'a>(
args: &'a [CompiledNode],
iter_arg_kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
evaluate_quantifier(
args,
iter_arg_kind,
ctx,
engine,
arena,
QuantifierShape {
short_circuit_on: true,
invert_final: false,
empty_result: false,
},
)
}
#[inline]
pub(crate) fn evaluate_none<'a>(
args: &'a [CompiledNode],
iter_arg_kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
evaluate_quantifier(
args,
iter_arg_kind,
ctx,
engine,
arena,
QuantifierShape {
short_circuit_on: true,
invert_final: true,
empty_result: true,
},
)
}