Skip to main content

datalogic_rs/operators/
info.rs

1//! Public description of the built-in operators, read from the operator
2//! table. Backs [`crate::Engine::operators`].
3
4use super::meta::{Frames, OpMeta};
5use crate::OpCode;
6
7/// One built-in operator, as its operator-table row declares it.
8///
9/// Returned by [`crate::Engine::operators`]. Every field is a build-time
10/// fact: the same rows drive compilation, dispatch and the optimizer, so
11/// this description cannot drift from what the engine does.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[non_exhaustive]
14pub struct OperatorInfo {
15    /// Canonical name (`"val"`, `"if"`, `"switch"`).
16    pub name: &'static str,
17    /// Other names the compiler accepts for the same operator (`"var"`,
18    /// `"?:"`, `"match"`).
19    pub aliases: &'static [&'static str],
20    /// Operator family (`"Core"`, `"DateTime"`, `"ExtString"`, ...).
21    pub family: &'static str,
22    /// The Cargo feature that gates the family, or `None` for the core
23    /// JSONLogic operators.
24    pub feature: Option<&'static str>,
25    /// Fewest arguments the operator reads, as declared on its table row:
26    /// `==` and `map` report 2, `and` and `if` report 1. Variadics that
27    /// accept an empty list (`+`, `cat`, `missing`) report `0`.
28    pub min_args: usize,
29    /// Most arguments the operator's signature reads, or `None` when it
30    /// does not declare a maximum.
31    pub max_args: Option<usize>,
32    /// Whether the operator reads the data context (`val`, `missing`,
33    /// `exists`, `fractional`).
34    pub reads_context: bool,
35    /// What evaluating the operator does besides computing a value:
36    /// `"pure"`, `"clock"` (`now`), `"throws"` (`throw`) or `"catches"`
37    /// (`try`).
38    pub effect: &'static str,
39    /// What the operator's work is proportional to: `"node"` (constant),
40    /// `"bytes"`, `"per_item"`, `"n_log_n"`, `"quadratic"` or `"elements"`.
41    pub cost: &'static str,
42    /// Which argument, if any, runs under a pushed context frame (an
43    /// iterator body, a key expression, a catch arm).
44    pub scoped_arg: Option<ScopedArg>,
45}
46
47/// An argument position that runs under a pushed context frame, where
48/// `val` paths resolve against the current element (or the caught error)
49/// rather than the enclosing data.
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[non_exhaustive]
52pub enum ScopedArg {
53    /// The argument at this index (`map`'s body is `Index(1)`, `sort`'s
54    /// key expression `Index(2)`).
55    Index(usize),
56    /// The last argument, when there are at least two (`try`'s catch arm).
57    LastOfMany,
58}
59
60impl OperatorInfo {
61    pub(crate) fn of(op: OpCode) -> Option<Self> {
62        let entry = op.catalogue_entry();
63        let (name, aliases) = entry.names.split_first()?;
64        let meta: &OpMeta = op.meta();
65        let arity = op.arity();
66        Some(OperatorInfo {
67            name,
68            aliases,
69            family: entry.family,
70            feature: entry.feature(),
71            min_args: arity.min as usize,
72            max_args: arity.max.map(usize::from),
73            reads_context: meta.reads_context,
74            effect: meta.effect.name(),
75            cost: meta.cost.name(),
76            scoped_arg: match meta.frames {
77                Frames::None => None,
78                Frames::At(i) => Some(ScopedArg::Index(i as usize)),
79                Frames::LastIfMulti => Some(ScopedArg::LastOfMany),
80            },
81        })
82    }
83}
84
85/// Every operator of the families in `families` (a set of family bits)
86/// compiled into this build that has a name, in table order. Internal
87/// opcodes (no name) are left out.
88pub(crate) fn operators_in(families: u32) -> impl Iterator<Item = OperatorInfo> {
89    OpCode::ALL
90        .iter()
91        .filter(move |op| op.in_families(families))
92        .filter_map(|op| OperatorInfo::of(*op))
93}