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