Skip to main content

json_eval_rs/rlogic/evaluator/
mod.rs

1use super::compiled::CompiledLogic;
2use super::config::RLogicConfig;
3use index::TableIndex;
4use serde_json::Value;
5use std::cell::UnsafeCell;
6use std::collections::HashMap;
7use std::sync::RwLock;
8
9pub mod arithmetic;
10pub mod array_lookup;
11pub mod array_ops;
12pub mod comparison;
13pub mod date_ops;
14pub mod helpers;
15pub mod index;
16pub mod logical;
17pub mod math_ops;
18pub mod optimizations;
19pub mod string_ops;
20pub mod types;
21
22pub use helpers::*;
23pub use types::*;
24
25/// Active self-table scope set during `evaluate_table_inner`.
26///
27/// # Safety
28/// `rows` is a raw pointer to `local_rows` on the stack of `evaluate_table_inner`.
29/// Valid lifetime: from `enter_table_scope()` to `TableScopeGuard::drop()`.
30/// Evaluation is single-threaded (protected by `eval_lock` in `evaluate_internal`).
31const EMPTY_CACHE_SLOT: std::cell::Cell<(usize, u32, u32)> = std::cell::Cell::new((0, 0, 0));
32
33pub(crate) struct TableScope {
34    /// Normalized JSON pointer path to the table being evaluated
35    pub path: String,
36    /// Path without leading '#' for zero-overhead matching
37    pub path_no_hash: String,
38    /// Pointer to the local rows being built in table_evaluate_inner
39    pub rows: *const Vec<Value>,
40    /// Pointer to flat cells storage (total_rows * col_count) during Repeat
41    pub flat_cells: *mut Value,
42    pub col_count: usize,
43    pub total_rows: usize,
44    pub existing_row_count: usize,
45    /// Fast mapping from column name to column index
46    pub col_map: rapidhash::RapidHashMap<String, usize>,
47    /// Direct-mapped 256-slot cache with pointer-identity fast path for rapid column resolution
48    pub col_cache: [std::cell::Cell<(usize, u32, u32)>; 256],
49    /// Optional cursor to the current row index being evaluated (for fast $column lookup)
50    pub current_row: Option<usize>,
51    /// Precomputed row base pointer in flat_cells for O(1) cell access
52    pub current_row_base: *mut Value,
53    /// Raw iteration integer value
54    pub iteration_raw: Option<i64>,
55    /// Optional pre-computed iteration value for O(1) $iteration resolution
56    pub iteration_val: Option<Value>,
57    /// Optional pre-computed threshold value for O(1) $threshold resolution
58    pub threshold_val: Option<Value>,
59    /// Memoization cache for combined array lookups on immutable borrowed reference tables
60    pub lookup_cache:
61        std::cell::RefCell<rapidhash::RapidHashMap<types::CombinedLookupKey, Option<usize>>>,
62}
63
64impl TableScope {
65    #[inline(always)]
66    pub fn get_col_idx(&self, col_name: &str) -> Option<usize> {
67        let ptr = col_name.as_ptr() as usize;
68        let len = col_name.len() as u32;
69        let slot = (ptr ^ (ptr >> 6) ^ (len as usize)) & 255;
70        let entry = self.col_cache[slot].get();
71        if entry.0 == ptr && entry.1 == len && ptr != 0 {
72            return Some(entry.2 as usize);
73        }
74        if let Some(&col_idx) = self.col_map.get(col_name) {
75            self.col_cache[slot].set((ptr, len, col_idx as u32));
76            Some(col_idx)
77        } else {
78            None
79        }
80    }
81}
82
83thread_local! {
84    static TABLE_SCOPE: UnsafeCell<Option<TableScope>> = const { UnsafeCell::new(None) };
85    static STATIC_ARRAYS: UnsafeCell<Option<std::sync::Arc<indexmap::IndexMap<String, std::sync::Arc<Value>>>>> = const { UnsafeCell::new(None) };
86}
87
88// SAFETY: TableScope and STATIC_ARRAYS are accessed only by the current thread via thread_local storage.
89unsafe impl Send for TableScope {}
90unsafe impl Send for Evaluator {}
91unsafe impl Sync for Evaluator {}
92
93/// RAII guard that restores the previous active STATIC_ARRAYS on drop
94pub struct StaticArraysGuard {
95    previous: Option<std::sync::Arc<indexmap::IndexMap<String, std::sync::Arc<Value>>>>,
96}
97
98impl Drop for StaticArraysGuard {
99    fn drop(&mut self) {
100        STATIC_ARRAYS.with(|cell| unsafe {
101            *cell.get() = self.previous.take();
102        });
103    }
104}
105
106/// RAII guard that clears the active TableScope on drop
107pub struct TableScopeGuard<'a> {
108    evaluator: &'a Evaluator,
109}
110
111impl<'a> Drop for TableScopeGuard<'a> {
112    fn drop(&mut self) {
113        unsafe {
114            *self.evaluator.table_scope_mut() = None;
115        }
116    }
117}
118
119/// High-performance zero-copy evaluator with dual-context support
120///
121/// ## Design Principles
122/// 1. **Zero-copy**: All data access via references, no cloning
123/// 2. **Dual-context**: Separate user_data and internal_context for scoped variables
124/// 3. **Recursive**: Clean recursive evaluation with depth tracking
125///
126/// ## Context Resolution
127/// - Variables ($var) lookup order: internal_context → user_data
128/// - Internal context holds: $iteration, $threshold, $loopIteration, etc.
129pub struct Evaluator {
130    config: RLogicConfig,
131    /// Upfront indices for large tables (name -> index)
132    indices: RwLock<HashMap<String, TableIndex>>,
133}
134
135impl Evaluator {
136    pub fn new() -> Self {
137        Self {
138            config: RLogicConfig::default(),
139            indices: RwLock::new(HashMap::new()),
140        }
141    }
142
143    #[inline(always)]
144    pub(crate) unsafe fn table_scope_ref(&self) -> &Option<TableScope> {
145        TABLE_SCOPE.with(|cell| &*cell.get())
146    }
147
148    #[inline(always)]
149    pub(crate) unsafe fn table_scope_mut(&self) -> &mut Option<TableScope> {
150        TABLE_SCOPE.with(|cell| &mut *cell.get())
151    }
152
153    #[inline(always)]
154    pub(crate) unsafe fn static_arrays_ref(
155        &self,
156    ) -> &Option<std::sync::Arc<indexmap::IndexMap<String, std::sync::Arc<Value>>>> {
157        STATIC_ARRAYS.with(|cell| &*cell.get())
158    }
159
160    #[inline(always)]
161    pub(crate) unsafe fn static_arrays_mut(
162        &self,
163    ) -> &mut Option<std::sync::Arc<indexmap::IndexMap<String, std::sync::Arc<Value>>>> {
164        STATIC_ARRAYS.with(|cell| &mut *cell.get())
165    }
166
167    /// Register a table scope for self-reference interception.
168    ///
169    /// Returns a guard that clears the scope on drop.
170    ///
171    /// # Safety
172    /// `rows` must outlive the returned guard. The guard MUST be dropped before
173    /// `rows` is moved or dropped. Caller (table_evaluate_inner) is responsible.
174    pub(crate) fn enter_table_scope<'a>(
175        &'a self,
176        path: String,
177        rows: &Vec<Value>,
178    ) -> TableScopeGuard<'a> {
179        let path_no_hash = path.trim_start_matches('#').to_string();
180        unsafe {
181            *self.table_scope_mut() = Some(TableScope {
182                path,
183                path_no_hash,
184                rows: rows as *const Vec<Value>,
185                flat_cells: std::ptr::null_mut(),
186                col_count: 0,
187                total_rows: 0,
188                existing_row_count: 0,
189                col_map: rapidhash::RapidHashMap::default(),
190                col_cache: [EMPTY_CACHE_SLOT; 256],
191                current_row: None,
192                current_row_base: std::ptr::null_mut(),
193                iteration_raw: None,
194                iteration_val: None,
195                threshold_val: None,
196                lookup_cache: std::cell::RefCell::new(rapidhash::RapidHashMap::default()),
197            });
198        }
199        TableScopeGuard { evaluator: self }
200    }
201
202    /// Register flat cell buffer and column mappings for fast direct indexed evaluation
203    pub(crate) fn set_table_scope_flat_cells(
204        &self,
205        cells: *mut Value,
206        col_count: usize,
207        total_rows: usize,
208        existing_row_count: usize,
209        col_map: rapidhash::RapidHashMap<String, usize>,
210    ) {
211        unsafe {
212            if let Some(ts) = (*self.table_scope_mut()).as_mut() {
213                ts.flat_cells = cells;
214                ts.col_count = col_count;
215                ts.total_rows = total_rows;
216                ts.existing_row_count = existing_row_count;
217                ts.col_map = col_map;
218                ts.col_cache = [EMPTY_CACHE_SLOT; 256];
219                ts.current_row_base = std::ptr::null_mut();
220            }
221        }
222    }
223
224    /// Update the rows pointer in the active table scope.
225    pub(crate) fn update_table_scope_rows(&self, rows: &Vec<Value>) {
226        unsafe {
227            if let Some(ts) = (*self.table_scope_mut()).as_mut() {
228                ts.rows = rows as *const Vec<Value>;
229            }
230        }
231    }
232
233    /// Set the row cursor for the active table scope
234    pub(crate) fn set_table_scope_row(&self, row_idx: Option<usize>) {
235        unsafe {
236            if let Some(ts) = (*self.table_scope_mut()).as_mut() {
237                ts.current_row = row_idx;
238                if let Some(r) = row_idx {
239                    if ts.col_count > 0
240                        && !ts.flat_cells.is_null()
241                        && r >= ts.existing_row_count
242                        && r < ts.existing_row_count + ts.total_rows
243                    {
244                        ts.current_row_base = ts
245                            .flat_cells
246                            .add((r - ts.existing_row_count) * ts.col_count);
247                    } else {
248                        ts.current_row_base = std::ptr::null_mut();
249                    }
250                } else {
251                    ts.current_row_base = std::ptr::null_mut();
252                }
253            }
254        }
255    }
256
257    /// Set the row cursor and pre-computed iteration value for the active table scope
258    pub(crate) fn set_table_scope_cursor(&self, row_idx: Option<usize>, iteration: Option<i64>) {
259        unsafe {
260            if let Some(ts) = (*self.table_scope_mut()).as_mut() {
261                ts.current_row = row_idx;
262                ts.iteration_raw = iteration;
263                ts.iteration_val = iteration.map(Value::from);
264                if let Some(r) = row_idx {
265                    if ts.col_count > 0
266                        && !ts.flat_cells.is_null()
267                        && r >= ts.existing_row_count
268                        && r < ts.existing_row_count + ts.total_rows
269                    {
270                        ts.current_row_base = ts
271                            .flat_cells
272                            .add((r - ts.existing_row_count) * ts.col_count);
273                    } else {
274                        ts.current_row_base = std::ptr::null_mut();
275                    }
276                } else {
277                    ts.current_row_base = std::ptr::null_mut();
278                }
279            }
280        }
281    }
282
283    /// Set the threshold value for the active table scope
284    pub(crate) fn set_table_scope_threshold(&self, threshold: i64) {
285        unsafe {
286            if let Some(ts) = (*self.table_scope_mut()).as_mut() {
287                ts.threshold_val = Some(Value::from(threshold));
288            }
289        }
290    }
291
292    pub fn with_config(mut self, config: RLogicConfig) -> Self {
293        self.config = config;
294        self
295    }
296
297    /// Bind static arrays to the current thread for the duration of a scope
298    pub fn bind_static_arrays_scope(
299        &self,
300        static_arrays: std::sync::Arc<indexmap::IndexMap<String, std::sync::Arc<Value>>>,
301    ) -> StaticArraysGuard {
302        let previous = STATIC_ARRAYS.with(|cell| unsafe {
303            let prev = (*cell.get()).take();
304            *cell.get() = Some(static_arrays);
305            prev
306        });
307        StaticArraysGuard { previous }
308    }
309
310    /// Set static arrays for evaluation context on the current thread
311    pub fn set_static_arrays(
312        &self,
313        static_arrays: std::sync::Arc<indexmap::IndexMap<String, std::sync::Arc<Value>>>,
314    ) {
315        unsafe {
316            *self.static_arrays_mut() = Some(static_arrays);
317        }
318    }
319
320    /// Clear static arrays for the current thread
321    pub fn clear_static_arrays(&self) {
322        unsafe {
323            *self.static_arrays_mut() = None;
324        }
325    }
326
327    /// Build and store index for a table
328    pub fn index_table(&self, name: &str, data: &Value) {
329        if let Some(index) = TableIndex::new(data) {
330            if let Ok(mut indices) = self.indices.write() {
331                indices.insert(name.to_string(), index);
332            }
333        }
334    }
335
336    /// Clear all stored indices
337    pub fn clear_indices(&self) {
338        if let Ok(mut indices) = self.indices.write() {
339            indices.clear();
340        }
341    }
342
343    /// Public API: Evaluate compiled logic with user data only
344    /// Uses fast path for simple cases to avoid recursion overhead
345    #[inline]
346    pub fn evaluate(&self, logic: &CompiledLogic, data: &Value) -> Result<Value, String> {
347        // Fast path for literals (most common cases)
348        match logic {
349            CompiledLogic::Null => return Ok(Value::Null),
350            CompiledLogic::Bool(b) => return Ok(Value::Bool(*b)),
351            CompiledLogic::Number(n) => {
352                return Ok(self.f64_to_json(*n));
353            }
354            CompiledLogic::String(s) => return Ok(Value::String(s.clone())),
355            CompiledLogic::Var(name, None) if !name.is_empty() => {
356                // Simple variable without default
357                return self.eval_var_or_default(name, &None, data, &Value::Null, 0);
358            }
359            CompiledLogic::Ref(path, None) if !path.is_empty() => {
360                // Simple variable without default
361                return self.eval_var_or_default(path, &None, data, &Value::Null, 0);
362            }
363            // Fast path for arithmetic operations
364            CompiledLogic::Add(_)
365            | CompiledLogic::Subtract(_)
366            | CompiledLogic::Multiply(_)
367            | CompiledLogic::Divide(_) => {
368                if let Some(result) = self.eval_f64(logic, data, &Value::Null, 0)? {
369                    return Ok(self.f64_to_json(result));
370                }
371            }
372            _ => {}
373        }
374
375        // Fall back to full evaluation for complex cases
376        self.evaluate_with_context(logic, data, &Value::Null, 0)
377    }
378
379    /// Evaluate with internal context (for scoped variables)
380    ///
381    /// # Arguments
382    /// * `logic` - The compiled logic expression to evaluate
383    /// * `user_data` - User's data (primary lookup source)
384    /// * `internal_context` - Internal variables (e.g., $iteration, $loopIteration)
385    ///
386    /// # Zero-Copy Guarantee
387    /// This method uses only references and never clones the data contexts.
388    /// Internal variables are looked up first in `internal_context`, then fall back to `user_data`.
389    #[inline]
390    pub fn evaluate_with_internal_context(
391        &self,
392        logic: &CompiledLogic,
393        user_data: &Value,
394        internal_context: &Value,
395    ) -> Result<Value, String> {
396        self.evaluate_with_context(logic, user_data, internal_context, 0)
397    }
398
399    /// Internal recursive evaluation with depth tracking
400    ///
401    /// # Context Resolution Order
402    /// 1. Check internal_context first (for scoped variables like $loopIteration)
403    /// 2. Fall back to user_data (for regular user variables)
404    ///
405    /// This enables zero-copy scoped variable handling without merging contexts.
406    fn evaluate_with_context(
407        &self,
408        logic: &CompiledLogic,
409        user_data: &Value,
410        internal_context: &Value,
411        depth: usize,
412    ) -> Result<Value, String> {
413        // Recursion limit check
414        if depth > self.config.recursion_limit {
415            return Err("Recursion limit exceeded".to_string());
416        }
417
418        match logic {
419            // ========== Literals ==========
420            CompiledLogic::Null => Ok(Value::Null),
421            CompiledLogic::Bool(b) => Ok(Value::Bool(*b)),
422            CompiledLogic::Number(n) => Ok(self.f64_to_json(*n)),
423            CompiledLogic::String(s) => Ok(Value::String(s.clone())),
424            CompiledLogic::Array(arr) => {
425                let results: Result<Vec<_>, _> = arr
426                    .iter()
427                    .map(|item| {
428                        self.evaluate_with_context(item, user_data, internal_context, depth + 1)
429                    })
430                    .collect();
431                Ok(Value::Array(results?))
432            }
433
434            // ========== Variable Access (Zero-Copy) ==========
435            CompiledLogic::Var(name, default) => {
436                self.eval_var_or_default(name, default, user_data, internal_context, depth)
437            }
438
439            CompiledLogic::Ref(path, default) => {
440                self.eval_var_or_default(path, default, user_data, internal_context, depth)
441            }
442
443            // ========== Logical Operators ==========
444            CompiledLogic::And(items) => {
445                self.eval_and_or(items, true, user_data, internal_context, depth)
446            }
447            CompiledLogic::Or(items) => {
448                self.eval_and_or(items, false, user_data, internal_context, depth)
449            }
450            CompiledLogic::Not(expr) => {
451                let result =
452                    self.evaluate_with_context(expr, user_data, internal_context, depth + 1)?;
453                Ok(Value::Bool(!is_truthy(&result)))
454            }
455            CompiledLogic::If(cond, then_expr, else_expr) => {
456                if self.eval_truthy(cond, user_data, internal_context, depth + 1)? {
457                    self.evaluate_with_context(then_expr, user_data, internal_context, depth + 1)
458                } else {
459                    self.evaluate_with_context(else_expr, user_data, internal_context, depth + 1)
460                }
461            }
462
463            // ========== Comparison Operators ==========
464            CompiledLogic::Equal(a, b) => {
465                self.eval_binary_compare(CompOp::Eq, a, b, user_data, internal_context, depth)
466            }
467            CompiledLogic::StrictEqual(a, b) => {
468                self.eval_binary_compare(CompOp::StrictEq, a, b, user_data, internal_context, depth)
469            }
470            CompiledLogic::NotEqual(a, b) => {
471                self.eval_binary_compare(CompOp::Ne, a, b, user_data, internal_context, depth)
472            }
473            CompiledLogic::StrictNotEqual(a, b) => {
474                self.eval_binary_compare(CompOp::StrictNe, a, b, user_data, internal_context, depth)
475            }
476            CompiledLogic::LessThan(a, b) => {
477                self.eval_binary_compare(CompOp::Lt, a, b, user_data, internal_context, depth)
478            }
479            CompiledLogic::LessThanOrEqual(a, b) => {
480                self.eval_binary_compare(CompOp::Le, a, b, user_data, internal_context, depth)
481            }
482            CompiledLogic::GreaterThan(a, b) => {
483                self.eval_binary_compare(CompOp::Gt, a, b, user_data, internal_context, depth)
484            }
485            CompiledLogic::GreaterThanOrEqual(a, b) => {
486                self.eval_binary_compare(CompOp::Ge, a, b, user_data, internal_context, depth)
487            }
488
489            // ========== Arithmetic Operators ==========
490            CompiledLogic::Add(_)
491            | CompiledLogic::Subtract(_)
492            | CompiledLogic::Multiply(_)
493            | CompiledLogic::Divide(_)
494            | CompiledLogic::Power(_, _)
495            | CompiledLogic::Modulo(_, _) => {
496                match self.eval_f64(logic, user_data, internal_context, depth)? {
497                    Some(result) => Ok(self.f64_to_json(result)),
498                    None => Ok(Value::Null),
499                }
500            }
501
502            // ========== Array Operations ==========
503            CompiledLogic::Map(array_expr, logic_expr) => {
504                self.eval_map(array_expr, logic_expr, user_data, internal_context, depth)
505            }
506            CompiledLogic::Filter(array_expr, logic_expr) => {
507                self.eval_filter(array_expr, logic_expr, user_data, internal_context, depth)
508            }
509            CompiledLogic::Reduce(array_expr, logic_expr, initial_expr) => self.eval_reduce(
510                array_expr,
511                logic_expr,
512                initial_expr,
513                user_data,
514                internal_context,
515                depth,
516            ),
517            CompiledLogic::All(array_expr, logic_expr) => self.eval_quantifier(
518                Quantifier::All,
519                array_expr,
520                logic_expr,
521                user_data,
522                internal_context,
523                depth,
524            ),
525            CompiledLogic::Some(array_expr, logic_expr) => self.eval_quantifier(
526                Quantifier::Some,
527                array_expr,
528                logic_expr,
529                user_data,
530                internal_context,
531                depth,
532            ),
533            CompiledLogic::None(array_expr, logic_expr) => self.eval_quantifier(
534                Quantifier::None,
535                array_expr,
536                logic_expr,
537                user_data,
538                internal_context,
539                depth,
540            ),
541            CompiledLogic::Merge(items) => {
542                self.eval_merge(items, user_data, internal_context, depth)
543            }
544            CompiledLogic::In(value_expr, array_expr) => {
545                self.eval_in(value_expr, array_expr, user_data, internal_context, depth)
546            }
547            CompiledLogic::Sum(array_expr, field_expr, threshold_expr) => self.eval_sum(
548                array_expr,
549                field_expr,
550                threshold_expr,
551                user_data,
552                internal_context,
553                depth,
554            ),
555            CompiledLogic::For(start_expr, end_expr, logic_expr) => self.eval_for(
556                start_expr,
557                end_expr,
558                logic_expr,
559                user_data,
560                internal_context,
561                depth,
562            ),
563            CompiledLogic::Multiplies(items) => {
564                self.eval_multiplies(items, user_data, internal_context, depth)
565            }
566            CompiledLogic::Divides(items) => {
567                self.eval_divides(items, user_data, internal_context, depth)
568            }
569
570            // ========== Array Lookup Operations ==========
571            CompiledLogic::ValueAt(table_expr, row_idx_expr, col_name_expr) => self.eval_valueat(
572                table_expr,
573                row_idx_expr,
574                col_name_expr,
575                user_data,
576                internal_context,
577                depth,
578            ),
579            CompiledLogic::MaxAt(table_expr, col_name_expr) => self.eval_maxat(
580                table_expr,
581                col_name_expr,
582                user_data,
583                internal_context,
584                depth,
585            ),
586            CompiledLogic::IndexAt(lookup_expr, table_expr, field_expr, range_expr) => self
587                .eval_indexat(
588                    lookup_expr,
589                    table_expr,
590                    field_expr,
591                    range_expr,
592                    user_data,
593                    internal_context,
594                    depth,
595                ),
596            CompiledLogic::Match(table_expr, conditions) => {
597                self.eval_match(table_expr, conditions, user_data, internal_context, depth)
598            }
599            CompiledLogic::MatchRange(table_expr, conditions) => {
600                self.eval_matchrange(table_expr, conditions, user_data, internal_context, depth)
601            }
602            CompiledLogic::Choose(table_expr, conditions) => {
603                self.eval_choose(table_expr, conditions, user_data, internal_context, depth)
604            }
605            CompiledLogic::FindIndex(table_expr, conditions) => {
606                self.eval_findindex(table_expr, conditions, user_data, internal_context, depth)
607            }
608
609            // ========== String Operations ==========
610            CompiledLogic::Cat(items) => {
611                self.concat_strings(items, user_data, internal_context, depth)
612            }
613            CompiledLogic::Substr(string_expr, start_expr, length_expr) => self.eval_substr(
614                string_expr,
615                start_expr,
616                length_expr,
617                user_data,
618                internal_context,
619                depth,
620            ),
621            CompiledLogic::Search(find_expr, within_expr, start_expr) => self.eval_search(
622                find_expr,
623                within_expr,
624                start_expr,
625                user_data,
626                internal_context,
627                depth,
628            ),
629            CompiledLogic::Left(text_expr, num_expr) => self.extract_text_side(
630                text_expr,
631                num_expr.as_deref(),
632                true,
633                user_data,
634                internal_context,
635                depth,
636            ),
637            CompiledLogic::Right(text_expr, num_expr) => self.extract_text_side(
638                text_expr,
639                num_expr.as_deref(),
640                false,
641                user_data,
642                internal_context,
643                depth,
644            ),
645            CompiledLogic::Mid(text_expr, start_expr, num_expr) => self.eval_mid(
646                text_expr,
647                start_expr,
648                num_expr,
649                user_data,
650                internal_context,
651                depth,
652            ),
653            CompiledLogic::SplitText(value_expr, sep_expr, index_expr) => self.eval_split_text(
654                value_expr,
655                sep_expr,
656                index_expr,
657                user_data,
658                internal_context,
659                depth,
660            ),
661            CompiledLogic::Concat(items) => {
662                self.concat_strings(items, user_data, internal_context, depth)
663            }
664            CompiledLogic::SplitValue(string_expr, sep_expr) => {
665                self.eval_split_value(string_expr, sep_expr, user_data, internal_context, depth)
666            }
667            CompiledLogic::StringFormat(value_expr, decimals, prefix, suffix, thousands_sep) => {
668                self.eval_string_format(
669                    value_expr,
670                    decimals,
671                    prefix,
672                    suffix,
673                    thousands_sep,
674                    user_data,
675                    internal_context,
676                    depth,
677                )
678            }
679            CompiledLogic::Length(expr) => {
680                self.eval_length(expr, user_data, internal_context, depth)
681            }
682            CompiledLogic::Len(expr) => self.eval_len(expr, user_data, internal_context, depth),
683
684            // ========== Math Operations ==========
685            CompiledLogic::Abs(expr) => {
686                self.eval_unary_math(expr, |n| n.abs(), user_data, internal_context, depth)
687            }
688            CompiledLogic::Max(items) => {
689                self.eval_min_max(items, true, user_data, internal_context, depth)
690            }
691            CompiledLogic::Min(items) => {
692                self.eval_min_max(items, false, user_data, internal_context, depth)
693            }
694            CompiledLogic::Pow(base_expr, exp_expr) => {
695                self.eval_pow(base_expr, exp_expr, user_data, internal_context, depth)
696            }
697            CompiledLogic::Round(expr, decimals) => {
698                self.apply_round(expr, decimals, 0, user_data, internal_context, depth)
699            }
700            CompiledLogic::RoundUp(expr, decimals) => {
701                self.apply_round(expr, decimals, 1, user_data, internal_context, depth)
702            }
703            CompiledLogic::RoundDown(expr, decimals) => {
704                self.apply_round(expr, decimals, 2, user_data, internal_context, depth)
705            }
706            CompiledLogic::Ceiling(expr, significance) => {
707                self.eval_ceiling(expr, significance, user_data, internal_context, depth)
708            }
709            CompiledLogic::Floor(expr, significance) => {
710                self.eval_floor(expr, significance, user_data, internal_context, depth)
711            }
712            CompiledLogic::Trunc(expr, decimals) => {
713                self.eval_trunc(expr, decimals, user_data, internal_context, depth)
714            }
715            CompiledLogic::Mround(value_expr, multiple_expr) => self.eval_mround(
716                value_expr,
717                multiple_expr,
718                user_data,
719                internal_context,
720                depth,
721            ),
722
723            // ========== Date Operations ==========
724            CompiledLogic::Today => self.eval_today(),
725            CompiledLogic::Now => self.eval_now(),
726            CompiledLogic::Days(end_expr, start_expr) => {
727                self.eval_days(end_expr, start_expr, user_data, internal_context, depth)
728            }
729            CompiledLogic::Year(expr) => {
730                self.extract_date_component(expr, "year", user_data, internal_context, depth)
731            }
732            CompiledLogic::Month(expr) => {
733                self.extract_date_component(expr, "month", user_data, internal_context, depth)
734            }
735            CompiledLogic::Day(expr) => {
736                self.extract_date_component(expr, "day", user_data, internal_context, depth)
737            }
738            CompiledLogic::Date(year_expr, month_expr, day_expr) => self.eval_date(
739                year_expr,
740                month_expr,
741                day_expr,
742                user_data,
743                internal_context,
744                depth,
745            ),
746            CompiledLogic::DateFormat(date_expr, format_expr) => {
747                self.eval_date_format(date_expr, format_expr, user_data, internal_context, depth)
748            }
749            CompiledLogic::YearFrac(start_expr, end_expr, basis_expr) => self.eval_year_frac(
750                start_expr,
751                end_expr,
752                basis_expr,
753                user_data,
754                internal_context,
755                depth,
756            ),
757            CompiledLogic::DateDif(start_expr, end_expr, unit_expr) => self.eval_date_dif(
758                start_expr,
759                end_expr,
760                unit_expr,
761                user_data,
762                internal_context,
763                depth,
764            ),
765
766            // ========== Utility Operators ==========
767            CompiledLogic::Missing(keys) => {
768                let missing: Vec<_> = keys
769                    .iter()
770                    .filter(|key| self.is_key_missing(user_data, key))
771                    .map(|k| Value::String(k.clone()))
772                    .collect();
773                Ok(Value::Array(missing))
774            }
775            CompiledLogic::MissingSome(min_expr, keys) => {
776                let min_val =
777                    self.evaluate_with_context(min_expr, user_data, internal_context, depth + 1)?;
778                let minimum = to_number(&min_val) as usize;
779
780                let present = keys
781                    .iter()
782                    .filter(|key| !self.is_key_missing(user_data, key))
783                    .count();
784
785                if present >= minimum {
786                    Ok(Value::Array(vec![]))
787                } else {
788                    let missing: Vec<_> = keys
789                        .iter()
790                        .filter(|key| self.is_key_missing(user_data, key))
791                        .map(|k| Value::String(k.clone()))
792                        .collect();
793                    Ok(Value::Array(missing))
794                }
795            }
796
797            // ========== Logical Utility Operators ==========
798            CompiledLogic::Xor(a_expr, b_expr) => {
799                let a_val =
800                    self.evaluate_with_context(a_expr, user_data, internal_context, depth + 1)?;
801                let b_val =
802                    self.evaluate_with_context(b_expr, user_data, internal_context, depth + 1)?;
803                Ok(Value::Bool(is_truthy(&a_val) ^ is_truthy(&b_val)))
804            }
805            CompiledLogic::IfNull(cond_expr, alt_expr) => {
806                let cond_val =
807                    self.evaluate_with_context(cond_expr, user_data, internal_context, depth + 1)?;
808                if is_null_like(&cond_val) {
809                    self.evaluate_with_context(alt_expr, user_data, internal_context, depth + 1)
810                } else {
811                    Ok(cond_val)
812                }
813            }
814            CompiledLogic::IsEmpty(expr) => {
815                let val =
816                    self.evaluate_with_context(expr, user_data, internal_context, depth + 1)?;
817                let empty = match &val {
818                    Value::Null => true,
819                    Value::String(s) => s.is_empty(),
820                    _ => false,
821                };
822                Ok(Value::Bool(empty))
823            }
824            CompiledLogic::Empty => Ok(Value::String(String::new())),
825
826            // ========== UI Helper Operators ==========
827            CompiledLogic::RangeOptions(min_expr, max_expr) => {
828                let min_val =
829                    self.evaluate_with_context(min_expr, user_data, internal_context, depth + 1)?;
830                let max_val =
831                    self.evaluate_with_context(max_expr, user_data, internal_context, depth + 1)?;
832
833                let min = to_number(&min_val) as i32;
834                let max = to_number(&max_val) as i32;
835
836                if min > max {
837                    return Ok(Value::Array(vec![]));
838                }
839
840                let options: Vec<Value> = (min..=max)
841                    .map(|i| {
842                        serde_json::json!({
843                            "label": i.to_string(),
844                            "value": i.to_string()
845                        })
846                    })
847                    .collect();
848
849                Ok(Value::Array(options))
850            }
851            CompiledLogic::MapOptions(table_expr, label_expr, value_expr) => {
852                let table_val =
853                    self.evaluate_with_context(table_expr, user_data, internal_context, depth + 1)?;
854                let label_val =
855                    self.evaluate_with_context(label_expr, user_data, internal_context, depth + 1)?;
856                let value_val =
857                    self.evaluate_with_context(value_expr, user_data, internal_context, depth + 1)?;
858
859                if let (Value::Array(arr), Value::String(label_field), Value::String(value_field)) =
860                    (&table_val, &label_val, &value_val)
861                {
862                    let options: Vec<Value> = arr
863                        .iter()
864                        .filter_map(|row| {
865                            row.as_object().and_then(|obj| {
866                                Some(create_option(obj.get(label_field)?, obj.get(value_field)?))
867                            })
868                        })
869                        .collect();
870                    Ok(Value::Array(options))
871                } else {
872                    Ok(Value::Array(vec![]))
873                }
874            }
875            CompiledLogic::MapOptionsIf(table_expr, label_expr, value_expr, conditions) => {
876                let table_val =
877                    self.evaluate_with_context(table_expr, user_data, internal_context, depth + 1)?;
878                let label_val =
879                    self.evaluate_with_context(label_expr, user_data, internal_context, depth + 1)?;
880                let value_val =
881                    self.evaluate_with_context(value_expr, user_data, internal_context, depth + 1)?;
882
883                if let (Value::Array(arr), Value::String(label_field), Value::String(value_field)) =
884                    (&table_val, &label_val, &value_val)
885                {
886                    let mut options = Vec::new();
887
888                    for row in arr {
889                        let obj = match row.as_object() {
890                            Some(obj) => obj,
891                            None => continue,
892                        };
893
894                        let mut all_match = true;
895
896                        for condition in conditions {
897                            // Evaluate condition with row as primary context, user_data as fallback
898                            let result =
899                                self.evaluate_with_context(condition, row, user_data, depth + 1)?;
900                            if !is_truthy(&result) {
901                                all_match = false;
902                                break;
903                            }
904                        }
905
906                        if all_match {
907                            if let (Some(label), Some(value)) =
908                                (obj.get(label_field), obj.get(value_field))
909                            {
910                                options.push(create_option(label, value));
911                            }
912                        }
913                    }
914
915                    Ok(Value::Array(options))
916                } else {
917                    Ok(Value::Array(vec![]))
918                }
919            }
920            CompiledLogic::Return(value) => {
921                // Return the raw value as-is without any evaluation
922                Ok(value.as_ref().clone())
923            }
924        }
925    }
926
927    /// Helper for evaluating variable/ref with default (zero-copy)
928    #[inline]
929    fn eval_var_or_default(
930        &self,
931        name: &str,
932        default: &Option<Box<CompiledLogic>>,
933        user_data: &Value,
934        internal_context: &Value,
935        depth: usize,
936    ) -> Result<Value, String> {
937        // Fast path: check active table scope first.
938        // When evaluating a table's own columns (forward/backward pass), Var/Ref nodes
939        // that resolve to the table's own path (e.g. used in MAP/FILTER/REDUCE over self)
940        // must see local_rows, not stale data in scope_data.
941        if !name.is_empty() {
942            let scope = unsafe { self.table_scope_ref() };
943            if let Some(ts) = scope.as_ref() {
944                if name == ts.path || name.trim_start_matches('#') == ts.path_no_hash.as_str() {
945                    if ts.col_count > 0 && !ts.flat_cells.is_null() {
946                        let mut arr = Vec::with_capacity(ts.existing_row_count + ts.total_rows);
947                        let rows = unsafe { &*ts.rows };
948                        for r in 0..ts.existing_row_count {
949                            if let Some(row) = rows.get(r) {
950                                arr.push(row.clone());
951                            }
952                        }
953                        for r in 0..ts.total_rows {
954                            let mut row_map = serde_json::Map::with_capacity(ts.col_count);
955                            let row_offset = r * ts.col_count;
956                            for (c_name, &c_idx) in ts.col_map.iter() {
957                                let cell = unsafe { &*ts.flat_cells.add(row_offset + c_idx) };
958                                row_map.insert(c_name.clone(), cell.clone());
959                            }
960                            arr.push(Value::Object(row_map));
961                        }
962                        return Ok(Value::Array(arr));
963                    }
964                    // SAFETY: local_rows outlives this evaluation frame
965                    let rows = unsafe { &*ts.rows };
966                    return Ok(Value::Array(rows.clone()));
967                }
968            }
969        }
970
971        // Special case: empty name "" refers to root context (user_data only)
972        // For named variables, try internal context first (for $loopIteration, $iteration, etc.)
973        let value = if name.is_empty() {
974            self.get_var(user_data, name)
975        } else {
976            self.get_var(internal_context, name)
977                .or_else(|| self.get_var(user_data, name))
978        };
979        match value {
980            Some(v) if !v.is_null() => Ok(v.clone()), // Only clone the resolved value
981            _ => {
982                if let Some(def) = default {
983                    self.evaluate_with_context(def, user_data, internal_context, depth + 1)
984                } else {
985                    Ok(Value::Null)
986                }
987            }
988        }
989    }
990
991    /// Convert f64 to JSON number
992    #[inline(always)]
993    pub fn f64_to_value(&self, f: f64) -> Value {
994        helpers::f64_to_json(f, self.config.safe_nan_handling)
995    }
996
997    #[inline(always)]
998    fn f64_to_json(&self, f: f64) -> Value {
999        self.f64_to_value(f)
1000    }
1001
1002    #[inline(always)]
1003    pub fn eval_fast_f64(
1004        &self,
1005        logic: &CompiledLogic,
1006        user_data: &Value,
1007        internal_context: &Value,
1008    ) -> Result<Option<f64>, String> {
1009        self.eval_f64(logic, user_data, internal_context, 0)
1010    }
1011}
1012
1013impl Default for Evaluator {
1014    fn default() -> Self {
1015        Self::new()
1016    }
1017}