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" => Ok(CompiledLogic::Abs(Box::new(Self::compile(args)?))),
377            "max" => {
378                let arr = args.as_array().ok_or("max requires array")?;
379                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
380                Ok(CompiledLogic::Max(compiled?))
381            }
382            "min" => {
383                let arr = args.as_array().ok_or("min requires array")?;
384                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
385                Ok(CompiledLogic::Min(compiled?))
386            }
387            "pow" | "**" => Self::compile_binary(args, |a, b| CompiledLogic::Pow(a, b)),
388            "round" | "ROUND" => {
389                if let Value::Array(arr) = args {
390                    let decimals = if arr.len() > 1 {
391                        Some(Box::new(Self::compile(&arr[1])?))
392                    } else {
393                        None
394                    };
395                    Ok(CompiledLogic::Round(Box::new(Self::compile(&arr[0])?), decimals))
396                } else {
397                    Ok(CompiledLogic::Round(Box::new(Self::compile(args)?), None))
398                }
399            }
400            "roundup" | "ROUNDUP" => {
401                if let Value::Array(arr) = args {
402                    let decimals = if arr.len() > 1 {
403                        Some(Box::new(Self::compile(&arr[1])?))
404                    } else {
405                        None
406                    };
407                    Ok(CompiledLogic::RoundUp(Box::new(Self::compile(&arr[0])?), decimals))
408                } else {
409                    Ok(CompiledLogic::RoundUp(Box::new(Self::compile(args)?), None))
410                }
411            }
412            "rounddown" | "ROUNDDOWN" => {
413                if let Value::Array(arr) = args {
414                    let decimals = if arr.len() > 1 {
415                        Some(Box::new(Self::compile(&arr[1])?))
416                    } else {
417                        None
418                    };
419                    Ok(CompiledLogic::RoundDown(Box::new(Self::compile(&arr[0])?), decimals))
420                } else {
421                    Ok(CompiledLogic::RoundDown(Box::new(Self::compile(args)?), None))
422                }
423            }
424            "ceiling" | "CEILING" => {
425                if let Value::Array(arr) = args {
426                    let significance = if arr.len() > 1 {
427                        Some(Box::new(Self::compile(&arr[1])?))
428                    } else {
429                        None
430                    };
431                    Ok(CompiledLogic::Ceiling(Box::new(Self::compile(&arr[0])?), significance))
432                } else {
433                    Ok(CompiledLogic::Ceiling(Box::new(Self::compile(args)?), None))
434                }
435            }
436            "floor" | "FLOOR" => {
437                if let Value::Array(arr) = args {
438                    let significance = if arr.len() > 1 {
439                        Some(Box::new(Self::compile(&arr[1])?))
440                    } else {
441                        None
442                    };
443                    Ok(CompiledLogic::Floor(Box::new(Self::compile(&arr[0])?), significance))
444                } else {
445                    Ok(CompiledLogic::Floor(Box::new(Self::compile(args)?), None))
446                }
447            }
448            "trunc" | "TRUNC" => {
449                if let Value::Array(arr) = args {
450                    let decimals = if arr.len() > 1 {
451                        Some(Box::new(Self::compile(&arr[1])?))
452                    } else {
453                        None
454                    };
455                    Ok(CompiledLogic::Trunc(Box::new(Self::compile(&arr[0])?), decimals))
456                } else {
457                    Ok(CompiledLogic::Trunc(Box::new(Self::compile(args)?), None))
458                }
459            }
460            "mround" | "MROUND" => Self::compile_binary(args, |a, b| CompiledLogic::Mround(a, b)),
461            
462            // Custom operators - String
463            "length" => Ok(CompiledLogic::Length(Box::new(Self::compile(args)?))),
464            "len" | "LEN" => Ok(CompiledLogic::Len(Box::new(Self::compile(args)?))),
465            "search" | "SEARCH" => {
466                let arr = args.as_array().ok_or("search requires array")?;
467                if arr.len() < 2 {
468                    return Err("search requires at least 2 arguments".to_string());
469                }
470                let start_num = if arr.len() > 2 {
471                    Some(Box::new(Self::compile(&arr[2])?))
472                } else {
473                    None
474                };
475                Ok(CompiledLogic::Search(
476                    Box::new(Self::compile(&arr[0])?),
477                    Box::new(Self::compile(&arr[1])?),
478                    start_num,
479                ))
480            }
481            "left" | "LEFT" => {
482                if let Value::Array(arr) = args {
483                    let num_chars = if arr.len() > 1 {
484                        Some(Box::new(Self::compile(&arr[1])?))
485                    } else {
486                        None
487                    };
488                    Ok(CompiledLogic::Left(Box::new(Self::compile(&arr[0])?), num_chars))
489                } else {
490                    Ok(CompiledLogic::Left(Box::new(Self::compile(args)?), None))
491                }
492            }
493            "right" | "RIGHT" => {
494                if let Value::Array(arr) = args {
495                    let num_chars = if arr.len() > 1 {
496                        Some(Box::new(Self::compile(&arr[1])?))
497                    } else {
498                        None
499                    };
500                    Ok(CompiledLogic::Right(Box::new(Self::compile(&arr[0])?), num_chars))
501                } else {
502                    Ok(CompiledLogic::Right(Box::new(Self::compile(args)?), None))
503                }
504            }
505            "mid" | "MID" => {
506                let arr = args.as_array().ok_or("mid requires array")?;
507                if arr.len() < 3 {
508                    return Err("mid requires 3 arguments".to_string());
509                }
510                Ok(CompiledLogic::Mid(
511                    Box::new(Self::compile(&arr[0])?),
512                    Box::new(Self::compile(&arr[1])?),
513                    Box::new(Self::compile(&arr[2])?),
514                ))
515            }
516            "splittext" | "SPLITTEXT" => {
517                let arr = args.as_array().ok_or("splittext requires array")?;
518                if arr.len() < 2 {
519                    return Err("splittext requires at least 2 arguments".to_string());
520                }
521                let index = if arr.len() > 2 {
522                    Some(Box::new(Self::compile(&arr[2])?))
523                } else {
524                    None
525                };
526                Ok(CompiledLogic::SplitText(
527                    Box::new(Self::compile(&arr[0])?),
528                    Box::new(Self::compile(&arr[1])?),
529                    index,
530                ))
531            }
532            "concat" | "CONCAT" => {
533                let arr = args.as_array().ok_or("concat requires array")?;
534                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
535                Ok(CompiledLogic::Concat(compiled?))
536            }
537            "splitvalue" | "SPLITVALUE" => Self::compile_binary(args, |a, b| CompiledLogic::SplitValue(a, b)),
538            "stringformat" | "STRINGFORMAT" => {
539                let arr = args.as_array().ok_or("stringformat requires array")?;
540                if arr.is_empty() {
541                    return Err("stringformat requires at least 1 argument".to_string());
542                }
543                let decimals = if arr.len() > 1 {
544                    Some(Box::new(Self::compile(&arr[1])?))
545                } else {
546                    None
547                };
548                let prefix = if arr.len() > 2 {
549                    Some(Box::new(Self::compile(&arr[2])?))
550                } else {
551                    None
552                };
553                let suffix = if arr.len() > 3 {
554                    Some(Box::new(Self::compile(&arr[3])?))
555                } else {
556                    None
557                };
558                let thousands_sep = if arr.len() > 4 {
559                    Some(Box::new(Self::compile(&arr[4])?))
560                } else {
561                    None
562                };
563                Ok(CompiledLogic::StringFormat(
564                    Box::new(Self::compile(&arr[0])?),
565                    decimals,
566                    prefix,
567                    suffix,
568                    thousands_sep,
569                ))
570            }
571            
572            // Custom operators - Logical
573            "xor" => Self::compile_binary(args, |a, b| CompiledLogic::Xor(a, b)),
574            "ifnull" | "IFNULL" => Self::compile_binary(args, |a, b| CompiledLogic::IfNull(a, b)),
575            "isempty" | "ISEMPTY" => Ok(CompiledLogic::IsEmpty(Box::new(Self::compile(args)?))),
576            "empty" | "EMPTY" => Ok(CompiledLogic::Empty),
577            
578            // Custom operators - Date
579            "today" | "TODAY" => Ok(CompiledLogic::Today),
580            "now" | "NOW" => Ok(CompiledLogic::Now),
581            "days" | "DAYS" => Self::compile_binary(args, |a, b| CompiledLogic::Days(a, b)),
582            "year" | "YEAR" => Ok(CompiledLogic::Year(Box::new(Self::compile(args)?))),
583            "month" | "MONTH" => Ok(CompiledLogic::Month(Box::new(Self::compile(args)?))),
584            "day" | "DAY" => Ok(CompiledLogic::Day(Box::new(Self::compile(args)?))),
585            "date" | "DATE" => {
586                let arr = args.as_array().ok_or("date requires array")?;
587                if arr.len() < 3 {
588                    return Err("date requires 3 arguments".to_string());
589                }
590                Ok(CompiledLogic::Date(
591                    Box::new(Self::compile(&arr[0])?),
592                    Box::new(Self::compile(&arr[1])?),
593                    Box::new(Self::compile(&arr[2])?),
594                ))
595            }
596            "dateformat" | "DATEFORMAT" => {
597                if let Value::Array(arr) = args {
598                    if arr.is_empty() {
599                        return Err("dateformat requires at least 1 argument".to_string());
600                    }
601                    let format = if arr.len() > 1 {
602                        Some(Box::new(Self::compile(&arr[1])?))
603                    } else {
604                        None
605                    };
606                    Ok(CompiledLogic::DateFormat(Box::new(Self::compile(&arr[0])?), format))
607                } else {
608                    Ok(CompiledLogic::DateFormat(Box::new(Self::compile(args)?), None))
609                }
610            }
611            
612            // Custom operators - Array/Table
613            "sum" | "SUM" => {
614                if let Value::Array(arr) = args {
615                    if arr.is_empty() {
616                        return Err("sum requires at least 1 argument".to_string());
617                    }
618                    let field = if arr.len() > 1 {
619                        Some(Box::new(Self::compile(&arr[1])?))
620                    } else {
621                        None
622                    };
623                    let threshold = if arr.len() > 2 {
624                        Some(Box::new(Self::compile(&arr[2])?))
625                    } else {
626                        None
627                    };
628                    Ok(CompiledLogic::Sum(Box::new(Self::compile(&arr[0])?), field, threshold))
629                } else {
630                    Ok(CompiledLogic::Sum(Box::new(Self::compile(args)?), None, None))
631                }
632            }
633            "FOR" => {
634                let arr = args.as_array().ok_or("FOR requires array")?;
635                if arr.len() < 3 {
636                    return Err("FOR requires 3 arguments: start, end, logic".to_string());
637                }
638                Ok(CompiledLogic::For(
639                    Box::new(Self::compile(&arr[0])?),
640                    Box::new(Self::compile(&arr[1])?),
641                    Box::new(Self::compile(&arr[2])?),
642                ))
643            }
644            
645            // Complex table operations
646            "VALUEAT" => {
647                let arr = args.as_array().ok_or("VALUEAT requires array")?;
648                if arr.len() < 2 {
649                    return Err("VALUEAT requires at least 2 arguments".to_string());
650                }
651                let col_name = if arr.len() > 2 {
652                    Some(Box::new(Self::compile(&arr[2])?))
653                } else {
654                    None
655                };
656                Ok(CompiledLogic::ValueAt(
657                    Box::new(Self::compile(&arr[0])?),
658                    Box::new(Self::compile(&arr[1])?),
659                    col_name,
660                ))
661            }
662            "MAXAT" => Self::compile_binary(args, |a, b| CompiledLogic::MaxAt(a, b)),
663            "INDEXAT" => {
664                let arr = args.as_array().ok_or("INDEXAT requires array")?;
665                if arr.len() < 3 {
666                    return Err("INDEXAT requires at least 3 arguments".to_string());
667                }
668                let range = if arr.len() > 3 {
669                    Some(Box::new(Self::compile(&arr[3])?))
670                } else {
671                    None
672                };
673                Ok(CompiledLogic::IndexAt(
674                    Box::new(Self::compile(&arr[0])?),
675                    Box::new(Self::compile(&arr[1])?),
676                    Box::new(Self::compile(&arr[2])?),
677                    range,
678                ))
679            }
680            "MATCH" => {
681                let arr = args.as_array().ok_or("MATCH requires array")?;
682                if arr.is_empty() {
683                    return Err("MATCH requires at least 1 argument".to_string());
684                }
685                let table = Box::new(Self::compile(&arr[0])?);
686                let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
687                Ok(CompiledLogic::Match(table, conditions?))
688            }
689            "MATCHRANGE" => {
690                let arr = args.as_array().ok_or("MATCHRANGE requires array")?;
691                if arr.is_empty() {
692                    return Err("MATCHRANGE requires at least 1 argument".to_string());
693                }
694                let table = Box::new(Self::compile(&arr[0])?);
695                let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
696                Ok(CompiledLogic::MatchRange(table, conditions?))
697            }
698            "CHOOSE" => {
699                let arr = args.as_array().ok_or("CHOOSE requires array")?;
700                if arr.is_empty() {
701                    return Err("CHOOSE requires at least 1 argument".to_string());
702                }
703                let table = Box::new(Self::compile(&arr[0])?);
704                let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
705                Ok(CompiledLogic::Choose(table, conditions?))
706            }
707            "FINDINDEX" => {
708                let arr = args.as_array().ok_or("FINDINDEX requires array")?;
709                if arr.len() < 2 {
710                    return Err("FINDINDEX requires at least 2 arguments".to_string());
711                }
712                let table = Box::new(Self::compile(&arr[0])?);
713                // CRITICAL: Convert string literals to var references in conditions
714                // This allows ergonomic syntax: "INSAGE" instead of {"var": "INSAGE"}
715                let conditions: Result<Vec<_>, _> = arr[1..]
716                    .iter()
717                    .map(|cond| Self::compile(&Self::preprocess_table_condition(cond)))
718                    .collect();
719                Ok(CompiledLogic::FindIndex(table, conditions?))
720            }
721            
722            // Array operations
723            "MULTIPLIES" => {
724                let arr = args.as_array().ok_or("MULTIPLIES requires array")?;
725                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
726                Ok(CompiledLogic::Multiplies(compiled?))
727            }
728            "DIVIDES" => {
729                let arr = args.as_array().ok_or("DIVIDES requires array")?;
730                let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
731                Ok(CompiledLogic::Divides(compiled?))
732            }
733            
734            // Advanced date functions
735            "YEARFRAC" => {
736                let arr = args.as_array().ok_or("YEARFRAC requires array")?;
737                if arr.len() < 2 {
738                    return Err("YEARFRAC requires at least 2 arguments".to_string());
739                }
740                let basis = if arr.len() > 2 {
741                    Some(Box::new(Self::compile(&arr[2])?))
742                } else {
743                    None
744                };
745                Ok(CompiledLogic::YearFrac(
746                    Box::new(Self::compile(&arr[0])?),
747                    Box::new(Self::compile(&arr[1])?),
748                    basis,
749                ))
750            }
751            "DATEDIF" => {
752                let arr = args.as_array().ok_or("DATEDIF requires array")?;
753                if arr.len() < 3 {
754                    return Err("DATEDIF requires 3 arguments".to_string());
755                }
756                Ok(CompiledLogic::DateDif(
757                    Box::new(Self::compile(&arr[0])?),
758                    Box::new(Self::compile(&arr[1])?),
759                    Box::new(Self::compile(&arr[2])?),
760                ))
761            }
762            
763            // UI helpers
764            "RANGEOPTIONS" => Self::compile_binary(args, |a, b| CompiledLogic::RangeOptions(a, b)),
765            "MAPOPTIONS" => {
766                let arr = args.as_array().ok_or("MAPOPTIONS requires array")?;
767                if arr.len() < 3 {
768                    return Err("MAPOPTIONS requires 3 arguments".to_string());
769                }
770                Ok(CompiledLogic::MapOptions(
771                    Box::new(Self::compile(&arr[0])?),
772                    Box::new(Self::compile(&arr[1])?),
773                    Box::new(Self::compile(&arr[2])?),
774                ))
775            }
776            "MAPOPTIONSIF" => {
777                let arr = args.as_array().ok_or("MAPOPTIONSIF requires array")?;
778                if arr.len() < 4 {
779                    return Err("MAPOPTIONSIF requires at least 4 arguments".to_string());
780                }
781                let table = Box::new(Self::compile(&arr[0])?);
782                let field_label = Box::new(Self::compile(&arr[1])?);
783                let field_value = Box::new(Self::compile(&arr[2])?);
784                
785                // Handle triplet syntax: value, operator, field -> {operator: [value, {var: field}]}
786                let condition_args = &arr[3..];
787                let mut conditions = Vec::new();
788                
789                let mut i = 0;
790                while i + 2 < condition_args.len() {
791                    let value = &condition_args[i];
792                    let operator = &condition_args[i + 1];
793                    let field = &condition_args[i + 2];
794                    
795                    if let Value::String(op) = operator {
796                        // Create comparison: {op: [value, {var: field}]}
797                        let field_var = if let Value::String(f) = field {
798                            serde_json::json!({"var": f})
799                        } else {
800                            field.clone()
801                        };
802                        
803                        let comparison = serde_json::json!({
804                            op: [value.clone(), field_var]
805                        });
806                        
807                        conditions.push(Self::compile(&comparison)?);
808                    }
809                    
810                    i += 3;
811                }
812                
813                // Handle any remaining individual conditions
814                while i < condition_args.len() {
815                    conditions.push(Self::compile(&Self::preprocess_table_condition(&condition_args[i]))?);
816                    i += 1;
817                }
818                
819                Ok(CompiledLogic::MapOptionsIf(table, field_label, field_value, conditions))
820            }
821            "return" => Ok(CompiledLogic::Return(Box::new(args.clone()))),
822            
823            _ => Err(format!("Unknown operator: {}", op)),
824        }
825    }
826    
827    fn compile_binary<F>(args: &Value, f: F) -> Result<Self, String>
828    where
829        F: FnOnce(Box<CompiledLogic>, Box<CompiledLogic>) -> CompiledLogic,
830    {
831        let arr = args.as_array().ok_or("Binary operator requires array")?;
832        if arr.len() != 2 {
833            return Err("Binary operator requires exactly 2 arguments".to_string());
834        }
835        Ok(f(
836            Box::new(Self::compile(&arr[0])?),
837            Box::new(Self::compile(&arr[1])?),
838        ))
839    }
840    
841    /// Preprocess table condition to convert string literals to var references
842    /// This allows ergonomic syntax in FINDINDEX/MATCH/CHOOSE conditions
843    /// 
844    /// Handles formats:
845    /// - Comparison triplets: ["==", value, "col"] -> {"==": [value, {"var": "col"}]}
846    /// - Logical operators: ["&&", cond1, cond2] -> {"and": [cond1, cond2]}
847    /// - String field names: "col" -> {"var": "col"}
848    fn preprocess_table_condition(value: &Value) -> Value {
849        match value {
850            Value::String(s) => {
851                // Convert standalone strings to var references
852                serde_json::json!({"var": s})
853            }
854            Value::Array(arr) => {
855                // Check if this is an operator in array shorthand format
856                if !arr.is_empty() {
857                    if let Some(op_str) = arr[0].as_str() {
858                        // Check for comparison operators: [op, value, col]
859                        let is_comparison = matches!(op_str, "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">=");
860                        
861                        if is_comparison && arr.len() >= 3 {
862                            // Comparison triplet: [op, value, col] -> {op: [col_var, value]}
863                            // Evaluates as: row[col] op value
864                            // DON'T preprocess the value (2nd arg) - keep it as-is
865                            let value_arg = arr[1].clone();
866                            
867                            // Only convert the column name (3rd arg) to var reference if it's a string
868                            let col_arg = if let Value::String(col) = &arr[2] {
869                                // Convert column name string to var reference
870                                serde_json::json!({"var": col})
871                            } else {
872                                // If it's not a string, preprocess it (could be nested expression)
873                                Self::preprocess_table_condition(&arr[2])
874                            };
875                            
876                            // Order matters: {op: [col_var, value]} means row[col] op value
877                            let mut obj = serde_json::Map::new();
878                            obj.insert(op_str.to_string(), Value::Array(vec![col_arg, value_arg]));
879                            return Value::Object(obj);
880                        }
881                        
882                        // Check for logical operators: [op, arg1, arg2, ...]
883                        let canonical_op = match op_str {
884                            "&&" => Some("and"),
885                            "||" => Some("or"),
886                            "and" | "or" | "!" | "not" | "if" => Some(op_str),
887                            _ => None,
888                        };
889                        
890                        if let Some(op_name) = canonical_op {
891                            // Convert ["op", arg1, arg2, ...] to {"op": [arg1, arg2, ...]}
892                            let args: Vec<Value> = arr[1..].iter()
893                                .map(Self::preprocess_table_condition)
894                                .collect();
895                            let mut obj = serde_json::Map::new();
896                            obj.insert(op_name.to_string(), Value::Array(args));
897                            return Value::Object(obj);
898                        }
899                    }
900                }
901                // Regular array - recursively process elements
902                Value::Array(arr.iter().map(Self::preprocess_table_condition).collect())
903            }
904            Value::Object(obj) => {
905                // Recursively process object values, but preserve operators
906                let mut new_obj = serde_json::Map::new();
907                for (key, val) in obj {
908                    // Don't convert strings inside $ref, var, or other special operators
909                    if key == "$ref" || key == "ref" || key == "var" {
910                        new_obj.insert(key.clone(), val.clone());
911                    } else {
912                        new_obj.insert(key.clone(), Self::preprocess_table_condition(val));
913                    }
914                }
915                Value::Object(new_obj)
916            }
917            _ => value.clone(),
918        }
919    }
920    
921    /// Check if this is a simple reference that doesn't need caching
922    pub fn is_simple_ref(&self) -> bool {
923        matches!(self, CompiledLogic::Ref(_, None) | CompiledLogic::Var(_, None))
924    }
925    
926    /// Extract all variable names referenced in this logic
927    pub fn referenced_vars(&self) -> Vec<String> {
928        let mut vars = Vec::new();
929        self.collect_vars(&mut vars);
930        vars.sort();
931        vars.dedup();
932        vars
933    }
934    
935    /// Flatten nested And operations only
936    fn flatten_and(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
937        let mut flattened = Vec::new();
938        for item in items {
939            match item {
940                CompiledLogic::And(nested) => {
941                    // Recursively flatten nested And operations
942                    flattened.extend(Self::flatten_and(nested));
943                }
944                _ => flattened.push(item),
945            }
946        }
947        flattened
948    }
949    
950    /// Flatten nested Or operations only
951    fn flatten_or(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
952        let mut flattened = Vec::new();
953        for item in items {
954            match item {
955                CompiledLogic::Or(nested) => {
956                    // Recursively flatten nested Or operations
957                    flattened.extend(Self::flatten_or(nested));
958                }
959                _ => flattened.push(item),
960            }
961        }
962        flattened
963    }
964    
965    /// Flatten nested Add operations only
966    fn flatten_add(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
967        let mut flattened = Vec::new();
968        for item in items {
969            match item {
970                CompiledLogic::Add(nested) => {
971                    // Recursively flatten nested Adds
972                    flattened.extend(Self::flatten_add(nested));
973                }
974                _ => flattened.push(item),
975            }
976        }
977        flattened
978    }
979    
980    /// Flatten nested Multiply operations only
981    fn flatten_multiply(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
982        let mut flattened = Vec::new();
983        for item in items {
984            match item {
985                CompiledLogic::Multiply(nested) => {
986                    // Recursively flatten nested Multiplies
987                    flattened.extend(Self::flatten_multiply(nested));
988                }
989                _ => flattened.push(item),
990            }
991        }
992        flattened
993    }
994    
995    /// Flatten nested Cat (concatenation) operations
996    /// Combines nested Cat operations into a single flat operation
997    fn flatten_cat(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
998        let mut flattened = Vec::new();
999        for item in items {
1000            match &item {
1001                CompiledLogic::Cat(nested) => {
1002                    // Recursively flatten nested Cat operations
1003                    flattened.extend(Self::flatten_cat(nested.clone()));
1004                }
1005                _ => flattened.push(item),
1006            }
1007        }
1008        flattened
1009    }
1010    
1011    /// Check if this logic contains forward references (e.g., VALUEAT with $iteration + N where N > 0)
1012    /// Returns true if it references future iterations in a table
1013    pub fn has_forward_reference(&self) -> bool {
1014        let result = self.check_forward_reference();
1015        result
1016    }
1017    
1018    fn check_forward_reference(&self) -> bool {        
1019        match self {
1020            // VALUEAT with $iteration arithmetic
1021            CompiledLogic::ValueAt(table, idx_logic, col_name) => {
1022                // Check if index contains $iteration + positive_number
1023                let has_fwd = idx_logic.contains_iteration_plus_positive();
1024                if has_fwd {
1025                    return true;
1026                }
1027                // Recursively check all parameters
1028                let table_fwd = table.check_forward_reference();
1029                let idx_fwd = idx_logic.check_forward_reference();
1030                let col_fwd = col_name.as_ref().map(|c| c.check_forward_reference()).unwrap_or(false);
1031                table_fwd || idx_fwd || col_fwd
1032            }
1033            // Recursively check compound operations
1034            CompiledLogic::Array(arr) => {
1035                arr.iter().any(|item| item.check_forward_reference())
1036            }
1037            CompiledLogic::And(items) | CompiledLogic::Or(items) 
1038            | CompiledLogic::Add(items) | CompiledLogic::Subtract(items)
1039            | CompiledLogic::Multiply(items) | CompiledLogic::Divide(items)
1040            | CompiledLogic::Merge(items) | CompiledLogic::Cat(items)
1041            | CompiledLogic::Max(items) | CompiledLogic::Min(items)
1042            | CompiledLogic::Concat(items) | CompiledLogic::Multiplies(items)
1043            | CompiledLogic::Divides(items) => {
1044                items.iter().any(|item| item.check_forward_reference())
1045            }
1046            CompiledLogic::Not(a) | CompiledLogic::Abs(a)
1047            | CompiledLogic::Length(a) | CompiledLogic::Len(a) | CompiledLogic::IsEmpty(a)
1048            | CompiledLogic::Year(a) | CompiledLogic::Month(a) | CompiledLogic::Day(a) => a.check_forward_reference(),
1049            CompiledLogic::Round(a, decimals) | CompiledLogic::RoundUp(a, decimals) | CompiledLogic::RoundDown(a, decimals)
1050            | CompiledLogic::Ceiling(a, decimals) | CompiledLogic::Floor(a, decimals) | CompiledLogic::Trunc(a, decimals)
1051            | CompiledLogic::DateFormat(a, decimals) => {
1052                a.check_forward_reference() || decimals.as_ref().map_or(false, |d| d.check_forward_reference())
1053            }
1054            CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1055                a.check_forward_reference() 
1056                || decimals.as_ref().map_or(false, |d| d.check_forward_reference())
1057                || prefix.as_ref().map_or(false, |p| p.check_forward_reference())
1058                || suffix.as_ref().map_or(false, |s| s.check_forward_reference())
1059                || sep.as_ref().map_or(false, |s| s.check_forward_reference())
1060            }
1061            CompiledLogic::Mround(a, b) => a.check_forward_reference() || b.check_forward_reference(),
1062            CompiledLogic::Return(_) => false, // Raw values don't have forward references
1063            CompiledLogic::If(cond, then_val, else_val) => {
1064                cond.check_forward_reference() || then_val.check_forward_reference() || else_val.check_forward_reference()
1065            }
1066            CompiledLogic::Equal(a, b) | CompiledLogic::StrictEqual(a, b)
1067            | CompiledLogic::NotEqual(a, b) | CompiledLogic::StrictNotEqual(a, b)
1068            | CompiledLogic::LessThan(a, b) | CompiledLogic::LessThanOrEqual(a, b)
1069            | CompiledLogic::GreaterThan(a, b) | CompiledLogic::GreaterThanOrEqual(a, b)
1070            | CompiledLogic::Modulo(a, b) | CompiledLogic::Power(a, b)
1071            | CompiledLogic::Map(a, b) | CompiledLogic::Filter(a, b) 
1072            | CompiledLogic::All(a, b) | CompiledLogic::Some(a, b) 
1073            | CompiledLogic::None(a, b) | CompiledLogic::In(a, b) 
1074            | CompiledLogic::Pow(a, b) | CompiledLogic::Xor(a, b) 
1075            | CompiledLogic::IfNull(a, b) | CompiledLogic::Days(a, b) 
1076            | CompiledLogic::SplitValue(a, b) | CompiledLogic::MaxAt(a, b) 
1077            | CompiledLogic::RangeOptions(a, b) => {
1078                a.check_forward_reference() || b.check_forward_reference()
1079            }
1080            CompiledLogic::Reduce(a, b, c) | CompiledLogic::Mid(a, b, c)
1081            | CompiledLogic::Date(a, b, c) | CompiledLogic::DateDif(a, b, c)
1082            | CompiledLogic::MapOptions(a, b, c) | CompiledLogic::For(a, b, c) => {
1083                a.check_forward_reference() || b.check_forward_reference() || c.check_forward_reference()
1084            }
1085            CompiledLogic::Substr(s, start, len) | CompiledLogic::Search(s, start, len)
1086            | CompiledLogic::SplitText(s, start, len) | CompiledLogic::YearFrac(s, start, len) => {
1087                s.check_forward_reference() || start.check_forward_reference() 
1088                || len.as_ref().map(|l| l.check_forward_reference()).unwrap_or(false)
1089            }
1090            CompiledLogic::Left(a, opt) | CompiledLogic::Right(a, opt) => {
1091                a.check_forward_reference() || opt.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1092            }
1093            CompiledLogic::Sum(a, opt1, opt2) => {
1094                a.check_forward_reference() 
1095                || opt1.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1096                || opt2.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1097            }
1098            CompiledLogic::IndexAt(a, b, c, opt) => {
1099                a.check_forward_reference() || b.check_forward_reference() 
1100                || c.check_forward_reference() || opt.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1101            }
1102            CompiledLogic::Match(table, conds) | CompiledLogic::MatchRange(table, conds)
1103            | CompiledLogic::Choose(table, conds) | CompiledLogic::FindIndex(table, conds) => {
1104                table.check_forward_reference() || conds.iter().any(|c| c.check_forward_reference())
1105            }
1106            CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1107                table.check_forward_reference() || label.check_forward_reference() 
1108                || value.check_forward_reference() || conds.iter().any(|c| c.check_forward_reference())
1109            }
1110            CompiledLogic::MissingSome(min, _) => {
1111                min.check_forward_reference()
1112            }
1113            _ => false,
1114        }
1115    }
1116    
1117    /// Check if logic contains $iteration + positive_number pattern
1118    fn contains_iteration_plus_positive(&self) -> bool {
1119        match self {
1120            CompiledLogic::Add(items) => {
1121                // Check if one operand references $iteration and another is a positive number literal
1122                let has_iteration = items.iter().any(|item| {
1123                    item.referenced_vars().iter().any(|v| v == "$iteration")
1124                });
1125
1126                let has_positive = items.iter().any(|item| match item {
1127                    CompiledLogic::Number(n) => {
1128                        n.parse::<f64>().unwrap_or(0.0) > 0.0
1129                    },
1130                    _ => false,
1131                });
1132
1133                let result = has_iteration && has_positive;
1134                result
1135            }
1136            _ => false,
1137        }
1138    }
1139    
1140    /// Normalize JSON Schema reference path to dot notation
1141    /// Handles: #/schema/path, #/properties/field, /properties/field, field.path
1142    /// Trims /properties/ and .properties. segments
1143    fn normalize_ref_path(path: &str) -> String {
1144        let mut normalized = path.to_string();
1145        
1146        // Remove leading #/ if present
1147        if normalized.starts_with("#/") {
1148            normalized = normalized[2..].to_string();
1149        } else if normalized.starts_with('/') {
1150            normalized = normalized[1..].to_string();
1151        }
1152        
1153        // Replace / with . for JSON pointer notation
1154        normalized = normalized.replace('/', ".");
1155        
1156        // Remove /properties/ or .properties. segments
1157        normalized = normalized.replace("properties.", "");
1158        normalized = normalized.replace(".properties.", ".");
1159        
1160        // Clean up any double dots
1161        while normalized.contains("..") {
1162            normalized = normalized.replace("..", ".");
1163        }
1164        
1165        // Remove leading/trailing dots
1166        normalized = normalized.trim_matches('.').to_string();
1167        
1168        normalized
1169    }
1170    
1171    pub fn collect_vars(&self, vars: &mut Vec<String>) {
1172        match self {
1173            CompiledLogic::Var(name, default) => {
1174                vars.push(name.clone());
1175                if let Some(def) = default {
1176                    def.collect_vars(vars);
1177                }
1178            }
1179            CompiledLogic::Ref(path, default) => {
1180                // Normalize the path and add it
1181                vars.push(Self::normalize_ref_path(path));
1182                if let Some(def) = default {
1183                    def.collect_vars(vars);
1184                }
1185            }
1186            CompiledLogic::Array(arr) => {
1187                for item in arr {
1188                    item.collect_vars(vars);
1189                }
1190            }
1191            CompiledLogic::And(items) | CompiledLogic::Or(items) 
1192            | CompiledLogic::Add(items) | CompiledLogic::Subtract(items)
1193            | CompiledLogic::Multiply(items) | CompiledLogic::Divide(items)
1194            | CompiledLogic::Merge(items) | CompiledLogic::Cat(items)
1195            | CompiledLogic::Max(items) | CompiledLogic::Min(items)
1196            | CompiledLogic::Concat(items) => {
1197                for item in items {
1198                    item.collect_vars(vars);
1199                }
1200            }
1201            CompiledLogic::Not(a) | CompiledLogic::Abs(a)
1202            | CompiledLogic::Length(a) | CompiledLogic::Len(a) | CompiledLogic::IsEmpty(a)
1203            | CompiledLogic::Year(a) | CompiledLogic::Month(a) | CompiledLogic::Day(a) => {
1204                a.collect_vars(vars);
1205            }
1206            CompiledLogic::Round(a, decimals) | CompiledLogic::RoundUp(a, decimals) | CompiledLogic::RoundDown(a, decimals)
1207            | CompiledLogic::Ceiling(a, decimals) | CompiledLogic::Floor(a, decimals) | CompiledLogic::Trunc(a, decimals)
1208            | CompiledLogic::DateFormat(a, decimals) => {
1209                a.collect_vars(vars);
1210                if let Some(d) = decimals {
1211                    d.collect_vars(vars);
1212                }
1213            }
1214            CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1215                a.collect_vars(vars);
1216                if let Some(d) = decimals { d.collect_vars(vars); }
1217                if let Some(p) = prefix { p.collect_vars(vars); }
1218                if let Some(s) = suffix { s.collect_vars(vars); }
1219                if let Some(s) = sep { s.collect_vars(vars); }
1220            }
1221            CompiledLogic::Mround(a, b) => {
1222                a.collect_vars(vars);
1223                b.collect_vars(vars);
1224            }
1225            CompiledLogic::Return(_) => {} // Raw values don't contain vars
1226            CompiledLogic::If(cond, then_val, else_val) => {
1227                cond.collect_vars(vars);
1228                then_val.collect_vars(vars);
1229                else_val.collect_vars(vars);
1230            }
1231            CompiledLogic::Equal(a, b) | CompiledLogic::StrictEqual(a, b)
1232            | CompiledLogic::NotEqual(a, b) | CompiledLogic::StrictNotEqual(a, b)
1233            | CompiledLogic::LessThan(a, b) | CompiledLogic::LessThanOrEqual(a, b)
1234            | CompiledLogic::GreaterThan(a, b) | CompiledLogic::GreaterThanOrEqual(a, b)
1235            | CompiledLogic::Modulo(a, b) | CompiledLogic::Power(a, b)
1236            | CompiledLogic::Map(a, b) | CompiledLogic::Filter(a, b) 
1237            | CompiledLogic::All(a, b) | CompiledLogic::Some(a, b) 
1238            | CompiledLogic::None(a, b) | CompiledLogic::In(a, b) 
1239            | CompiledLogic::Pow(a, b) | CompiledLogic::Xor(a, b) 
1240            | CompiledLogic::IfNull(a, b) | CompiledLogic::Days(a, b) 
1241            | CompiledLogic::SplitValue(a, b) | CompiledLogic::MaxAt(a, b) 
1242            | CompiledLogic::RangeOptions(a, b) => {
1243                a.collect_vars(vars);
1244                b.collect_vars(vars);
1245            }
1246            CompiledLogic::Reduce(a, b, c) | CompiledLogic::Mid(a, b, c)
1247            | CompiledLogic::Date(a, b, c) | CompiledLogic::DateDif(a, b, c)
1248            | CompiledLogic::MapOptions(a, b, c) | CompiledLogic::For(a, b, c) => {
1249                a.collect_vars(vars);
1250                b.collect_vars(vars);
1251                c.collect_vars(vars);
1252            }
1253            CompiledLogic::Substr(s, start, len) | CompiledLogic::Search(s, start, len)
1254            | CompiledLogic::SplitText(s, start, len) | CompiledLogic::YearFrac(s, start, len) => {
1255                s.collect_vars(vars);
1256                start.collect_vars(vars);
1257                if let Some(l) = len {
1258                    l.collect_vars(vars);
1259                }
1260            }
1261            CompiledLogic::Left(a, opt) | CompiledLogic::Right(a, opt)
1262            | CompiledLogic::ValueAt(a, _, opt) => {
1263                a.collect_vars(vars);
1264                if let Some(o) = opt {
1265                    o.collect_vars(vars);
1266                }
1267            }
1268            CompiledLogic::Sum(a, opt1, opt2) => {
1269                a.collect_vars(vars);
1270                if let Some(o) = opt1 {
1271                    o.collect_vars(vars);
1272                }
1273                if let Some(o) = opt2 {
1274                    o.collect_vars(vars);
1275                }
1276            }
1277            CompiledLogic::IndexAt(a, b, c, opt) => {
1278                a.collect_vars(vars);
1279                b.collect_vars(vars);
1280                c.collect_vars(vars);
1281                if let Some(o) = opt {
1282                    o.collect_vars(vars);
1283                }
1284            }
1285            CompiledLogic::Match(table, conds) | CompiledLogic::MatchRange(table, conds)
1286            | CompiledLogic::Choose(table, conds) | CompiledLogic::FindIndex(table, conds) => {
1287                table.collect_vars(vars);
1288                for cond in conds {
1289                    cond.collect_vars(vars);
1290                }
1291            }
1292            CompiledLogic::Multiplies(items) | CompiledLogic::Divides(items) => {
1293                for item in items {
1294                    item.collect_vars(vars);
1295                }
1296            }
1297            CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1298                table.collect_vars(vars);
1299                label.collect_vars(vars);
1300                value.collect_vars(vars);
1301                for cond in conds {
1302                    cond.collect_vars(vars);
1303                }
1304            }
1305            CompiledLogic::MissingSome(min, _) => {
1306                min.collect_vars(vars);
1307            }
1308            _ => {}
1309        }
1310    }
1311}
1312
1313/// Storage for compiled logic expressions with dependency tracking
1314/// 
1315/// This store uses the global compiled logic cache to avoid recompiling
1316/// the same logic across different instances. Each instance maintains
1317/// its own local ID mapping to the global storage.
1318pub struct CompiledLogicStore {
1319    next_id: u64,
1320    store: AHashMap<LogicId, CompiledLogic>,
1321    dependencies: AHashMap<LogicId, Vec<String>>,
1322}
1323
1324impl CompiledLogicStore {
1325    pub fn new() -> Self {
1326        Self {
1327            next_id: 0,
1328            store: AHashMap::default(),
1329            dependencies: AHashMap::default(),
1330        }
1331    }
1332    
1333    /// Compile and store a JSON Logic expression
1334    /// 
1335    /// Uses global storage to avoid recompiling the same logic across instances.
1336    /// The logic is compiled once globally and reused, with this instance maintaining
1337    /// its own local ID for tracking dependencies.
1338    pub fn compile(&mut self, logic: &Value) -> Result<LogicId, String> {
1339        // Use global storage - compiles once and caches globally
1340        let _global_id = super::compiled_logic_store::compile_logic_value(logic)?;
1341        
1342        // Get the compiled logic from global store (O(1) lookup)
1343        let compiled = super::compiled_logic_store::get_compiled_logic(_global_id)
1344            .ok_or_else(|| "Failed to retrieve compiled logic from global store".to_string())?;
1345        
1346        // Track dependencies locally
1347        let deps = compiled.referenced_vars();
1348        
1349        // Assign local ID for this instance
1350        let id = LogicId(self.next_id);
1351        self.next_id += 1;
1352        
1353        // Store locally with instance-specific ID
1354        self.store.insert(id, compiled);
1355        self.dependencies.insert(id, deps);
1356        
1357        Ok(id)
1358    }
1359    
1360    /// Get a compiled logic by ID
1361    pub fn get(&self, id: &LogicId) -> Option<&CompiledLogic> {
1362        self.store.get(id)
1363    }
1364    
1365    /// Remove a compiled logic by ID
1366    pub fn remove(&mut self, id: &LogicId) -> Option<CompiledLogic> {
1367        self.dependencies.remove(id);
1368        self.store.remove(id)
1369    }
1370    
1371    /// Get dependencies for a logic ID
1372    pub fn get_dependencies(&self, id: &LogicId) -> Option<&[String]> {
1373        self.dependencies.get(id).map(|v| v.as_slice())
1374    }
1375}
1376
1377impl Default for CompiledLogicStore {
1378    fn default() -> Self {
1379        Self::new()
1380    }
1381}