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