#![cfg_attr(not(feature = "all-operators"), allow(dead_code, unused_imports))]
use bumpalo::Bump;
use super::extract::{Extract, IntoValue};
use super::meta::{Extra, Miss, OpMeta, Singleton};
use crate::arena::{ContextStack, DataValue};
use crate::operators::array::{Items, IterArgKind, ResolvedInput, resolve_iter_input};
use crate::{CompiledNode, Engine, Error, OpCode, Result};
pub(crate) struct Cx<'c, 'a> {
pub(crate) ctx: &'c mut ContextStack<'a>,
pub(crate) engine: &'c Engine,
pub(crate) arena: &'a Bump,
}
impl<'c, 'a> Cx<'c, 'a> {
#[inline(always)]
pub(crate) fn new(ctx: &'c mut ContextStack<'a>, engine: &'c Engine, arena: &'a Bump) -> Self {
Cx { ctx, engine, arena }
}
#[inline(always)]
pub(crate) fn eval(&mut self, node: &'a CompiledNode) -> Result<&'a DataValue<'a>> {
self.engine.dispatch_node(node, self.ctx, self.arena)
}
#[inline(always)]
pub(crate) fn charge(&mut self, n: u64) -> Result<()> {
self.ctx.charge(n)
}
#[inline(always)]
pub(crate) fn charge_bytes(&mut self, bytes: usize) -> Result<()> {
self.ctx.charge_bytes(bytes)
}
#[inline(always)]
pub(crate) fn alloc<T>(&self, value: T) -> &'a mut T {
self.arena.alloc(value)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct Arity {
pub min: u8,
pub max: Option<u8>,
}
impl Arity {
pub(crate) const ANY: Arity = Arity { min: 0, max: None };
pub(crate) const fn exactly(n: u8) -> Arity {
Arity {
min: n,
max: Some(n),
}
}
pub(crate) const fn at_least(min: u8) -> Arity {
Arity { min, max: None }
}
pub(crate) const fn between(min: u8, max: u8) -> Arity {
assert!(min <= max, "an empty arity range");
Arity {
min,
max: Some(max),
}
}
pub(crate) const fn of(spans: &[(u8, Option<u8>)]) -> Arity {
let mut min = 0u8;
let mut max = Some(0u8);
let mut optional_seen = false;
let mut i = 0;
while i < spans.len() {
let (lo, hi) = spans[i];
if lo == 0 {
optional_seen = true;
} else if optional_seen {
panic!("a required extractor follows an optional one");
}
min += lo;
max = match (max, hi) {
(Some(max), Some(hi)) => Some(max + hi),
(Some(_), None) if i + 1 == spans.len() => None,
(Some(_), None) => panic!("an unbounded extractor must be the last one"),
(None, _) => unreachable!(),
};
i += 1;
}
Arity { min, max }
}
}
#[inline(always)]
fn check_arity(
len: usize,
arity: Arity,
meta: &OpMeta,
) -> Result<Option<&'static DataValue<'static>>> {
if len < arity.min as usize {
return match meta.on_missing {
Miss::InvalidArgs => Err(Error::invalid_args()),
Miss::Err(msg) => Err(Error::invalid_arguments(msg)),
Miss::Return(value) => Ok(Some(value())),
};
}
if let Some(max) = arity.max
&& len > max as usize
{
return match meta.on_extra {
Extra::Ignore => Ok(None),
Extra::InvalidArgs => Err(Error::invalid_args()),
Extra::Err(msg) => Err(Error::invalid_arguments(msg)),
Extra::Return(value) => Ok(Some(value())),
};
}
Ok(None)
}
pub(crate) trait ExtractList<'a> {
type Raw;
type Out;
fn fetch(args: &'a [CompiledNode], cx: &mut Cx<'_, 'a>) -> Result<Self::Raw>;
fn coerce(raw: Self::Raw, cx: &mut Cx<'_, 'a>) -> Result<Self::Out>;
}
pub(crate) trait Body<'a, L: ExtractList<'a>, R> {
fn call(self, cx: &mut Cx<'_, 'a>, args: L::Out) -> Result<R>;
}
pub(crate) struct Bound<F, K>(pub(crate) F, pub(crate) K);
macro_rules! arity_impls {
($($A:ident $raw:ident $val:ident $i:tt),*) => {
impl<'a, $($A: Extract<'a>),*> ExtractList<'a> for ($($A,)*) {
type Raw = ($($A::Raw,)*);
type Out = ($($A::Out,)*);
#[inline(always)]
#[allow(unused_variables)]
fn fetch(args: &'a [CompiledNode], cx: &mut Cx<'_, 'a>) -> Result<Self::Raw> {
Ok(($($A::fetch(args, $i, cx)?,)*))
}
#[inline(always)]
#[allow(unused_variables)]
fn coerce(raw: Self::Raw, cx: &mut Cx<'_, 'a>) -> Result<Self::Out> {
let ($($raw,)*) = raw;
Ok(($($A::coerce($raw, cx)?,)*))
}
}
impl<'a, F, R, $($A: Extract<'a>),*> Body<'a, ($($A,)*), R> for F
where
F: for<'c> FnOnce(&mut Cx<'c, 'a>, $($A::Out),*) -> Result<R>,
{
#[inline(always)]
fn call(self, cx: &mut Cx<'_, 'a>, args: <($($A,)*) as ExtractList<'a>>::Out) -> Result<R> {
let ($($val,)*) = args;
self(cx, $($val),*)
}
}
impl<'a, F, K, R, $($A: Extract<'a>),*> Body<'a, ($($A,)*), R> for Bound<F, K>
where
F: for<'c> FnOnce(&mut Cx<'c, 'a>, $($A::Out,)* K) -> Result<R>,
{
#[inline(always)]
fn call(self, cx: &mut Cx<'_, 'a>, args: <($($A,)*) as ExtractList<'a>>::Out) -> Result<R> {
let ($($val,)*) = args;
(self.0)(cx, $($val,)* self.1)
}
}
};
}
arity_impls!();
arity_impls!(A0 r0 v0 0);
arity_impls!(A0 r0 v0 0, A1 r1 v1 1);
arity_impls!(A0 r0 v0 0, A1 r1 v1 1, A2 r2 v2 2);
arity_impls!(A0 r0 v0 0, A1 r1 v1 1, A2 r2 v2 2, A3 r3 v3 3);
arity_impls!(A0 r0 v0 0, A1 r1 v1 1, A2 r2 v2 2, A3 r3 v3 3, A4 r4 v4 4);
arity_impls!(A0 r0 v0 0, A1 r1 v1 1, A2 r2 v2 2, A3 r3 v3 3, A4 r4 v4 4, A5 r5 v5 5);
#[inline]
pub(crate) fn call<'a, const OP: u8, L, R, B>(
args: &'a [CompiledNode],
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
body: B,
) -> Result<&'a DataValue<'a>>
where
L: ExtractList<'a>,
R: IntoValue<'a>,
B: Body<'a, L, R>,
{
let meta: &'static OpMeta = const { OpCode::ALL[OP as usize].meta() };
let arity: Arity = const { OpCode::ALL[OP as usize].arity() };
if let Some(early) = check_arity(args.len(), arity, meta)? {
return Ok(early);
}
let mut cx = Cx::new(ctx, engine, arena);
let raw = L::fetch(args, &mut cx)?;
let values = L::coerce(raw, &mut cx)?;
let out = body.call(&mut cx, values)?;
Ok(out.into_value(&mut cx))
}
#[inline]
pub(crate) fn raw<'a, const OP: u8, F>(
args: &'a [CompiledNode],
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
body: F,
) -> Result<&'a DataValue<'a>>
where
F: for<'c> FnOnce(
&'a [CompiledNode],
&'c mut ContextStack<'a>,
&'c Engine,
&'a Bump,
) -> Result<&'a DataValue<'a>>,
{
let meta: &'static OpMeta = const { OpCode::ALL[OP as usize].meta() };
let arity: Arity = const { OpCode::ALL[OP as usize].arity() };
if let Some(early) = check_arity(args.len(), arity, meta)? {
return Ok(early);
}
body(args, ctx, engine, arena)
}
#[inline]
pub(crate) fn iter<'a, const OP: u8, F>(
args: &'a [CompiledNode],
iter_arg_kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
body: F,
) -> Result<&'a DataValue<'a>>
where
F: for<'c> FnOnce(
&'a [CompiledNode],
IterArgKind,
&'c mut ContextStack<'a>,
&'c Engine,
&'a Bump,
) -> Result<&'a DataValue<'a>>,
{
let meta: &'static OpMeta = const { OpCode::ALL[OP as usize].meta() };
let arity: Arity = const { OpCode::ALL[OP as usize].arity() };
if let Some(early) = check_arity(args.len(), arity, meta)? {
return Ok(early);
}
body(args, iter_arg_kind, ctx, engine, arena)
}
#[inline]
pub(crate) fn each<'a, const OP: u8, F>(
args: &'a [CompiledNode],
iter_arg_kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
body: F,
) -> Result<&'a DataValue<'a>>
where
F: for<'c> FnOnce(
Items<'a>,
&'a [CompiledNode],
&'c mut ContextStack<'a>,
&'c Engine,
&'a Bump,
) -> Result<&'a DataValue<'a>>,
{
let meta: &'static OpMeta = const { OpCode::ALL[OP as usize].meta() };
let arity: Arity = const {
let arity = OpCode::ALL[OP as usize].arity();
assert!(arity.min >= 1, "an `each` row reads at least its source");
arity
};
let on_empty: Singleton = const {
match OpCode::ALL[OP as usize].meta().on_empty_source {
Some(value) => value,
None => panic!("an `each` row must declare `on_empty_source`"),
}
};
if let Some(early) = check_arity(args.len(), arity, meta)? {
return Ok(early);
}
let items = match resolve_iter_input(&args[0], iter_arg_kind, ctx, engine, arena)? {
ResolvedInput::Iterable(src) if !src.is_empty() => Items::Array(src),
ResolvedInput::Iterable(_) | ResolvedInput::Empty => return Ok(on_empty()),
ResolvedInput::Bridge(DataValue::Object(pairs)) => Items::Object(pairs),
ResolvedInput::Bridge(value) => Items::Scalar(value),
};
body(items, args, ctx, engine, arena)
}