datalogic_rs/
opcode.rs

1//! OpCode-based dispatch system for built-in operators.
2//!
3//! This module implements a high-performance dispatch mechanism using enum variants
4//! instead of string matching or vtable lookups at runtime.
5//!
6//! # Performance Design
7//!
8//! The `OpCode` enum provides O(1) operator dispatch through:
9//!
10//! 1. **Compile-time resolution**: Operator strings are converted to `OpCode` variants
11//!    during the compilation phase, not during evaluation
12//! 2. **Direct dispatch**: The `evaluate_direct` method uses a match statement that
13//!    compiles to an efficient jump table
14//! 3. **No boxing or vtables**: Direct function calls without trait object overhead
15//! 4. **Cache-friendly**: The `#[repr(u8)]` attribute ensures compact memory layout
16//!
17//! # Operator Categories
18//!
19//! Operators are grouped by functionality for code organization:
20//!
21//! - Variable Access (0-1, 57): `var`, `val`, `exists`
22//! - Comparison (2-9): `==`, `===`, `!=`, `!==`, `>`, `>=`, `<`, `<=`
23//! - Logical (10-13): `!`, `!!`, `and`, `or`
24//! - Control Flow (14-15, 56): `if`, `?:`, `??`
25//! - Arithmetic (16-22, 49-51): `+`, `-`, `*`, `/`, `%`, `max`, `min`, `abs`, `ceil`, `floor`
26//! - String (23-25, 38-43, 53): `cat`, `substr`, `in`, `starts_with`, etc.
27//! - Array (26-32, 54-55): `merge`, `filter`, `map`, `reduce`, `all`, `some`, `none`, `sort`, `slice`
28//! - DateTime (44-48, 58): `datetime`, `timestamp`, `parse_date`, `format_date`, `date_diff`, `now`
29//! - Error Handling (35-36): `try`, `throw`
30//! - Type/Missing (33-34, 37): `missing`, `missing_some`, `type`
31//! - Special (52): `preserve`
32//!
33//! # Adding New Operators
34//!
35//! 1. Add a new variant to the `OpCode` enum
36//! 2. Add string mapping in `FromStr` implementation
37//! 3. Add reverse mapping in `as_str()` method
38//! 4. Add dispatch case in `evaluate_direct()`
39//! 5. Implement the operator function in the appropriate `operators/` module
40
41use std::str::FromStr;
42
43/// OpCode enum for fast built-in operator lookup
44#[repr(u8)]
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum OpCode {
47    // === Variable & Data Access ===
48    Var = 0,
49    Val = 1,
50    Exists = 57,
51
52    // === Comparison Operators ===
53    Equals = 2,
54    StrictEquals = 3,
55    NotEquals = 4,
56    StrictNotEquals = 5,
57    GreaterThan = 6,
58    GreaterThanEqual = 7,
59    LessThan = 8,
60    LessThanEqual = 9,
61
62    // === Logical Operators ===
63    Not = 10,
64    DoubleNot = 11,
65    And = 12,
66    Or = 13,
67
68    // === Control Flow ===
69    If = 14,
70    Ternary = 15,
71    Coalesce = 56,
72
73    // === Arithmetic Operators ===
74    Add = 16,
75    Subtract = 17,
76    Multiply = 18,
77    Divide = 19,
78    Modulo = 20,
79    Max = 21,
80    Min = 22,
81    Abs = 49,
82    Ceil = 50,
83    Floor = 51,
84
85    // === String Operations ===
86    Cat = 23,
87    Substr = 24,
88    In = 25,
89    Length = 53,
90    StartsWith = 38,
91    EndsWith = 39,
92    Upper = 40,
93    Lower = 41,
94    Trim = 42,
95    Split = 43,
96
97    // === Array Operations ===
98    Merge = 26,
99    Filter = 27,
100    Map = 28,
101    Reduce = 29,
102    All = 30,
103    Some = 31,
104    None = 32,
105    Sort = 54,
106    Slice = 55,
107
108    // === DateTime Operations ===
109    Datetime = 44,
110    Timestamp = 45,
111    ParseDate = 46,
112    FormatDate = 47,
113    DateDiff = 48,
114    Now = 58,
115
116    // === Error Handling ===
117    Try = 35,
118    Throw = 36,
119
120    // === Type Operations ===
121    Type = 37,
122
123    // === Missing Value Handling ===
124    Missing = 33,
125    MissingSome = 34,
126
127    // === Special Operations ===
128    Preserve = 52,
129}
130
131impl FromStr for OpCode {
132    type Err = ();
133
134    fn from_str(s: &str) -> Result<Self, Self::Err> {
135        match s {
136            "var" => Ok(OpCode::Var),
137            "val" => Ok(OpCode::Val),
138            "==" => Ok(OpCode::Equals),
139            "===" => Ok(OpCode::StrictEquals),
140            "!=" => Ok(OpCode::NotEquals),
141            "!==" => Ok(OpCode::StrictNotEquals),
142            ">" => Ok(OpCode::GreaterThan),
143            ">=" => Ok(OpCode::GreaterThanEqual),
144            "<" => Ok(OpCode::LessThan),
145            "<=" => Ok(OpCode::LessThanEqual),
146            "!" => Ok(OpCode::Not),
147            "!!" => Ok(OpCode::DoubleNot),
148            "and" => Ok(OpCode::And),
149            "or" => Ok(OpCode::Or),
150            "if" => Ok(OpCode::If),
151            "?:" => Ok(OpCode::Ternary),
152            "+" => Ok(OpCode::Add),
153            "-" => Ok(OpCode::Subtract),
154            "*" => Ok(OpCode::Multiply),
155            "/" => Ok(OpCode::Divide),
156            "%" => Ok(OpCode::Modulo),
157            "max" => Ok(OpCode::Max),
158            "min" => Ok(OpCode::Min),
159            "cat" => Ok(OpCode::Cat),
160            "substr" => Ok(OpCode::Substr),
161            "in" => Ok(OpCode::In),
162            "merge" => Ok(OpCode::Merge),
163            "filter" => Ok(OpCode::Filter),
164            "map" => Ok(OpCode::Map),
165            "reduce" => Ok(OpCode::Reduce),
166            "all" => Ok(OpCode::All),
167            "some" => Ok(OpCode::Some),
168            "none" => Ok(OpCode::None),
169            "missing" => Ok(OpCode::Missing),
170            "missing_some" => Ok(OpCode::MissingSome),
171            "try" => Ok(OpCode::Try),
172            "throw" => Ok(OpCode::Throw),
173            "type" => Ok(OpCode::Type),
174            "starts_with" => Ok(OpCode::StartsWith),
175            "ends_with" => Ok(OpCode::EndsWith),
176            "upper" => Ok(OpCode::Upper),
177            "lower" => Ok(OpCode::Lower),
178            "trim" => Ok(OpCode::Trim),
179            "split" => Ok(OpCode::Split),
180            "datetime" => Ok(OpCode::Datetime),
181            "timestamp" => Ok(OpCode::Timestamp),
182            "parse_date" => Ok(OpCode::ParseDate),
183            "format_date" => Ok(OpCode::FormatDate),
184            "date_diff" => Ok(OpCode::DateDiff),
185            "now" => Ok(OpCode::Now),
186            "abs" => Ok(OpCode::Abs),
187            "ceil" => Ok(OpCode::Ceil),
188            "floor" => Ok(OpCode::Floor),
189            "preserve" => Ok(OpCode::Preserve),
190            "length" => Ok(OpCode::Length),
191            "sort" => Ok(OpCode::Sort),
192            "slice" => Ok(OpCode::Slice),
193            "??" => Ok(OpCode::Coalesce),
194            "exists" => Ok(OpCode::Exists),
195            _ => Err(()),
196        }
197    }
198}
199
200impl OpCode {
201    /// Total number of built-in operators
202    pub const COUNT: usize = 59;
203
204    /// Convert OpCode back to string (for debugging/display)
205    pub fn as_str(&self) -> &'static str {
206        match self {
207            OpCode::Var => "var",
208            OpCode::Val => "val",
209            OpCode::Equals => "==",
210            OpCode::StrictEquals => "===",
211            OpCode::NotEquals => "!=",
212            OpCode::StrictNotEquals => "!==",
213            OpCode::GreaterThan => ">",
214            OpCode::GreaterThanEqual => ">=",
215            OpCode::LessThan => "<",
216            OpCode::LessThanEqual => "<=",
217            OpCode::Not => "!",
218            OpCode::DoubleNot => "!!",
219            OpCode::And => "and",
220            OpCode::Or => "or",
221            OpCode::If => "if",
222            OpCode::Ternary => "?:",
223            OpCode::Add => "+",
224            OpCode::Subtract => "-",
225            OpCode::Multiply => "*",
226            OpCode::Divide => "/",
227            OpCode::Modulo => "%",
228            OpCode::Max => "max",
229            OpCode::Min => "min",
230            OpCode::Cat => "cat",
231            OpCode::Substr => "substr",
232            OpCode::In => "in",
233            OpCode::Merge => "merge",
234            OpCode::Filter => "filter",
235            OpCode::Map => "map",
236            OpCode::Reduce => "reduce",
237            OpCode::All => "all",
238            OpCode::Some => "some",
239            OpCode::None => "none",
240            OpCode::Missing => "missing",
241            OpCode::MissingSome => "missing_some",
242            OpCode::Try => "try",
243            OpCode::Throw => "throw",
244            OpCode::Type => "type",
245            OpCode::StartsWith => "starts_with",
246            OpCode::EndsWith => "ends_with",
247            OpCode::Upper => "upper",
248            OpCode::Lower => "lower",
249            OpCode::Trim => "trim",
250            OpCode::Split => "split",
251            OpCode::Datetime => "datetime",
252            OpCode::Timestamp => "timestamp",
253            OpCode::ParseDate => "parse_date",
254            OpCode::FormatDate => "format_date",
255            OpCode::DateDiff => "date_diff",
256            OpCode::Now => "now",
257            OpCode::Abs => "abs",
258            OpCode::Ceil => "ceil",
259            OpCode::Floor => "floor",
260            OpCode::Preserve => "preserve",
261            OpCode::Length => "length",
262            OpCode::Sort => "sort",
263            OpCode::Slice => "slice",
264            OpCode::Coalesce => "??",
265            OpCode::Exists => "exists",
266        }
267    }
268
269    /// Direct evaluation method - no boxing, no vtables, no array lookups
270    #[inline]
271    pub fn evaluate_direct(
272        &self,
273        args: &[crate::CompiledNode],
274        context: &mut crate::ContextStack,
275        engine: &crate::DataLogic,
276    ) -> crate::Result<serde_json::Value> {
277        use crate::operators::{
278            abs, arithmetic, array, ceil, comparison, control, datetime, floor, logical, missing,
279            preserve, string, string_ops, throw, try_op, type_op, variable,
280        };
281
282        match self {
283            // Variable access operators - direct function calls
284            OpCode::Var => variable::evaluate_var(args, context, engine),
285            OpCode::Val => variable::evaluate_val(args, context, engine),
286            OpCode::Exists => variable::evaluate_exists(args, context, engine),
287
288            // Comparison operators - direct function calls
289            OpCode::Equals => comparison::evaluate_equals(args, context, engine),
290            OpCode::StrictEquals => comparison::evaluate_strict_equals(args, context, engine),
291            OpCode::NotEquals => comparison::evaluate_not_equals(args, context, engine),
292            OpCode::StrictNotEquals => {
293                comparison::evaluate_strict_not_equals(args, context, engine)
294            }
295            OpCode::GreaterThan => comparison::evaluate_greater_than(args, context, engine),
296            OpCode::GreaterThanEqual => {
297                comparison::evaluate_greater_than_equal(args, context, engine)
298            }
299            OpCode::LessThan => comparison::evaluate_less_than(args, context, engine),
300            OpCode::LessThanEqual => comparison::evaluate_less_than_equal(args, context, engine),
301
302            // Logical operators - direct function calls
303            OpCode::Not => logical::evaluate_not(args, context, engine),
304            OpCode::DoubleNot => logical::evaluate_double_not(args, context, engine),
305            OpCode::And => logical::evaluate_and(args, context, engine),
306            OpCode::Or => logical::evaluate_or(args, context, engine),
307
308            // Control flow - direct function calls
309            OpCode::If => control::evaluate_if(args, context, engine),
310            OpCode::Ternary => control::evaluate_ternary(args, context, engine),
311            OpCode::Coalesce => control::evaluate_coalesce(args, context, engine),
312
313            // Arithmetic operators - direct function calls
314            OpCode::Add => arithmetic::evaluate_add(args, context, engine),
315            OpCode::Subtract => arithmetic::evaluate_subtract(args, context, engine),
316            OpCode::Multiply => arithmetic::evaluate_multiply(args, context, engine),
317            OpCode::Divide => arithmetic::evaluate_divide(args, context, engine),
318            OpCode::Modulo => arithmetic::evaluate_modulo(args, context, engine),
319            OpCode::Max => arithmetic::evaluate_max(args, context, engine),
320            OpCode::Min => arithmetic::evaluate_min(args, context, engine),
321            OpCode::Abs => abs::evaluate_abs(args, context, engine),
322            OpCode::Ceil => ceil::evaluate_ceil(args, context, engine),
323            OpCode::Floor => floor::evaluate_floor(args, context, engine),
324
325            // String operators - direct function calls
326            OpCode::Cat => string::evaluate_cat(args, context, engine),
327            OpCode::Substr => string::evaluate_substr(args, context, engine),
328            OpCode::In => string::evaluate_in(args, context, engine),
329            OpCode::Length => string::evaluate_length(args, context, engine),
330            OpCode::StartsWith => string_ops::evaluate_starts_with(args, context, engine),
331            OpCode::EndsWith => string_ops::evaluate_ends_with(args, context, engine),
332            OpCode::Upper => string_ops::evaluate_upper(args, context, engine),
333            OpCode::Lower => string_ops::evaluate_lower(args, context, engine),
334            OpCode::Trim => string_ops::evaluate_trim(args, context, engine),
335            OpCode::Split => string_ops::evaluate_split(args, context, engine),
336
337            // Array operators - direct function calls
338            OpCode::Merge => array::evaluate_merge(args, context, engine),
339            OpCode::Filter => array::evaluate_filter(args, context, engine),
340            OpCode::Map => array::evaluate_map(args, context, engine),
341            OpCode::Reduce => array::evaluate_reduce(args, context, engine),
342            OpCode::All => array::evaluate_all(args, context, engine),
343            OpCode::Some => array::evaluate_some(args, context, engine),
344            OpCode::None => array::evaluate_none(args, context, engine),
345            OpCode::Sort => array::evaluate_sort(args, context, engine),
346            OpCode::Slice => array::evaluate_slice(args, context, engine),
347
348            // Special operators - direct function calls
349            OpCode::Missing => missing::evaluate_missing(args, context, engine),
350            OpCode::MissingSome => missing::evaluate_missing_some(args, context, engine),
351            OpCode::Try => try_op::evaluate_try(args, context, engine),
352            OpCode::Throw => throw::evaluate_throw(args, context, engine),
353            OpCode::Type => type_op::evaluate_type(args, context, engine),
354            OpCode::Preserve => preserve::evaluate_preserve(args, context, engine),
355
356            // DateTime operators - direct function calls
357            OpCode::Datetime => datetime::evaluate_datetime(args, context, engine),
358            OpCode::Timestamp => datetime::evaluate_timestamp(args, context, engine),
359            OpCode::ParseDate => datetime::evaluate_parse_date(args, context, engine),
360            OpCode::FormatDate => datetime::evaluate_format_date(args, context, engine),
361            OpCode::DateDiff => datetime::evaluate_date_diff(args, context, engine),
362            OpCode::Now => datetime::evaluate_now(args, context, engine),
363        }
364    }
365
366    /// Traced evaluation method - records steps for debugging.
367    ///
368    /// This method dispatches to traced versions of operators that need special
369    /// handling (iteration and short-circuit operators), while regular operators
370    /// use the standard evaluation with child tracing.
371    #[inline]
372    pub fn evaluate_traced(
373        &self,
374        args: &[crate::CompiledNode],
375        context: &mut crate::ContextStack,
376        engine: &crate::DataLogic,
377        collector: &mut crate::trace::TraceCollector,
378        node_id_map: &std::collections::HashMap<usize, u32>,
379    ) -> crate::Result<serde_json::Value> {
380        use crate::operators::{array, control, logical};
381
382        match self {
383            // Iteration operators - need traced versions
384            OpCode::Map => {
385                array::evaluate_map_traced(args, context, engine, collector, node_id_map)
386            }
387            OpCode::Filter => {
388                array::evaluate_filter_traced(args, context, engine, collector, node_id_map)
389            }
390            OpCode::Reduce => {
391                array::evaluate_reduce_traced(args, context, engine, collector, node_id_map)
392            }
393            OpCode::All => {
394                array::evaluate_all_traced(args, context, engine, collector, node_id_map)
395            }
396            OpCode::Some => {
397                array::evaluate_some_traced(args, context, engine, collector, node_id_map)
398            }
399            OpCode::None => {
400                array::evaluate_none_traced(args, context, engine, collector, node_id_map)
401            }
402
403            // Short-circuit logical operators - need traced versions
404            OpCode::And => {
405                logical::evaluate_and_traced(args, context, engine, collector, node_id_map)
406            }
407            OpCode::Or => {
408                logical::evaluate_or_traced(args, context, engine, collector, node_id_map)
409            }
410
411            // Control flow operators - need traced versions
412            OpCode::If => {
413                control::evaluate_if_traced(args, context, engine, collector, node_id_map)
414            }
415            OpCode::Ternary => {
416                control::evaluate_ternary_traced(args, context, engine, collector, node_id_map)
417            }
418            OpCode::Coalesce => {
419                control::evaluate_coalesce_traced(args, context, engine, collector, node_id_map)
420            }
421
422            // All other operators - evaluate children with tracing, then apply operator
423            _ => {
424                // Evaluate all arguments with tracing
425                let mut evaluated_args: Vec<serde_json::Value> = Vec::with_capacity(args.len());
426                for arg in args {
427                    let value =
428                        engine.evaluate_node_traced(arg, context, collector, node_id_map)?;
429                    evaluated_args.push(value);
430                }
431
432                // Create temporary Value nodes for the direct evaluation
433                let value_nodes: Vec<crate::CompiledNode> = evaluated_args
434                    .into_iter()
435                    .map(|v| crate::CompiledNode::Value { value: v })
436                    .collect();
437
438                // Evaluate the operator with pre-evaluated arguments
439                self.evaluate_direct(&value_nodes, context, engine)
440            }
441        }
442    }
443}