Skip to main content

resolver/
lib.rs

1//! Eval is a powerful expression evaluator.
2//!
3//! Supported operators: `!` `!=` `""` `''` `()` `[]` `.` `,` `>` `<` `>=` `<=`
4//! `==` `+` `-` `*` `/` `%` `&&` `||` `n..m`.
5//!
6//! Built-in functions: `min()` `max()` `len()` `is_empty()` `array()`.
7//!
8//! ## Examples
9//!
10//! You can do mathematical calculations with supported operators:
11//!
12//! ```
13//! use resolver::{eval, to_value};
14//!
15//! assert_eq!(eval("1 + 2 + 3"), Ok(to_value(6)));
16//! assert_eq!(eval("2 * 2 + 3"), Ok(to_value(7)));
17//! assert_eq!(eval("2 / 2 + 3"), Ok(to_value(4.0)));
18//! assert_eq!(eval("2 / 2 + 3 / 3"), Ok(to_value(2.0)));
19//! ```
20//!
21//! You can eval with context:
22//!
23//! ```
24//! use resolver::{Expr, to_value};
25//!
26//! assert_eq!(Expr::new("foo == bar")
27//!                .value("foo", true)
28//!                .value("bar", true)
29//!                .exec(),
30//!            Ok(to_value(true)));
31//! ```
32//!
33//! You can access data like javascript by using `.` and `[]`. `[]` supports expression.
34//!
35//! ```
36//! use resolver::{Expr, to_value};
37//! use std::collections::HashMap;
38//!
39//! let mut object = HashMap::new();
40//! object.insert("foos", vec!["Hello", "world", "!"]);
41//!
42//! assert_eq!(Expr::new("object.foos[2-1] == 'world'") // Access field `foos` and index `2-1`
43//!                .value("object", object)
44//!                .exec(),
45//!            Ok(to_value(true)));
46//! ```
47//!
48//! You can eval with function:
49//!
50//! ```
51//! use resolver::{Expr, to_value};
52//!
53//! assert_eq!(Expr::new("say_hello()")
54//!                .function("say_hello", |_| Ok(to_value("Hello world!")))
55//!                .exec(),
56//!            Ok(to_value("Hello world!")));
57//! ```
58//!
59//! You can create an array with `array()`:
60//!
61//! ```
62//! use resolver::{eval, to_value};
63//!
64//! assert_eq!(eval("array(1, 2, 3, 4, 5)"), Ok(to_value(vec![1, 2, 3, 4, 5])));
65//! ```
66//!
67//! You can create an integer array with `n..m`:
68//!
69//! ```
70//! use resolver::{eval, to_value};
71//!
72//! assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
73//! ```
74//!
75//! ## Built-in functions
76//!
77//! ### min()
78//! Accept multiple arguments and return the minimum value.
79//!
80//! ### max()
81//! Accept multiple arguments and return the maximum value.
82//!
83//! ### len()
84//! Accept single arguments and return the length of value. Only accept String, Array, Object and Null.
85//!
86//! ### is_empty()
87//! Accept single arguments and return a boolean. Check whether the value is empty or not.
88//!
89//! ### array()
90//! Accept multiple arguments and return an array.
91//!
92//!
93#![recursion_limit="200"]
94#![deny(missing_docs)]
95
96#![forbid(unsafe_code)]
97#[macro_use]
98extern crate quick_error;
99extern crate serde;
100extern crate serde_json;
101
102mod math;
103mod function;
104mod operator;
105mod node;
106mod tree;
107mod error;
108mod builtin;
109mod expr;
110
111pub use expr::ExecOptions;
112use function::ConstFunction;
113pub use serde_json::Value;
114pub use error::Error;
115pub use function::Function;
116pub use expr::Expr;
117
118use std::{collections::HashMap, rc::Rc, cell::RefCell};
119use serde_json::to_value as json_to_value;
120use serde::Serialize;
121
122/// Convert variable to `serde_json::Value`
123pub fn to_value<S: Serialize>(v: S) -> Value {
124    json_to_value(v).unwrap()
125}
126
127/// Custom context.
128pub type Context = HashMap<String, Value>;
129/// Custom contexts. The value of the last context is searched first.
130pub type Contexts = Vec<Context>;
131/// Custom functions.
132pub type Functions = HashMap<String, Function>;
133/// Custom static function. Like fn
134pub type ConstFunctions = HashMap<String, ConstFunction>;
135
136/// Evaluates the value of an expression.
137pub fn eval(expr: &str) -> Result<Value, Error> {
138    Expr::new(expr).compile()?.exec()
139}
140
141type Compiled = Box<dyn Fn(&[Context], &Functions, Rc<RefCell<ConstFunctions>>) -> Result<Value, Error>>;
142
143#[cfg(test)]
144mod tests {
145    use std::collections::HashMap;
146    
147    use crate::to_value;
148    use crate::error::Error;
149    use crate::Expr;
150    use crate::tree::Tree;
151    use crate::Value;
152    use crate::eval;
153
154    #[test]
155    fn test_add() {
156        assert_eq!(eval("2 + 3"), Ok(to_value(5)));
157    }
158
159    #[test]
160    fn test_brackets_add() {
161        assert_eq!(eval("(2 + 3) + (3 + 5)"), Ok(to_value(13)));
162    }
163
164    #[test]
165    fn test_brackets_float_add() {
166        assert_eq!(eval("(2 + 3.2) + (3 + 5)"), Ok(to_value(13.2)));
167    }
168
169    #[test]
170    fn test_brackets_float_mul() {
171        assert_eq!(eval("(2 + 3.2) * 5"), Ok(to_value(26.0)));
172    }
173
174    #[test]
175    fn test_brackets_sub() {
176        assert_eq!(eval("(4 - 3) * 5"), Ok(to_value(5)));
177    }
178
179    #[test]
180    fn test_useless_brackets() {
181        assert_eq!(eval("2 + 3 + (5)"), Ok(to_value(10)));
182    }
183
184    #[test]
185    fn test_error_brackets_not_with_function() {
186        assert_eq!(eval("5 + ()"), Err(Error::BracketNotWithFunction));
187    }
188
189    #[test]
190    fn test_deep_brackets() {
191        assert_eq!(eval("(2 + (3 + 4) + (6 + (6 + 7)) + 5)"), Ok(to_value(33)));
192    }
193
194    #[test]
195    fn test_brackets_div() {
196        assert_eq!(eval("(4 / (2 + 2)) * 5"), Ok(to_value(5.0)));
197    }
198
199    #[test]
200    fn test_min() {
201        assert_eq!(eval("min(30, 5, 245, 20)"), Ok(to_value(5)));
202    }
203
204    #[test]
205    fn test_min_brackets() {
206        assert_eq!(
207            eval("(min(30, 5, 245, 20) * 10 + (5 + 5) * 5)"),
208            Ok(to_value(100))
209        );
210    }
211
212    #[test]
213    fn test_min_and_mul() {
214        assert_eq!(eval("min(30, 5, 245, 20) * 10"), Ok(to_value(50)));
215    }
216
217    #[test]
218    fn test_max() {
219        assert_eq!(eval("max(30, 5, 245, 20)"), Ok(to_value(245)));
220    }
221
222    #[test]
223    fn test_max_brackets() {
224        assert_eq!(
225            eval("(max(30, 5, 245, 20) * 10 + (5 + 5) * 5)"),
226            Ok(to_value(2500))
227        );
228    }
229
230    #[test]
231    fn test_max_and_mul() {
232        assert_eq!(eval("max(30, 5, 245, 20) * 10"), Ok(to_value(2450)));
233    }
234
235    #[test]
236    fn test_len_array() {
237        assert_eq!(eval("len(array(2, 3, 4, 5, 6))"), Ok(to_value(5)));
238    }
239
240    #[test]
241    fn test_null_and_number() {
242        assert_eq!(eval("hos != 0"), Ok(to_value(true)));
243        assert_eq!(eval("hos > 0"), Ok(to_value(false)));
244    }
245
246    #[test]
247    fn test_len_string() {
248        assert_eq!(eval("len('Hello world!')"), Ok(to_value(12)));
249    }
250
251    #[test]
252    fn test_len_object() {
253        let mut object = HashMap::new();
254        object.insert("field1", "value1");
255        object.insert("field2", "value2");
256        object.insert("field3", "value3");
257        assert_eq!(
258            Expr::new("len(object)").value("object", object).exec(),
259            Ok(to_value(3_i64))
260        );
261    }
262
263    #[test]
264    fn test_brackets_1() {
265        assert_eq!(eval("(5) + (min(3, 4, 5)) + 20"), Ok(to_value(28)));
266    }
267
268    #[test]
269    fn test_brackets_2() {
270        assert_eq!(eval("(((5) / 5))"), Ok(to_value(1.0)));
271    }
272
273    #[test]
274    fn test_string_add() {
275        assert_eq!(eval(r#""Hello"+", world!""#), Ok(to_value("Hello, world!")));
276    }
277
278    #[test]
279    fn test_equal() {
280        assert_eq!(eval("1 == 1"), Ok(to_value(true)));
281    }
282
283    #[test]
284    fn test_not_equal() {
285        assert_eq!(eval("1 != 2"), Ok(to_value(true)));
286    }
287
288    #[test]
289    fn test_multiple_equal() {
290        assert_eq!(eval("(1 == 2) == (2 == 3)"), Ok(to_value(true)));
291    }
292
293    #[test]
294    fn test_multiple_not_equal() {
295        assert_eq!(eval("(1 != 2) == (2 != 3)"), Ok(to_value(true)));
296    }
297
298    #[test]
299    fn test_greater_than() {
300        assert_eq!(eval("1 > 2"), Ok(to_value(false)));
301        assert_eq!(eval("2 > 1"), Ok(to_value(true)));
302    }
303
304    #[test]
305    fn test_less_than() {
306        assert_eq!(eval("2 < 1"), Ok(to_value(false)));
307        assert_eq!(eval("1 < 2"), Ok(to_value(true)));
308    }
309
310    #[test]
311    fn test_greater_and_less() {
312        assert_eq!(eval("(2 > 1) == (1 < 2)"), Ok(to_value(true)));
313    }
314
315    #[test]
316    fn test_ge() {
317        assert_eq!(eval("2 >= 1"), Ok(to_value(true)));
318        assert_eq!(eval("2 >= 2"), Ok(to_value(true)));
319        assert_eq!(eval("2 >= 3"), Ok(to_value(false)));
320    }
321
322    #[test]
323    fn test_le() {
324        assert_eq!(eval("2 <= 1"), Ok(to_value(false)));
325        assert_eq!(eval("2 <= 2"), Ok(to_value(true)));
326        assert_eq!(eval("2 <= 3"), Ok(to_value(true)));
327    }
328
329    #[test]
330    fn test_quotes() {
331        assert_eq!(eval(r#""1><2" + "3<>4""#), Ok(to_value("1><23<>4")));
332        assert_eq!(eval(r#""1==2" + "3--4""#), Ok(to_value("1==23--4")));
333        assert_eq!(eval(r#""1!=2" + "3>>4""#), Ok(to_value("1!=23>>4")));
334        assert_eq!(eval(r#""><1!=2" + "3>>4""#), Ok(to_value("><1!=23>>4")));
335    }
336
337    #[test]
338    fn test_single_quote() {
339        assert_eq!(eval(r#"'1><2' + '3<>4'"#), Ok(to_value("1><23<>4")));
340        assert_eq!(eval(r#"'1==2' + '3--4'"#), Ok(to_value("1==23--4")));
341        assert_eq!(eval(r#"'1!=2' + '3>>4'"#), Ok(to_value("1!=23>>4")));
342        assert_eq!(eval(r#"'!=1<>2' + '3>>4'"#), Ok(to_value("!=1<>23>>4")));
343    }
344
345    #[test]
346    fn test_single_and_double_quote() {
347        assert_eq!(
348            eval(r#"' """" ' + ' """" '"#),
349            Ok(to_value(r#" """"  """" "#))
350        );
351    }
352
353    #[test]
354    fn test_double_and_single_quote() {
355        assert_eq!(
356            eval(r#"" '''' " + " '''' ""#),
357            Ok(to_value(r#" ''''  '''' "#))
358        );
359    }
360
361    #[test]
362    fn test_array() {
363        assert_eq!(eval("array(1, 2, 3, 4)"), Ok(to_value(vec![1, 2, 3, 4])));
364    }
365
366    #[test]
367    fn test_range() {
368        assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
369    }
370
371    #[test]
372    fn test_range_and_min() {
373        assert_eq!(eval("min(0..5)"), Ok(to_value(0)));
374    }
375
376    #[test]
377    fn test_rem_1() {
378        assert_eq!(eval("2 % 2"), Ok(to_value(0)));
379    }
380
381    #[test]
382    fn test_rem_2() {
383        assert_eq!(eval("5 % 56 % 5"), Ok(to_value(0)));
384    }
385
386    #[test]
387    fn test_rem_3() {
388        assert_eq!(eval("5.5 % 23"), Ok(to_value(5.5)));
389    }
390
391    #[test]
392    fn test_rem_4() {
393        assert_eq!(eval("23 % 5.5"), Ok(to_value(1.0)));
394    }
395
396    #[test]
397    fn test_and_1() {
398        assert_eq!(eval("3 > 2 && 2 > 1"), Ok(to_value(true)));
399    }
400
401    #[test]
402    fn test_and_2() {
403        assert_eq!(eval("3 == 2 && 2 == 1"), Ok(to_value(false)));
404    }
405
406    #[test]
407    fn test_and_3() {
408        assert_eq!(eval("3 > 2 && 2 == 1"), Ok(to_value(false)));
409    }
410
411    #[test]
412    fn test_or_1() {
413        assert_eq!(eval("3 > 2 || 2 > 1"), Ok(to_value(true)));
414    }
415
416    #[test]
417    fn test_or_2() {
418        assert_eq!(eval("3 < 2 || 2 < 1"), Ok(to_value(false)));
419    }
420
421    #[test]
422    fn test_or_3() {
423        assert_eq!(eval("3 > 2 || 2 < 1"), Ok(to_value(true)));
424    }
425
426    #[test]
427    fn test_or_4() {
428        assert_eq!(eval("3 < 2 || 2 > 1"), Ok(to_value(true)));
429    }
430
431    #[test]
432    fn test_not() {
433        assert_eq!(eval("!false"), Ok(to_value(true)));
434        assert_eq!(eval("!true"), Ok(to_value(false)));
435        assert_eq!(eval("!(1 != 2)"), Ok(to_value(false)));
436        assert_eq!(eval("!(1 == 2)"), Ok(to_value(true)));
437        assert_eq!(eval("!(1 == 2) == true"), Ok(to_value(true)));
438    }
439
440    #[test]
441    fn test_not_and_brackets() {
442        assert_eq!(eval("(!(1 == 2)) == true"), Ok(to_value(true)));
443    }
444
445    #[test]
446    fn test_object_access() {
447        let mut object = HashMap::new();
448        object.insert("foo", "Foo, hello world!");
449        object.insert("bar", "Bar, hello world!");
450        assert_eq!(
451            Expr::new("object.foo == 'Foo, hello world!'")
452                .value("object", object)
453                .exec(),
454            Ok(to_value(true))
455        );
456    }
457
458    #[test]
459    fn test_object_dynamic_access() {
460        let mut object = HashMap::new();
461        object.insert("foo", "Foo, hello world!");
462        object.insert("bar", "Bar, hello world!");
463        assert_eq!(
464            Expr::new("object['foo'] == 'Foo, hello world!'")
465                .value("object", object)
466                .exec(),
467            Ok(to_value(true))
468        );
469    }
470
471    #[test]
472    fn test_object_dynamic_access_2() {
473        let mut object = HashMap::new();
474        object.insert("foo", "Foo, hello world!");
475        object.insert("bar", "Bar, hello world!");
476        assert_eq!(
477            Expr::new("object[foo] == 'Foo, hello world!'")
478                .value("object", object)
479                .value("foo", "foo")
480                .exec(),
481            Ok(to_value(true))
482        );
483    }
484
485    #[test]
486    fn test_path() {
487        assert_eq!(Expr::new("array[2-2].foo[2-2]").exec(), Ok(Value::Null));
488    }
489
490    #[test]
491    fn test_array_access() {
492        let array = vec!["hello", "world", "!"];
493        assert_eq!(
494            Expr::new(
495                "array[1-1] == 'hello' && array[1] == 'world' && array[2] == '!'",
496            ).value("array", array)
497                .exec(),
498            Ok(to_value(true))
499        );
500    }
501
502    #[test]
503    fn test_builtin_is_empty() {
504        assert_eq!(
505            Expr::new("is_empty(array)")
506                .value("array", Vec::<String>::new())
507                .exec(),
508            Ok(to_value(true))
509        );
510    }
511
512    #[test]
513    fn test_builtin_min() {
514        assert_eq!(
515            Expr::new("min(array)")
516                .value("array", vec![23_i32, 34_i32, 45_i32, 2_i32])
517                .exec(),
518            Ok(to_value(2_i32))
519        );
520    }
521
522    #[test]
523    fn test_custom_function() {
524        assert_eq!(
525            Expr::new("output()")
526                .function(
527                    "output",
528                    |_| Ok(to_value("This is custom function's output")),
529                )
530                .exec(),
531            Ok(to_value("This is custom function's output"))
532        );
533    }
534
535    #[test]
536    fn test_error_start_with_non_value_operator() {
537        let mut tree = Tree {
538            raw: "+ + 5".to_owned(),
539            ..Default::default()
540        };
541
542        tree.parse_pos().unwrap();
543        tree.parse_operators().unwrap();
544
545        assert_eq!(tree.parse_node(), Err(Error::StartWithNonValueOperator));
546    }
547
548    #[test]
549    fn test_error_duplicate_operator() {
550        let mut tree = Tree {
551            raw: "5 + + 5".to_owned(),
552            ..Default::default()
553        };
554
555        tree.parse_pos().unwrap();
556        tree.parse_operators().unwrap();
557
558        assert_eq!(tree.parse_node(), Err(Error::DuplicateOperatorNode));
559    }
560
561    #[test]
562    fn test_error_duplicate_value() {
563        let mut tree = Tree {
564            raw: "2 + 6 5".to_owned(),
565            ..Default::default()
566        };
567
568        tree.parse_pos().unwrap();
569        tree.parse_operators().unwrap();
570
571        assert_eq!(tree.parse_node(), Err(Error::DuplicateValueNode));
572    }
573
574    #[test]
575    fn test_error_unpaired_brackets() {
576        let mut tree = Tree {
577            raw: "(2 + 3)) * 5".to_owned(),
578            ..Default::default()
579        };
580
581        tree.parse_pos().unwrap();
582
583        assert_eq!(tree.parse_operators(), Err(Error::UnpairedBrackets));
584    }
585
586    #[test]
587    fn test_error_comma() {
588        let mut tree = Tree {
589            raw: ", 2 + 5".to_owned(),
590            ..Default::default()
591        };
592
593        tree.parse_pos().unwrap();
594        tree.parse_operators().unwrap();
595
596        assert_eq!(tree.parse_node(), Err(Error::CommaNotWithFunction));
597    }
598
599    #[test]
600    fn test_eval_issue_2() {
601        assert_eq!(eval("2 * (4 + 0) + 4"), Ok(to_value(12)));
602        assert_eq!(eval("2 * (2 + 2) + (1 + 3)"), Ok(to_value(12)));
603        assert_eq!(eval("2 * (4) + (4)"), Ok(to_value(12)));
604    }
605
606    #[test]
607    fn test_eval_math_function(){
608        fn pow(v: Vec<Value>)->Result<Value, Error>{
609            let Some(base) = v.get(0) else {
610                return Err(Error::ArgumentsLess(2));
611            };
612            let Some(pow) = v.get(1) else {
613                return Err(Error::ArgumentsLess(2));
614            };
615            let Value::Number(base) = base else {
616                return Err(Error::ExpectedNumber);
617            };
618            let Value::Number(pow) = pow else {
619                return Err(Error::ExpectedNumber);
620            };
621            let Some(base) = base.as_i64() else {
622                return Err(Error::Custom("Must can into i64".into()));
623            };
624            let Some(pow) = pow.as_u64() else {
625                return Err(Error::Custom("Must can into u64".into()));
626            };
627            Ok(base.pow(pow as u32).into())
628        }
629        fn add2(v: Vec<Value>)->Result<Value, Error>{
630            let Some(base) = v.get(0) else {
631                return Err(Error::ArgumentsLess(1));
632            }; 
633            let Value::Number(base) = base else {
634                return Err(Error::ExpectedNumber);
635            };
636            let Some(base) = base.as_i64() else {
637                return Err(Error::Custom("Must can into i64".into()));
638            };
639            Ok((base + 2).into())
640        }
641        let e = Expr::new("add2(pow(2, 2) + pow(2, 2))").const_function("pow", pow).const_function("add2",add2);
642        assert_eq!(e.compile().unwrap().exec(), Ok(to_value(4 + 4 + 2)));
643    }
644
645    #[test]
646    fn test_div_by_zero() {
647        use crate::eval;
648        assert_eq!(eval("5 / 0"), Err(Error::DivisionByZero));
649    }
650
651    #[test]
652    fn test_rem_by_zero() {
653        use crate::eval;
654        assert_eq!(eval("5 % 0"), Err(Error::ModuloByZero));
655    }
656
657    #[test]
658    fn test_short_circuit_and() {
659        use crate::eval;
660        // right side would produce DivisionByZero if evaluated
661        assert_eq!(eval("false && (1 / 0 > 0)"), Ok(to_value(false)));
662    }
663
664    #[test]
665    fn test_short_circuit_or() {
666        use crate::eval;
667        // right side would produce DivisionByZero if evaluated
668        assert_eq!(eval("true || (1 / 0 > 0)"), Ok(to_value(true)));
669    }
670
671    #[test]
672    fn test_empty_string_double_quotes() {
673        use crate::eval;
674        assert_eq!(eval(r#""""#), Ok(to_value("")));
675    }
676
677    #[test]
678    fn test_empty_string_single_quotes() {
679        use crate::eval;
680        assert_eq!(eval("''"), Ok(to_value("")));
681    }
682
683    #[test]
684    fn test_not_non_boolean() {
685        use crate::eval;
686        // !(2 + 3) forces Not to operate on a numeric result
687        assert_eq!(eval("!(2 + 3)"), Err(Error::ExpectedBoolean(to_value(5_u64))));
688    }
689
690    #[test]
691    fn test_len_boolean() {
692        assert!(Expr::new("len(v)").value("v", true).exec().is_err());
693    }
694
695    #[test]
696    fn test_string_gt() {
697        use crate::eval;
698        assert!(eval("'b' > 'a'").is_err());
699    }
700
701    #[test]
702    fn test_min_no_args() {
703        use crate::eval;
704        assert_eq!(eval("min()"), Err(Error::ArgumentsLess(1)));
705    }
706
707    #[test]
708    fn test_is_empty_null() {
709        use crate::eval;
710        // unset variable resolves to Null, is_empty(Null) should be true
711        assert_eq!(eval("is_empty(hos)"), Ok(to_value(true)));
712    }
713
714    #[test]
715    fn test_is_empty_number() {
716        assert_eq!(
717            Expr::new("is_empty(v)").value("v", 0_i32).exec(),
718            Ok(to_value(false))
719        );
720    }
721
722    #[test]
723    fn test_chained_dot_access() {
724        let mut inner = HashMap::new();
725        inner.insert("b", "deep");
726        let mut outer = HashMap::new();
727        outer.insert("a", to_value(inner));
728        assert_eq!(
729            Expr::new("obj.a.b").value("obj", outer).exec(),
730            Ok(to_value("deep"))
731        );
732    }
733
734    #[test]
735    fn test_and_non_boolean() {
736        use crate::eval;
737        assert!(eval("1 && true").is_err());
738    }
739
740    #[test]
741    fn test_or_non_boolean() {
742        use crate::eval;
743        assert!(eval("1 || false").is_err());
744    }
745
746    #[test]
747    fn test_null_arithmetic() {
748        use crate::eval;
749        assert!(eval("hos + 1").is_err());
750    }
751
752    #[test]
753    fn test_div_float_by_zero() {
754        use crate::eval;
755        assert_eq!(eval("5.0 / 0.0"), Err(Error::DivisionByZero));
756    }
757
758    #[test]
759    fn test_rem_float_by_zero() {
760        use crate::eval;
761        assert_eq!(eval("5.5 % 0.0"), Err(Error::ModuloByZero));
762    }
763
764    // ── Unary minus ──────────────────────────────────────────────────────────
765
766    #[test]
767    fn test_unary_minus_literal() {
768        assert_eq!(eval("-5"), Ok(to_value(-5_i64)));
769    }
770
771    #[test]
772    fn test_unary_minus_float() {
773        assert_eq!(eval("-2.5"), Ok(to_value(-2.5_f64)));
774    }
775
776    #[test]
777    fn test_unary_minus_double() {
778        assert_eq!(eval("-(-3)"), Ok(to_value(3_i64)));
779    }
780
781    #[test]
782    fn test_unary_minus_variable() {
783        assert_eq!(Expr::new("-x").value("x", 5_i32).exec(), Ok(to_value(-5_i64)));
784    }
785
786    #[test]
787    fn test_unary_minus_in_add() {
788        assert_eq!(eval("10 + -3"), Ok(to_value(7_i64)));
789    }
790
791    #[test]
792    fn test_unary_minus_in_mul() {
793        assert_eq!(eval("10 * -2"), Ok(to_value(-20_i64)));
794    }
795
796    #[test]
797    fn test_unary_minus_pow_precedence() {
798        // -2 ** 2 should be -(2**2) = -4.0, not (-2)**2 = 4
799        assert_eq!(eval("-2 ** 2"), Ok(to_value(-4.0_f64)));
800    }
801
802    // ── Exponentiation (**) ──────────────────────────────────────────────────
803
804    #[test]
805    fn test_pow_operator_basic() {
806        assert_eq!(eval("2 ** 10"), Ok(to_value(1024.0_f64)));
807    }
808
809    #[test]
810    fn test_pow_operator_zero_exp() {
811        assert_eq!(eval("5 ** 0"), Ok(to_value(1.0_f64)));
812    }
813
814    #[test]
815    fn test_pow_operator_fractional() {
816        assert_eq!(eval("4 ** 0.5"), Ok(to_value(2.0_f64)));
817    }
818
819    #[test]
820    fn test_pow_operator_priority_add() {
821        // 2 + 3 ** 2 == 2 + 9 == 11
822        assert_eq!(eval("2 + 3 ** 2"), Ok(to_value(11.0_f64)));
823    }
824
825    #[test]
826    fn test_pow_operator_priority_mul() {
827        // 2 * 3 ** 2 == 2 * 9 == 18
828        assert_eq!(eval("2 * 3 ** 2"), Ok(to_value(18.0_f64)));
829    }
830
831    #[test]
832    fn test_pow_paren_base() {
833        assert_eq!(eval("(-2) ** 2"), Ok(to_value(4.0_f64)));
834    }
835
836    // ── Null-coalescing (??) ─────────────────────────────────────────────────
837
838    #[test]
839    fn test_null_coalesce_null_var() {
840        assert_eq!(
841            Expr::new("missing ?? 'default'").exec(),
842            Ok(to_value("default"))
843        );
844    }
845
846    #[test]
847    fn test_null_coalesce_present_var() {
848        assert_eq!(
849            Expr::new("v ?? 'default'").value("v", "hello").exec(),
850            Ok(to_value("hello"))
851        );
852    }
853
854    #[test]
855    fn test_null_coalesce_zero_not_null() {
856        assert_eq!(
857            Expr::new("v ?? 99").value("v", 0_i32).exec(),
858            Ok(to_value(0_i64))
859        );
860    }
861
862    #[test]
863    fn test_null_coalesce_false_not_null() {
864        assert_eq!(
865            Expr::new("v ?? true").value("v", false).exec(),
866            Ok(to_value(false))
867        );
868    }
869
870    #[test]
871    fn test_null_coalesce_chained() {
872        // both sides null -> final fallback
873        assert_eq!(eval("missing ?? another ?? 42"), Ok(to_value(42_i64)));
874    }
875
876    // ── `in` operator ────────────────────────────────────────────────────────
877
878    #[test]
879    fn test_in_array_found() {
880        assert_eq!(eval("1 in array(1, 2, 3)"), Ok(to_value(true)));
881    }
882
883    #[test]
884    fn test_in_array_not_found() {
885        assert_eq!(eval("5 in array(1, 2, 3)"), Ok(to_value(false)));
886    }
887
888    #[test]
889    fn test_in_array_variable() {
890        assert_eq!(
891            Expr::new("x in arr")
892                .value("x", 2_i32)
893                .value("arr", vec![1_i32, 2, 3])
894                .exec(),
895            Ok(to_value(true))
896        );
897    }
898
899    #[test]
900    fn test_in_object_key_found() {
901        let mut map = HashMap::new();
902        map.insert("foo", 1_i32);
903        map.insert("bar", 2_i32);
904        assert_eq!(
905            Expr::new("'foo' in obj").value("obj", map).exec(),
906            Ok(to_value(true))
907        );
908    }
909
910    #[test]
911    fn test_in_object_key_not_found() {
912        let mut map = HashMap::new();
913        map.insert("foo", 1_i32);
914        assert_eq!(
915            Expr::new("'baz' in obj").value("obj", map).exec(),
916            Ok(to_value(false))
917        );
918    }
919
920    #[test]
921    fn test_in_string_found() {
922        assert_eq!(eval("'lo' in 'hello'"), Ok(to_value(true)));
923    }
924
925    #[test]
926    fn test_in_string_not_found() {
927        assert_eq!(eval("'xyz' in 'hello'"), Ok(to_value(false)));
928    }
929
930    #[test]
931    fn test_in_with_and() {
932        assert_eq!(
933            eval("1 in array(1,2,3) && 2 in array(2,3,4)"),
934            Ok(to_value(true))
935        );
936    }
937
938    // ── Math builtins ────────────────────────────────────────────────────────
939
940    #[test]
941    fn test_abs_negative() {
942        assert_eq!(eval("abs(-7)"), Ok(to_value(7_i64)));
943    }
944
945    #[test]
946    fn test_abs_positive() {
947        assert_eq!(eval("abs(3)"), Ok(to_value(3_i64)));
948    }
949
950    #[test]
951    fn test_abs_float() {
952        assert_eq!(eval("abs(-3.5)"), Ok(to_value(3.5_f64)));
953    }
954
955    #[test]
956    fn test_floor() {
957        assert_eq!(eval("floor(3.9)"),  Ok(to_value(3.0_f64)));
958        assert_eq!(eval("floor(-3.1)"), Ok(to_value(-4.0_f64)));
959    }
960
961    #[test]
962    fn test_ceil() {
963        assert_eq!(eval("ceil(3.1)"),  Ok(to_value(4.0_f64)));
964        assert_eq!(eval("ceil(-3.9)"), Ok(to_value(-3.0_f64)));
965    }
966
967    #[test]
968    fn test_round() {
969        assert_eq!(eval("round(3.5)"), Ok(to_value(4.0_f64)));
970        assert_eq!(eval("round(3.4)"), Ok(to_value(3.0_f64)));
971    }
972
973    #[test]
974    fn test_sqrt() {
975        assert_eq!(eval("sqrt(16.0)"), Ok(to_value(4.0_f64)));
976        assert_eq!(eval("sqrt(2.0)"),  Ok(to_value(2.0_f64.sqrt())));
977    }
978
979    #[test]
980    fn test_pow_builtin() {
981        assert_eq!(eval("pow(2, 8)"), Ok(to_value(256.0_f64)));
982        assert_eq!(eval("pow(3, 0)"), Ok(to_value(1.0_f64)));
983    }
984
985    // ── String builtins ──────────────────────────────────────────────────────
986
987    #[test]
988    fn test_contains_string_found() {
989        assert_eq!(eval("contains('hello world', 'world')"), Ok(to_value(true)));
990    }
991
992    #[test]
993    fn test_contains_string_not_found() {
994        assert_eq!(eval("contains('hello', 'xyz')"), Ok(to_value(false)));
995    }
996
997    #[test]
998    fn test_contains_array_found() {
999        assert_eq!(eval("contains(array(1,2,3), 2)"), Ok(to_value(true)));
1000    }
1001
1002    #[test]
1003    fn test_contains_array_not_found() {
1004        assert_eq!(eval("contains(array(1,2,3), 9)"), Ok(to_value(false)));
1005    }
1006
1007    #[test]
1008    fn test_starts_with_true() {
1009        assert_eq!(eval("starts_with('hello', 'hel')"), Ok(to_value(true)));
1010    }
1011
1012    #[test]
1013    fn test_starts_with_false() {
1014        assert_eq!(eval("starts_with('hello', 'llo')"), Ok(to_value(false)));
1015    }
1016
1017    #[test]
1018    fn test_ends_with_true() {
1019        assert_eq!(eval("ends_with('hello', 'llo')"), Ok(to_value(true)));
1020    }
1021
1022    #[test]
1023    fn test_ends_with_false() {
1024        assert_eq!(eval("ends_with('hello', 'hel')"), Ok(to_value(false)));
1025    }
1026
1027    #[test]
1028    fn test_upper() {
1029        assert_eq!(eval("upper('hello')"), Ok(to_value("HELLO")));
1030    }
1031
1032    #[test]
1033    fn test_lower() {
1034        assert_eq!(eval("lower('HELLO')"), Ok(to_value("hello")));
1035    }
1036
1037    #[test]
1038    fn test_trim() {
1039        assert_eq!(eval("trim('  hello  ')"), Ok(to_value("hello")));
1040        assert_eq!(eval("trim('no-spaces')"),  Ok(to_value("no-spaces")));
1041    }
1042
1043    // ── Type-checking builtins ───────────────────────────────────────────────
1044
1045    #[test]
1046    fn test_is_null_of_null_value() {
1047        assert_eq!(
1048            Expr::new("is_null(v)").value("v", Value::Null).exec(),
1049            Ok(to_value(true))
1050        );
1051    }
1052
1053    #[test]
1054    fn test_is_null_unset_var() {
1055        assert_eq!(eval("is_null(missing)"), Ok(to_value(true)));
1056    }
1057
1058    #[test]
1059    fn test_is_null_false() {
1060        assert_eq!(eval("is_null(5)"), Ok(to_value(false)));
1061    }
1062
1063    #[test]
1064    fn test_is_number_int() {
1065        assert_eq!(eval("is_number(42)"), Ok(to_value(true)));
1066    }
1067
1068    #[test]
1069    fn test_is_number_float() {
1070        assert_eq!(eval("is_number(3.14)"), Ok(to_value(true)));
1071    }
1072
1073    #[test]
1074    fn test_is_number_false() {
1075        assert_eq!(eval("is_number('hello')"), Ok(to_value(false)));
1076    }
1077
1078    #[test]
1079    fn test_is_string_true() {
1080        assert_eq!(eval("is_string('hello')"), Ok(to_value(true)));
1081    }
1082
1083    #[test]
1084    fn test_is_string_false() {
1085        assert_eq!(eval("is_string(42)"), Ok(to_value(false)));
1086    }
1087
1088    #[test]
1089    fn test_is_array_true() {
1090        assert_eq!(eval("is_array(array(1,2,3))"), Ok(to_value(true)));
1091    }
1092
1093    #[test]
1094    fn test_is_array_false() {
1095        assert_eq!(eval("is_array(42)"), Ok(to_value(false)));
1096    }
1097
1098    // ── Type-conversion builtins ─────────────────────────────────────────────
1099
1100    #[test]
1101    fn test_int_from_string() {
1102        assert_eq!(eval("int('42')"), Ok(to_value(42_i64)));
1103    }
1104
1105    #[test]
1106    fn test_int_from_float_string() {
1107        assert_eq!(eval("int('3.9')"), Ok(to_value(3_i64)));
1108    }
1109
1110    #[test]
1111    fn test_int_from_number() {
1112        assert_eq!(eval("int(7)"), Ok(to_value(7_i64)));
1113    }
1114
1115    #[test]
1116    fn test_int_from_bool() {
1117        assert_eq!(eval("int(true)"),  Ok(to_value(1_i64)));
1118        assert_eq!(eval("int(false)"), Ok(to_value(0_i64)));
1119    }
1120
1121    #[test]
1122    fn test_float_from_string() {
1123        assert_eq!(eval("float('3.14')"), Ok(to_value(3.14_f64)));
1124    }
1125
1126    #[test]
1127    fn test_float_from_number() {
1128        assert_eq!(eval("float(5)"), Ok(to_value(5.0_f64)));
1129    }
1130
1131    #[test]
1132    fn test_str_from_number() {
1133        assert_eq!(eval("str(42)"), Ok(to_value("42")));
1134    }
1135
1136    #[test]
1137    fn test_str_from_bool() {
1138        assert_eq!(eval("str(true)"), Ok(to_value("true")));
1139    }
1140
1141    #[test]
1142    fn test_str_from_null() {
1143        assert_eq!(eval("str(missing)"), Ok(to_value("null")));
1144    }
1145
1146    #[test]
1147    fn test_str_already_string() {
1148        assert_eq!(eval("str('hello')"), Ok(to_value("hello")));
1149    }
1150
1151    // ── Operator precedence edge cases ───────────────────────────────────────
1152
1153    #[test]
1154    fn test_and_or_precedence() {
1155        // && binds tighter than ||: false || (true && false) = false
1156        assert_eq!(eval("false || true && false"), Ok(to_value(false)));
1157    }
1158
1159    #[test]
1160    fn test_and_or_precedence_2() {
1161        // true || (false && false) = true
1162        assert_eq!(eval("true || false && false"), Ok(to_value(true)));
1163    }
1164
1165    #[test]
1166    fn test_in_with_or() {
1167        assert_eq!(
1168            eval("5 in array(1,2,3) || 2 in array(2,3,4)"),
1169            Ok(to_value(true))
1170        );
1171    }
1172
1173    #[test]
1174    fn test_null_coalesce_with_and() {
1175        // ?? (priority 1) < && (priority 4), so `a ?? (b && c)`
1176        assert_eq!(
1177            Expr::new("missing ?? true && false").exec(),
1178            Ok(to_value(false))
1179        );
1180    }
1181
1182    #[test]
1183    fn test_null_coalesce_null_literal() {
1184        // both sides null
1185        assert_eq!(eval("missing ?? another"), Ok(Value::Null));
1186    }
1187
1188    // ── Exponentiation edge cases ────────────────────────────────────────────
1189
1190    #[test]
1191    fn test_pow_zero_zero() {
1192        // 0 ** 0 = 1 (standard IEEE 754 behaviour)
1193        assert_eq!(eval("0 ** 0"), Ok(to_value(1.0_f64)));
1194    }
1195
1196    #[test]
1197    fn test_pow_negative_exponent() {
1198        // 2 ** -1 = 0.5
1199        assert_eq!(eval("2 ** -1"), Ok(to_value(0.5_f64)));
1200    }
1201
1202    #[test]
1203    fn test_pow_chained_left_assoc() {
1204        // left-associative: (2 ** 3) ** 2 = 8 ** 2 = 64
1205        assert_eq!(eval("2 ** 3 ** 2"), Ok(to_value(64.0_f64)));
1206    }
1207
1208    // ── Comparison edge cases ────────────────────────────────────────────────
1209
1210    #[test]
1211    fn test_null_equals_null() {
1212        // two unset variables both resolve to null; null == null is true
1213        assert_eq!(eval("missing == another"), Ok(to_value(true)));
1214    }
1215
1216    #[test]
1217    fn test_cross_type_equality() {
1218        // number vs string: not equal, no error
1219        assert_eq!(eval("1 == '1'"), Ok(to_value(false)));
1220    }
1221
1222    #[test]
1223    fn test_null_not_equal_to_zero() {
1224        assert_eq!(eval("missing != 0"), Ok(to_value(true)));
1225    }
1226
1227    // ── `in` error cases ─────────────────────────────────────────────────────
1228
1229    #[test]
1230    fn test_in_unsupported_rhs_type() {
1231        // right-hand side is a number, not array/object/string -> error
1232        assert!(eval("1 in 42").is_err());
1233    }
1234
1235    #[test]
1236    fn test_in_object_non_string_key() {
1237        // left-hand side is not a string when right is an object -> error
1238        let mut map = HashMap::new();
1239        map.insert("foo", 1_i32);
1240        assert!(Expr::new("42 in obj").value("obj", map).exec().is_err());
1241    }
1242
1243    // ── Array/index edge cases ───────────────────────────────────────────────
1244
1245    #[test]
1246    fn test_array_out_of_bounds() {
1247        // accessing index beyond the end returns null
1248        let arr = vec![1_i32, 2, 3];
1249        assert_eq!(
1250            Expr::new("arr[10]").value("arr", arr).exec(),
1251            Ok(Value::Null)
1252        );
1253    }
1254
1255    #[test]
1256    fn test_array_negative_index() {
1257        // negative index produces a descriptive error, not a panic
1258        let arr = vec![1_i32, 2, 3];
1259        assert!(Expr::new("arr[-1]").value("arr", arr).exec().is_err());
1260    }
1261
1262    #[test]
1263    fn test_len_empty_string() {
1264        assert_eq!(eval("len('')"), Ok(to_value(0_i64)));
1265    }
1266
1267    #[test]
1268    fn test_len_empty_array() {
1269        assert_eq!(eval("len(array())"), Ok(to_value(0_i64)));
1270    }
1271
1272    #[test]
1273    fn test_is_empty_empty_string() {
1274        assert_eq!(eval("is_empty('')"), Ok(to_value(true)));
1275    }
1276
1277    #[test]
1278    fn test_is_empty_nonempty_array() {
1279        assert_eq!(eval("is_empty(array(1))"), Ok(to_value(false)));
1280    }
1281
1282    #[test]
1283    fn test_is_empty_empty_array() {
1284        assert_eq!(eval("is_empty(array())"), Ok(to_value(true)));
1285    }
1286
1287    // ── FunctionNotExists error ──────────────────────────────────────────────
1288
1289    #[test]
1290    fn test_function_not_exists() {
1291        assert_eq!(eval("no_such_fn(1)"), Err(Error::FunctionNotExists("no_such_fn".into())));
1292    }
1293
1294    // ── Math builtin edge cases ──────────────────────────────────────────────
1295
1296    #[test]
1297    fn test_abs_zero() {
1298        assert_eq!(eval("abs(0)"), Ok(to_value(0_i64)));
1299    }
1300
1301    #[test]
1302    fn test_abs_non_number() {
1303        assert!(eval("abs('hello')").is_err());
1304    }
1305
1306    #[test]
1307    fn test_sqrt_zero() {
1308        assert_eq!(eval("sqrt(0)"), Ok(to_value(0.0_f64)));
1309    }
1310
1311    #[test]
1312    fn test_round_negative_half() {
1313        // round-half-away-from-zero: -0.5 rounds to -1.0
1314        assert_eq!(eval("round(-0.5)"), Ok(to_value(-1.0_f64)));
1315    }
1316
1317    #[test]
1318    fn test_floor_integer_input() {
1319        assert_eq!(eval("floor(5)"), Ok(to_value(5.0_f64)));
1320    }
1321
1322    #[test]
1323    fn test_ceil_integer_input() {
1324        assert_eq!(eval("ceil(5)"), Ok(to_value(5.0_f64)));
1325    }
1326
1327    // ── String builtin edge cases ────────────────────────────────────────────
1328
1329    #[test]
1330    fn test_starts_with_empty_prefix() {
1331        // empty prefix is always a prefix of any string
1332        assert_eq!(eval("starts_with('hello', '')"), Ok(to_value(true)));
1333    }
1334
1335    #[test]
1336    fn test_ends_with_empty_suffix() {
1337        assert_eq!(eval("ends_with('hello', '')"), Ok(to_value(true)));
1338    }
1339
1340    #[test]
1341    fn test_contains_empty_needle() {
1342        assert_eq!(eval("contains('hello', '')"), Ok(to_value(true)));
1343    }
1344
1345    #[test]
1346    fn test_upper_already_upper() {
1347        assert_eq!(eval("upper('HELLO')"), Ok(to_value("HELLO")));
1348    }
1349
1350    #[test]
1351    fn test_lower_already_lower() {
1352        assert_eq!(eval("lower('hello')"), Ok(to_value("hello")));
1353    }
1354
1355    #[test]
1356    fn test_trim_only_leading() {
1357        assert_eq!(eval("trim('  hi')"), Ok(to_value("hi")));
1358    }
1359
1360    #[test]
1361    fn test_trim_only_trailing() {
1362        assert_eq!(eval("trim('hi  ')"), Ok(to_value("hi")));
1363    }
1364
1365    // ── Type-conversion edge cases ───────────────────────────────────────────
1366
1367    #[test]
1368    fn test_int_invalid_string() {
1369        assert!(eval("int('not-a-number')").is_err());
1370    }
1371
1372    #[test]
1373    fn test_float_invalid_string() {
1374        assert!(eval("float('not-a-number')").is_err());
1375    }
1376
1377    #[test]
1378    fn test_float_from_bool() {
1379        assert_eq!(eval("float(true)"),  Ok(to_value(1.0_f64)));
1380        assert_eq!(eval("float(false)"), Ok(to_value(0.0_f64)));
1381    }
1382
1383    #[test]
1384    fn test_int_from_null() {
1385        // null cannot be meaningfully converted to int -> error
1386        assert!(eval("int(missing)").is_err());
1387    }
1388
1389    #[test]
1390    fn test_float_from_null() {
1391        assert!(eval("float(missing)").is_err());
1392    }
1393
1394    // ── Range edge cases ─────────────────────────────────────────────────────
1395
1396    #[test]
1397    fn test_range_single_element() {
1398        assert_eq!(eval("3..4"), Ok(to_value(vec![3_i64])));
1399    }
1400
1401    #[test]
1402    fn test_range_empty() {
1403        // equal bounds -> empty array
1404        assert_eq!(eval("5..5"), Ok(to_value(Vec::<i64>::new())));
1405    }
1406
1407    #[test]
1408    fn test_range_inverted_empty() {
1409        // inverted range produces an empty array (mirrors Rust's start..end semantics)
1410        assert_eq!(eval("5..2"), Ok(to_value(Vec::<i64>::new())));
1411    }
1412
1413    // ── if() conditional ─────────────────────────────────────────────────────
1414
1415    #[test]
1416    fn test_if_true_branch() {
1417        assert_eq!(eval("if(true, 1, 2)"), Ok(to_value(1_u64)));
1418    }
1419
1420    #[test]
1421    fn test_if_false_branch() {
1422        assert_eq!(eval("if(false, 1, 2)"), Ok(to_value(2_u64)));
1423    }
1424
1425    #[test]
1426    fn test_if_with_expression_condition() {
1427        assert_eq!(eval("if(3 > 2, 'yes', 'no')"), Ok(to_value("yes")));
1428        assert_eq!(eval("if(1 > 2, 'yes', 'no')"), Ok(to_value("no")));
1429    }
1430
1431    #[test]
1432    fn test_if_short_circuits_true() {
1433        // false branch should not be evaluated (division by zero)
1434        assert_eq!(eval("if(true, 42, 1 / 0)"), Ok(to_value(42_u64)));
1435    }
1436
1437    #[test]
1438    fn test_if_short_circuits_false() {
1439        assert_eq!(eval("if(false, 1 / 0, 99)"), Ok(to_value(99_u64)));
1440    }
1441
1442    #[test]
1443    fn test_if_nested() {
1444        assert_eq!(eval("if(true, if(false, 1, 2), 3)"), Ok(to_value(2_u64)));
1445    }
1446
1447    #[test]
1448    fn test_if_with_variable() {
1449        assert_eq!(
1450            Expr::new("if(x > 0, 'pos', 'non-pos')").value("x", 5_i32).exec(),
1451            Ok(to_value("pos"))
1452        );
1453    }
1454
1455    #[test]
1456    fn test_if_non_boolean_condition() {
1457        assert!(eval("if(1, 'a', 'b')").is_err());
1458    }
1459
1460    #[test]
1461    fn test_if_wrong_arg_count() {
1462        assert!(eval("if(true, 1)").is_err());
1463        assert!(eval("if(true, 1, 2, 3)").is_err());
1464    }
1465
1466    // ── not in ───────────────────────────────────────────────────────────────
1467
1468    #[test]
1469    fn test_not_in_array_true() {
1470        assert_eq!(eval("5 not in array(1, 2, 3)"), Ok(to_value(true)));
1471    }
1472
1473    #[test]
1474    fn test_not_in_array_false() {
1475        assert_eq!(eval("2 not in array(1, 2, 3)"), Ok(to_value(false)));
1476    }
1477
1478    #[test]
1479    fn test_not_in_string() {
1480        assert_eq!(eval("'xyz' not in 'hello'"), Ok(to_value(true)));
1481        assert_eq!(eval("'lo' not in 'hello'"),  Ok(to_value(false)));
1482    }
1483
1484    #[test]
1485    fn test_not_in_object() {
1486        let mut map = HashMap::new();
1487        map.insert("foo", 1_i32);
1488        assert_eq!(
1489            Expr::new("'bar' not in obj").value("obj", map).exec(),
1490            Ok(to_value(true))
1491        );
1492    }
1493
1494    #[test]
1495    fn test_not_in_with_and() {
1496        assert_eq!(
1497            eval("5 not in array(1,2,3) && 9 not in array(2,3,4)"),
1498            Ok(to_value(true))
1499        );
1500    }
1501
1502    // ── Bitwise operators ────────────────────────────────────────────────────
1503
1504    #[test]
1505    fn test_bitwise_and() {
1506        assert_eq!(eval("12 & 10"), Ok(to_value(8_i64)));   // 1100 & 1010 = 1000
1507    }
1508
1509    #[test]
1510    fn test_bitwise_or() {
1511        assert_eq!(eval("12 | 10"), Ok(to_value(14_i64)));  // 1100 | 1010 = 1110
1512    }
1513
1514    #[test]
1515    fn test_bitwise_xor() {
1516        assert_eq!(eval("12 ^ 10"), Ok(to_value(6_i64)));   // 1100 ^ 1010 = 0110
1517    }
1518
1519    #[test]
1520    fn test_bitwise_not() {
1521        assert_eq!(eval("~0"), Ok(to_value(-1_i64)));
1522        assert_eq!(eval("~(-1)"), Ok(to_value(0_i64)));
1523    }
1524
1525    #[test]
1526    fn test_shift_left() {
1527        assert_eq!(eval("1 << 4"), Ok(to_value(16_i64)));
1528        assert_eq!(eval("3 << 2"), Ok(to_value(12_i64)));
1529    }
1530
1531    #[test]
1532    fn test_shift_right() {
1533        assert_eq!(eval("16 >> 2"), Ok(to_value(4_i64)));
1534        assert_eq!(eval("8 >> 1"),  Ok(to_value(4_i64)));
1535    }
1536
1537    #[test]
1538    fn test_bitwise_priority_vs_add() {
1539        // & (7) binds tighter than + (8)? No: + is 8, & is 7, so + binds tighter.
1540        // 1 + 2 & 3 = (1 + 2) & 3 = 3 & 3 = 3
1541        assert_eq!(eval("1 + 2 & 3"), Ok(to_value(3_i64)));
1542    }
1543
1544    #[test]
1545    fn test_bitwise_and_vs_or_priority() {
1546        // & (7) binds tighter than | (3): 1 | 2 & 3 = 1 | (2 & 3) = 1 | 2 = 3
1547        assert_eq!(eval("1 | 2 & 3"), Ok(to_value(3_i64)));
1548    }
1549
1550    #[test]
1551    fn test_shift_left_too_large() {
1552        assert!(eval("1 << 64").is_err());
1553    }
1554
1555    #[test]
1556    fn test_bitwise_on_non_integer() {
1557        assert!(eval("1.5 & 3").is_err());
1558    }
1559
1560    // ── split / join ─────────────────────────────────────────────────────────
1561
1562    #[test]
1563    fn test_split_basic() {
1564        assert_eq!(
1565            eval("split('a,b,c', ',')"),
1566            Ok(to_value(vec!["a", "b", "c"]))
1567        );
1568    }
1569
1570    #[test]
1571    fn test_split_empty_string() {
1572        assert_eq!(
1573            eval("split('', ',')"),
1574            Ok(to_value(vec![""]))
1575        );
1576    }
1577
1578    #[test]
1579    fn test_split_no_delimiter() {
1580        assert_eq!(
1581            eval("split('hello', ',')"),
1582            Ok(to_value(vec!["hello"]))
1583        );
1584    }
1585
1586    #[test]
1587    fn test_join_basic() {
1588        assert_eq!(
1589            eval("join(array('a', 'b', 'c'), '-')"),
1590            Ok(to_value("a-b-c"))
1591        );
1592    }
1593
1594    #[test]
1595    fn test_join_empty_delimiter() {
1596        assert_eq!(
1597            eval("join(array('x', 'y'), '')"),
1598            Ok(to_value("xy"))
1599        );
1600    }
1601
1602    #[test]
1603    fn test_join_numbers() {
1604        assert_eq!(
1605            eval("join(array(1, 2, 3), ', ')"),
1606            Ok(to_value("1, 2, 3"))
1607        );
1608    }
1609
1610    #[test]
1611    fn test_split_then_join() {
1612        assert_eq!(
1613            eval("join(split('a,b,c', ','), '-')"),
1614            Ok(to_value("a-b-c"))
1615        );
1616    }
1617
1618    // ── replace ──────────────────────────────────────────────────────────────
1619
1620    #[test]
1621    fn test_replace_basic() {
1622        assert_eq!(eval("replace('hello world', 'world', 'Rust')"), Ok(to_value("hello Rust")));
1623    }
1624
1625    #[test]
1626    fn test_replace_all_occurrences() {
1627        assert_eq!(eval("replace('aaa', 'a', 'b')"), Ok(to_value("bbb")));
1628    }
1629
1630    #[test]
1631    fn test_replace_not_found() {
1632        assert_eq!(eval("replace('hello', 'xyz', 'ABC')"), Ok(to_value("hello")));
1633    }
1634
1635    #[test]
1636    fn test_replace_empty_from() {
1637        // replacing "" inserts the replacement before every char and at end
1638        assert_eq!(
1639            eval("replace('ab', '', '-')"),
1640            Ok(to_value("-a-b-"))
1641        );
1642    }
1643
1644    // ── keys / values ────────────────────────────────────────────────────────
1645
1646    #[test]
1647    fn test_keys_basic() {
1648        let mut map = HashMap::new();
1649        map.insert("a", 1_i32);
1650        let result = Expr::new("keys(obj)").value("obj", map).exec().unwrap();
1651        let mut keys: Vec<String> = result.as_array().unwrap()
1652            .iter().map(|v| v.as_str().unwrap().to_owned()).collect();
1653        keys.sort();
1654        assert_eq!(keys, vec!["a"]);
1655    }
1656
1657    #[test]
1658    fn test_keys_non_object_error() {
1659        assert!(eval("keys(array(1,2))").is_err());
1660    }
1661
1662    #[test]
1663    fn test_values_basic() {
1664        let mut map = HashMap::new();
1665        map.insert("x", 42_i32);
1666        let result = Expr::new("values(obj)").value("obj", map).exec().unwrap();
1667        let vals: Vec<i64> = result.as_array().unwrap()
1668            .iter().map(|v| v.as_i64().unwrap()).collect();
1669        assert_eq!(vals, vec![42]);
1670    }
1671
1672    // ── type_of ──────────────────────────────────────────────────────────────
1673
1674    #[test]
1675    fn test_type_of_null() {
1676        assert_eq!(eval("type_of(missing)"), Ok(to_value("null")));
1677    }
1678
1679    #[test]
1680    fn test_type_of_number() {
1681        assert_eq!(eval("type_of(42)"),   Ok(to_value("number")));
1682        assert_eq!(eval("type_of(3.14)"), Ok(to_value("number")));
1683    }
1684
1685    #[test]
1686    fn test_type_of_string() {
1687        assert_eq!(eval("type_of('hi')"), Ok(to_value("string")));
1688    }
1689
1690    #[test]
1691    fn test_type_of_bool() {
1692        assert_eq!(eval("type_of(true)"),  Ok(to_value("bool")));
1693        assert_eq!(eval("type_of(false)"), Ok(to_value("bool")));
1694    }
1695
1696    #[test]
1697    fn test_type_of_array() {
1698        assert_eq!(eval("type_of(array(1,2))"), Ok(to_value("array")));
1699    }
1700
1701    // ── clamp ────────────────────────────────────────────────────────────────
1702
1703    #[test]
1704    fn test_clamp_in_range() {
1705        assert_eq!(eval("clamp(5, 1, 10)"), Ok(to_value(5.0_f64)));
1706    }
1707
1708    #[test]
1709    fn test_clamp_below_min() {
1710        assert_eq!(eval("clamp(-5, 0, 10)"), Ok(to_value(0.0_f64)));
1711    }
1712
1713    #[test]
1714    fn test_clamp_above_max() {
1715        assert_eq!(eval("clamp(15, 0, 10)"), Ok(to_value(10.0_f64)));
1716    }
1717
1718    #[test]
1719    fn test_clamp_at_boundary() {
1720        assert_eq!(eval("clamp(0, 0, 10)"),  Ok(to_value(0.0_f64)));
1721        assert_eq!(eval("clamp(10, 0, 10)"), Ok(to_value(10.0_f64)));
1722    }
1723
1724    // ── log / log2 / log10 ───────────────────────────────────────────────────
1725
1726    #[test]
1727    fn test_log_e() {
1728        let result = eval("log(2.718281828)").unwrap();
1729        let f = result.as_f64().unwrap();
1730        assert!((f - 1.0).abs() < 1e-6, "log(e) ≈ 1, got {f}");
1731    }
1732
1733    #[test]
1734    fn test_log2_basic() {
1735        assert_eq!(eval("log2(8)"), Ok(to_value(3.0_f64)));
1736    }
1737
1738    #[test]
1739    fn test_log10_basic() {
1740        assert_eq!(eval("log10(1000)"), Ok(to_value(3.0_f64)));
1741    }
1742
1743    #[test]
1744    fn test_log_one() {
1745        assert_eq!(eval("log(1)"), Ok(to_value(0.0_f64)));
1746    }
1747
1748    // ── index_of ─────────────────────────────────────────────────────────────
1749
1750    #[test]
1751    fn test_index_of_found() {
1752        assert_eq!(eval("index_of(array(10, 20, 30), 20)"), Ok(to_value(1_i64)));
1753    }
1754
1755    #[test]
1756    fn test_index_of_not_found() {
1757        assert_eq!(eval("index_of(array(1, 2, 3), 99)"), Ok(to_value(-1_i64)));
1758    }
1759
1760    #[test]
1761    fn test_index_of_first_occurrence() {
1762        // returns first occurrence only
1763        assert_eq!(eval("index_of(array(1, 2, 1), 1)"), Ok(to_value(0_i64)));
1764    }
1765
1766    // ── sort / reverse / unique ───────────────────────────────────────────────
1767
1768    #[test]
1769    fn test_sort_numbers() {
1770        assert_eq!(
1771            eval("sort(array(3, 1, 2))"),
1772            Ok(to_value(vec![1_i64, 2, 3]))
1773        );
1774    }
1775
1776    #[test]
1777    fn test_sort_strings() {
1778        assert_eq!(
1779            eval("sort(array('banana', 'apple', 'cherry'))"),
1780            Ok(to_value(vec!["apple", "banana", "cherry"]))
1781        );
1782    }
1783
1784    #[test]
1785    fn test_sort_already_sorted() {
1786        assert_eq!(
1787            eval("sort(array(1, 2, 3))"),
1788            Ok(to_value(vec![1_i64, 2, 3]))
1789        );
1790    }
1791
1792    #[test]
1793    fn test_reverse_basic() {
1794        assert_eq!(
1795            eval("reverse(array(1, 2, 3))"),
1796            Ok(to_value(vec![3_i64, 2, 1]))
1797        );
1798    }
1799
1800    #[test]
1801    fn test_reverse_single() {
1802        assert_eq!(eval("reverse(array(42))"), Ok(to_value(vec![42_i64])));
1803    }
1804
1805    #[test]
1806    fn test_unique_basic() {
1807        assert_eq!(
1808            eval("unique(array(1, 2, 1, 3, 2))"),
1809            Ok(to_value(vec![1_i64, 2, 3]))
1810        );
1811    }
1812
1813    #[test]
1814    fn test_unique_no_duplicates() {
1815        assert_eq!(
1816            eval("unique(array(1, 2, 3))"),
1817            Ok(to_value(vec![1_i64, 2, 3]))
1818        );
1819    }
1820
1821    #[test]
1822    fn test_unique_all_same() {
1823        assert_eq!(eval("unique(array(5, 5, 5))"), Ok(to_value(vec![5_i64])));
1824    }
1825
1826    // ── any / all ────────────────────────────────────────────────────────────
1827
1828    #[test]
1829    fn test_any_found() {
1830        assert_eq!(eval("any(array(1, 2, 3), 2)"), Ok(to_value(true)));
1831    }
1832
1833    #[test]
1834    fn test_any_not_found() {
1835        assert_eq!(eval("any(array(1, 2, 3), 9)"), Ok(to_value(false)));
1836    }
1837
1838    #[test]
1839    fn test_any_empty_array() {
1840        assert_eq!(eval("any(array(), 1)"), Ok(to_value(false)));
1841    }
1842
1843    #[test]
1844    fn test_all_match() {
1845        assert_eq!(eval("all(array(2, 2, 2), 2)"), Ok(to_value(true)));
1846    }
1847
1848    #[test]
1849    fn test_all_partial_match() {
1850        assert_eq!(eval("all(array(2, 2, 3), 2)"), Ok(to_value(false)));
1851    }
1852
1853    #[test]
1854    fn test_all_empty_array() {
1855        // vacuously true
1856        assert_eq!(eval("all(array(), 1)"), Ok(to_value(true)));
1857    }
1858
1859    // ── format ───────────────────────────────────────────────────────────────
1860
1861    #[test]
1862    fn test_format_basic() {
1863        assert_eq!(eval("format('Hello, {}!', 'world')"), Ok(to_value("Hello, world!")));
1864    }
1865
1866    #[test]
1867    fn test_format_multiple_args() {
1868        assert_eq!(
1869            eval("format('{} + {} = {}', 1, 2, 3)"),
1870            Ok(to_value("1 + 2 = 3"))
1871        );
1872    }
1873
1874    #[test]
1875    fn test_format_no_placeholders() {
1876        assert_eq!(eval("format('no placeholders')"), Ok(to_value("no placeholders")));
1877    }
1878
1879    #[test]
1880    fn test_format_with_variable() {
1881        assert_eq!(
1882            Expr::new("format('Hello, {}!', name)").value("name", "Alice").exec(),
1883            Ok(to_value("Hello, Alice!"))
1884        );
1885    }
1886
1887    #[test]
1888    fn test_format_number() {
1889        assert_eq!(eval("format('value: {}', 42)"), Ok(to_value("value: 42")));
1890    }
1891}