1use std::collections::HashMap;
31use thiserror::Error;
32use winnow::ascii::{digit1, multispace0};
33use winnow::combinator::{alt, delimited, opt, preceded, repeat, separated, terminated};
34use winnow::error::ContextError;
35use winnow::prelude::*;
36use winnow::token::{any, none_of, one_of, take_while};
37
38#[derive(Debug, Clone, PartialEq)]
41pub enum Value {
42 Number(f64),
43 String(String),
44 Bool(bool),
45 Null,
46 Array(Vec<Value>),
47 Object(HashMap<String, Value>),
48 Type(String),
49}
50
51impl Value {
52 pub fn as_bool(&self) -> Result<bool, EvalError> {
53 match self {
54 Value::Bool(b) => Ok(*b),
55 _ => Err(EvalError::TypeError {
56 expected: "bool",
57 got: self.type_name(),
58 }),
59 }
60 }
61
62 pub fn as_number(&self) -> Result<f64, EvalError> {
63 match self {
64 Value::Number(n) => Ok(*n),
65 _ => Err(EvalError::TypeError {
66 expected: "number",
67 got: self.type_name(),
68 }),
69 }
70 }
71
72 pub fn as_string(&self) -> Result<&str, EvalError> {
73 match self {
74 Value::String(s) => Ok(s),
75 _ => Err(EvalError::TypeError {
76 expected: "string",
77 got: self.type_name(),
78 }),
79 }
80 }
81
82 pub fn as_array(&self) -> Result<&[Value], EvalError> {
83 match self {
84 Value::Array(a) => Ok(a),
85 _ => Err(EvalError::TypeError {
86 expected: "array",
87 got: self.type_name(),
88 }),
89 }
90 }
91
92 pub fn as_object(&self) -> Result<&HashMap<String, Value>, EvalError> {
93 match self {
94 Value::Object(o) => Ok(o),
95 _ => Err(EvalError::TypeError {
96 expected: "object",
97 got: self.type_name(),
98 }),
99 }
100 }
101
102 pub fn type_name(&self) -> &'static str {
103 match self {
104 Value::Number(_) => "number",
105 Value::String(_) => "string",
106 Value::Bool(_) => "bool",
107 Value::Null => "null",
108 Value::Array(_) => "array",
109 Value::Object(_) => "object",
110 Value::Type(_) => "type",
111 }
112 }
113
114 pub fn type_value(&self) -> Value {
115 Value::Type(self.type_name().to_string())
116 }
117}
118
119#[derive(Debug, Clone, PartialEq)]
122pub enum Expr {
123 Number(f64),
124 String(String),
125 Bool(bool),
126 Null,
127 Var(String),
128 Array(Vec<Expr>),
129 Object(Vec<(String, Expr)>),
130 TypeLiteral(String),
131 UnaryOp {
132 op: UnaryOp,
133 expr: Box<Expr>,
134 },
135 BinaryOp {
136 op: BinaryOp,
137 left: Box<Expr>,
138 right: Box<Expr>,
139 },
140 FuncCall {
141 name: String,
142 args: Vec<Expr>,
143 },
144 Index {
145 expr: Box<Expr>,
146 index: Box<Expr>,
147 },
148 Property {
149 expr: Box<Expr>,
150 name: String,
151 },
152 ForAll {
153 predicate: Box<Expr>,
154 var: String,
155 iterable: Box<Expr>,
156 },
157}
158
159#[derive(Debug, Clone, Copy, PartialEq)]
160pub enum UnaryOp {
161 Not,
162 Neg,
163}
164
165#[derive(Debug, Clone, Copy, PartialEq)]
166pub enum BinaryOp {
167 Add,
168 Sub,
169 Mul,
170 Div,
171 Mod,
172 Pow,
173 Eq,
174 Ne,
175 Lt,
176 Le,
177 Gt,
178 Ge,
179 And,
180 Or,
181 In,
182 Contains,
183 StartsWith,
184 EndsWith,
185 Matches,
186}
187
188#[derive(Error, Debug, Clone, PartialEq)]
189pub enum EvalError {
190 #[error("type error: expected {expected}, got {got}")]
191 TypeError {
192 expected: &'static str,
193 got: &'static str,
194 },
195 #[error("undefined variable: {0}")]
196 UndefinedVariable(String),
197 #[error("undefined function: {0}")]
198 UndefinedFunction(String),
199 #[error("invalid regex: {0}")]
200 InvalidRegex(String),
201 #[error("division by zero")]
202 DivisionByZero,
203 #[error("parse error: {0}")]
204 ParseError(String),
205 #[error("wrong number of arguments for {func}: expected {expected}, got {got}")]
206 WrongArgCount {
207 func: String,
208 expected: usize,
209 got: usize,
210 },
211 #[error("index out of bounds: {index} (len: {len})")]
212 IndexOutOfBounds { index: i64, len: usize },
213 #[error("key not found: {0}")]
214 KeyNotFound(String),
215}
216
217fn ws<'a, P, O>(p: P) -> impl Parser<&'a str, O, ContextError>
220where
221 P: Parser<&'a str, O, ContextError>,
222{
223 delimited(multispace0, p, multispace0)
224}
225
226fn number(input: &mut &str) -> ModalResult<Expr> {
227 let neg: Option<char> = opt('-').parse_next(input)?;
228 let int_part: &str = digit1.parse_next(input)?;
229 let frac_part: Option<&str> = opt(preceded('.', digit1)).parse_next(input)?;
230
231 let mut s = String::new();
232 if neg.is_some() {
233 s.push('-');
234 }
235 s.push_str(int_part);
236 if let Some(frac) = frac_part {
237 s.push('.');
238 s.push_str(frac);
239 }
240
241 Ok(Expr::Number(s.parse().unwrap()))
242}
243
244fn string_char(input: &mut &str) -> ModalResult<char> {
245 let c: char = none_of('"').parse_next(input)?;
246 if c == '\\' {
247 let escaped: char = any.parse_next(input)?;
248 Ok(match escaped {
249 'n' => '\n',
250 't' => '\t',
251 'r' => '\r',
252 '"' => '"',
253 '\\' => '\\',
254 c => c,
255 })
256 } else {
257 Ok(c)
258 }
259}
260
261fn string_literal(input: &mut &str) -> ModalResult<Expr> {
262 let chars: String = delimited(
263 '"',
264 repeat(0.., string_char).fold(String::new, |mut s, c| {
265 s.push(c);
266 s
267 }),
268 '"',
269 )
270 .parse_next(input)?;
271 Ok(Expr::String(chars))
272}
273
274fn regex_literal(input: &mut &str) -> ModalResult<Expr> {
275 '/'.parse_next(input)?;
276 let mut s = String::new();
277 loop {
278 let c: char = any.parse_next(input)?;
279 if c == '/' {
280 break;
281 }
282 if c == '\\' {
283 let escaped: char = any.parse_next(input)?;
284 s.push('\\');
285 s.push(escaped);
286 } else {
287 s.push(c);
288 }
289 }
290 Ok(Expr::String(s))
291}
292
293fn ident(input: &mut &str) -> ModalResult<String> {
294 let first: char = one_of(|c: char| c.is_ascii_alphabetic() || c == '_').parse_next(input)?;
295 let rest: &str =
296 take_while(0.., |c: char| c.is_ascii_alphanumeric() || c == '_').parse_next(input)?;
297 Ok(format!("{}{}", first, rest))
298}
299
300fn var_or_bool_or_func(input: &mut &str) -> ModalResult<Expr> {
301 let name = ident.parse_next(input)?;
302
303 let _ = multispace0.parse_next(input)?;
304 if input.starts_with('(') {
305 '('.parse_next(input)?;
306 let _ = multispace0.parse_next(input)?;
307 let args: Vec<Expr> = separated(0.., ws(expr), ws(',')).parse_next(input)?;
308 let _ = multispace0.parse_next(input)?;
309 ')'.parse_next(input)?;
310 return Ok(Expr::FuncCall { name, args });
311 }
312
313 match name.as_str() {
314 "true" => Ok(Expr::Bool(true)),
315 "false" => Ok(Expr::Bool(false)),
316 "null" => Ok(Expr::TypeLiteral(name)),
319 "number" | "string" | "bool" | "array" | "object" => Ok(Expr::TypeLiteral(name)),
321 _ => Ok(Expr::Var(name)),
322 }
323}
324
325fn array(input: &mut &str) -> ModalResult<Expr> {
326 let elements: Vec<Expr> = delimited(
327 ('[', multispace0),
328 separated(0.., ws(expr), ws(',')),
329 (multispace0, ']'),
330 )
331 .parse_next(input)?;
332 Ok(Expr::Array(elements))
333}
334
335fn object_key(input: &mut &str) -> ModalResult<String> {
336 alt((
337 delimited(
339 '"',
340 repeat(0.., string_char).fold(String::new, |mut s, c| {
341 s.push(c);
342 s
343 }),
344 '"',
345 ),
346 ident,
348 ))
349 .parse_next(input)
350}
351
352fn object_entry(input: &mut &str) -> ModalResult<(String, Expr)> {
353 let key = ws(object_key).parse_next(input)?;
354 ws(':').parse_next(input)?;
355 let value = ws(expr).parse_next(input)?;
356 Ok((key, value))
357}
358
359fn object(input: &mut &str) -> ModalResult<Expr> {
360 let entries: Vec<(String, Expr)> = delimited(
361 ('{', multispace0),
362 separated(0.., object_entry, ws(',')),
363 (multispace0, '}'),
364 )
365 .parse_next(input)?;
366 Ok(Expr::Object(entries))
367}
368
369const TYPE_KEYWORDS: &[&str] = &["number", "string", "bool", "null", "array", "object"];
370
371fn type_literal(input: &mut &str) -> ModalResult<Expr> {
372 for &kw in TYPE_KEYWORDS {
373 if input.starts_with(kw) {
374 let after = &(*input)[kw.len()..];
375 let next_char = after.chars().next();
376 if next_char
377 .map(|c| c.is_ascii_alphanumeric() || c == '_')
378 .unwrap_or(false)
379 {
380 continue;
381 }
382 *input = after;
383 return Ok(Expr::TypeLiteral(kw.to_string()));
384 }
385 }
386 Err(winnow::error::ErrMode::Backtrack(ContextError::new()))
387}
388
389fn atom(input: &mut &str) -> ModalResult<Expr> {
390 let _ = multispace0.parse_next(input)?;
391 alt((
392 delimited(('(', multispace0), expr, (multispace0, ')')),
393 array,
394 object,
395 string_literal,
396 regex_literal,
397 number,
398 var_or_bool_or_func,
399 type_literal,
400 ))
401 .parse_next(input)
402}
403
404fn postfix(input: &mut &str) -> ModalResult<Expr> {
405 let mut base = atom.parse_next(input)?;
406 loop {
407 let _ = multispace0.parse_next(input)?;
408 if input.starts_with('[') {
409 '['.parse_next(input)?;
410 let _ = multispace0.parse_next(input)?;
411 let index = expr.parse_next(input)?;
412 let _ = multispace0.parse_next(input)?;
413 ']'.parse_next(input)?;
414 base = Expr::Index {
415 expr: Box::new(base),
416 index: Box::new(index),
417 };
418 } else if input.starts_with('.') {
419 '.'.parse_next(input)?;
420 let name = ident.parse_next(input)?;
421 base = Expr::Property {
422 expr: Box::new(base),
423 name,
424 };
425 } else {
426 break;
427 }
428 }
429 Ok(base)
430}
431
432fn unary(input: &mut &str) -> ModalResult<Expr> {
433 let _ = multispace0.parse_next(input)?;
434 let neg: Option<char> = opt('-').parse_next(input)?;
435 if neg.is_some() {
436 let e = unary.parse_next(input)?;
437 return Ok(Expr::UnaryOp {
438 op: UnaryOp::Neg,
439 expr: Box::new(e),
440 });
441 }
442 postfix(input)
443}
444
445fn pow(input: &mut &str) -> ModalResult<Expr> {
446 let base = unary.parse_next(input)?;
447 let _ = multispace0.parse_next(input)?;
448 let caret: Option<char> = opt('^').parse_next(input)?;
449 if caret.is_some() {
450 let _ = multispace0.parse_next(input)?;
451 let exp = pow.parse_next(input)?;
452 Ok(Expr::BinaryOp {
453 op: BinaryOp::Pow,
454 left: Box::new(base),
455 right: Box::new(exp),
456 })
457 } else {
458 Ok(base)
459 }
460}
461
462fn term(input: &mut &str) -> ModalResult<Expr> {
463 let init = pow.parse_next(input)?;
464
465 repeat(0.., (ws(one_of(['*', '/', '%'])), pow))
466 .fold(
467 move || init.clone(),
468 |acc, (op_char, val): (char, Expr)| {
469 let op = match op_char {
470 '*' => BinaryOp::Mul,
471 '/' => BinaryOp::Div,
472 '%' => BinaryOp::Mod,
473 _ => unreachable!(),
474 };
475 Expr::BinaryOp {
476 op,
477 left: Box::new(acc),
478 right: Box::new(val),
479 }
480 },
481 )
482 .parse_next(input)
483}
484
485fn arith(input: &mut &str) -> ModalResult<Expr> {
486 let init = term.parse_next(input)?;
487
488 repeat(0.., (ws(one_of(['+', '-'])), term))
489 .fold(
490 move || init.clone(),
491 |acc, (op_char, val): (char, Expr)| {
492 let op = if op_char == '+' {
493 BinaryOp::Add
494 } else {
495 BinaryOp::Sub
496 };
497 Expr::BinaryOp {
498 op,
499 left: Box::new(acc),
500 right: Box::new(val),
501 }
502 },
503 )
504 .parse_next(input)
505}
506
507fn peek_non_ident(input: &mut &str) -> ModalResult<()> {
508 let next = input.chars().next();
509 if next
510 .map(|c| c.is_ascii_alphanumeric() || c == '_')
511 .unwrap_or(false)
512 {
513 Err(winnow::error::ErrMode::Backtrack(ContextError::new()))
514 } else {
515 Ok(())
516 }
517}
518
519fn cmp_op(input: &mut &str) -> ModalResult<BinaryOp> {
520 alt((
521 "==".value(BinaryOp::Eq),
522 "!=".value(BinaryOp::Ne),
523 "<=".value(BinaryOp::Le),
524 ">=".value(BinaryOp::Ge),
525 "<".value(BinaryOp::Lt),
526 ">".value(BinaryOp::Gt),
527 terminated("in", peek_non_ident).value(BinaryOp::In),
528 terminated("contains", peek_non_ident).value(BinaryOp::Contains),
529 terminated("startswith", peek_non_ident).value(BinaryOp::StartsWith),
530 terminated("endswith", peek_non_ident).value(BinaryOp::EndsWith),
531 terminated("matches", peek_non_ident).value(BinaryOp::Matches),
532 ))
533 .parse_next(input)
534}
535
536fn comparison(input: &mut &str) -> ModalResult<Expr> {
537 let left = arith.parse_next(input)?;
538 let _ = multispace0.parse_next(input)?;
539
540 let op_opt: Option<BinaryOp> = opt(cmp_op).parse_next(input)?;
541 match op_opt {
542 Some(op) => {
543 let _ = multispace0.parse_next(input)?;
544 let right = arith.parse_next(input)?;
545 Ok(Expr::BinaryOp {
546 op,
547 left: Box::new(left),
548 right: Box::new(right),
549 })
550 }
551 None => Ok(left),
552 }
553}
554
555fn not_expr(input: &mut &str) -> ModalResult<Expr> {
556 let _ = multispace0.parse_next(input)?;
557 let not_kw: Option<&str> = opt(terminated("not", peek_non_ident)).parse_next(input)?;
558 if not_kw.is_some() {
559 let _ = multispace0.parse_next(input)?;
560 let e = not_expr.parse_next(input)?;
561 Ok(Expr::UnaryOp {
562 op: UnaryOp::Not,
563 expr: Box::new(e),
564 })
565 } else {
566 comparison(input)
567 }
568}
569
570fn and_expr(input: &mut &str) -> ModalResult<Expr> {
571 let init = not_expr.parse_next(input)?;
572
573 repeat(
574 0..,
575 preceded((multispace0, "and", peek_non_ident, multispace0), not_expr),
576 )
577 .fold(
578 move || init.clone(),
579 |acc, val| Expr::BinaryOp {
580 op: BinaryOp::And,
581 left: Box::new(acc),
582 right: Box::new(val),
583 },
584 )
585 .parse_next(input)
586}
587
588fn or_expr(input: &mut &str) -> ModalResult<Expr> {
589 let init = and_expr.parse_next(input)?;
590
591 repeat(
592 0..,
593 preceded((multispace0, "or", peek_non_ident, multispace0), and_expr),
594 )
595 .fold(
596 move || init.clone(),
597 |acc, val| Expr::BinaryOp {
598 op: BinaryOp::Or,
599 left: Box::new(acc),
600 right: Box::new(val),
601 },
602 )
603 .parse_next(input)
604}
605
606fn forall_expr(input: &mut &str) -> ModalResult<Expr> {
607 let predicate = or_expr.parse_next(input)?;
608 let _ = multispace0.parse_next(input)?;
609
610 let forall_kw: Option<&str> = opt(terminated("forall", peek_non_ident)).parse_next(input)?;
611 if forall_kw.is_some() {
612 let _ = multispace0.parse_next(input)?;
613 let var = ident.parse_next(input)?;
614 let _ = multispace0.parse_next(input)?;
615 terminated("in", peek_non_ident).parse_next(input)?;
616 let _ = multispace0.parse_next(input)?;
617 let iterable = or_expr.parse_next(input)?;
618 Ok(Expr::ForAll {
619 predicate: Box::new(predicate),
620 var,
621 iterable: Box::new(iterable),
622 })
623 } else {
624 Ok(predicate)
625 }
626}
627
628fn expr(input: &mut &str) -> ModalResult<Expr> {
629 forall_expr(input)
630}
631
632pub fn parse(input: &str) -> Result<Expr, EvalError> {
633 let mut input = input.trim();
634 match expr.parse_next(&mut input) {
635 Ok(e) => {
636 let remaining = input.trim();
637 if remaining.is_empty() {
638 Ok(e)
639 } else {
640 Err(EvalError::ParseError(format!(
641 "unexpected trailing input: {:?}",
642 remaining
643 )))
644 }
645 }
646 Err(e) => Err(EvalError::ParseError(format!("{:?}", e))),
647 }
648}
649
650pub fn evaluate(expr: &Expr, vars: &HashMap<String, Value>) -> Result<Value, EvalError> {
653 match expr {
654 Expr::Number(n) => Ok(Value::Number(*n)),
655 Expr::String(s) => Ok(Value::String(s.clone())),
656 Expr::Bool(b) => Ok(Value::Bool(*b)),
657 Expr::Null => Ok(Value::Null),
658 Expr::TypeLiteral(t) => Ok(Value::Type(t.clone())),
659 Expr::Var(name) => vars
660 .get(name)
661 .cloned()
662 .ok_or_else(|| EvalError::UndefinedVariable(name.clone())),
663 Expr::Array(elements) => {
664 let values: Result<Vec<_>, _> = elements.iter().map(|e| evaluate(e, vars)).collect();
665 Ok(Value::Array(values?))
666 }
667 Expr::Object(entries) => {
668 let mut map = HashMap::new();
669 for (key, val_expr) in entries {
670 map.insert(key.clone(), evaluate(val_expr, vars)?);
671 }
672 Ok(Value::Object(map))
673 }
674 Expr::UnaryOp { op, expr } => {
675 let val = evaluate(expr, vars)?;
676 match op {
677 UnaryOp::Not => Ok(Value::Bool(!val.as_bool()?)),
678 UnaryOp::Neg => Ok(Value::Number(-val.as_number()?)),
679 }
680 }
681 Expr::BinaryOp { op, left, right } => eval_binary_op(*op, left, right, vars),
682 Expr::FuncCall { name, args } => eval_func_call(name, args, vars),
683 Expr::Index { expr, index } => {
684 let base = evaluate(expr, vars)?;
685 let idx = evaluate(index, vars)?;
686 match &base {
687 Value::Array(arr) => {
688 let i = idx.as_number()?;
689 let actual_index = if i < 0.0 {
690 let neg_idx = (-i) as usize;
692 if neg_idx > arr.len() {
693 return Err(EvalError::IndexOutOfBounds {
694 index: i as i64,
695 len: arr.len(),
696 });
697 }
698 arr.len() - neg_idx
699 } else {
700 i as usize
701 };
702 arr.get(actual_index)
703 .cloned()
704 .ok_or(EvalError::IndexOutOfBounds {
705 index: i as i64,
706 len: arr.len(),
707 })
708 }
709 Value::String(s) => {
710 let i = idx.as_number()?;
711 let chars: Vec<char> = s.chars().collect();
712 let actual_index = if i < 0.0 {
713 let neg_idx = (-i) as usize;
714 if neg_idx > chars.len() {
715 return Err(EvalError::IndexOutOfBounds {
716 index: i as i64,
717 len: chars.len(),
718 });
719 }
720 chars.len() - neg_idx
721 } else {
722 i as usize
723 };
724 chars
725 .get(actual_index)
726 .map(|c| Value::String(c.to_string()))
727 .ok_or(EvalError::IndexOutOfBounds {
728 index: i as i64,
729 len: chars.len(),
730 })
731 }
732 Value::Object(obj) => {
733 let key = idx.as_string()?;
734 obj.get(key)
735 .cloned()
736 .ok_or_else(|| EvalError::KeyNotFound(key.to_string()))
737 }
738 _ => Err(EvalError::TypeError {
739 expected: "array, string, or object",
740 got: base.type_name(),
741 }),
742 }
743 }
744 Expr::Property { expr, name } => {
745 let base = evaluate(expr, vars)?;
746 let obj = base.as_object()?;
747 obj.get(name)
748 .cloned()
749 .ok_or_else(|| EvalError::KeyNotFound(name.clone()))
750 }
751 Expr::ForAll {
752 predicate,
753 var,
754 iterable,
755 } => {
756 let iter_val = evaluate(iterable, vars)?;
757 let items = match &iter_val {
758 Value::Array(arr) => arr.clone(),
759 Value::Object(obj) => obj.values().cloned().collect(),
760 _ => {
761 return Err(EvalError::TypeError {
762 expected: "array or object",
763 got: iter_val.type_name(),
764 });
765 }
766 };
767 for item in items {
768 let mut local_vars = vars.clone();
769 local_vars.insert(var.clone(), item);
770 let result = evaluate(predicate, &local_vars)?;
771 if !result.as_bool()? {
772 return Ok(Value::Bool(false));
773 }
774 }
775 Ok(Value::Bool(true))
776 }
777 }
778}
779
780fn eval_func_call(
781 name: &str,
782 args: &[Expr],
783 vars: &HashMap<String, Value>,
784) -> Result<Value, EvalError> {
785 match name {
786 "len" => {
787 if args.len() != 1 {
788 return Err(EvalError::WrongArgCount {
789 func: name.to_string(),
790 expected: 1,
791 got: args.len(),
792 });
793 }
794 let val = evaluate(&args[0], vars)?;
795 match val {
796 Value::String(s) => Ok(Value::Number(s.chars().count() as f64)),
797 Value::Array(a) => Ok(Value::Number(a.len() as f64)),
798 Value::Object(o) => Ok(Value::Number(o.len() as f64)),
799 _ => Err(EvalError::TypeError {
800 expected: "string, array, or object",
801 got: val.type_name(),
802 }),
803 }
804 }
805 "type" => {
806 if args.len() != 1 {
807 return Err(EvalError::WrongArgCount {
808 func: name.to_string(),
809 expected: 1,
810 got: args.len(),
811 });
812 }
813 let val = evaluate(&args[0], vars)?;
814 Ok(Value::Type(val.type_name().to_string()))
815 }
816 "keys" => {
817 if args.len() != 1 {
818 return Err(EvalError::WrongArgCount {
819 func: name.to_string(),
820 expected: 1,
821 got: args.len(),
822 });
823 }
824 let val = evaluate(&args[0], vars)?;
825 let obj = val.as_object()?;
826 let mut keys: Vec<String> = obj.keys().cloned().collect();
827 keys.sort();
828 let keys: Vec<Value> = keys.into_iter().map(Value::String).collect();
829 Ok(Value::Array(keys))
830 }
831 "values" => {
832 if args.len() != 1 {
833 return Err(EvalError::WrongArgCount {
834 func: name.to_string(),
835 expected: 1,
836 got: args.len(),
837 });
838 }
839 let val = evaluate(&args[0], vars)?;
840 let obj = val.as_object()?;
841 let mut pairs: Vec<(&String, &Value)> = obj.iter().collect();
843 pairs.sort_by_key(|(k, _)| *k);
844 let values: Vec<Value> = pairs.into_iter().map(|(_, v)| v.clone()).collect();
845 Ok(Value::Array(values))
846 }
847 "sum" => {
848 if args.len() != 1 {
849 return Err(EvalError::WrongArgCount {
850 func: name.to_string(),
851 expected: 1,
852 got: args.len(),
853 });
854 }
855 let val = evaluate(&args[0], vars)?;
856 let arr = val.as_array()?;
857 let mut total = 0.0;
858 for item in arr {
859 total += item.as_number()?;
860 }
861 Ok(Value::Number(total))
862 }
863 "min" => {
864 if args.len() != 1 {
865 return Err(EvalError::WrongArgCount {
866 func: name.to_string(),
867 expected: 1,
868 got: args.len(),
869 });
870 }
871 let val = evaluate(&args[0], vars)?;
872 let arr = val.as_array()?;
873 if arr.is_empty() {
874 return Err(EvalError::TypeError {
875 expected: "non-empty array",
876 got: "empty array",
877 });
878 }
879 let mut min_val = arr[0].as_number()?;
880 for item in arr.iter().skip(1) {
881 let n = item.as_number()?;
882 if n < min_val {
883 min_val = n;
884 }
885 }
886 Ok(Value::Number(min_val))
887 }
888 "max" => {
889 if args.len() != 1 {
890 return Err(EvalError::WrongArgCount {
891 func: name.to_string(),
892 expected: 1,
893 got: args.len(),
894 });
895 }
896 let val = evaluate(&args[0], vars)?;
897 let arr = val.as_array()?;
898 if arr.is_empty() {
899 return Err(EvalError::TypeError {
900 expected: "non-empty array",
901 got: "empty array",
902 });
903 }
904 let mut max_val = arr[0].as_number()?;
905 for item in arr.iter().skip(1) {
906 let n = item.as_number()?;
907 if n > max_val {
908 max_val = n;
909 }
910 }
911 Ok(Value::Number(max_val))
912 }
913 "abs" => {
914 if args.len() != 1 {
915 return Err(EvalError::WrongArgCount {
916 func: name.to_string(),
917 expected: 1,
918 got: args.len(),
919 });
920 }
921 let val = evaluate(&args[0], vars)?;
922 Ok(Value::Number(val.as_number()?.abs()))
923 }
924 "lower" => {
925 if args.len() != 1 {
926 return Err(EvalError::WrongArgCount {
927 func: name.to_string(),
928 expected: 1,
929 got: args.len(),
930 });
931 }
932 let val = evaluate(&args[0], vars)?;
933 Ok(Value::String(val.as_string()?.to_lowercase()))
934 }
935 "upper" => {
936 if args.len() != 1 {
937 return Err(EvalError::WrongArgCount {
938 func: name.to_string(),
939 expected: 1,
940 got: args.len(),
941 });
942 }
943 let val = evaluate(&args[0], vars)?;
944 Ok(Value::String(val.as_string()?.to_uppercase()))
945 }
946 "unique" => {
947 if args.len() != 1 {
948 return Err(EvalError::WrongArgCount {
949 func: name.to_string(),
950 expected: 1,
951 got: args.len(),
952 });
953 }
954 let val = evaluate(&args[0], vars)?;
955 let arr = val.as_array()?;
956 let mut result = Vec::new();
957 for item in arr {
958 if !result.iter().any(|v| values_equal(v, item)) {
959 result.push(item.clone());
960 }
961 }
962 Ok(Value::Array(result))
963 }
964 _ => Err(EvalError::UndefinedFunction(name.to_string())),
965 }
966}
967
968fn eval_binary_op(
969 op: BinaryOp,
970 left: &Expr,
971 right: &Expr,
972 vars: &HashMap<String, Value>,
973) -> Result<Value, EvalError> {
974 if op == BinaryOp::And {
975 let l = evaluate(left, vars)?.as_bool()?;
976 if !l {
977 return Ok(Value::Bool(false));
978 }
979 return Ok(Value::Bool(evaluate(right, vars)?.as_bool()?));
980 }
981 if op == BinaryOp::Or {
982 let l = evaluate(left, vars)?.as_bool()?;
983 if l {
984 return Ok(Value::Bool(true));
985 }
986 return Ok(Value::Bool(evaluate(right, vars)?.as_bool()?));
987 }
988
989 let l = evaluate(left, vars)?;
990 let r = evaluate(right, vars)?;
991
992 match op {
993 BinaryOp::Add => match (&l, &r) {
994 (Value::String(ls), Value::String(rs)) => Ok(Value::String(format!("{}{}", ls, rs))),
995 (Value::Array(la), Value::Array(ra)) => {
996 let mut result = la.clone();
997 result.extend(ra.clone());
998 Ok(Value::Array(result))
999 }
1000 _ => Ok(Value::Number(l.as_number()? + r.as_number()?)),
1001 },
1002 BinaryOp::Sub => Ok(Value::Number(l.as_number()? - r.as_number()?)),
1003 BinaryOp::Mul => Ok(Value::Number(l.as_number()? * r.as_number()?)),
1004 BinaryOp::Mod => Ok(Value::Number(l.as_number()? % r.as_number()?)),
1005 BinaryOp::Div => {
1006 let divisor = r.as_number()?;
1007 if divisor == 0.0 {
1008 Err(EvalError::DivisionByZero)
1009 } else {
1010 Ok(Value::Number(l.as_number()? / divisor))
1011 }
1012 }
1013 BinaryOp::Pow => Ok(Value::Number(l.as_number()?.powf(r.as_number()?))),
1014 BinaryOp::Eq => Ok(Value::Bool(values_equal(&l, &r))),
1015 BinaryOp::Ne => Ok(Value::Bool(!values_equal(&l, &r))),
1016 BinaryOp::Lt => match (&l, &r) {
1017 (Value::String(ls), Value::String(rs)) => Ok(Value::Bool(ls < rs)),
1018 _ => Ok(Value::Bool(l.as_number()? < r.as_number()?)),
1019 },
1020 BinaryOp::Le => match (&l, &r) {
1021 (Value::String(ls), Value::String(rs)) => Ok(Value::Bool(ls <= rs)),
1022 _ => Ok(Value::Bool(l.as_number()? <= r.as_number()?)),
1023 },
1024 BinaryOp::Gt => match (&l, &r) {
1025 (Value::String(ls), Value::String(rs)) => Ok(Value::Bool(ls > rs)),
1026 _ => Ok(Value::Bool(l.as_number()? > r.as_number()?)),
1027 },
1028 BinaryOp::Ge => match (&l, &r) {
1029 (Value::String(ls), Value::String(rs)) => Ok(Value::Bool(ls >= rs)),
1030 _ => Ok(Value::Bool(l.as_number()? >= r.as_number()?)),
1031 },
1032 BinaryOp::In => {
1033 let arr = r.as_array()?;
1034 Ok(Value::Bool(arr.iter().any(|v| values_equal(&l, v))))
1035 }
1036 BinaryOp::Contains => {
1037 let haystack = l.as_string()?;
1038 let needle = r.as_string()?;
1039 Ok(Value::Bool(haystack.contains(needle)))
1040 }
1041 BinaryOp::StartsWith => {
1042 let s = l.as_string()?;
1043 let prefix = r.as_string()?;
1044 Ok(Value::Bool(s.starts_with(prefix)))
1045 }
1046 BinaryOp::EndsWith => {
1047 let s = l.as_string()?;
1048 let suffix = r.as_string()?;
1049 Ok(Value::Bool(s.ends_with(suffix)))
1050 }
1051 BinaryOp::Matches => {
1052 let s = l.as_string()?;
1053 let pattern = r.as_string()?;
1054 let re =
1055 regex::Regex::new(pattern).map_err(|e| EvalError::InvalidRegex(e.to_string()))?;
1056 Ok(Value::Bool(re.is_match(s)))
1057 }
1058 BinaryOp::And | BinaryOp::Or => unreachable!(),
1059 }
1060}
1061
1062fn values_equal(a: &Value, b: &Value) -> bool {
1063 match (a, b) {
1064 (Value::Number(a), Value::Number(b)) => (a - b).abs() < f64::EPSILON,
1065 (Value::String(a), Value::String(b)) => a == b,
1066 (Value::Bool(a), Value::Bool(b)) => a == b,
1067 (Value::Null, Value::Null) => true,
1068 (Value::Null, Value::Type(t)) | (Value::Type(t), Value::Null) => t == "null",
1070 (Value::Array(a), Value::Array(b)) => {
1071 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1072 }
1073 (Value::Object(a), Value::Object(b)) => {
1074 a.len() == b.len()
1075 && a.iter()
1076 .all(|(k, v)| b.get(k).map(|bv| values_equal(v, bv)).unwrap_or(false))
1077 }
1078 (Value::Type(a), Value::Type(b)) => a == b,
1079 _ => false,
1080 }
1081}
1082
1083pub fn eval_bool(expr_str: &str, vars: &HashMap<String, Value>) -> Result<bool, EvalError> {
1086 let ast = parse(expr_str)?;
1087 let result = evaluate(&ast, vars)?;
1088 result.as_bool()
1089}
1090
1091#[cfg(test)]
1092mod tests {
1093 use super::*;
1094
1095 fn vars(pairs: &[(&str, Value)]) -> HashMap<String, Value> {
1096 pairs
1097 .iter()
1098 .map(|(k, v)| (k.to_string(), v.clone()))
1099 .collect()
1100 }
1101
1102 #[test]
1103 fn test_number_parsing() {
1104 assert_eq!(parse("42").unwrap(), Expr::Number(42.0));
1105 assert_eq!(parse("0.5").unwrap(), Expr::Number(0.5));
1106 }
1107
1108 #[test]
1109 fn test_string_parsing() {
1110 assert_eq!(
1111 parse(r#""hello""#).unwrap(),
1112 Expr::String("hello".to_string())
1113 );
1114 }
1115
1116 #[test]
1117 fn test_arithmetic() {
1118 let v = vars(&[]);
1119 assert!(eval_bool("1 + 2 == 3", &v).unwrap());
1120 assert!(eval_bool("10 - 3 == 7", &v).unwrap());
1121 assert!(eval_bool("4 * 5 == 20", &v).unwrap());
1122 assert!(eval_bool("10 / 2 == 5", &v).unwrap());
1123 assert!(eval_bool("2 ^ 3 == 8", &v).unwrap());
1124 assert!(eval_bool("1 + 2 * 3 == 7", &v).unwrap());
1125 assert!(eval_bool("(1 + 2) * 3 == 9", &v).unwrap());
1126 }
1127
1128 #[test]
1129 fn test_comparisons() {
1130 let v = vars(&[("n", Value::Number(42.0))]);
1131 assert!(eval_bool("n > 0", &v).unwrap());
1132 assert!(eval_bool("n < 100", &v).unwrap());
1133 assert!(eval_bool("n >= 42", &v).unwrap());
1134 assert!(eval_bool("n <= 42", &v).unwrap());
1135 assert!(eval_bool("n == 42", &v).unwrap());
1136 assert!(eval_bool("n != 0", &v).unwrap());
1137 }
1138
1139 #[test]
1140 fn test_boolean_logic() {
1141 let v = vars(&[("n", Value::Number(42.0))]);
1142 assert!(eval_bool("n > 0 and n < 100", &v).unwrap());
1143 assert!(eval_bool("n < 0 or n > 0", &v).unwrap());
1144 assert!(eval_bool("not (n < 0)", &v).unwrap());
1145 }
1146
1147 #[test]
1148 fn test_in_operator() {
1149 let v = vars(&[("n", Value::Number(2.0))]);
1150 assert!(eval_bool("n in [1, 2, 3]", &v).unwrap());
1151 assert!(!eval_bool("n in [4, 5, 6]", &v).unwrap());
1152 }
1153
1154 #[test]
1155 fn test_string_operators() {
1156 let v = vars(&[("s", Value::String("hello world".to_string()))]);
1157 assert!(eval_bool(r#"s contains "world""#, &v).unwrap());
1158 assert!(eval_bool(r#"s startswith "hello""#, &v).unwrap());
1159 assert!(eval_bool(r#"s endswith "world""#, &v).unwrap());
1160 }
1161
1162 #[test]
1163 fn test_regex_matches() {
1164 let v = vars(&[("s", Value::String("hello123".to_string()))]);
1165 assert!(eval_bool(r#"s matches /^hello\d+$/"#, &v).unwrap());
1166 }
1167
1168 #[test]
1169 fn test_len_function() {
1170 let v = vars(&[("s", Value::String("hello".to_string()))]);
1171 assert!(eval_bool("len(s) == 5", &v).unwrap());
1172 }
1173
1174 #[test]
1175 fn test_backslash_in_string() {
1176 let v = vars(&[("p", Value::String("C:\\Users\\test".to_string()))]);
1178
1179 assert!(eval_bool(r#"p contains "test""#, &v).unwrap());
1181
1182 assert!(eval_bool(r#"p contains "\\""#, &v).unwrap());
1184
1185 assert!(eval_bool(r#"p contains "Users""#, &v).unwrap());
1187 }
1188
1189 #[test]
1190 fn test_array_indexing() {
1191 let v = vars(&[(
1192 "a",
1193 Value::Array(vec![
1194 Value::Number(10.0),
1195 Value::Number(20.0),
1196 Value::Number(30.0),
1197 ]),
1198 )]);
1199 assert!(eval_bool("a[0] == 10", &v).unwrap());
1200 assert!(eval_bool("a[1] == 20", &v).unwrap());
1201 assert!(eval_bool("a[2] == 30", &v).unwrap());
1202 }
1203
1204 #[test]
1205 fn test_object_property_access() {
1206 let mut obj = HashMap::new();
1207 obj.insert("name".to_string(), Value::String("alice".to_string()));
1208 obj.insert("age".to_string(), Value::Number(30.0));
1209 let v = vars(&[("o", Value::Object(obj))]);
1210
1211 assert!(eval_bool(r#"o.name == "alice""#, &v).unwrap());
1212 assert!(eval_bool("o.age == 30", &v).unwrap());
1213 assert!(eval_bool(r#"o["name"] == "alice""#, &v).unwrap());
1214 }
1215
1216 #[test]
1217 fn test_nested_access() {
1218 let inner = Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]);
1219 let mut obj = HashMap::new();
1220 obj.insert("items".to_string(), inner);
1221 let v = vars(&[("o", Value::Object(obj))]);
1222
1223 assert!(eval_bool("o.items[0] == 1", &v).unwrap());
1224 assert!(eval_bool("o.items[1] == 2", &v).unwrap());
1225 assert!(eval_bool("len(o.items) == 2", &v).unwrap());
1226 }
1227
1228 #[test]
1229 fn test_type_function() {
1230 let v = vars(&[
1231 ("n", Value::Number(42.0)),
1232 ("s", Value::String("hello".to_string())),
1233 ("b", Value::Bool(true)),
1234 ("a", Value::Array(vec![])),
1235 ]);
1236
1237 assert!(eval_bool("type(n) == number", &v).unwrap());
1238 assert!(eval_bool("type(s) == string", &v).unwrap());
1239 assert!(eval_bool("type(b) == bool", &v).unwrap());
1240 assert!(eval_bool("type(a) == array", &v).unwrap());
1241 }
1242
1243 #[test]
1244 fn test_keys_function() {
1245 let mut obj = HashMap::new();
1246 obj.insert("a".to_string(), Value::Number(1.0));
1247 obj.insert("b".to_string(), Value::Number(2.0));
1248 let v = vars(&[("o", Value::Object(obj))]);
1249
1250 assert!(eval_bool("len(keys(o)) == 2", &v).unwrap());
1251 }
1252
1253 #[test]
1254 fn test_forall_array() {
1255 let v = vars(&[(
1256 "a",
1257 Value::Array(vec![
1258 Value::Number(1.0),
1259 Value::Number(2.0),
1260 Value::Number(3.0),
1261 ]),
1262 )]);
1263
1264 assert!(eval_bool("x <= 3 forall x in a", &v).unwrap());
1265 assert!(eval_bool("x > 0 forall x in a", &v).unwrap());
1266 assert!(!eval_bool("x > 2 forall x in a", &v).unwrap());
1267 }
1268
1269 #[test]
1270 fn test_forall_object() {
1271 let mut obj = HashMap::new();
1272 obj.insert("a".to_string(), Value::Number(1.0));
1273 obj.insert("b".to_string(), Value::Number(2.0));
1274 obj.insert("c".to_string(), Value::Number(3.0));
1275 let v = vars(&[("o", Value::Object(obj))]);
1276
1277 assert!(eval_bool("x <= 3 forall x in o", &v).unwrap());
1278 assert!(eval_bool("type(x) == number forall x in o", &v).unwrap());
1279 }
1280
1281 #[test]
1282 fn test_object_literal() {
1283 let v = vars(&[]);
1284 assert!(eval_bool(r#"{"a": 1, "b": 2}.a == 1"#, &v).unwrap());
1285 assert!(eval_bool(r#"len({"x": 1, "y": 2}) == 2"#, &v).unwrap());
1286 }
1287
1288 #[test]
1289 fn test_type_literal() {
1290 let v = vars(&[("n", Value::Number(42.0))]);
1291 assert!(eval_bool("type(n) == number", &v).unwrap());
1292 assert!(!eval_bool("type(n) == string", &v).unwrap());
1293 }
1294
1295 #[test]
1296 fn test_len_object() {
1297 let mut obj = HashMap::new();
1298 obj.insert("a".to_string(), Value::Number(1.0));
1299 obj.insert("b".to_string(), Value::Number(2.0));
1300 let v = vars(&[("o", Value::Object(obj))]);
1301
1302 assert!(eval_bool("len(o) == 2", &v).unwrap());
1303 }
1304
1305 #[test]
1306 fn test_bool_comparison() {
1307 let v = vars(&[("b", Value::Bool(true))]);
1308 assert!(eval_bool("b == true", &v).unwrap());
1309 assert!(eval_bool("b != false", &v).unwrap());
1310 assert!(eval_bool("(1 == 1) == true", &v).unwrap());
1311 }
1312}