json_eval_rs/rlogic/
compiled.rs

1use ahash::AHashMap;
2use serde::Serialize;
3use serde_json::Value;
4use crate::path_utils;
5
6/// Unique identifier for compiled logic expressions
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
8pub struct LogicId(pub(crate) u64);
9
10/// Compiled JSON Logic expression optimized for fast evaluation
11#[derive(Debug, Clone)]
12pub enum CompiledLogic {
13    // Literal values
14    Null,
15    Bool(bool),
16    Number(String), // Store as string to preserve precision with arbitrary_precision
17    String(String),
18    Array(Vec<CompiledLogic>),
19    
20    // Variable access
21    Var(String, Option<Box<CompiledLogic>>), // name, default
22    Ref(String, Option<Box<CompiledLogic>>), // JSON Schema reference path, default
23    
24    // Logical operators
25    And(Vec<CompiledLogic>),
26    Or(Vec<CompiledLogic>),
27    Not(Box<CompiledLogic>),
28    If(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), // condition, then, else
29    
30    // Comparison operators
31    Equal(Box<CompiledLogic>, Box<CompiledLogic>),
32    StrictEqual(Box<CompiledLogic>, Box<CompiledLogic>),
33    NotEqual(Box<CompiledLogic>, Box<CompiledLogic>),
34    StrictNotEqual(Box<CompiledLogic>, Box<CompiledLogic>),
35    LessThan(Box<CompiledLogic>, Box<CompiledLogic>),
36    LessThanOrEqual(Box<CompiledLogic>, Box<CompiledLogic>),
37    GreaterThan(Box<CompiledLogic>, Box<CompiledLogic>),
38    GreaterThanOrEqual(Box<CompiledLogic>, Box<CompiledLogic>),
39    
40    // Arithmetic operators
41    Add(Vec<CompiledLogic>),
42    Subtract(Vec<CompiledLogic>),
43    Multiply(Vec<CompiledLogic>),
44    Divide(Vec<CompiledLogic>),
45    Modulo(Box<CompiledLogic>, Box<CompiledLogic>),
46    Power(Box<CompiledLogic>, Box<CompiledLogic>),
47    
48    // Array operators
49    Map(Box<CompiledLogic>, Box<CompiledLogic>), // array, logic
50    Filter(Box<CompiledLogic>, Box<CompiledLogic>), // array, logic
51    Reduce(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), // array, logic, initial
52    All(Box<CompiledLogic>, Box<CompiledLogic>), // array, logic
53    Some(Box<CompiledLogic>, Box<CompiledLogic>), // array, logic
54    None(Box<CompiledLogic>, Box<CompiledLogic>), // array, logic
55    Merge(Vec<CompiledLogic>),
56    In(Box<CompiledLogic>, Box<CompiledLogic>), // value, array
57    
58    // String operators
59    Cat(Vec<CompiledLogic>),
60    Substr(Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), // string, start, length
61    
62    // Utility operators
63    Missing(Vec<String>),
64    MissingSome(Box<CompiledLogic>, Vec<String>), // minimum, keys
65    
66    // Custom operators - Math
67    Abs(Box<CompiledLogic>),
68    Max(Vec<CompiledLogic>),
69    Min(Vec<CompiledLogic>),
70    Pow(Box<CompiledLogic>, Box<CompiledLogic>),
71    Round(Box<CompiledLogic>, Option<Box<CompiledLogic>>), // value, decimals
72    RoundUp(Box<CompiledLogic>, Option<Box<CompiledLogic>>), // value, decimals
73    RoundDown(Box<CompiledLogic>, Option<Box<CompiledLogic>>), // value, decimals
74    Ceiling(Box<CompiledLogic>, Option<Box<CompiledLogic>>), // value, significance (Excel CEILING)
75    Floor(Box<CompiledLogic>, Option<Box<CompiledLogic>>), // value, significance (Excel FLOOR)
76    Trunc(Box<CompiledLogic>, Option<Box<CompiledLogic>>), // value, decimals (Excel TRUNC)
77    Mround(Box<CompiledLogic>, Box<CompiledLogic>), // value, multiple (Excel MROUND)
78    
79    // Custom operators - String
80    Length(Box<CompiledLogic>),
81    Search(Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), // find, within, start_num
82    Left(Box<CompiledLogic>, Option<Box<CompiledLogic>>), // text, num_chars
83    Right(Box<CompiledLogic>, Option<Box<CompiledLogic>>), // text, num_chars
84    Mid(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), // text, start, num_chars
85    Len(Box<CompiledLogic>),
86    SplitText(Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), // value, separator, index
87    Concat(Vec<CompiledLogic>),
88    SplitValue(Box<CompiledLogic>, Box<CompiledLogic>), // string, separator
89    StringFormat(Box<CompiledLogic>, Option<Box<CompiledLogic>>, Option<Box<CompiledLogic>>, Option<Box<CompiledLogic>>, Option<Box<CompiledLogic>>), // value, decimals, prefix, suffix, thousands_sep
90    
91    // Custom operators - Logical
92    Xor(Box<CompiledLogic>, Box<CompiledLogic>),
93    IfNull(Box<CompiledLogic>, Box<CompiledLogic>),
94    IsEmpty(Box<CompiledLogic>),
95    Empty,
96    
97    // Custom operators - Date
98    Today,
99    Now,
100    Days(Box<CompiledLogic>, Box<CompiledLogic>), // end_date, start_date
101    Year(Box<CompiledLogic>),
102    Month(Box<CompiledLogic>),
103    Day(Box<CompiledLogic>),
104    Date(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), // year, month, day
105    DateFormat(Box<CompiledLogic>, Option<Box<CompiledLogic>>), // date, format
106    
107    // Custom operators - Array/Table
108    Sum(Box<CompiledLogic>, Option<Box<CompiledLogic>>, Option<Box<CompiledLogic>>), // array/value, optional field name, optional index threshold
109    For(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), // start, end, logic (with $iteration variable)
110    
111    // Complex table operations
112    ValueAt(Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), // table, row_idx, col_name
113    MaxAt(Box<CompiledLogic>, Box<CompiledLogic>), // table, col_name
114    IndexAt(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), // lookup_val, table, field, range
115    Match(Box<CompiledLogic>, Vec<CompiledLogic>), // table, conditions (pairs of value, field)
116    MatchRange(Box<CompiledLogic>, Vec<CompiledLogic>), // table, conditions (triplets of min_col, max_col, value)
117    Choose(Box<CompiledLogic>, Vec<CompiledLogic>), // table, conditions (pairs of value, field)
118    FindIndex(Box<CompiledLogic>, Vec<CompiledLogic>), // table, conditions (complex nested logic)
119    
120    // Array operations
121    Multiplies(Vec<CompiledLogic>), // flatten and multiply
122    Divides(Vec<CompiledLogic>), // flatten and divide
123    
124    // Advanced date functions
125    YearFrac(Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), // start_date, end_date, basis
126    DateDif(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), // start_date, end_date, unit
127    
128    // UI helpers
129    RangeOptions(Box<CompiledLogic>, Box<CompiledLogic>), // min, max
130    MapOptions(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), // table, field_label, field_value
131    MapOptionsIf(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>, Vec<CompiledLogic>), // table, field_label, field_value, conditions
132    Return(Box<Value>), // return value as-is (no-op, just returns the raw value)
133}
134
135impl CompiledLogic {
136    /// Compile a JSON Logic expression from JSON Value
137    pub fn compile(logic: &Value) -> Result<Self, String> {
138        match logic {
139            Value::Null => Ok(CompiledLogic::Null),
140            Value::Bool(b) => Ok(CompiledLogic::Bool(*b)),
141            Value::Number(n) => {
142                // With arbitrary_precision, store as string to preserve precision
143                Ok(CompiledLogic::Number(n.to_string()))
144            }
145            Value::String(s) => Ok(CompiledLogic::String(s.clone())),
146            Value::Array(arr) => {
147                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
148                Ok(CompiledLogic::Array(compiled?))
149            }
150            Value::Object(obj) => {
151                if obj.is_empty() {
152                    return Ok(CompiledLogic::Null);
153                }
154                
155                // Get the operator (first key)
156                let (op, args) = obj.iter().next().unwrap();
157                
158                Self::compile_operator(op, args)
159            }
160        }
161    }
162    
163    fn compile_operator(op: &str, args: &Value) -> Result<Self, String> {
164        match op {
165            // Variable access
166            "var" => {
167                if let Value::String(name) = args {
168                    // OPTIMIZED: Pre-normalize path during compilation
169                    let normalized = path_utils::normalize_to_json_pointer(name);
170                    Ok(CompiledLogic::Var(normalized, None))
171                } else if let Value::Array(arr) = args {
172                    if arr.is_empty() {
173                        return Err("var requires at least one argument".to_string());
174                    }
175                    let name = arr[0].as_str()
176                        .ok_or("var name must be a string")?;
177                    // OPTIMIZED: Pre-normalize path during compilation
178                    let normalized = path_utils::normalize_to_json_pointer(name);
179                    let default = if arr.len() > 1 {
180                        Some(Box::new(Self::compile(&arr[1])?))
181                    } else {
182                        None
183                    };
184                    Ok(CompiledLogic::Var(normalized, default))
185                } else {
186                    Err("var requires string or array".to_string())
187                }
188            }
189            "$ref" | "ref" => {
190                if let Value::String(path) = args {
191                    // OPTIMIZED: Pre-normalize path during compilation
192                    let normalized = path_utils::normalize_to_json_pointer(path);
193                    Ok(CompiledLogic::Ref(normalized, None))
194                } else if let Value::Array(arr) = args {
195                    if arr.is_empty() {
196                        return Err("$ref requires at least one argument".to_string());
197                    }
198                    let path = arr[0].as_str()
199                        .ok_or("$ref path must be a string")?;
200                    // OPTIMIZED: Pre-normalize path during compilation
201                    let normalized = path_utils::normalize_to_json_pointer(path);
202                    let default = if arr.len() > 1 {
203                        Some(Box::new(Self::compile(&arr[1])?))
204                    } else {
205                        None
206                    };
207                    Ok(CompiledLogic::Ref(normalized, default))
208                } else {
209                    Err("$ref requires string or array".to_string())
210                }
211            }
212            
213            // Logical operators
214            "and" => {
215                let arr = args.as_array().ok_or("and requires array")?;
216                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
217                let items = compiled?;
218                // OPTIMIZATION: Flatten nested and operations (And(And(a,b),c) -> And(a,b,c))
219                Ok(CompiledLogic::And(Self::flatten_and(items)))
220            }
221            "or" => {
222                let arr = args.as_array().ok_or("or requires array")?;
223                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
224                let items = compiled?;
225                // OPTIMIZATION: Flatten nested or operations (Or(Or(a,b),c) -> Or(a,b,c))
226                Ok(CompiledLogic::Or(Self::flatten_or(items)))
227            }
228            "!" | "not" => {
229                // Handle both array format [value] and direct value format
230                let value_to_negate = if let Value::Array(arr) = args {
231                    if arr.is_empty() {
232                        return Err("! requires an argument".to_string());
233                    }
234                    &arr[0]
235                } else {
236                    args
237                };
238                
239                let inner = Self::compile(value_to_negate)?;
240                // OPTIMIZATION: Eliminate double negation (!(!x) -> x)
241                if let CompiledLogic::Not(inner_expr) = inner {
242                    Ok(*inner_expr)
243                } else {
244                    Ok(CompiledLogic::Not(Box::new(inner)))
245                }
246            }
247            "if" => {
248                let arr = args.as_array().ok_or("if requires array")?;
249                if arr.len() < 3 {
250                    return Err("if requires at least 3 arguments".to_string());
251                }
252                Ok(CompiledLogic::If(
253                    Box::new(Self::compile(&arr[0])?),
254                    Box::new(Self::compile(&arr[1])?),
255                    Box::new(Self::compile(&arr[2])?),
256                ))
257            }
258            
259            // Comparison operators
260            "==" => Self::compile_binary(args, |a, b| CompiledLogic::Equal(a, b)),
261            "===" => Self::compile_binary(args, |a, b| CompiledLogic::StrictEqual(a, b)),
262            "!=" => Self::compile_binary(args, |a, b| CompiledLogic::NotEqual(a, b)),
263            "!==" => Self::compile_binary(args, |a, b| CompiledLogic::StrictNotEqual(a, b)),
264            "<" => Self::compile_binary(args, |a, b| CompiledLogic::LessThan(a, b)),
265            "<=" => Self::compile_binary(args, |a, b| CompiledLogic::LessThanOrEqual(a, b)),
266            ">" => Self::compile_binary(args, |a, b| CompiledLogic::GreaterThan(a, b)),
267            ">=" => Self::compile_binary(args, |a, b| CompiledLogic::GreaterThanOrEqual(a, b)),
268            
269            // Arithmetic operators
270            "+" => {
271                let arr = args.as_array().ok_or("+ requires array")?;
272                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
273                let items = compiled?;
274                // OPTIMIZATION: Flatten nested additions (Add(Add(a,b),c) -> Add(a,b,c))
275                Ok(CompiledLogic::Add(Self::flatten_add(items)))
276            }
277            "-" => {
278                let arr = args.as_array().ok_or("- requires array")?;
279                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
280                Ok(CompiledLogic::Subtract(compiled?))
281            }
282            "*" => {
283                let arr = args.as_array().ok_or("* requires array")?;
284                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
285                let items = compiled?;
286                // OPTIMIZATION: Flatten nested multiplications
287                Ok(CompiledLogic::Multiply(Self::flatten_multiply(items)))
288            }
289            "/" => {
290                let arr = args.as_array().ok_or("/ requires array")?;
291                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
292                Ok(CompiledLogic::Divide(compiled?))
293            }
294            "%" => Self::compile_binary(args, |a, b| CompiledLogic::Modulo(a, b)),
295            "^" => Self::compile_binary(args, |a, b| CompiledLogic::Power(a, b)),
296            
297            // Array operators
298            "map" => Self::compile_binary(args, |a, b| CompiledLogic::Map(a, b)),
299            "filter" => Self::compile_binary(args, |a, b| CompiledLogic::Filter(a, b)),
300            "reduce" => {
301                let arr = args.as_array().ok_or("reduce requires array")?;
302                if arr.len() < 3 {
303                    return Err("reduce requires 3 arguments".to_string());
304                }
305                Ok(CompiledLogic::Reduce(
306                    Box::new(Self::compile(&arr[0])?),
307                    Box::new(Self::compile(&arr[1])?),
308                    Box::new(Self::compile(&arr[2])?),
309                ))
310            }
311            "all" => Self::compile_binary(args, |a, b| CompiledLogic::All(a, b)),
312            "some" => Self::compile_binary(args, |a, b| CompiledLogic::Some(a, b)),
313            "none" => Self::compile_binary(args, |a, b| CompiledLogic::None(a, b)),
314            "merge" => {
315                let arr = args.as_array().ok_or("merge requires array")?;
316                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
317                Ok(CompiledLogic::Merge(compiled?))
318            }
319            "in" => Self::compile_binary(args, |a, b| CompiledLogic::In(a, b)),
320            
321            // String operators
322            "cat" => {
323                let arr = args.as_array().ok_or("cat requires array")?;
324                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
325                let items = compiled?;
326                // OPTIMIZATION: Flatten nested concatenations (Cat(Cat(a,b),c) -> Cat(a,b,c))
327                Ok(CompiledLogic::Cat(Self::flatten_cat(items)))
328            }
329            "substr" => {
330                let arr = args.as_array().ok_or("substr requires array")?;
331                if arr.len() < 2 {
332                    return Err("substr requires at least 2 arguments".to_string());
333                }
334                let length = if arr.len() > 2 {
335                    Some(Box::new(Self::compile(&arr[2])?))
336                } else {
337                    None
338                };
339                Ok(CompiledLogic::Substr(
340                    Box::new(Self::compile(&arr[0])?),
341                    Box::new(Self::compile(&arr[1])?),
342                    length,
343                ))
344            }
345            
346            // Utility operators
347            "missing" => {
348                let keys = if let Value::Array(arr) = args {
349                    arr.iter()
350                        .map(|v| v.as_str().ok_or("missing key must be string").map(|s| s.to_string()))
351                        .collect::<Result<Vec<_>, _>>()?
352                } else if let Value::String(s) = args {
353                    vec![s.clone()]
354                } else {
355                    return Err("missing requires string or array".to_string());
356                };
357                Ok(CompiledLogic::Missing(keys))
358            }
359            "missing_some" => {
360                let arr = args.as_array().ok_or("missing_some requires array")?;
361                if arr.len() < 2 {
362                    return Err("missing_some requires at least 2 arguments".to_string());
363                }
364                let minimum = Box::new(Self::compile(&arr[0])?);
365                let keys = if let Value::Array(key_arr) = &arr[1] {
366                    key_arr.iter()
367                        .map(|v| v.as_str().ok_or("key must be string").map(|s| s.to_string()))
368                        .collect::<Result<Vec<_>, _>>()?
369                } else {
370                    return Err("missing_some keys must be array".to_string());
371                };
372                Ok(CompiledLogic::MissingSome(minimum, keys))
373            }
374            
375            // Custom operators - Math
376            "abs" => {
377                let arg = if let Value::Array(arr) = args {
378                    if arr.is_empty() {
379                        return Err("abs requires at least one argument".to_string());
380                    }
381                    &arr[0]
382                } else {
383                    args
384                };
385                Ok(CompiledLogic::Abs(Box::new(Self::compile(arg)?)))
386            }
387            "max" => {
388                let arr = args.as_array().ok_or("max requires array")?;
389                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
390                Ok(CompiledLogic::Max(compiled?))
391            }
392            "min" => {
393                let arr = args.as_array().ok_or("min requires array")?;
394                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
395                Ok(CompiledLogic::Min(compiled?))
396            }
397            "pow" | "**" => Self::compile_binary(args, |a, b| CompiledLogic::Pow(a, b)),
398            "round" | "ROUND" => {
399                if let Value::Array(arr) = args {
400                    let decimals = if arr.len() > 1 {
401                        Some(Box::new(Self::compile(&arr[1])?))
402                    } else {
403                        None
404                    };
405                    Ok(CompiledLogic::Round(Box::new(Self::compile(&arr[0])?), decimals))
406                } else {
407                    Ok(CompiledLogic::Round(Box::new(Self::compile(args)?), None))
408                }
409            }
410            "roundup" | "ROUNDUP" => {
411                if let Value::Array(arr) = args {
412                    let decimals = if arr.len() > 1 {
413                        Some(Box::new(Self::compile(&arr[1])?))
414                    } else {
415                        None
416                    };
417                    Ok(CompiledLogic::RoundUp(Box::new(Self::compile(&arr[0])?), decimals))
418                } else {
419                    Ok(CompiledLogic::RoundUp(Box::new(Self::compile(args)?), None))
420                }
421            }
422            "rounddown" | "ROUNDDOWN" => {
423                if let Value::Array(arr) = args {
424                    let decimals = if arr.len() > 1 {
425                        Some(Box::new(Self::compile(&arr[1])?))
426                    } else {
427                        None
428                    };
429                    Ok(CompiledLogic::RoundDown(Box::new(Self::compile(&arr[0])?), decimals))
430                } else {
431                    Ok(CompiledLogic::RoundDown(Box::new(Self::compile(args)?), None))
432                }
433            }
434            "ceiling" | "CEILING" => {
435                if let Value::Array(arr) = args {
436                    let significance = if arr.len() > 1 {
437                        Some(Box::new(Self::compile(&arr[1])?))
438                    } else {
439                        None
440                    };
441                    Ok(CompiledLogic::Ceiling(Box::new(Self::compile(&arr[0])?), significance))
442                } else {
443                    Ok(CompiledLogic::Ceiling(Box::new(Self::compile(args)?), None))
444                }
445            }
446            "floor" | "FLOOR" => {
447                if let Value::Array(arr) = args {
448                    let significance = if arr.len() > 1 {
449                        Some(Box::new(Self::compile(&arr[1])?))
450                    } else {
451                        None
452                    };
453                    Ok(CompiledLogic::Floor(Box::new(Self::compile(&arr[0])?), significance))
454                } else {
455                    Ok(CompiledLogic::Floor(Box::new(Self::compile(args)?), None))
456                }
457            }
458            "trunc" | "TRUNC" => {
459                if let Value::Array(arr) = args {
460                    let decimals = if arr.len() > 1 {
461                        Some(Box::new(Self::compile(&arr[1])?))
462                    } else {
463                        None
464                    };
465                    Ok(CompiledLogic::Trunc(Box::new(Self::compile(&arr[0])?), decimals))
466                } else {
467                    Ok(CompiledLogic::Trunc(Box::new(Self::compile(args)?), None))
468                }
469            }
470            "mround" | "MROUND" => Self::compile_binary(args, |a, b| CompiledLogic::Mround(a, b)),
471            
472            // Custom operators - String
473            "length" => {
474                let arg = if let Value::Array(arr) = args {
475                    if arr.is_empty() {
476                        return Err("length requires at least one argument".to_string());
477                    }
478                    &arr[0]
479                } else {
480                    args
481                };
482                Ok(CompiledLogic::Length(Box::new(Self::compile(arg)?)))
483            }
484            "len" | "LEN" => {
485                let arg = if let Value::Array(arr) = args {
486                    if arr.is_empty() {
487                        return Err("len requires at least one argument".to_string());
488                    }
489                    &arr[0]
490                } else {
491                    args
492                };
493                Ok(CompiledLogic::Len(Box::new(Self::compile(arg)?)))
494            }
495            "search" | "SEARCH" => {
496                let arr = args.as_array().ok_or("search requires array")?;
497                if arr.len() < 2 {
498                    return Err("search requires at least 2 arguments".to_string());
499                }
500                let start_num = if arr.len() > 2 {
501                    Some(Box::new(Self::compile(&arr[2])?))
502                } else {
503                    None
504                };
505                Ok(CompiledLogic::Search(
506                    Box::new(Self::compile(&arr[0])?),
507                    Box::new(Self::compile(&arr[1])?),
508                    start_num,
509                ))
510            }
511            "left" | "LEFT" => {
512                if let Value::Array(arr) = args {
513                    let num_chars = if arr.len() > 1 {
514                        Some(Box::new(Self::compile(&arr[1])?))
515                    } else {
516                        None
517                    };
518                    Ok(CompiledLogic::Left(Box::new(Self::compile(&arr[0])?), num_chars))
519                } else {
520                    Ok(CompiledLogic::Left(Box::new(Self::compile(args)?), None))
521                }
522            }
523            "right" | "RIGHT" => {
524                if let Value::Array(arr) = args {
525                    let num_chars = if arr.len() > 1 {
526                        Some(Box::new(Self::compile(&arr[1])?))
527                    } else {
528                        None
529                    };
530                    Ok(CompiledLogic::Right(Box::new(Self::compile(&arr[0])?), num_chars))
531                } else {
532                    Ok(CompiledLogic::Right(Box::new(Self::compile(args)?), None))
533                }
534            }
535            "mid" | "MID" => {
536                let arr = args.as_array().ok_or("mid requires array")?;
537                if arr.len() < 3 {
538                    return Err("mid requires 3 arguments".to_string());
539                }
540                Ok(CompiledLogic::Mid(
541                    Box::new(Self::compile(&arr[0])?),
542                    Box::new(Self::compile(&arr[1])?),
543                    Box::new(Self::compile(&arr[2])?),
544                ))
545            }
546            "splittext" | "SPLITTEXT" => {
547                let arr = args.as_array().ok_or("splittext requires array")?;
548                if arr.len() < 2 {
549                    return Err("splittext requires at least 2 arguments".to_string());
550                }
551                let index = if arr.len() > 2 {
552                    Some(Box::new(Self::compile(&arr[2])?))
553                } else {
554                    None
555                };
556                Ok(CompiledLogic::SplitText(
557                    Box::new(Self::compile(&arr[0])?),
558                    Box::new(Self::compile(&arr[1])?),
559                    index,
560                ))
561            }
562            "concat" | "CONCAT" => {
563                let arr = args.as_array().ok_or("concat requires array")?;
564                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
565                Ok(CompiledLogic::Concat(compiled?))
566            }
567            "splitvalue" | "SPLITVALUE" => Self::compile_binary(args, |a, b| CompiledLogic::SplitValue(a, b)),
568            "stringformat" | "STRINGFORMAT" => {
569                let arr = args.as_array().ok_or("stringformat requires array")?;
570                if arr.is_empty() {
571                    return Err("stringformat requires at least 1 argument".to_string());
572                }
573                let decimals = if arr.len() > 1 {
574                    Some(Box::new(Self::compile(&arr[1])?))
575                } else {
576                    None
577                };
578                let prefix = if arr.len() > 2 {
579                    Some(Box::new(Self::compile(&arr[2])?))
580                } else {
581                    None
582                };
583                let suffix = if arr.len() > 3 {
584                    Some(Box::new(Self::compile(&arr[3])?))
585                } else {
586                    None
587                };
588                let thousands_sep = if arr.len() > 4 {
589                    Some(Box::new(Self::compile(&arr[4])?))
590                } else {
591                    None
592                };
593                Ok(CompiledLogic::StringFormat(
594                    Box::new(Self::compile(&arr[0])?),
595                    decimals,
596                    prefix,
597                    suffix,
598                    thousands_sep,
599                ))
600            }
601            
602            // Custom operators - Logical
603            "xor" => Self::compile_binary(args, |a, b| CompiledLogic::Xor(a, b)),
604            "ifnull" | "IFNULL" => Self::compile_binary(args, |a, b| CompiledLogic::IfNull(a, b)),
605            "isempty" | "ISEMPTY" => {
606                let arg = if let Value::Array(arr) = args {
607                    if arr.is_empty() {
608                        return Err("ISEMPTY requires at least one argument".to_string());
609                    }
610                    &arr[0]
611                } else {
612                    args
613                };
614                Ok(CompiledLogic::IsEmpty(Box::new(Self::compile(arg)?)))
615            }
616            "empty" | "EMPTY" => Ok(CompiledLogic::Empty),
617            
618            // Custom operators - Date
619            "today" | "TODAY" => Ok(CompiledLogic::Today),
620            "now" | "NOW" => Ok(CompiledLogic::Now),
621            "days" | "DAYS" => Self::compile_binary(args, |a, b| CompiledLogic::Days(a, b)),
622            "year" | "YEAR" => {
623                let arg = if let Value::Array(arr) = args {
624                    if arr.is_empty() {
625                        return Err("year requires at least one argument".to_string());
626                    }
627                    &arr[0]
628                } else {
629                    args
630                };
631                Ok(CompiledLogic::Year(Box::new(Self::compile(arg)?)))
632            }
633            "month" | "MONTH" => {
634                let arg = if let Value::Array(arr) = args {
635                    if arr.is_empty() {
636                        return Err("month requires at least one argument".to_string());
637                    }
638                    &arr[0]
639                } else {
640                    args
641                };
642                Ok(CompiledLogic::Month(Box::new(Self::compile(arg)?)))
643            }
644            "day" | "DAY" => {
645                let arg = if let Value::Array(arr) = args {
646                    if arr.is_empty() {
647                        return Err("day requires at least one argument".to_string());
648                    }
649                    &arr[0]
650                } else {
651                    args
652                };
653                Ok(CompiledLogic::Day(Box::new(Self::compile(arg)?)))
654            }
655            "date" | "DATE" => {
656                let arr = args.as_array().ok_or("date requires array")?;
657                if arr.len() < 3 {
658                    return Err("date requires 3 arguments".to_string());
659                }
660                Ok(CompiledLogic::Date(
661                    Box::new(Self::compile(&arr[0])?),
662                    Box::new(Self::compile(&arr[1])?),
663                    Box::new(Self::compile(&arr[2])?),
664                ))
665            }
666            "dateformat" | "DATEFORMAT" => {
667                if let Value::Array(arr) = args {
668                    if arr.is_empty() {
669                        return Err("dateformat requires at least 1 argument".to_string());
670                    }
671                    let format = if arr.len() > 1 {
672                        Some(Box::new(Self::compile(&arr[1])?))
673                    } else {
674                        None
675                    };
676                    Ok(CompiledLogic::DateFormat(Box::new(Self::compile(&arr[0])?), format))
677                } else {
678                    Ok(CompiledLogic::DateFormat(Box::new(Self::compile(args)?), None))
679                }
680            }
681            
682            // Custom operators - Array/Table
683            "sum" | "SUM" => {
684                if let Value::Array(arr) = args {
685                    if arr.is_empty() {
686                        return Err("sum requires at least 1 argument".to_string());
687                    }
688                    let field = if arr.len() > 1 {
689                        Some(Box::new(Self::compile(&arr[1])?))
690                    } else {
691                        None
692                    };
693                    let threshold = if arr.len() > 2 {
694                        Some(Box::new(Self::compile(&arr[2])?))
695                    } else {
696                        None
697                    };
698                    Ok(CompiledLogic::Sum(Box::new(Self::compile(&arr[0])?), field, threshold))
699                } else {
700                    Ok(CompiledLogic::Sum(Box::new(Self::compile(args)?), None, None))
701                }
702            }
703            "FOR" => {
704                let arr = args.as_array().ok_or("FOR requires array")?;
705                if arr.len() < 3 {
706                    return Err("FOR requires 3 arguments: start, end, logic".to_string());
707                }
708                Ok(CompiledLogic::For(
709                    Box::new(Self::compile(&arr[0])?),
710                    Box::new(Self::compile(&arr[1])?),
711                    Box::new(Self::compile(&arr[2])?),
712                ))
713            }
714            
715            // Complex table operations
716            "VALUEAT" => {
717                let arr = args.as_array().ok_or("VALUEAT requires array")?;
718                if arr.len() < 2 {
719                    return Err("VALUEAT requires at least 2 arguments".to_string());
720                }
721                let col_name = if arr.len() > 2 {
722                    Some(Box::new(Self::compile(&arr[2])?))
723                } else {
724                    None
725                };
726                Ok(CompiledLogic::ValueAt(
727                    Box::new(Self::compile(&arr[0])?),
728                    Box::new(Self::compile(&arr[1])?),
729                    col_name,
730                ))
731            }
732            "MAXAT" => Self::compile_binary(args, |a, b| CompiledLogic::MaxAt(a, b)),
733            "INDEXAT" => {
734                let arr = args.as_array().ok_or("INDEXAT requires array")?;
735                if arr.len() < 3 {
736                    return Err("INDEXAT requires at least 3 arguments".to_string());
737                }
738                let range = if arr.len() > 3 {
739                    Some(Box::new(Self::compile(&arr[3])?))
740                } else {
741                    None
742                };
743                Ok(CompiledLogic::IndexAt(
744                    Box::new(Self::compile(&arr[0])?),
745                    Box::new(Self::compile(&arr[1])?),
746                    Box::new(Self::compile(&arr[2])?),
747                    range,
748                ))
749            }
750            "MATCH" => {
751                let arr = args.as_array().ok_or("MATCH requires array")?;
752                if arr.is_empty() {
753                    return Err("MATCH requires at least 1 argument".to_string());
754                }
755                let table = Box::new(Self::compile(&arr[0])?);
756                let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
757                Ok(CompiledLogic::Match(table, conditions?))
758            }
759            "MATCHRANGE" => {
760                let arr = args.as_array().ok_or("MATCHRANGE requires array")?;
761                if arr.is_empty() {
762                    return Err("MATCHRANGE requires at least 1 argument".to_string());
763                }
764                let table = Box::new(Self::compile(&arr[0])?);
765                let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
766                Ok(CompiledLogic::MatchRange(table, conditions?))
767            }
768            "CHOOSE" => {
769                let arr = args.as_array().ok_or("CHOOSE requires array")?;
770                if arr.is_empty() {
771                    return Err("CHOOSE requires at least 1 argument".to_string());
772                }
773                let table = Box::new(Self::compile(&arr[0])?);
774                let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
775                Ok(CompiledLogic::Choose(table, conditions?))
776            }
777            "FINDINDEX" => {
778                let arr = args.as_array().ok_or("FINDINDEX requires array")?;
779                if arr.len() < 2 {
780                    return Err("FINDINDEX requires at least 2 arguments".to_string());
781                }
782                let table = Box::new(Self::compile(&arr[0])?);
783                // CRITICAL: Convert string literals to var references in conditions
784                // This allows ergonomic syntax: "INSAGE" instead of {"var": "INSAGE"}
785                let conditions: Result<Vec<_>, _> = arr[1..]
786                    .iter()
787                    .map(|cond| Self::compile(&Self::preprocess_table_condition(cond)))
788                    .collect();
789                Ok(CompiledLogic::FindIndex(table, conditions?))
790            }
791            
792            // Array operations
793            "MULTIPLIES" => {
794                let arr = args.as_array().ok_or("MULTIPLIES requires array")?;
795                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
796                Ok(CompiledLogic::Multiplies(compiled?))
797            }
798            "DIVIDES" => {
799                let arr = args.as_array().ok_or("DIVIDES requires array")?;
800                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
801                Ok(CompiledLogic::Divides(compiled?))
802            }
803            
804            // Advanced date functions
805            "YEARFRAC" => {
806                let arr = args.as_array().ok_or("YEARFRAC requires array")?;
807                if arr.len() < 2 {
808                    return Err("YEARFRAC requires at least 2 arguments".to_string());
809                }
810                let basis = if arr.len() > 2 {
811                    Some(Box::new(Self::compile(&arr[2])?))
812                } else {
813                    None
814                };
815                Ok(CompiledLogic::YearFrac(
816                    Box::new(Self::compile(&arr[0])?),
817                    Box::new(Self::compile(&arr[1])?),
818                    basis,
819                ))
820            }
821            "DATEDIF" => {
822                let arr = args.as_array().ok_or("DATEDIF requires array")?;
823                if arr.len() < 3 {
824                    return Err("DATEDIF requires 3 arguments".to_string());
825                }
826                Ok(CompiledLogic::DateDif(
827                    Box::new(Self::compile(&arr[0])?),
828                    Box::new(Self::compile(&arr[1])?),
829                    Box::new(Self::compile(&arr[2])?),
830                ))
831            }
832            
833            // UI helpers
834            "RANGEOPTIONS" => Self::compile_binary(args, |a, b| CompiledLogic::RangeOptions(a, b)),
835            "MAPOPTIONS" => {
836                let arr = args.as_array().ok_or("MAPOPTIONS requires array")?;
837                if arr.len() < 3 {
838                    return Err("MAPOPTIONS requires 3 arguments".to_string());
839                }
840                Ok(CompiledLogic::MapOptions(
841                    Box::new(Self::compile(&arr[0])?),
842                    Box::new(Self::compile(&arr[1])?),
843                    Box::new(Self::compile(&arr[2])?),
844                ))
845            }
846            "MAPOPTIONSIF" => {
847                let arr = args.as_array().ok_or("MAPOPTIONSIF requires array")?;
848                if arr.len() < 4 {
849                    return Err("MAPOPTIONSIF requires at least 4 arguments".to_string());
850                }
851                let table = Box::new(Self::compile(&arr[0])?);
852                let field_label = Box::new(Self::compile(&arr[1])?);
853                let field_value = Box::new(Self::compile(&arr[2])?);
854                
855                // Handle triplet syntax: value, operator, field -> {operator: [value, {var: field}]}
856                let condition_args = &arr[3..];
857                let mut conditions = Vec::new();
858                
859                let mut i = 0;
860                while i + 2 < condition_args.len() {
861                    let value = &condition_args[i];
862                    let operator = &condition_args[i + 1];
863                    let field = &condition_args[i + 2];
864                    
865                    if let Value::String(op) = operator {
866                        // Create comparison: {op: [value, {var: field}]}
867                        let field_var = if let Value::String(f) = field {
868                            serde_json::json!({"var": f})
869                        } else {
870                            field.clone()
871                        };
872                        
873                        let comparison = serde_json::json!({
874                            op: [value.clone(), field_var]
875                        });
876                        
877                        conditions.push(Self::compile(&comparison)?);
878                    }
879                    
880                    i += 3;
881                }
882                
883                // Handle any remaining individual conditions
884                while i < condition_args.len() {
885                    conditions.push(Self::compile(&Self::preprocess_table_condition(&condition_args[i]))?);
886                    i += 1;
887                }
888                
889                Ok(CompiledLogic::MapOptionsIf(table, field_label, field_value, conditions))
890            }
891            "return" => Ok(CompiledLogic::Return(Box::new(args.clone()))),
892            
893            _ => Err(format!("Unknown operator: {}", op)),
894        }
895    }
896    
897    fn compile_binary<F>(args: &Value, f: F) -> Result<Self, String>
898    where
899        F: FnOnce(Box<CompiledLogic>, Box<CompiledLogic>) -> CompiledLogic,
900    {
901        let arr = args.as_array().ok_or("Binary operator requires array")?;
902        if arr.len() != 2 {
903            return Err("Binary operator requires exactly 2 arguments".to_string());
904        }
905        Ok(f(
906            Box::new(Self::compile(&arr[0])?),
907            Box::new(Self::compile(&arr[1])?),
908        ))
909    }
910    
911    /// Preprocess table condition to convert string literals to var references
912    /// This allows ergonomic syntax in FINDINDEX/MATCH/CHOOSE conditions
913    /// 
914    /// Handles formats:
915    /// - Comparison triplets: ["==", value, "col"] -> {"==": [value, {"var": "col"}]}
916    /// - Logical operators: ["&&", cond1, cond2] -> {"and": [cond1, cond2]}
917    /// - String field names: "col" -> {"var": "col"}
918    fn preprocess_table_condition(value: &Value) -> Value {
919        match value {
920            Value::String(s) => {
921                // Convert standalone strings to var references
922                serde_json::json!({"var": s})
923            }
924            Value::Array(arr) => {
925                // Check if this is an operator in array shorthand format
926                if !arr.is_empty() {
927                    if let Some(op_str) = arr[0].as_str() {
928                        // Check for comparison operators: [op, value, col]
929                        let is_comparison = matches!(op_str, "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">=");
930                        
931                        if is_comparison && arr.len() >= 3 {
932                            // Comparison triplet: [op, value, col] -> {op: [col_var, value]}
933                            // Evaluates as: row[col] op value
934                            // DON'T preprocess the value (2nd arg) - keep it as-is
935                            let value_arg = arr[1].clone();
936                            
937                            // Only convert the column name (3rd arg) to var reference if it's a string
938                            let col_arg = if let Value::String(col) = &arr[2] {
939                                // Convert column name string to var reference
940                                serde_json::json!({"var": col})
941                            } else {
942                                // If it's not a string, preprocess it (could be nested expression)
943                                Self::preprocess_table_condition(&arr[2])
944                            };
945                            
946                            // Order matters: {op: [col_var, value]} means row[col] op value
947                            let mut obj = serde_json::Map::new();
948                            obj.insert(op_str.to_string(), Value::Array(vec![col_arg, value_arg]));
949                            return Value::Object(obj);
950                        }
951                        
952                        // Check for logical operators: [op, arg1, arg2, ...]
953                        let canonical_op = match op_str {
954                            "&&" => Some("and"),
955                            "||" => Some("or"),
956                            "and" | "or" | "!" | "not" | "if" => Some(op_str),
957                            _ => None,
958                        };
959                        
960                        if let Some(op_name) = canonical_op {
961                            // Convert ["op", arg1, arg2, ...] to {"op": [arg1, arg2, ...]}
962                            let args: Vec<Value> = arr[1..].iter()
963                                .map(Self::preprocess_table_condition)
964                                .collect();
965                            let mut obj = serde_json::Map::new();
966                            obj.insert(op_name.to_string(), Value::Array(args));
967                            return Value::Object(obj);
968                        }
969                    }
970                }
971                // Regular array - recursively process elements
972                Value::Array(arr.iter().map(Self::preprocess_table_condition).collect())
973            }
974            Value::Object(obj) => {
975                // Recursively process object values, but preserve operators
976                let mut new_obj = serde_json::Map::new();
977                for (key, val) in obj {
978                    // Don't convert strings inside $ref, var, or other special operators
979                    if key == "$ref" || key == "ref" || key == "var" {
980                        new_obj.insert(key.clone(), val.clone());
981                    } else {
982                        new_obj.insert(key.clone(), Self::preprocess_table_condition(val));
983                    }
984                }
985                Value::Object(new_obj)
986            }
987            _ => value.clone(),
988        }
989    }
990    
991    /// Check if this is a simple reference that doesn't need caching
992    pub fn is_simple_ref(&self) -> bool {
993        matches!(self, CompiledLogic::Ref(_, None) | CompiledLogic::Var(_, None))
994    }
995    
996    /// Extract all variable names referenced in this logic
997    pub fn referenced_vars(&self) -> Vec<String> {
998        let mut vars = Vec::new();
999        self.collect_vars(&mut vars);
1000        vars.sort();
1001        vars.dedup();
1002        vars
1003    }
1004    
1005    /// Flatten nested And operations only
1006    fn flatten_and(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1007        let mut flattened = Vec::new();
1008        for item in items {
1009            match item {
1010                CompiledLogic::And(nested) => {
1011                    // Recursively flatten nested And operations
1012                    flattened.extend(Self::flatten_and(nested));
1013                }
1014                _ => flattened.push(item),
1015            }
1016        }
1017        flattened
1018    }
1019    
1020    /// Flatten nested Or operations only
1021    fn flatten_or(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1022        let mut flattened = Vec::new();
1023        for item in items {
1024            match item {
1025                CompiledLogic::Or(nested) => {
1026                    // Recursively flatten nested Or operations
1027                    flattened.extend(Self::flatten_or(nested));
1028                }
1029                _ => flattened.push(item),
1030            }
1031        }
1032        flattened
1033    }
1034    
1035    /// Flatten nested Add operations only
1036    fn flatten_add(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1037        let mut flattened = Vec::new();
1038        for item in items {
1039            match item {
1040                CompiledLogic::Add(nested) => {
1041                    // Recursively flatten nested Adds
1042                    flattened.extend(Self::flatten_add(nested));
1043                }
1044                _ => flattened.push(item),
1045            }
1046        }
1047        flattened
1048    }
1049    
1050    /// Flatten nested Multiply operations only
1051    fn flatten_multiply(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1052        let mut flattened = Vec::new();
1053        for item in items {
1054            match item {
1055                CompiledLogic::Multiply(nested) => {
1056                    // Recursively flatten nested Multiplies
1057                    flattened.extend(Self::flatten_multiply(nested));
1058                }
1059                _ => flattened.push(item),
1060            }
1061        }
1062        flattened
1063    }
1064    
1065    /// Flatten nested Cat (concatenation) operations
1066    /// Combines nested Cat operations into a single flat operation
1067    fn flatten_cat(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1068        let mut flattened = Vec::new();
1069        for item in items {
1070            match &item {
1071                CompiledLogic::Cat(nested) => {
1072                    // Recursively flatten nested Cat operations
1073                    flattened.extend(Self::flatten_cat(nested.clone()));
1074                }
1075                _ => flattened.push(item),
1076            }
1077        }
1078        flattened
1079    }
1080    
1081    /// Check if this logic contains forward references (e.g., VALUEAT with $iteration + N where N > 0)
1082    /// Returns true if it references future iterations in a table
1083    pub fn has_forward_reference(&self) -> bool {
1084        let result = self.check_forward_reference();
1085        result
1086    }
1087    
1088    fn check_forward_reference(&self) -> bool {        
1089        match self {
1090            // VALUEAT with $iteration arithmetic
1091            CompiledLogic::ValueAt(table, idx_logic, col_name) => {
1092                // Check if index contains $iteration + positive_number
1093                let has_fwd = idx_logic.contains_iteration_plus_positive();
1094                if has_fwd {
1095                    return true;
1096                }
1097                // Recursively check all parameters
1098                let table_fwd = table.check_forward_reference();
1099                let idx_fwd = idx_logic.check_forward_reference();
1100                let col_fwd = col_name.as_ref().map(|c| c.check_forward_reference()).unwrap_or(false);
1101                table_fwd || idx_fwd || col_fwd
1102            }
1103            // Recursively check compound operations
1104            CompiledLogic::Array(arr) => {
1105                arr.iter().any(|item| item.check_forward_reference())
1106            }
1107            CompiledLogic::And(items) | CompiledLogic::Or(items) 
1108            | CompiledLogic::Add(items) | CompiledLogic::Subtract(items)
1109            | CompiledLogic::Multiply(items) | CompiledLogic::Divide(items)
1110            | CompiledLogic::Merge(items) | CompiledLogic::Cat(items)
1111            | CompiledLogic::Max(items) | CompiledLogic::Min(items)
1112            | CompiledLogic::Concat(items) | CompiledLogic::Multiplies(items)
1113            | CompiledLogic::Divides(items) => {
1114                items.iter().any(|item| item.check_forward_reference())
1115            }
1116            CompiledLogic::Not(a) | CompiledLogic::Abs(a)
1117            | CompiledLogic::Length(a) | CompiledLogic::Len(a) | CompiledLogic::IsEmpty(a)
1118            | CompiledLogic::Year(a) | CompiledLogic::Month(a) | CompiledLogic::Day(a) => a.check_forward_reference(),
1119            CompiledLogic::Round(a, decimals) | CompiledLogic::RoundUp(a, decimals) | CompiledLogic::RoundDown(a, decimals)
1120            | CompiledLogic::Ceiling(a, decimals) | CompiledLogic::Floor(a, decimals) | CompiledLogic::Trunc(a, decimals)
1121            | CompiledLogic::DateFormat(a, decimals) => {
1122                a.check_forward_reference() || decimals.as_ref().map_or(false, |d| d.check_forward_reference())
1123            }
1124            CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1125                a.check_forward_reference() 
1126                || decimals.as_ref().map_or(false, |d| d.check_forward_reference())
1127                || prefix.as_ref().map_or(false, |p| p.check_forward_reference())
1128                || suffix.as_ref().map_or(false, |s| s.check_forward_reference())
1129                || sep.as_ref().map_or(false, |s| s.check_forward_reference())
1130            }
1131            CompiledLogic::Mround(a, b) => a.check_forward_reference() || b.check_forward_reference(),
1132            CompiledLogic::Return(_) => false, // Raw values don't have forward references
1133            CompiledLogic::If(cond, then_val, else_val) => {
1134                cond.check_forward_reference() || then_val.check_forward_reference() || else_val.check_forward_reference()
1135            }
1136            CompiledLogic::Equal(a, b) | CompiledLogic::StrictEqual(a, b)
1137            | CompiledLogic::NotEqual(a, b) | CompiledLogic::StrictNotEqual(a, b)
1138            | CompiledLogic::LessThan(a, b) | CompiledLogic::LessThanOrEqual(a, b)
1139            | CompiledLogic::GreaterThan(a, b) | CompiledLogic::GreaterThanOrEqual(a, b)
1140            | CompiledLogic::Modulo(a, b) | CompiledLogic::Power(a, b)
1141            | CompiledLogic::Map(a, b) | CompiledLogic::Filter(a, b) 
1142            | CompiledLogic::All(a, b) | CompiledLogic::Some(a, b) 
1143            | CompiledLogic::None(a, b) | CompiledLogic::In(a, b) 
1144            | CompiledLogic::Pow(a, b) | CompiledLogic::Xor(a, b) 
1145            | CompiledLogic::IfNull(a, b) | CompiledLogic::Days(a, b) 
1146            | CompiledLogic::SplitValue(a, b) | CompiledLogic::MaxAt(a, b) 
1147            | CompiledLogic::RangeOptions(a, b) => {
1148                a.check_forward_reference() || b.check_forward_reference()
1149            }
1150            CompiledLogic::Reduce(a, b, c) | CompiledLogic::Mid(a, b, c)
1151            | CompiledLogic::Date(a, b, c) | CompiledLogic::DateDif(a, b, c)
1152            | CompiledLogic::MapOptions(a, b, c) | CompiledLogic::For(a, b, c) => {
1153                a.check_forward_reference() || b.check_forward_reference() || c.check_forward_reference()
1154            }
1155            CompiledLogic::Substr(s, start, len) | CompiledLogic::Search(s, start, len)
1156            | CompiledLogic::SplitText(s, start, len) | CompiledLogic::YearFrac(s, start, len) => {
1157                s.check_forward_reference() || start.check_forward_reference() 
1158                || len.as_ref().map(|l| l.check_forward_reference()).unwrap_or(false)
1159            }
1160            CompiledLogic::Left(a, opt) | CompiledLogic::Right(a, opt) => {
1161                a.check_forward_reference() || opt.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1162            }
1163            CompiledLogic::Sum(a, opt1, opt2) => {
1164                a.check_forward_reference() 
1165                || opt1.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1166                || opt2.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1167            }
1168            CompiledLogic::IndexAt(a, b, c, opt) => {
1169                a.check_forward_reference() || b.check_forward_reference() 
1170                || c.check_forward_reference() || opt.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1171            }
1172            CompiledLogic::Match(table, conds) | CompiledLogic::MatchRange(table, conds)
1173            | CompiledLogic::Choose(table, conds) | CompiledLogic::FindIndex(table, conds) => {
1174                table.check_forward_reference() || conds.iter().any(|c| c.check_forward_reference())
1175            }
1176            CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1177                table.check_forward_reference() || label.check_forward_reference() 
1178                || value.check_forward_reference() || conds.iter().any(|c| c.check_forward_reference())
1179            }
1180            CompiledLogic::MissingSome(min, _) => {
1181                min.check_forward_reference()
1182            }
1183            _ => false,
1184        }
1185    }
1186    
1187    /// Check if logic contains $iteration + positive_number pattern
1188    fn contains_iteration_plus_positive(&self) -> bool {
1189        match self {
1190            CompiledLogic::Add(items) => {
1191                // Check if one operand references $iteration and another is a positive number literal
1192                let has_iteration = items.iter().any(|item| {
1193                    item.referenced_vars().iter().any(|v| v == "$iteration")
1194                });
1195
1196                let has_positive = items.iter().any(|item| match item {
1197                    CompiledLogic::Number(n) => {
1198                        n.parse::<f64>().unwrap_or(0.0) > 0.0
1199                    },
1200                    _ => false,
1201                });
1202
1203                let result = has_iteration && has_positive;
1204                result
1205            }
1206            _ => false,
1207        }
1208    }
1209    
1210    /// Normalize JSON Schema reference path to dot notation
1211    /// Handles: #/schema/path, #/properties/field, /properties/field, field.path
1212    /// Trims /properties/ and .properties. segments
1213    fn normalize_ref_path(path: &str) -> String {
1214        let mut normalized = path.to_string();
1215        
1216        // Remove leading #/ if present
1217        if normalized.starts_with("#/") {
1218            normalized = normalized[2..].to_string();
1219        } else if normalized.starts_with('/') {
1220            normalized = normalized[1..].to_string();
1221        }
1222        
1223        // Replace / with . for JSON pointer notation
1224        normalized = normalized.replace('/', ".");
1225        
1226        // Remove /properties/ or .properties. segments
1227        normalized = normalized.replace("properties.", "");
1228        normalized = normalized.replace(".properties.", ".");
1229        
1230        // Clean up any double dots
1231        while normalized.contains("..") {
1232            normalized = normalized.replace("..", ".");
1233        }
1234        
1235        // Remove leading/trailing dots
1236        normalized = normalized.trim_matches('.').to_string();
1237        
1238        normalized
1239    }
1240    
1241    pub fn collect_vars(&self, vars: &mut Vec<String>) {
1242        match self {
1243            CompiledLogic::Var(name, default) => {
1244                vars.push(name.clone());
1245                if let Some(def) = default {
1246                    def.collect_vars(vars);
1247                }
1248            }
1249            CompiledLogic::Ref(path, default) => {
1250                // Normalize the path and add it
1251                vars.push(Self::normalize_ref_path(path));
1252                if let Some(def) = default {
1253                    def.collect_vars(vars);
1254                }
1255            }
1256            CompiledLogic::Array(arr) => {
1257                for item in arr {
1258                    item.collect_vars(vars);
1259                }
1260            }
1261            CompiledLogic::And(items) | CompiledLogic::Or(items) 
1262            | CompiledLogic::Add(items) | CompiledLogic::Subtract(items)
1263            | CompiledLogic::Multiply(items) | CompiledLogic::Divide(items)
1264            | CompiledLogic::Merge(items) | CompiledLogic::Cat(items)
1265            | CompiledLogic::Max(items) | CompiledLogic::Min(items)
1266            | CompiledLogic::Concat(items) => {
1267                for item in items {
1268                    item.collect_vars(vars);
1269                }
1270            }
1271            CompiledLogic::Not(a) | CompiledLogic::Abs(a)
1272            | CompiledLogic::Length(a) | CompiledLogic::Len(a) | CompiledLogic::IsEmpty(a)
1273            | CompiledLogic::Year(a) | CompiledLogic::Month(a) | CompiledLogic::Day(a) => {
1274                a.collect_vars(vars);
1275            }
1276            CompiledLogic::Round(a, decimals) | CompiledLogic::RoundUp(a, decimals) | CompiledLogic::RoundDown(a, decimals)
1277            | CompiledLogic::Ceiling(a, decimals) | CompiledLogic::Floor(a, decimals) | CompiledLogic::Trunc(a, decimals)
1278            | CompiledLogic::DateFormat(a, decimals) => {
1279                a.collect_vars(vars);
1280                if let Some(d) = decimals {
1281                    d.collect_vars(vars);
1282                }
1283            }
1284            CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1285                a.collect_vars(vars);
1286                if let Some(d) = decimals { d.collect_vars(vars); }
1287                if let Some(p) = prefix { p.collect_vars(vars); }
1288                if let Some(s) = suffix { s.collect_vars(vars); }
1289                if let Some(s) = sep { s.collect_vars(vars); }
1290            }
1291            CompiledLogic::Mround(a, b) => {
1292                a.collect_vars(vars);
1293                b.collect_vars(vars);
1294            }
1295            CompiledLogic::Return(_) => {} // Raw values don't contain vars
1296            CompiledLogic::If(cond, then_val, else_val) => {
1297                cond.collect_vars(vars);
1298                then_val.collect_vars(vars);
1299                else_val.collect_vars(vars);
1300            }
1301            CompiledLogic::Equal(a, b) | CompiledLogic::StrictEqual(a, b)
1302            | CompiledLogic::NotEqual(a, b) | CompiledLogic::StrictNotEqual(a, b)
1303            | CompiledLogic::LessThan(a, b) | CompiledLogic::LessThanOrEqual(a, b)
1304            | CompiledLogic::GreaterThan(a, b) | CompiledLogic::GreaterThanOrEqual(a, b)
1305            | CompiledLogic::Modulo(a, b) | CompiledLogic::Power(a, b)
1306            | CompiledLogic::Map(a, b) | CompiledLogic::Filter(a, b) 
1307            | CompiledLogic::All(a, b) | CompiledLogic::Some(a, b) 
1308            | CompiledLogic::None(a, b) | CompiledLogic::In(a, b) 
1309            | CompiledLogic::Pow(a, b) | CompiledLogic::Xor(a, b) 
1310            | CompiledLogic::IfNull(a, b) | CompiledLogic::Days(a, b) 
1311            | CompiledLogic::SplitValue(a, b) | CompiledLogic::MaxAt(a, b) 
1312            | CompiledLogic::RangeOptions(a, b) => {
1313                a.collect_vars(vars);
1314                b.collect_vars(vars);
1315            }
1316            CompiledLogic::Reduce(a, b, c) | CompiledLogic::Mid(a, b, c)
1317            | CompiledLogic::Date(a, b, c) | CompiledLogic::DateDif(a, b, c)
1318            | CompiledLogic::MapOptions(a, b, c) | CompiledLogic::For(a, b, c) => {
1319                a.collect_vars(vars);
1320                b.collect_vars(vars);
1321                c.collect_vars(vars);
1322            }
1323            CompiledLogic::Substr(s, start, len) | CompiledLogic::Search(s, start, len)
1324            | CompiledLogic::SplitText(s, start, len) | CompiledLogic::YearFrac(s, start, len) => {
1325                s.collect_vars(vars);
1326                start.collect_vars(vars);
1327                if let Some(l) = len {
1328                    l.collect_vars(vars);
1329                }
1330            }
1331            CompiledLogic::Left(a, opt) | CompiledLogic::Right(a, opt)
1332            | CompiledLogic::ValueAt(a, _, opt) => {
1333                a.collect_vars(vars);
1334                if let Some(o) = opt {
1335                    o.collect_vars(vars);
1336                }
1337            }
1338            CompiledLogic::Sum(a, opt1, opt2) => {
1339                a.collect_vars(vars);
1340                if let Some(o) = opt1 {
1341                    o.collect_vars(vars);
1342                }
1343                if let Some(o) = opt2 {
1344                    o.collect_vars(vars);
1345                }
1346            }
1347            CompiledLogic::IndexAt(a, b, c, opt) => {
1348                a.collect_vars(vars);
1349                b.collect_vars(vars);
1350                c.collect_vars(vars);
1351                if let Some(o) = opt {
1352                    o.collect_vars(vars);
1353                }
1354            }
1355            CompiledLogic::Match(table, conds) | CompiledLogic::MatchRange(table, conds)
1356            | CompiledLogic::Choose(table, conds) | CompiledLogic::FindIndex(table, conds) => {
1357                table.collect_vars(vars);
1358                for cond in conds {
1359                    cond.collect_vars(vars);
1360                }
1361            }
1362            CompiledLogic::Multiplies(items) | CompiledLogic::Divides(items) => {
1363                for item in items {
1364                    item.collect_vars(vars);
1365                }
1366            }
1367            CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1368                table.collect_vars(vars);
1369                label.collect_vars(vars);
1370                value.collect_vars(vars);
1371                for cond in conds {
1372                    cond.collect_vars(vars);
1373                }
1374            }
1375            CompiledLogic::MissingSome(min, _) => {
1376                min.collect_vars(vars);
1377            }
1378            _ => {}
1379        }
1380    }
1381}
1382
1383/// Storage for compiled logic expressions with dependency tracking
1384/// 
1385/// This store uses the global compiled logic cache to avoid recompiling
1386/// the same logic across different instances. Each instance maintains
1387/// its own local ID mapping to the global storage.
1388pub struct CompiledLogicStore {
1389    next_id: u64,
1390    store: AHashMap<LogicId, CompiledLogic>,
1391    dependencies: AHashMap<LogicId, Vec<String>>,
1392}
1393
1394impl CompiledLogicStore {
1395    pub fn new() -> Self {
1396        Self {
1397            next_id: 0,
1398            store: AHashMap::default(),
1399            dependencies: AHashMap::default(),
1400        }
1401    }
1402    
1403    /// Compile and store a JSON Logic expression
1404    /// 
1405    /// Uses global storage to avoid recompiling the same logic across instances.
1406    /// The logic is compiled once globally and reused, with this instance maintaining
1407    /// its own local ID for tracking dependencies.
1408    pub fn compile(&mut self, logic: &Value) -> Result<LogicId, String> {
1409        // Use global storage - compiles once and caches globally
1410        let _global_id = super::compiled_logic_store::compile_logic_value(logic)?;
1411        
1412        // Get the compiled logic from global store (O(1) lookup)
1413        let compiled = super::compiled_logic_store::get_compiled_logic(_global_id)
1414            .ok_or_else(|| "Failed to retrieve compiled logic from global store".to_string())?;
1415        
1416        // Track dependencies locally
1417        let deps = compiled.referenced_vars();
1418        
1419        // Assign local ID for this instance
1420        let id = LogicId(self.next_id);
1421        self.next_id += 1;
1422        
1423        // Store locally with instance-specific ID
1424        self.store.insert(id, compiled);
1425        self.dependencies.insert(id, deps);
1426        
1427        Ok(id)
1428    }
1429    
1430    /// Get a compiled logic by ID
1431    pub fn get(&self, id: &LogicId) -> Option<&CompiledLogic> {
1432        self.store.get(id)
1433    }
1434    
1435    /// Remove a compiled logic by ID
1436    pub fn remove(&mut self, id: &LogicId) -> Option<CompiledLogic> {
1437        self.dependencies.remove(id);
1438        self.store.remove(id)
1439    }
1440    
1441    /// Get dependencies for a logic ID
1442    pub fn get_dependencies(&self, id: &LogicId) -> Option<&[String]> {
1443        self.dependencies.get(id).map(|v| v.as_slice())
1444    }
1445}
1446
1447impl Default for CompiledLogicStore {
1448    fn default() -> Self {
1449        Self::new()
1450    }
1451}