#![cfg_attr(not(feature = "all-operators"), allow(dead_code, unused_imports))]
use datavalue::OwnedDataValue;
use crate::arena::DataValue;
use crate::node::{CompileCtx, CompiledNode, node_is_static};
#[derive(Clone, Copy)]
pub(crate) struct OpMeta {
pub args_form: ArgsForm,
pub reads_context: bool,
pub effect: Effect,
pub frames: Frames,
pub fold: Fold,
pub cse: Cse,
pub cost: Cost,
pub on_missing: Miss,
pub on_extra: Extra,
pub on_empty_source: Option<Singleton>,
pub algebra: Option<Algebra>,
pub compile: Option<CompileHook>,
pub display: Option<&'static str>,
pub literal_args: LiteralArgs,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ArgsForm {
Any,
ArrayOnly,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Effect {
Pure,
Clock,
Throws,
Catches,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Frames {
None,
At(u8),
LastIfMulti,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum LiteralArgs {
None,
All,
Sole,
At(u8),
}
impl LiteralArgs {
pub(crate) const fn reads(self, index: usize, len: usize) -> bool {
match self {
LiteralArgs::None => false,
LiteralArgs::All => true,
LiteralArgs::Sole => len == 1,
LiteralArgs::At(i) => index == i as usize,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Fold {
Default,
Never,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Cse {
Default,
Never,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Cost {
Node,
Bytes,
PerItem,
NLogN,
Quadratic,
Elements,
}
impl Cost {
pub(crate) const fn name(self) -> &'static str {
match self {
Cost::Node => "node",
Cost::Bytes => "bytes",
Cost::PerItem => "per_item",
Cost::NLogN => "n_log_n",
Cost::Quadratic => "quadratic",
Cost::Elements => "elements",
}
}
}
impl Effect {
pub(crate) const fn name(self) -> &'static str {
match self {
Effect::Pure => "pure",
Effect::Clock => "clock",
Effect::Throws => "throws",
Effect::Catches => "catches",
}
}
}
pub(crate) type Singleton = fn() -> &'static DataValue<'static>;
#[derive(Clone, Copy)]
pub(crate) enum Miss {
InvalidArgs,
Err(&'static str),
Return(Singleton),
}
#[derive(Clone, Copy)]
pub(crate) enum Extra {
Ignore,
InvalidArgs,
Err(&'static str),
Return(Singleton),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Algebra {
Arith(ArithOp),
Div(DivOp),
Ord(OrdOp),
Eq(EqOp),
Logic(Logic),
Extremum(Extremum),
Quant(Quant),
Truth(Truth),
Concat,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct EqOp {
pub strict: bool,
pub negate: bool,
}
impl EqOp {
pub(crate) const LOOSE: EqOp = EqOp {
strict: false,
negate: false,
};
pub(crate) const STRICT: EqOp = EqOp {
strict: true,
negate: false,
};
pub(crate) const LOOSE_NE: EqOp = EqOp {
strict: false,
negate: true,
};
pub(crate) const STRICT_NE: EqOp = EqOp {
strict: true,
negate: true,
};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum DivOp {
Divide,
Modulo,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Logic {
And,
Or,
}
impl Logic {
#[inline(always)]
pub(crate) const fn absorbing(self) -> bool {
matches!(self, Logic::Or)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Truth {
Not,
Bool,
}
impl Truth {
#[inline(always)]
pub(crate) const fn apply(self, truthy: bool) -> bool {
match self {
Truth::Not => !truthy,
Truth::Bool => truthy,
}
}
#[inline]
pub(crate) const fn compose(self, inner: Truth) -> Truth {
if matches!(self, Truth::Not) != matches!(inner, Truth::Not) {
Truth::Not
} else {
Truth::Bool
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Extremum {
Max,
Min,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Quant {
All,
Some,
None,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ArithOp {
Add,
Sub,
Mul,
}
impl ArithOp {
#[inline(always)]
pub(crate) fn checked_i64(self, a: i64, b: i64) -> Option<i64> {
match self {
ArithOp::Add => a.checked_add(b),
ArithOp::Sub => a.checked_sub(b),
ArithOp::Mul => a.checked_mul(b),
}
}
#[inline(always)]
pub(crate) fn apply_f64(self, a: f64, b: f64) -> f64 {
match self {
ArithOp::Add => a + b,
ArithOp::Sub => a - b,
ArithOp::Mul => a * b,
}
}
#[inline]
pub(crate) const fn is_associative(self) -> bool {
matches!(self, ArithOp::Add | ArithOp::Mul)
}
#[inline(always)]
pub(crate) const fn right_identity(self) -> i64 {
match self {
ArithOp::Add | ArithOp::Sub => 0,
ArithOp::Mul => 1,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum OrdOp {
Gt,
Ge,
Lt,
Le,
}
impl OrdOp {
#[inline(always)]
pub(crate) const fn flip(self) -> OrdOp {
match self {
OrdOp::Gt => OrdOp::Lt,
OrdOp::Ge => OrdOp::Le,
OrdOp::Lt => OrdOp::Gt,
OrdOp::Le => OrdOp::Ge,
}
}
#[inline(always)]
pub(crate) fn holds<T: PartialOrd + ?Sized>(self, a: &T, b: &T) -> bool {
match self {
OrdOp::Gt => a > b,
OrdOp::Ge => a >= b,
OrdOp::Lt => a < b,
OrdOp::Le => a <= b,
}
}
#[inline(always)]
pub(crate) fn cmp_f64(self, a: f64, b: f64) -> bool {
match self {
OrdOp::Gt => a > b,
OrdOp::Ge => a >= b,
OrdOp::Lt => a < b,
OrdOp::Le => a <= b,
}
}
}
pub(crate) struct HookArgs<'h> {
pub opcode: crate::OpCode,
pub op_name: &'h str,
pub args_value: &'h OwnedDataValue,
pub ctx: &'h mut CompileCtx,
}
pub(crate) enum Hooked {
Node(CompiledNode),
Generic(Box<[CompiledNode]>),
}
#[derive(Clone, Copy)]
pub(crate) enum CompileHook {
Raw(fn(&OwnedDataValue, &mut CompileCtx) -> Option<CompiledNode>),
Args(fn(Box<[CompiledNode]>, HookArgs<'_>) -> Hooked),
}
pub(crate) const PURE: OpMeta = OpMeta {
args_form: ArgsForm::Any,
reads_context: false,
effect: Effect::Pure,
frames: Frames::None,
fold: Fold::Default,
cse: Cse::Default,
cost: Cost::Node,
on_missing: Miss::InvalidArgs,
on_extra: Extra::Ignore,
on_empty_source: None,
algebra: None,
compile: None,
display: None,
literal_args: LiteralArgs::None,
};
pub(crate) const STRING: OpMeta = OpMeta {
cost: Cost::Bytes,
..PURE
};
pub(crate) const ITERATOR: OpMeta = OpMeta {
frames: Frames::At(1),
cost: Cost::PerItem,
on_extra: Extra::InvalidArgs,
..PURE
};
pub(crate) const TENSOR: OpMeta = OpMeta {
fold: Fold::Never,
cse: Cse::Never,
cost: Cost::Elements,
on_missing: Miss::Err("missing argument"),
on_extra: Extra::Err("too many arguments"),
..PURE
};
impl OpMeta {
pub(crate) fn can_fold(&self, args: &[CompiledNode]) -> bool {
!self.reads_context
&& self.effect == Effect::Pure
&& self.fold == Fold::Default
&& (0..args.len()).all(|i| self.frames_for(i, args.len()) == 0)
&& args.iter().all(node_is_static)
}
pub(crate) fn cse_pure(&self) -> bool {
self.effect == Effect::Pure && self.cse == Cse::Default
}
pub(crate) fn is_iterator(&self) -> bool {
matches!(self.frames, Frames::At(_))
}
pub(crate) fn frames_for(&self, index: usize, len: usize) -> u32 {
match self.frames {
Frames::None => 0,
Frames::At(at) => u32::from(index == at as usize),
Frames::LastIfMulti => u32::from(len >= 2 && index == len - 1),
}
}
}