1use crate::ast::*;
6use crate::lexer::Lexer;
7use crate::token::{Token, TokenKind, Span, TemplatePart};
8use thiserror::Error;
9
10#[derive(Debug, Error, Clone)]
12pub enum ParseError {
13 #[error("Unexpected token: expected {expected}, found {found} at position {position}")]
14 UnexpectedToken {
15 expected: String,
16 found: String,
17 position: usize,
18 },
19
20 #[error("Unexpected end of input")]
21 UnexpectedEof,
22
23 #[error("Invalid expression: {0}")]
24 InvalidExpression(String),
25
26 #[error("Lexer error: {0}")]
27 LexerError(String),
28}
29
30pub struct Parser<'a> {
32 lexer: Lexer<'a>,
33 current: Token,
34 previous: Token,
35}
36
37impl<'a> Parser<'a> {
38 pub fn new(mut lexer: Lexer<'a>) -> Self {
40 let current = lexer.next_token();
41 Self {
42 lexer,
43 current: current.clone(),
44 previous: current,
45 }
46 }
47
48 pub fn parse(&mut self) -> Result<Expr, ParseError> {
50 let expr = self.parse_expression()?;
51
52 if !self.is_at_end() {
53 return Err(ParseError::UnexpectedToken {
54 expected: "end of expression".into(),
55 found: format!("{}", self.current.kind),
56 position: self.current.span.start,
57 });
58 }
59
60 Ok(expr)
61 }
62
63 fn parse_expression(&mut self) -> Result<Expr, ParseError> {
65 self.parse_null_coalesce()
66 }
67
68 fn parse_null_coalesce(&mut self) -> Result<Expr, ParseError> {
70 let mut expr = self.parse_or()?;
71
72 while self.match_token(TokenKind::QuestionQuestion) {
73 let right = self.parse_or()?;
74 expr = Expr::NullCoalesce(Box::new(expr), Box::new(right));
75 }
76
77 Ok(expr)
78 }
79
80 fn parse_or(&mut self) -> Result<Expr, ParseError> {
82 let mut expr = self.parse_and()?;
83
84 while self.match_token(TokenKind::Or) {
85 let right = self.parse_and()?;
86 expr = Expr::binary(expr, BinaryOp::Or, right);
87 }
88
89 Ok(expr)
90 }
91
92 fn parse_and(&mut self) -> Result<Expr, ParseError> {
94 let mut expr = self.parse_equality()?;
95
96 while self.match_token(TokenKind::And) {
97 let right = self.parse_equality()?;
98 expr = Expr::binary(expr, BinaryOp::And, right);
99 }
100
101 Ok(expr)
102 }
103
104 fn parse_equality(&mut self) -> Result<Expr, ParseError> {
106 let mut expr = self.parse_comparison()?;
107
108 loop {
109 let op = if self.match_token(TokenKind::EqEq) {
110 BinaryOp::Eq
111 } else if self.match_token(TokenKind::EqEqEq)
112 || self.match_token(TokenKind::Is)
113 || self.match_token(TokenKind::Eq)
114 || self.match_token(TokenKind::Equals)
115 || self.match_token(TokenKind::SameAs)
116 {
117 BinaryOp::StrictEq
118 } else if self.match_token(TokenKind::NotEq) || self.match_token(TokenKind::LtGt) {
119 BinaryOp::NotEq
120 } else if self.match_token(TokenKind::NotEqEq)
121 || self.match_token(TokenKind::Isnt)
122 || self.match_token(TokenKind::IsNot)
123 || self.match_token(TokenKind::NotEqKw)
124 {
125 BinaryOp::StrictNotEq
126 } else {
127 break;
128 };
129
130 let right = self.parse_comparison()?;
131 expr = Expr::binary(expr, op, right);
132 }
133
134 Ok(expr)
135 }
136
137 fn parse_comparison(&mut self) -> Result<Expr, ParseError> {
139 let mut expr = self.parse_additive()?;
140
141 loop {
142 let op = if self.match_token(TokenKind::Lt) {
143 BinaryOp::Lt
144 } else if self.match_token(TokenKind::Gt) {
145 BinaryOp::Gt
146 } else if self.match_token(TokenKind::LtEq) {
147 BinaryOp::LtEq
148 } else if self.match_token(TokenKind::GtEq) {
149 BinaryOp::GtEq
150 } else if self.check(TokenKind::In) {
151 self.advance();
153 BinaryOp::In
154 } else {
155 break;
156 };
157
158 let right = self.parse_additive()?;
159 expr = Expr::binary(expr, op, right);
160 }
161
162 Ok(expr)
163 }
164
165 fn parse_additive(&mut self) -> Result<Expr, ParseError> {
167 let mut expr = self.parse_multiplicative()?;
168
169 loop {
170 let op = if self.match_token(TokenKind::Plus) {
171 BinaryOp::Add
172 } else if self.match_token(TokenKind::Minus) {
173 BinaryOp::Sub
174 } else {
175 break;
176 };
177
178 let right = self.parse_multiplicative()?;
179 expr = Expr::binary(expr, op, right);
180 }
181
182 Ok(expr)
183 }
184
185 fn parse_multiplicative(&mut self) -> Result<Expr, ParseError> {
187 let mut expr = self.parse_power()?;
188
189 loop {
190 let op = if self.match_token(TokenKind::Star) {
191 BinaryOp::Mul
192 } else if self.match_token(TokenKind::Slash) {
193 BinaryOp::Div
194 } else if self.match_token(TokenKind::Percent) {
195 BinaryOp::Mod
196 } else if self.match_token(TokenKind::SlashQuestion) {
197 BinaryOp::SafeDivZero
198 } else if self.match_token(TokenKind::SlashBang) {
199 BinaryOp::SafeDivNull
200 } else {
201 break;
202 };
203
204 let right = self.parse_power()?;
205 expr = Expr::binary(expr, op, right);
206 }
207
208 Ok(expr)
209 }
210
211 fn parse_power(&mut self) -> Result<Expr, ParseError> {
213 let expr = self.parse_unary()?;
214
215 if self.match_token(TokenKind::Caret) {
216 let right = self.parse_power()?; return Ok(Expr::binary(expr, BinaryOp::Pow, right));
218 }
219
220 Ok(expr)
221 }
222
223 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
225 if self.match_token(TokenKind::Not) {
226 let expr = self.parse_unary()?;
227 return Ok(Expr::unary(UnaryOp::Not, expr));
228 }
229
230 if self.match_token(TokenKind::Minus) {
231 let expr = self.parse_unary()?;
232 return Ok(Expr::unary(UnaryOp::Neg, expr));
233 }
234
235 if self.match_token(TokenKind::Plus) {
236 let expr = self.parse_unary()?;
237 return Ok(Expr::unary(UnaryOp::Plus, expr));
238 }
239
240 self.parse_postfix()
241 }
242
243 fn parse_postfix(&mut self) -> Result<Expr, ParseError> {
245 let mut expr = self.parse_primary()?;
246
247 loop {
248 if self.match_token(TokenKind::Question) {
249 expr = Expr::Postfix(Box::new(PostfixExpr {
251 expr,
252 op: PostfixOp::Truthy,
253 span: self.previous.span,
254 }));
255 } else if self.match_token(TokenKind::Dot) {
256 let name = self.expect_identifier()?;
258
259 if self.match_token(TokenKind::LParen) {
260 let args = self.parse_args()?;
262 expr = Expr::MethodCall(Box::new(MethodCallExpr {
263 object: expr,
264 method: name,
265 args,
266 span: self.previous.span,
267 }));
268 } else {
269 expr = Expr::Property(Box::new(PropertyExpr {
271 object: expr,
272 property: name,
273 span: self.previous.span,
274 }));
275 }
276 } else if self.match_token(TokenKind::LBracket) {
277 let index = self.parse_expression()?;
279 self.expect(TokenKind::RBracket)?;
280 expr = Expr::Index(Box::new(IndexExpr {
281 object: expr,
282 index,
283 span: self.previous.span,
284 }));
285 } else {
286 break;
287 }
288 }
289
290 Ok(expr)
291 }
292
293 fn parse_primary(&mut self) -> Result<Expr, ParseError> {
295 if let TokenKind::Integer(n) = self.current.kind {
297 self.advance();
298 return Ok(Expr::int(n));
299 }
300
301 if let TokenKind::Float(n) = self.current.kind {
302 self.advance();
303 return Ok(Expr::float(n));
304 }
305
306 if let TokenKind::String(ref s) = self.current.kind {
307 let s = s.clone();
308 self.advance();
309 return Ok(Expr::string(s));
310 }
311
312 if let TokenKind::TemplateString(ref parts) = self.current.kind {
313 let parts = parts.clone();
314 self.advance();
315 return self.parse_template_parts(parts);
316 }
317
318 if self.match_token(TokenKind::True) {
319 return Ok(Expr::bool(true));
320 }
321
322 if self.match_token(TokenKind::False) {
323 return Ok(Expr::bool(false));
324 }
325
326 if self.match_token(TokenKind::Null) {
327 return Ok(Expr::null());
328 }
329
330 if self.match_token(TokenKind::If) {
332 return self.parse_if_expression();
333 }
334
335 if self.match_token(TokenKind::Let) {
337 return self.parse_let_expression();
338 }
339
340 if self.match_token(TokenKind::From) {
342 return self.parse_query_expression();
343 }
344
345 if self.match_token(TokenKind::LBracket) {
347 return self.parse_array_literal();
348 }
349
350 if self.match_token(TokenKind::LParen) {
352 return self.parse_grouped_or_lambda();
353 }
354
355 if let TokenKind::Ident(ref name) = self.current.kind {
357 let name = name.clone();
358 let span = self.current.span;
359 self.advance();
360
361 if self.match_token(TokenKind::Arrow) {
363 let body = self.parse_expression()?;
364 return Ok(Expr::Lambda(Box::new(LambdaExpr {
365 params: vec![name],
366 body,
367 span,
368 })));
369 }
370
371 if self.match_token(TokenKind::LParen) {
373 let args = self.parse_args()?;
374 return Ok(Expr::Call(Box::new(CallExpr {
375 function: name,
376 args,
377 span,
378 })));
379 }
380
381 return Ok(Expr::Var(VarExpr { name, span }));
383 }
384
385 if self.is_array_function_keyword() {
387 let name = format!("{}", self.current.kind);
388 let span = self.current.span;
389 self.advance();
390
391 if self.match_token(TokenKind::LParen) {
392 let args = self.parse_args()?;
393 return Ok(Expr::Call(Box::new(CallExpr {
394 function: name,
395 args,
396 span,
397 })));
398 }
399
400 return Err(ParseError::InvalidExpression(
401 format!("'{}' requires arguments", name),
402 ));
403 }
404
405 Err(ParseError::UnexpectedToken {
406 expected: "expression".into(),
407 found: format!("{}", self.current.kind),
408 position: self.current.span.start,
409 })
410 }
411
412 fn is_array_function_keyword(&self) -> bool {
414 matches!(
415 self.current.kind,
416 TokenKind::Map
417 | TokenKind::Filter
418 | TokenKind::Reduce
419 | TokenKind::All
420 | TokenKind::Some
421 | TokenKind::Merge
422 | TokenKind::Missing
423 | TokenKind::MissingSome
424 | TokenKind::Log
425 | TokenKind::Contains
426 )
427 }
428
429 fn parse_template_parts(&mut self, parts: Vec<TemplatePart>) -> Result<Expr, ParseError> {
431 let mut exprs = Vec::new();
432
433 for part in parts {
434 match part {
435 TemplatePart::Literal(s) => {
436 exprs.push(TemplateExpr::Literal(s));
437 }
438 TemplatePart::Expr(expr_str) => {
439 let mut inner_parser = Parser::new(Lexer::new(&expr_str));
441 let expr = inner_parser.parse()?;
442 exprs.push(TemplateExpr::Expr(expr));
443 }
444 }
445 }
446
447 Ok(Expr::Template(exprs))
448 }
449
450 fn parse_if_expression(&mut self) -> Result<Expr, ParseError> {
452 let span = self.previous.span;
453 let condition = self.parse_expression()?;
454
455 self.expect(TokenKind::Then)?;
456 let then_branch = self.parse_expression()?;
457
458 let mut else_ifs = Vec::new();
459 let mut else_branch = None;
460
461 while self.match_token(TokenKind::Else) {
463 if self.match_token(TokenKind::If) {
464 let cond = self.parse_expression()?;
466 self.expect(TokenKind::Then)?;
467 let then = self.parse_expression()?;
468 else_ifs.push((cond, then));
469 } else {
470 else_branch = Some(self.parse_expression()?);
472 break;
473 }
474 }
475
476 Ok(Expr::If(Box::new(IfExpr {
477 condition,
478 then_branch,
479 else_branch,
480 else_ifs,
481 span,
482 })))
483 }
484
485 fn parse_let_expression(&mut self) -> Result<Expr, ParseError> {
488 let span = self.previous.span;
489 let name = self.expect_identifier()?;
490
491 self.expect_kind(TokenKind::Assign)?; let value = self.parse_additive()?;
496
497 self.expect_kind(TokenKind::In)?;
499
500 let body = self.parse_expression()?;
501
502 Ok(Expr::Let(Box::new(LetExpr {
503 name,
504 value,
505 body,
506 span,
507 })))
508 }
509
510 fn parse_query_expression(&mut self) -> Result<Expr, ParseError> {
512 let span = self.previous.span;
513 let source = self.parse_primary()?; let filter = if self.match_token(TokenKind::Where) {
516 Some(self.parse_expression()?)
517 } else {
518 None
519 };
520
521 self.expect(TokenKind::Select)?;
522 let projection = self.parse_expression()?;
523
524 Ok(Expr::Query(Box::new(QueryExpr {
525 source,
526 filter,
527 projection,
528 span,
529 })))
530 }
531
532 fn parse_array_literal(&mut self) -> Result<Expr, ParseError> {
534 let mut items = Vec::new();
535
536 if !self.check(TokenKind::RBracket) {
537 loop {
538 items.push(self.parse_expression()?);
539 if !self.match_token(TokenKind::Comma) {
540 break;
541 }
542 if self.check(TokenKind::RBracket) {
544 break;
545 }
546 }
547 }
548
549 self.expect(TokenKind::RBracket)?;
550 Ok(Expr::Array(items))
551 }
552
553 fn parse_grouped_or_lambda(&mut self) -> Result<Expr, ParseError> {
555 if self.check_ident() {
557 let first_name = self.expect_identifier()?;
558
559 if self.match_token(TokenKind::Comma) {
560 let mut params = vec![first_name];
562
563 loop {
564 params.push(self.expect_identifier()?);
565 if !self.match_token(TokenKind::Comma) {
566 break;
567 }
568 }
569
570 self.expect(TokenKind::RParen)?;
571 self.expect(TokenKind::Arrow)?;
572 let body = self.parse_expression()?;
573
574 return Ok(Expr::Lambda(Box::new(LambdaExpr {
575 params,
576 body,
577 span: Span::new(0, 0),
578 })));
579 }
580
581 if self.match_token(TokenKind::RParen) {
583 if self.match_token(TokenKind::Arrow) {
584 let body = self.parse_expression()?;
585 return Ok(Expr::Lambda(Box::new(LambdaExpr {
586 params: vec![first_name],
587 body,
588 span: Span::new(0, 0),
589 })));
590 }
591
592 return Ok(Expr::Var(VarExpr {
594 name: first_name,
595 span: Span::new(0, 0),
596 }));
597 }
598
599 let var = Expr::Var(VarExpr {
603 name: first_name,
604 span: Span::new(0, 0),
605 });
606
607 let expr = self.continue_expression(var)?;
609 self.expect(TokenKind::RParen)?;
610 return Ok(expr);
611 }
612
613 let expr = self.parse_expression()?;
615 self.expect(TokenKind::RParen)?;
616 Ok(expr)
617 }
618
619 fn continue_expression(&mut self, left: Expr) -> Result<Expr, ParseError> {
621 let mut expr = left;
624
625 loop {
627 if self.match_token(TokenKind::Question) {
628 expr = Expr::Postfix(Box::new(PostfixExpr {
629 expr,
630 op: PostfixOp::Truthy,
631 span: self.previous.span,
632 }));
633 } else if self.match_token(TokenKind::Dot) {
634 let name = self.expect_identifier()?;
635 if self.match_token(TokenKind::LParen) {
636 let args = self.parse_args()?;
637 expr = Expr::MethodCall(Box::new(MethodCallExpr {
638 object: expr,
639 method: name,
640 args,
641 span: self.previous.span,
642 }));
643 } else {
644 expr = Expr::Property(Box::new(PropertyExpr {
645 object: expr,
646 property: name,
647 span: self.previous.span,
648 }));
649 }
650 } else if self.match_token(TokenKind::LBracket) {
651 let index = self.parse_expression()?;
652 self.expect(TokenKind::RBracket)?;
653 expr = Expr::Index(Box::new(IndexExpr {
654 object: expr,
655 index,
656 span: self.previous.span,
657 }));
658 } else {
659 break;
660 }
661 }
662
663 expr = self.parse_binary_rest(expr, 0)?;
666
667 Ok(expr)
668 }
669
670 fn parse_binary_rest(&mut self, mut left: Expr, min_prec: u8) -> Result<Expr, ParseError> {
672 loop {
673 let (op, prec) = match &self.current.kind {
674 TokenKind::Or => (BinaryOp::Or, 1),
675 TokenKind::And => (BinaryOp::And, 2),
676 TokenKind::EqEq => (BinaryOp::Eq, 3),
677 TokenKind::EqEqEq | TokenKind::Is | TokenKind::Eq
678 | TokenKind::Equals | TokenKind::SameAs => (BinaryOp::StrictEq, 3),
679 TokenKind::NotEq | TokenKind::LtGt => (BinaryOp::NotEq, 3),
680 TokenKind::NotEqEq | TokenKind::Isnt
681 | TokenKind::IsNot | TokenKind::NotEqKw => (BinaryOp::StrictNotEq, 3),
682 TokenKind::Lt => (BinaryOp::Lt, 4),
683 TokenKind::Gt => (BinaryOp::Gt, 4),
684 TokenKind::LtEq => (BinaryOp::LtEq, 4),
685 TokenKind::GtEq => (BinaryOp::GtEq, 4),
686 TokenKind::Plus => (BinaryOp::Add, 5),
687 TokenKind::Minus => (BinaryOp::Sub, 5),
688 TokenKind::Star => (BinaryOp::Mul, 6),
689 TokenKind::Slash => (BinaryOp::Div, 6),
690 TokenKind::Percent => (BinaryOp::Mod, 6),
691 TokenKind::SlashQuestion => (BinaryOp::SafeDivZero, 6),
692 TokenKind::SlashBang => (BinaryOp::SafeDivNull, 6),
693 TokenKind::Caret => (BinaryOp::Pow, 7),
694 _ => break,
695 };
696
697 if prec < min_prec {
698 break;
699 }
700
701 self.advance();
702 let right = self.parse_unary()?;
703 let next_min = if op.is_right_assoc() { prec } else { prec + 1 };
704 let right = self.parse_binary_rest(right, next_min)?;
705 left = Expr::binary(left, op, right);
706 }
707
708 Ok(left)
709 }
710
711 fn parse_args(&mut self) -> Result<Vec<Expr>, ParseError> {
713 let mut args = Vec::new();
714
715 if !self.check(TokenKind::RParen) {
716 loop {
717 args.push(self.parse_expression()?);
718 if !self.match_token(TokenKind::Comma) {
719 break;
720 }
721 }
722 }
723
724 self.expect(TokenKind::RParen)?;
725 Ok(args)
726 }
727
728 fn expect(&mut self, kind: TokenKind) -> Result<(), ParseError> {
730 if self.check(kind.clone()) {
731 self.advance();
732 Ok(())
733 } else {
734 Err(ParseError::UnexpectedToken {
735 expected: format!("{}", kind),
736 found: format!("{}", self.current.kind),
737 position: self.current.span.start,
738 })
739 }
740 }
741
742 fn expect_kind(&mut self, kind: TokenKind) -> Result<(), ParseError> {
744 self.expect(kind)
745 }
746
747 fn expect_identifier(&mut self) -> Result<String, ParseError> {
750 if let TokenKind::Ident(name) = &self.current.kind {
752 let name = name.clone();
753 self.advance();
754 return Ok(name);
755 }
756
757 let name = match &self.current.kind {
759 TokenKind::Map => "map",
760 TokenKind::Filter => "filter",
761 TokenKind::Reduce => "reduce",
762 TokenKind::All => "all",
763 TokenKind::Some => "some",
764 TokenKind::Merge => "merge",
765 TokenKind::Contains => "contains",
766 TokenKind::Missing => "missing",
767 TokenKind::MissingSome => "missing_some",
768 TokenKind::Log => "log",
769 TokenKind::In => "in",
770 _ => {
771 return Err(ParseError::UnexpectedToken {
772 expected: "identifier".into(),
773 found: format!("{}", self.current.kind),
774 position: self.current.span.start,
775 });
776 }
777 };
778
779 self.advance();
780 Ok(name.to_string())
781 }
782
783 fn check_ident(&self) -> bool {
785 matches!(self.current.kind, TokenKind::Ident(_))
786 }
787
788 fn check(&self, kind: TokenKind) -> bool {
790 std::mem::discriminant(&self.current.kind) == std::mem::discriminant(&kind)
791 }
792
793 fn match_token(&mut self, kind: TokenKind) -> bool {
795 if self.check(kind) {
796 self.advance();
797 true
798 } else {
799 false
800 }
801 }
802
803 fn advance(&mut self) {
805 self.previous = self.current.clone();
806 self.current = self.lexer.next_token();
807 }
808
809 fn is_at_end(&self) -> bool {
811 matches!(self.current.kind, TokenKind::Eof)
812 }
813}
814
815#[cfg(test)]
816mod tests {
817 use super::*;
818
819 fn parse(source: &str) -> Result<Expr, ParseError> {
820 let lexer = Lexer::new(source);
821 let mut parser = Parser::new(lexer);
822 parser.parse()
823 }
824
825 #[test]
826 fn test_simple_literal() {
827 assert_eq!(parse("42").unwrap(), Expr::int(42));
828 assert_eq!(parse("3.14").unwrap(), Expr::float(3.14));
829 assert_eq!(parse("true").unwrap(), Expr::bool(true));
830 assert_eq!(parse("null").unwrap(), Expr::null());
831 }
832
833 #[test]
834 fn test_variable() {
835 let expr = parse("foo").unwrap();
836 match expr {
837 Expr::Var(v) => assert_eq!(v.name, "foo"),
838 _ => panic!("Expected variable"),
839 }
840 }
841
842 #[test]
843 fn test_path_variable() {
844 let expr = parse("/users/count").unwrap();
845 match expr {
846 Expr::Var(v) => assert_eq!(v.name, "/users/count"),
847 _ => panic!("Expected variable"),
848 }
849 }
850
851 #[test]
852 fn test_binary_operators() {
853 let expr = parse("a + b").unwrap();
854 match expr {
855 Expr::Binary(b) => {
856 assert_eq!(b.op, BinaryOp::Add);
857 }
858 _ => panic!("Expected binary"),
859 }
860 }
861
862 #[test]
863 fn test_operator_precedence() {
864 let expr = parse("a + b * c").unwrap();
866 match expr {
867 Expr::Binary(b) => {
868 assert_eq!(b.op, BinaryOp::Add);
869 match &b.right {
870 Expr::Binary(inner) => {
871 assert_eq!(inner.op, BinaryOp::Mul);
872 }
873 _ => panic!("Expected nested binary"),
874 }
875 }
876 _ => panic!("Expected binary"),
877 }
878 }
879
880 #[test]
881 fn test_comparison() {
882 let expr = parse("x < 10").unwrap();
883 match expr {
884 Expr::Binary(b) => {
885 assert_eq!(b.op, BinaryOp::Lt);
886 }
887 _ => panic!("Expected binary"),
888 }
889 }
890
891 #[test]
892 fn test_logical_and() {
893 let expr = parse("a and b").unwrap();
894 match expr {
895 Expr::Binary(b) => {
896 assert_eq!(b.op, BinaryOp::And);
897 }
898 _ => panic!("Expected binary"),
899 }
900 }
901
902 #[test]
903 fn test_equality_synonyms() {
904 for source in &["a === b", "a is b", "a eq b", "a equals b", "a same_as b"] {
906 let expr = parse(source).unwrap();
907 match expr {
908 Expr::Binary(b) => {
909 assert_eq!(b.op, BinaryOp::StrictEq, "Failed for: {}", source);
910 }
911 _ => panic!("Expected binary for: {}", source),
912 }
913 }
914 }
915
916 #[test]
917 fn test_if_expression() {
918 let expr = parse("if x > 0 then 1 else 0").unwrap();
919 match expr {
920 Expr::If(i) => {
921 assert!(i.else_branch.is_some());
922 }
923 _ => panic!("Expected if"),
924 }
925 }
926
927 #[test]
928 fn test_function_call() {
929 let expr = parse("max(a, b, c)").unwrap();
930 match expr {
931 Expr::Call(c) => {
932 assert_eq!(c.function, "max");
933 assert_eq!(c.args.len(), 3);
934 }
935 _ => panic!("Expected call"),
936 }
937 }
938
939 #[test]
940 fn test_method_call() {
941 let expr = parse("items.filter(x => x > 0)").unwrap();
942 match expr {
943 Expr::MethodCall(m) => {
944 assert_eq!(m.method, "filter");
945 }
946 _ => panic!("Expected method call"),
947 }
948 }
949
950 #[test]
951 fn test_lambda() {
952 let expr = parse("x => x * 2").unwrap();
953 match expr {
954 Expr::Lambda(l) => {
955 assert_eq!(l.params, vec!["x"]);
956 }
957 _ => panic!("Expected lambda"),
958 }
959 }
960
961 #[test]
962 fn test_array_literal() {
963 let expr = parse("[1, 2, 3]").unwrap();
964 match expr {
965 Expr::Array(items) => {
966 assert_eq!(items.len(), 3);
967 }
968 _ => panic!("Expected array"),
969 }
970 }
971
972 #[test]
973 fn test_in_operator() {
974 let expr = parse("x in [1, 2, 3]").unwrap();
975 match expr {
976 Expr::Binary(b) => {
977 assert_eq!(b.op, BinaryOp::In);
978 }
979 _ => panic!("Expected binary"),
980 }
981 }
982
983 #[test]
984 fn test_safe_division() {
985 let expr = parse("a /? b").unwrap();
986 match expr {
987 Expr::Binary(b) => {
988 assert_eq!(b.op, BinaryOp::SafeDivZero);
989 }
990 _ => panic!("Expected binary"),
991 }
992
993 let expr = parse("a /! b").unwrap();
994 match expr {
995 Expr::Binary(b) => {
996 assert_eq!(b.op, BinaryOp::SafeDivNull);
997 }
998 _ => panic!("Expected binary"),
999 }
1000 }
1001
1002 #[test]
1003 fn test_truthy_postfix() {
1004 let expr = parse("x?").unwrap();
1005 match expr {
1006 Expr::Postfix(p) => {
1007 assert_eq!(p.op, PostfixOp::Truthy);
1008 }
1009 _ => panic!("Expected postfix"),
1010 }
1011 }
1012
1013 #[test]
1014 fn test_null_coalesce() {
1015 let expr = parse("a ?? b").unwrap();
1016 match expr {
1017 Expr::NullCoalesce(_, _) => {}
1018 _ => panic!("Expected null coalesce"),
1019 }
1020 }
1021
1022 #[test]
1023 fn test_complex_expression() {
1024 let expr = parse("alert_acknowledged and time_since_alert_secs < 120").unwrap();
1026 match expr {
1027 Expr::Binary(b) => {
1028 assert_eq!(b.op, BinaryOp::And);
1029 }
1030 _ => panic!("Expected binary"),
1031 }
1032 }
1033
1034 #[test]
1035 fn test_clamp_expression() {
1036 let expr = parse("clamp(0, 100, raw_score)").unwrap();
1037 match expr {
1038 Expr::Call(c) => {
1039 assert_eq!(c.function, "clamp");
1040 assert_eq!(c.args.len(), 3);
1041 }
1042 _ => panic!("Expected call"),
1043 }
1044 }
1045}