use crate::CompiledNode;
use crate::arena::DataValue;
use crate::operators::meta::ArithOp;
use datavalue::NumberValue;
use super::input::plain_var_segments;
use crate::operators::arithmetic::try_int_op;
pub(super) struct FieldCursor<'n> {
segments: &'n [crate::node::PathSegment],
single_key: Option<&'n str>,
hint: usize,
}
impl<'n> FieldCursor<'n> {
#[inline]
pub(super) fn new(segments: &'n [crate::node::PathSegment]) -> Self {
use crate::node::PathSegment;
let single_key = match segments {
[PathSegment::Field(k)] => Some(k.as_ref()),
[PathSegment::FieldOrIndex(k, _)] => Some(k.as_ref()),
_ => None,
};
Self {
segments,
single_key,
hint: 0,
}
}
#[inline(always)]
pub(super) fn resolve<'a>(&mut self, item: &'a DataValue<'a>) -> Option<&'a DataValue<'a>> {
if let (Some(key), DataValue::Object(pairs)) = (self.single_key, item) {
return crate::arena::value::object_lookup_field_hinted(pairs, key, &mut self.hint);
}
if self.segments.is_empty() {
Some(item)
} else {
crate::arena::value::traverse_segments(item, self.segments)
}
}
}
#[inline(always)]
pub(super) fn combine(
a: NumberValue,
b: NumberValue,
int_op: impl Fn(i64, i64) -> Option<i64>,
float_op: impl Fn(f64, f64) -> f64,
) -> NumberValue {
match (a.as_i64(), b.as_i64()) {
(Some(x), Some(y)) => try_int_op(x, y, int_op, float_op),
_ => NumberValue::from_f64(float_op(a.as_f64(), b.as_f64())),
}
}
#[inline(always)]
pub(super) fn arith_number(op: ArithOp, a: NumberValue, b: NumberValue) -> NumberValue {
with_arith!(op, |f| f(a, b))
}
macro_rules! with_ops {
($op:expr, |$int:ident, $float:ident| $body:expr) => {
match $op {
ArithOp::Add => {
let ($int, $float) = (i64::checked_add, |x: f64, y: f64| x + y);
$body
}
ArithOp::Sub => {
let ($int, $float) = (i64::checked_sub, |x: f64, y: f64| x - y);
$body
}
ArithOp::Mul => {
let ($int, $float) = (i64::checked_mul, |x: f64, y: f64| x * y);
$body
}
}
};
}
pub(super) use with_ops;
macro_rules! with_arith {
($op:expr, |$f:ident| $body:expr) => {
$crate::operators::array::fused::with_ops!($op, |int_op, float_op| {
let $f = |a: NumberValue, b: NumberValue| {
$crate::operators::array::fused::combine(a, b, int_op, float_op)
};
$body
})
};
}
pub(super) use with_arith;
pub(super) enum FusedMapBody<'n> {
Extract {
segments: &'n [crate::node::PathSegment],
},
ArithVarLit {
op: ArithOp,
segments: &'n [crate::node::PathSegment],
lit: &'n datavalue::OwnedDataValue,
var_is_lhs: bool,
},
ArithVarVar {
op: ArithOp,
a_segments: &'n [crate::node::PathSegment],
b_segments: &'n [crate::node::PathSegment],
},
}
impl FusedMapBody<'_> {
#[inline]
pub(super) fn body_cost(&self) -> u64 {
match self {
FusedMapBody::Extract { .. } => 1,
FusedMapBody::ArithVarLit { .. } => 2,
FusedMapBody::ArithVarVar { .. } => 3,
}
}
pub(super) fn detect(body: &CompiledNode) -> Option<FusedMapBody<'_>> {
if let Some(segments) = plain_var_segments(body) {
return Some(FusedMapBody::Extract { segments });
}
let CompiledNode::BuiltinOperator { opcode, args, .. } = body else {
return None;
};
let op = opcode.arith_op()?;
if args.len() != 2 {
return None;
}
match (&args[0], &args[1]) {
(var, CompiledNode::Value { value, .. }) => {
let segments = plain_var_segments(var)?;
Some(FusedMapBody::ArithVarLit {
op,
segments,
lit: value,
var_is_lhs: true,
})
}
(CompiledNode::Value { value, .. }, var) => {
let segments = plain_var_segments(var)?;
Some(FusedMapBody::ArithVarLit {
op,
segments,
lit: value,
var_is_lhs: false,
})
}
(a, b) => {
let a_segments = plain_var_segments(a)?;
let b_segments = plain_var_segments(b)?;
Some(FusedMapBody::ArithVarVar {
op,
a_segments,
b_segments,
})
}
}
}
}