1use super::ast::*;
4use super::{ParseError, Parser};
5use crate::lexer::Token;
6
7impl<'a> Parser<'a> {
8 pub fn parse_match_clause(&mut self, optional: bool) -> Result<MatchClause, ParseError> {
16 self.expect(&Token::Match)?;
17 let pattern = self.parse_pattern()?;
18
19 let temporal_predicate = self.parse_optional_temporal_predicate()?;
21
22 let where_clause = if self.eat(&Token::Where) {
23 Some(self.parse_expression()?)
24 } else {
25 None
26 };
27
28 Ok(MatchClause {
29 optional,
30 pattern,
31 temporal_predicate,
32 where_clause,
33 })
34 }
35
36 pub fn parse_return_clause(&mut self) -> Result<ReturnClause, ParseError> {
40 self.expect(&Token::Return)?;
41
42 let distinct = self.eat(&Token::Distinct);
43
44 let items = self.parse_return_items()?;
45
46 let order_by = if self.check(&Token::Order) {
47 Some(self.parse_order_by()?)
48 } else {
49 None
50 };
51
52 let skip = if self.eat(&Token::Skip) {
53 Some(self.parse_expression()?)
54 } else {
55 None
56 };
57
58 let limit = if self.eat(&Token::Limit) {
59 Some(self.parse_expression()?)
60 } else {
61 None
62 };
63
64 Ok(ReturnClause {
65 distinct,
66 items,
67 order_by,
68 skip,
69 limit,
70 })
71 }
72
73 pub fn parse_create_clause(&mut self) -> Result<CreateClause, ParseError> {
77 self.expect(&Token::Create)?;
78 let pattern = self.parse_pattern()?;
79 Ok(CreateClause { pattern })
80 }
81
82 pub fn parse_set_clause(&mut self) -> Result<SetClause, ParseError> {
86 self.expect(&Token::Set)?;
87
88 let mut items = Vec::new();
89 items.push(self.parse_set_item()?);
90 while self.eat(&Token::Comma) {
91 items.push(self.parse_set_item()?);
92 }
93
94 Ok(SetClause { items })
95 }
96
97 pub fn parse_remove_clause(&mut self) -> Result<RemoveClause, ParseError> {
101 self.expect(&Token::Remove)?;
102
103 let mut items = Vec::new();
104 items.push(self.parse_remove_item()?);
105 while self.eat(&Token::Comma) {
106 items.push(self.parse_remove_item()?);
107 }
108
109 Ok(RemoveClause { items })
110 }
111
112 pub fn parse_delete_clause(&mut self, detach: bool) -> Result<DeleteClause, ParseError> {
117 self.expect(&Token::Delete)?;
118
119 let mut exprs = Vec::new();
120 exprs.push(self.parse_expression()?);
121 while self.eat(&Token::Comma) {
122 exprs.push(self.parse_expression()?);
123 }
124
125 Ok(DeleteClause { detach, exprs })
126 }
127
128 pub fn parse_with_clause(&mut self) -> Result<WithClause, ParseError> {
132 self.expect(&Token::With)?;
133
134 let distinct = self.eat(&Token::Distinct);
135 let items = self.parse_return_items()?;
136
137 let where_clause = if self.eat(&Token::Where) {
138 Some(self.parse_expression()?)
139 } else {
140 None
141 };
142
143 Ok(WithClause {
144 distinct,
145 items,
146 where_clause,
147 })
148 }
149
150 pub fn parse_unwind_clause(&mut self) -> Result<UnwindClause, ParseError> {
154 self.expect(&Token::Unwind)?;
155 let expr = self.parse_expression()?;
156 self.expect(&Token::As)?;
157 let variable = self.expect_ident()?;
158 Ok(UnwindClause { expr, variable })
159 }
160
161 pub fn parse_merge_clause(&mut self) -> Result<MergeClause, ParseError> {
165 self.expect(&Token::Merge)?;
166 let pattern = self.parse_pattern()?;
167
168 let mut on_match = Vec::new();
169 let mut on_create = Vec::new();
170
171 loop {
173 if self.check(&Token::On) {
174 let next = self.tokens.get(self.pos + 1).map(|(t, _)| t.clone());
176 match next {
177 Some(Token::Match) => {
178 self.advance(); self.advance(); self.expect(&Token::Set)?;
181 on_match.push(self.parse_set_item()?);
182 while self.eat(&Token::Comma) {
183 on_match.push(self.parse_set_item()?);
184 }
185 }
186 Some(Token::Create) => {
187 self.advance(); self.advance(); self.expect(&Token::Set)?;
190 on_create.push(self.parse_set_item()?);
191 while self.eat(&Token::Comma) {
192 on_create.push(self.parse_set_item()?);
193 }
194 }
195 _ => break,
196 }
197 } else {
198 break;
199 }
200 }
201
202 Ok(MergeClause {
203 pattern,
204 on_match,
205 on_create,
206 })
207 }
208
209 pub fn parse_create_index_clause(&mut self) -> Result<CreateIndexClause, ParseError> {
214 self.expect(&Token::Index)?;
215
216 let name = if !self.check(&Token::On) {
218 Some(self.expect_ident()?)
219 } else {
220 None
221 };
222
223 self.expect(&Token::On)?;
224 self.expect(&Token::Colon)?;
225 let label = self.expect_ident()?;
226 self.expect(&Token::LParen)?;
227 let property = self.expect_ident()?;
228 self.expect(&Token::RParen)?;
229
230 Ok(CreateIndexClause {
231 name,
232 target: IndexTarget::NodeLabel(label),
233 property,
234 })
235 }
236
237 pub fn parse_create_edge_index_clause(&mut self) -> Result<CreateIndexClause, ParseError> {
242 self.expect(&Token::Edge)?;
243 self.expect(&Token::Index)?;
244
245 let name = if !self.check(&Token::On) {
247 Some(self.expect_ident()?)
248 } else {
249 None
250 };
251
252 self.expect(&Token::On)?;
253 self.expect(&Token::Colon)?;
254 let rel_type = self.expect_ident()?;
255 self.expect(&Token::LParen)?;
256 let property = self.expect_ident()?;
257 self.expect(&Token::RParen)?;
258
259 Ok(CreateIndexClause {
260 name,
261 target: IndexTarget::RelationshipType(rel_type),
262 property,
263 })
264 }
265
266 pub fn parse_drop_index_clause(&mut self) -> Result<DropIndexClause, ParseError> {
270 self.expect(&Token::Drop)?;
271 self.expect(&Token::Index)?;
272 let name = self.expect_ident()?;
273 Ok(DropIndexClause { name })
274 }
275
276 #[cfg(feature = "subgraph")]
281 pub fn parse_create_snapshot_clause(&mut self) -> Result<CreateSnapshotClause, ParseError> {
282 self.expect(&Token::Snapshot)?;
283
284 self.expect(&Token::LParen)?;
286
287 let variable = if self.check(&Token::Colon) {
289 None
290 } else {
291 match self.peek() {
292 Some(Token::Ident(_) | Token::BacktickIdent(_)) => Some(self.expect_ident()?),
293 _ => None,
294 }
295 };
296
297 let mut labels = Vec::new();
299 while self.eat(&Token::Colon) {
300 labels.push(self.expect_ident()?);
301 }
302
303 let properties = if self.check(&Token::LBrace) {
305 Some(self.parse_map_literal()?)
306 } else {
307 None
308 };
309
310 self.expect(&Token::RParen)?;
311
312 let temporal_anchor = if self.check(&Token::At) {
314 self.advance(); self.expect(&Token::Time)?;
316 Some(self.parse_expression()?)
317 } else {
318 None
319 };
320
321 self.expect(&Token::From)?;
323 let from_match = self.parse_match_clause(false)?;
324
325 self.expect(&Token::Return)?;
326 let from_return = self.parse_return_items()?;
327
328 Ok(CreateSnapshotClause {
329 variable,
330 labels,
331 properties,
332 temporal_anchor,
333 from_match,
334 from_return,
335 })
336 }
337
338 #[cfg(feature = "hypergraph")]
343 pub fn parse_create_hyperedge_clause(&mut self) -> Result<CreateHyperedgeClause, ParseError> {
344 self.expect(&Token::Hyperedge)?;
345
346 self.expect(&Token::LParen)?;
348
349 let variable = if self.check(&Token::Colon) {
351 None
352 } else {
353 match self.peek() {
354 Some(Token::Ident(_) | Token::BacktickIdent(_)) => Some(self.expect_ident()?),
355 _ => None,
356 }
357 };
358
359 let mut labels = Vec::new();
361 while self.eat(&Token::Colon) {
362 labels.push(self.expect_ident()?);
363 }
364
365 self.expect(&Token::RParen)?;
366
367 self.expect(&Token::From)?;
369 let sources = self.parse_hyperedge_participant_list()?;
370
371 self.expect_to_keyword()?;
373 let targets = self.parse_hyperedge_participant_list()?;
374
375 Ok(CreateHyperedgeClause {
376 variable,
377 labels,
378 sources,
379 targets,
380 })
381 }
382
383 #[cfg(feature = "hypergraph")]
388 pub fn parse_match_hyperedge_clause(&mut self) -> Result<MatchHyperedgeClause, ParseError> {
389 self.expect(&Token::Hyperedge)?;
390
391 self.expect(&Token::LParen)?;
392
393 let variable = if self.check(&Token::Colon) {
395 None
396 } else {
397 match self.peek() {
398 Some(Token::Ident(_) | Token::BacktickIdent(_)) => {
399 Some(self.expect_ident()?)
401 }
402 _ => None,
403 }
404 };
405
406 let mut labels = Vec::new();
408 while self.eat(&Token::Colon) {
409 labels.push(self.expect_ident()?);
410 }
411
412 self.expect(&Token::RParen)?;
413
414 Ok(MatchHyperedgeClause { variable, labels })
415 }
416
417 #[cfg(feature = "hypergraph")]
420 fn parse_hyperedge_participant_list(&mut self) -> Result<Vec<Expression>, ParseError> {
421 self.expect(&Token::LParen)?;
422 let mut participants = Vec::new();
423
424 if !self.check(&Token::RParen) {
425 participants.push(self.parse_hyperedge_participant()?);
426 while self.eat(&Token::Comma) {
427 participants.push(self.parse_hyperedge_participant()?);
428 }
429 }
430
431 self.expect(&Token::RParen)?;
432 Ok(participants)
433 }
434
435 #[cfg(feature = "hypergraph")]
437 fn parse_hyperedge_participant(&mut self) -> Result<Expression, ParseError> {
438 let name = self.expect_ident()?;
441 let base_expr = Expression::Variable(name);
442
443 if self.check(&Token::At) {
445 self.advance(); self.expect(&Token::Time)?;
447 let timestamp = self.parse_expression()?;
448 Ok(Expression::TemporalRef {
449 node: Box::new(base_expr),
450 timestamp: Box::new(timestamp),
451 })
452 } else {
453 Ok(base_expr)
454 }
455 }
456
457 #[cfg(feature = "hypergraph")]
459 fn expect_to_keyword(&mut self) -> Result<(), ParseError> {
460 match self.tokens.get(self.pos) {
461 Some((Token::Ident(name), _)) if name.eq_ignore_ascii_case("TO") => {
462 self.pos += 1;
463 Ok(())
464 }
465 _ => Err(self.error("expected TO keyword")),
466 }
467 }
468
469 fn parse_optional_temporal_predicate(
475 &mut self,
476 ) -> Result<Option<TemporalPredicate>, ParseError> {
477 if self.check(&Token::At) {
478 self.advance(); self.expect(&Token::Time)?;
481 let expr = self.parse_expression()?;
482 Ok(Some(TemporalPredicate::AsOf(expr)))
483 } else if self.check(&Token::Between) {
484 self.advance(); self.expect(&Token::Time)?;
487 let start = self.parse_expression_no_and()?;
488 self.expect(&Token::And)?;
489 let end = self.parse_expression_no_and()?;
490 Ok(Some(TemporalPredicate::Between(start, end)))
491 } else {
492 Ok(None)
493 }
494 }
495
496 fn parse_return_items(&mut self) -> Result<Vec<ReturnItem>, ParseError> {
500 let mut items = Vec::new();
501 items.push(self.parse_return_item()?);
502 while self.eat(&Token::Comma) {
503 items.push(self.parse_return_item()?);
504 }
505 Ok(items)
506 }
507
508 fn parse_return_item(&mut self) -> Result<ReturnItem, ParseError> {
510 let expr = self.parse_expression()?;
511 let alias = if self.eat(&Token::As) {
512 Some(self.expect_ident()?)
513 } else {
514 None
515 };
516 Ok(ReturnItem { expr, alias })
517 }
518
519 fn parse_order_by(&mut self) -> Result<Vec<OrderItem>, ParseError> {
521 self.expect(&Token::Order)?;
522 self.expect(&Token::By)?;
523
524 let mut items = Vec::new();
525 items.push(self.parse_order_item()?);
526 while self.eat(&Token::Comma) {
527 items.push(self.parse_order_item()?);
528 }
529 Ok(items)
530 }
531
532 fn parse_order_item(&mut self) -> Result<OrderItem, ParseError> {
534 let expr = self.parse_expression()?;
535 let ascending = if self.eat(&Token::Desc) {
536 false
537 } else {
538 self.eat(&Token::Asc);
540 true
541 };
542 Ok(OrderItem { expr, ascending })
543 }
544
545 fn parse_set_item(&mut self) -> Result<SetItem, ParseError> {
551 let name = self.expect_ident()?;
552 let mut target = Expression::Variable(name);
553 while self.eat(&Token::Dot) {
555 let prop = self.expect_ident()?;
556 target = Expression::Property(Box::new(target), prop);
557 }
558 self.expect(&Token::Eq)?;
559 let value = self.parse_expression()?;
560 Ok(SetItem::Property { target, value })
561 }
562
563 fn parse_remove_item(&mut self) -> Result<RemoveItem, ParseError> {
565 let name = self.expect_ident()?;
566
567 if self.eat(&Token::Colon) {
568 let label = self.expect_ident()?;
570 Ok(RemoveItem::Label {
571 variable: name,
572 label,
573 })
574 } else if self.eat(&Token::Dot) {
575 let prop = self.expect_ident()?;
577 let expr = Expression::Property(Box::new(Expression::Variable(name)), prop);
578 Ok(RemoveItem::Property(expr))
579 } else {
580 Err(self.error("expected '.' or ':' after identifier in REMOVE item"))
581 }
582 }
583}
584
585#[cfg(test)]
590mod tests {
591 use super::*;
592 use crate::lexer::lex;
593
594 fn make_parser(input: &str) -> (Vec<(Token, crate::lexer::Span)>, String) {
596 let tokens = lex(input).expect("lexing should succeed");
597 (tokens, input.to_string())
598 }
599
600 #[test]
605 fn match_simple_node() {
606 let (tokens, input) = make_parser("MATCH (n:Person)");
607 let mut p = Parser::new(&tokens, &input);
608 let mc = p.parse_match_clause(false).expect("should parse");
609
610 assert!(!mc.optional);
611 assert_eq!(mc.pattern.chains.len(), 1);
612 let node = match &mc.pattern.chains[0].elements[0] {
613 PatternElement::Node(n) => n,
614 _ => panic!("expected node"),
615 };
616 assert_eq!(node.variable, Some("n".to_string()));
617 assert_eq!(node.labels, vec!["Person".to_string()]);
618 assert!(mc.where_clause.is_none());
619 }
620
621 #[test]
622 fn match_with_where() {
623 let (tokens, input) = make_parser("MATCH (n:Person) WHERE n.age > 30");
624 let mut p = Parser::new(&tokens, &input);
625 let mc = p.parse_match_clause(false).expect("should parse");
626
627 assert!(mc.where_clause.is_some());
628 let where_expr = mc.where_clause.expect("checked above");
629 assert!(matches!(
630 where_expr,
631 Expression::BinaryOp(BinaryOp::Gt, _, _)
632 ));
633 }
634
635 #[test]
636 fn match_optional() {
637 let (tokens, input) = make_parser("MATCH (n)");
638 let mut p = Parser::new(&tokens, &input);
639 let mc = p.parse_match_clause(true).expect("should parse");
640
641 assert!(mc.optional);
642 }
643
644 #[test]
645 fn match_with_relationship() {
646 let (tokens, input) = make_parser("MATCH (a)-[:KNOWS]->(b)");
647 let mut p = Parser::new(&tokens, &input);
648 let mc = p.parse_match_clause(false).expect("should parse");
649
650 assert_eq!(mc.pattern.chains[0].elements.len(), 3);
651 }
652
653 #[test]
658 fn return_simple_variable() {
659 let (tokens, input) = make_parser("RETURN n");
660 let mut p = Parser::new(&tokens, &input);
661 let rc = p.parse_return_clause().expect("should parse");
662
663 assert!(!rc.distinct);
664 assert_eq!(rc.items.len(), 1);
665 assert_eq!(rc.items[0].expr, Expression::Variable("n".to_string()));
666 assert!(rc.items[0].alias.is_none());
667 assert!(rc.order_by.is_none());
668 assert!(rc.skip.is_none());
669 assert!(rc.limit.is_none());
670 }
671
672 #[test]
673 fn return_distinct() {
674 let (tokens, input) = make_parser("RETURN DISTINCT n.name");
675 let mut p = Parser::new(&tokens, &input);
676 let rc = p.parse_return_clause().expect("should parse");
677
678 assert!(rc.distinct);
679 }
680
681 #[test]
682 fn return_multiple_with_alias() {
683 let (tokens, input) = make_parser("RETURN n.name AS name, n.age AS age");
684 let mut p = Parser::new(&tokens, &input);
685 let rc = p.parse_return_clause().expect("should parse");
686
687 assert_eq!(rc.items.len(), 2);
688 assert_eq!(rc.items[0].alias, Some("name".to_string()));
689 assert_eq!(rc.items[1].alias, Some("age".to_string()));
690 }
691
692 #[test]
693 fn return_with_order_by() {
694 let (tokens, input) = make_parser("RETURN n.name ORDER BY n.name ASC");
695 let mut p = Parser::new(&tokens, &input);
696 let rc = p.parse_return_clause().expect("should parse");
697
698 let order = rc.order_by.expect("should have ORDER BY");
699 assert_eq!(order.len(), 1);
700 assert!(order[0].ascending);
701 }
702
703 #[test]
704 fn return_with_order_by_desc() {
705 let (tokens, input) = make_parser("RETURN n ORDER BY n.age DESC");
706 let mut p = Parser::new(&tokens, &input);
707 let rc = p.parse_return_clause().expect("should parse");
708
709 let order = rc.order_by.expect("should have ORDER BY");
710 assert!(!order[0].ascending);
711 }
712
713 #[test]
714 fn return_with_order_by_multiple() {
715 let (tokens, input) = make_parser("RETURN n ORDER BY n.name ASC, n.age DESC");
716 let mut p = Parser::new(&tokens, &input);
717 let rc = p.parse_return_clause().expect("should parse");
718
719 let order = rc.order_by.expect("should have ORDER BY");
720 assert_eq!(order.len(), 2);
721 assert!(order[0].ascending);
722 assert!(!order[1].ascending);
723 }
724
725 #[test]
726 fn return_with_skip() {
727 let (tokens, input) = make_parser("RETURN n SKIP 5");
728 let mut p = Parser::new(&tokens, &input);
729 let rc = p.parse_return_clause().expect("should parse");
730
731 assert_eq!(
732 rc.skip.expect("should have SKIP"),
733 Expression::Literal(Literal::Integer(5))
734 );
735 }
736
737 #[test]
738 fn return_with_limit() {
739 let (tokens, input) = make_parser("RETURN n LIMIT 10");
740 let mut p = Parser::new(&tokens, &input);
741 let rc = p.parse_return_clause().expect("should parse");
742
743 assert_eq!(
744 rc.limit.expect("should have LIMIT"),
745 Expression::Literal(Literal::Integer(10))
746 );
747 }
748
749 #[test]
750 fn return_with_order_skip_limit() {
751 let (tokens, input) = make_parser("RETURN n ORDER BY n.name SKIP 5 LIMIT 10");
752 let mut p = Parser::new(&tokens, &input);
753 let rc = p.parse_return_clause().expect("should parse");
754
755 assert!(rc.order_by.is_some());
756 assert!(rc.skip.is_some());
757 assert!(rc.limit.is_some());
758 }
759
760 #[test]
761 fn return_order_by_default_ascending() {
762 let (tokens, input) = make_parser("RETURN n ORDER BY n.name");
763 let mut p = Parser::new(&tokens, &input);
764 let rc = p.parse_return_clause().expect("should parse");
765
766 let order = rc.order_by.expect("should have ORDER BY");
767 assert!(order[0].ascending); }
769
770 #[test]
775 fn create_simple_node() {
776 let (tokens, input) = make_parser("CREATE (n:Person)");
777 let mut p = Parser::new(&tokens, &input);
778 let cc = p.parse_create_clause().expect("should parse");
779
780 let node = match &cc.pattern.chains[0].elements[0] {
781 PatternElement::Node(n) => n,
782 _ => panic!("expected node"),
783 };
784 assert_eq!(node.variable, Some("n".to_string()));
785 assert_eq!(node.labels, vec!["Person".to_string()]);
786 }
787
788 #[test]
789 fn create_node_with_properties() {
790 let (tokens, input) = make_parser("CREATE (n:Person {name: 'Alice', age: 30})");
791 let mut p = Parser::new(&tokens, &input);
792 let cc = p.parse_create_clause().expect("should parse");
793
794 let node = match &cc.pattern.chains[0].elements[0] {
795 PatternElement::Node(n) => n,
796 _ => panic!("expected node"),
797 };
798 assert!(node.properties.is_some());
799 let props = node.properties.as_ref().expect("checked above");
800 assert_eq!(props.len(), 2);
801 }
802
803 #[test]
804 fn create_relationship() {
805 let (tokens, input) =
806 make_parser("CREATE (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'})");
807 let mut p = Parser::new(&tokens, &input);
808 let cc = p.parse_create_clause().expect("should parse");
809
810 assert_eq!(cc.pattern.chains[0].elements.len(), 3);
811 }
812
813 #[test]
818 fn set_single_property() {
819 let (tokens, input) = make_parser("SET n.name = 'Alice'");
820 let mut p = Parser::new(&tokens, &input);
821 let sc = p.parse_set_clause().expect("should parse");
822
823 assert_eq!(sc.items.len(), 1);
824 match &sc.items[0] {
825 SetItem::Property { target, value } => {
826 assert_eq!(
827 *target,
828 Expression::Property(
829 Box::new(Expression::Variable("n".to_string())),
830 "name".to_string(),
831 )
832 );
833 assert_eq!(
834 *value,
835 Expression::Literal(Literal::String("Alice".to_string()))
836 );
837 }
838 }
839 }
840
841 #[test]
842 fn set_multiple_properties() {
843 let (tokens, input) = make_parser("SET n.name = 'Alice', n.age = 30");
844 let mut p = Parser::new(&tokens, &input);
845 let sc = p.parse_set_clause().expect("should parse");
846
847 assert_eq!(sc.items.len(), 2);
848 }
849
850 #[test]
851 fn remove_property() {
852 let (tokens, input) = make_parser("REMOVE n.email");
853 let mut p = Parser::new(&tokens, &input);
854 let rc = p.parse_remove_clause().expect("should parse");
855
856 assert_eq!(rc.items.len(), 1);
857 match &rc.items[0] {
858 RemoveItem::Property(expr) => {
859 assert_eq!(
860 *expr,
861 Expression::Property(
862 Box::new(Expression::Variable("n".to_string())),
863 "email".to_string(),
864 )
865 );
866 }
867 _ => panic!("expected property remove"),
868 }
869 }
870
871 #[test]
872 fn remove_label() {
873 let (tokens, input) = make_parser("REMOVE n:Person");
874 let mut p = Parser::new(&tokens, &input);
875 let rc = p.parse_remove_clause().expect("should parse");
876
877 assert_eq!(rc.items.len(), 1);
878 match &rc.items[0] {
879 RemoveItem::Label { variable, label } => {
880 assert_eq!(variable, "n");
881 assert_eq!(label, "Person");
882 }
883 _ => panic!("expected label remove"),
884 }
885 }
886
887 #[test]
888 fn remove_multiple() {
889 let (tokens, input) = make_parser("REMOVE n.email, n:Temp");
890 let mut p = Parser::new(&tokens, &input);
891 let rc = p.parse_remove_clause().expect("should parse");
892
893 assert_eq!(rc.items.len(), 2);
894 assert!(matches!(&rc.items[0], RemoveItem::Property(_)));
895 assert!(matches!(&rc.items[1], RemoveItem::Label { .. }));
896 }
897
898 #[test]
903 fn delete_single() {
904 let (tokens, input) = make_parser("DELETE n");
905 let mut p = Parser::new(&tokens, &input);
906 let dc = p.parse_delete_clause(false).expect("should parse");
907
908 assert!(!dc.detach);
909 assert_eq!(dc.exprs.len(), 1);
910 assert_eq!(dc.exprs[0], Expression::Variable("n".to_string()));
911 }
912
913 #[test]
914 fn delete_multiple() {
915 let (tokens, input) = make_parser("DELETE n, m");
916 let mut p = Parser::new(&tokens, &input);
917 let dc = p.parse_delete_clause(false).expect("should parse");
918
919 assert_eq!(dc.exprs.len(), 2);
920 }
921
922 #[test]
923 fn delete_detach() {
924 let (tokens, input) = make_parser("DELETE n");
925 let mut p = Parser::new(&tokens, &input);
926 let dc = p.parse_delete_clause(true).expect("should parse");
927
928 assert!(dc.detach);
929 }
930
931 #[test]
936 fn with_simple() {
937 let (tokens, input) = make_parser("WITH n, m");
938 let mut p = Parser::new(&tokens, &input);
939 let wc = p.parse_with_clause().expect("should parse");
940
941 assert!(!wc.distinct);
942 assert_eq!(wc.items.len(), 2);
943 assert!(wc.where_clause.is_none());
944 }
945
946 #[test]
947 fn with_distinct_and_where() {
948 let (tokens, input) = make_parser("WITH DISTINCT n WHERE n.age > 30");
949 let mut p = Parser::new(&tokens, &input);
950 let wc = p.parse_with_clause().expect("should parse");
951
952 assert!(wc.distinct);
953 assert_eq!(wc.items.len(), 1);
954 assert!(wc.where_clause.is_some());
955 }
956
957 #[test]
958 fn merge_simple() {
959 let (tokens, input) = make_parser("MERGE (n:Person {name: 'Alice'})");
960 let mut p = Parser::new(&tokens, &input);
961 let mc = p.parse_merge_clause().expect("should parse");
962
963 let node = match &mc.pattern.chains[0].elements[0] {
964 PatternElement::Node(n) => n,
965 _ => panic!("expected node"),
966 };
967 assert_eq!(node.variable, Some("n".to_string()));
968 assert_eq!(node.labels, vec!["Person".to_string()]);
969 assert!(mc.on_match.is_empty());
970 assert!(mc.on_create.is_empty());
971 }
972
973 #[test]
975 fn merge_on_create_set() {
976 let (tokens, input) =
977 make_parser("MERGE (n:Person {name: 'Alice'}) ON CREATE SET n.created = true");
978 let mut p = Parser::new(&tokens, &input);
979 let mc = p.parse_merge_clause().expect("should parse");
980
981 assert!(mc.on_match.is_empty());
982 assert_eq!(mc.on_create.len(), 1);
983 match &mc.on_create[0] {
984 SetItem::Property { target, value } => {
985 assert_eq!(
986 *target,
987 Expression::Property(
988 Box::new(Expression::Variable("n".to_string())),
989 "created".to_string(),
990 )
991 );
992 assert_eq!(*value, Expression::Literal(Literal::Bool(true)));
993 }
994 }
995 }
996
997 #[test]
999 fn merge_on_match_set() {
1000 let (tokens, input) =
1001 make_parser("MERGE (n:Person {name: 'Alice'}) ON MATCH SET n.seen = true");
1002 let mut p = Parser::new(&tokens, &input);
1003 let mc = p.parse_merge_clause().expect("should parse");
1004
1005 assert_eq!(mc.on_match.len(), 1);
1006 assert!(mc.on_create.is_empty());
1007 }
1008
1009 #[test]
1011 fn merge_on_create_and_on_match() {
1012 let (tokens, input) = make_parser(
1013 "MERGE (n:Person {name: 'Alice'}) ON CREATE SET n.created = true ON MATCH SET n.seen = true",
1014 );
1015 let mut p = Parser::new(&tokens, &input);
1016 let mc = p.parse_merge_clause().expect("should parse");
1017
1018 assert_eq!(mc.on_create.len(), 1);
1019 assert_eq!(mc.on_match.len(), 1);
1020 }
1021
1022 #[test]
1024 fn merge_on_create_multiple_items() {
1025 let (tokens, input) = make_parser(
1026 "MERGE (n:Person {name: 'Alice'}) ON CREATE SET n.created = true, n.age = 1",
1027 );
1028 let mut p = Parser::new(&tokens, &input);
1029 let mc = p.parse_merge_clause().expect("should parse");
1030
1031 assert_eq!(mc.on_create.len(), 2);
1032 }
1033
1034 #[test]
1039 fn match_at_time_literal() {
1040 let (tokens, input) = make_parser("MATCH (n:Person) AT TIME 1000 WHERE n.age > 30");
1041 let mut p = Parser::new(&tokens, &input);
1042 let mc = p.parse_match_clause(false).expect("should parse");
1043
1044 assert!(mc.temporal_predicate.is_some());
1045 match mc.temporal_predicate.as_ref().expect("checked above") {
1046 TemporalPredicate::AsOf(expr) => {
1047 assert_eq!(*expr, Expression::Literal(Literal::Integer(1000)));
1048 }
1049 _ => panic!("expected AsOf temporal predicate"),
1050 }
1051 assert!(mc.where_clause.is_some());
1052 }
1053
1054 #[test]
1055 fn match_at_time_function_call() {
1056 let (tokens, input) =
1057 make_parser("MATCH (n:Person) AT TIME datetime('2024-01-15T00:00:00Z')");
1058 let mut p = Parser::new(&tokens, &input);
1059 let mc = p.parse_match_clause(false).expect("should parse");
1060
1061 assert!(mc.temporal_predicate.is_some());
1062 match mc.temporal_predicate.as_ref().expect("checked above") {
1063 TemporalPredicate::AsOf(Expression::FunctionCall { name, .. }) => {
1064 assert_eq!(name, "datetime");
1065 }
1066 _ => panic!("expected AsOf with datetime function call"),
1067 }
1068 }
1069
1070 #[test]
1071 fn match_at_time_no_where() {
1072 let (tokens, input) = make_parser("MATCH (n:Person) AT TIME 1000");
1073 let mut p = Parser::new(&tokens, &input);
1074 let mc = p.parse_match_clause(false).expect("should parse");
1075
1076 assert!(mc.temporal_predicate.is_some());
1077 assert!(mc.where_clause.is_none());
1078 }
1079
1080 #[test]
1081 fn match_no_temporal_predicate() {
1082 let (tokens, input) = make_parser("MATCH (n:Person) WHERE n.age > 30");
1083 let mut p = Parser::new(&tokens, &input);
1084 let mc = p.parse_match_clause(false).expect("should parse");
1085
1086 assert!(mc.temporal_predicate.is_none());
1087 assert!(mc.where_clause.is_some());
1088 }
1089
1090 #[test]
1092 fn match_between_time() {
1093 let (tokens, input) = make_parser("MATCH (n:Person) BETWEEN TIME 100 AND 200");
1094 let mut p = Parser::new(&tokens, &input);
1095 let mc = p.parse_match_clause(false).expect("should parse");
1096
1097 assert!(mc.temporal_predicate.is_some());
1098 match mc.temporal_predicate.as_ref().expect("checked above") {
1099 TemporalPredicate::Between(start, end) => {
1100 assert_eq!(*start, Expression::Literal(Literal::Integer(100)));
1101 assert_eq!(*end, Expression::Literal(Literal::Integer(200)));
1102 }
1103 _ => panic!("expected Between temporal predicate"),
1104 }
1105 }
1106
1107 #[test]
1108 fn match_between_time_with_where() {
1109 let (tokens, input) =
1110 make_parser("MATCH (n:Person) BETWEEN TIME 100 AND 200 WHERE n.age > 30");
1111 let mut p = Parser::new(&tokens, &input);
1112 let mc = p.parse_match_clause(false).expect("should parse");
1113
1114 assert!(mc.temporal_predicate.is_some());
1115 assert!(matches!(
1116 mc.temporal_predicate.as_ref().expect("checked"),
1117 TemporalPredicate::Between(_, _)
1118 ));
1119 assert!(mc.where_clause.is_some());
1120 }
1121
1122 #[test]
1127 fn unwind_list_literal() {
1128 let (tokens, input) = make_parser("UNWIND [1, 2, 3] AS x");
1129 let mut p = Parser::new(&tokens, &input);
1130 let uc = p.parse_unwind_clause().expect("should parse");
1131
1132 assert_eq!(
1133 uc.expr,
1134 Expression::ListLiteral(vec![
1135 Expression::Literal(Literal::Integer(1)),
1136 Expression::Literal(Literal::Integer(2)),
1137 Expression::Literal(Literal::Integer(3)),
1138 ])
1139 );
1140 assert_eq!(uc.variable, "x");
1141 }
1142
1143 #[test]
1144 fn unwind_variable_expression() {
1145 let (tokens, input) = make_parser("UNWIND items AS item");
1146 let mut p = Parser::new(&tokens, &input);
1147 let uc = p.parse_unwind_clause().expect("should parse");
1148
1149 assert_eq!(uc.expr, Expression::Variable("items".to_string()));
1150 assert_eq!(uc.variable, "item");
1151 }
1152
1153 #[test]
1154 fn unwind_property_expression() {
1155 let (tokens, input) = make_parser("UNWIND n.hobbies AS h");
1156 let mut p = Parser::new(&tokens, &input);
1157 let uc = p.parse_unwind_clause().expect("should parse");
1158
1159 assert_eq!(
1160 uc.expr,
1161 Expression::Property(
1162 Box::new(Expression::Variable("n".to_string())),
1163 "hobbies".to_string(),
1164 )
1165 );
1166 assert_eq!(uc.variable, "h");
1167 }
1168
1169 #[test]
1170 fn unwind_empty_list() {
1171 let (tokens, input) = make_parser("UNWIND [] AS x");
1172 let mut p = Parser::new(&tokens, &input);
1173 let uc = p.parse_unwind_clause().expect("should parse");
1174
1175 assert_eq!(uc.expr, Expression::ListLiteral(vec![]));
1176 assert_eq!(uc.variable, "x");
1177 }
1178
1179 #[cfg(feature = "hypergraph")]
1184 mod hyperedge_tests {
1185 use super::*;
1186 use crate::parser::parse_query;
1187
1188 #[test]
1190 fn hyperedge_basic() {
1191 let (tokens, input) =
1192 make_parser("CREATE HYPEREDGE (h:GroupMigration) FROM (a, b) TO (c)");
1193 let mut p = Parser::new(&tokens, &input);
1194 p.advance(); let hc = p.parse_create_hyperedge_clause().expect("should parse");
1196
1197 assert_eq!(hc.variable, Some("h".to_string()));
1198 assert_eq!(hc.labels, vec!["GroupMigration".to_string()]);
1199 assert_eq!(hc.sources.len(), 2);
1200 assert_eq!(hc.sources[0], Expression::Variable("a".to_string()));
1201 assert_eq!(hc.sources[1], Expression::Variable("b".to_string()));
1202 assert_eq!(hc.targets.len(), 1);
1203 assert_eq!(hc.targets[0], Expression::Variable("c".to_string()));
1204 }
1205
1206 #[test]
1208 fn hyperedge_with_temporal_ref() {
1209 let (tokens, input) = make_parser(
1210 "CREATE HYPEREDGE (h:TemporalShift) FROM (person AT TIME 1000) TO (city2)",
1211 );
1212 let mut p = Parser::new(&tokens, &input);
1213 p.advance(); let hc = p.parse_create_hyperedge_clause().expect("should parse");
1215
1216 assert_eq!(hc.variable, Some("h".to_string()));
1217 assert_eq!(hc.labels, vec!["TemporalShift".to_string()]);
1218 assert_eq!(hc.sources.len(), 1);
1219 assert!(matches!(&hc.sources[0], Expression::TemporalRef { .. }));
1221 if let Expression::TemporalRef { node, timestamp } = &hc.sources[0] {
1222 assert_eq!(**node, Expression::Variable("person".to_string()));
1223 assert_eq!(**timestamp, Expression::Literal(Literal::Integer(1000)));
1224 }
1225 assert_eq!(hc.targets.len(), 1);
1226 }
1227
1228 #[test]
1230 fn hyperedge_no_variable() {
1231 let (tokens, input) = make_parser("CREATE HYPEREDGE (:Migration) FROM (a) TO (b)");
1232 let mut p = Parser::new(&tokens, &input);
1233 p.advance(); let hc = p.parse_create_hyperedge_clause().expect("should parse");
1235
1236 assert!(hc.variable.is_none());
1237 assert_eq!(hc.labels, vec!["Migration".to_string()]);
1238 }
1239
1240 #[test]
1242 fn hyperedge_multiple_sources_and_targets() {
1243 let (tokens, input) = make_parser("CREATE HYPEREDGE (h:Foo) FROM (a, b, c) TO (d, e)");
1244 let mut p = Parser::new(&tokens, &input);
1245 p.advance(); let hc = p.parse_create_hyperedge_clause().expect("should parse");
1247
1248 assert_eq!(hc.sources.len(), 3);
1249 assert_eq!(hc.targets.len(), 2);
1250 }
1251
1252 #[test]
1254 fn match_hyperedge_basic() {
1255 let (tokens, input) = make_parser("MATCH HYPEREDGE (h:GroupMigration)");
1256 let mut p = Parser::new(&tokens, &input);
1257 p.advance(); let mhc = p.parse_match_hyperedge_clause().expect("should parse");
1259
1260 assert_eq!(mhc.variable, Some("h".to_string()));
1261 assert_eq!(mhc.labels, vec!["GroupMigration".to_string()]);
1262 }
1263
1264 #[test]
1266 fn match_hyperedge_no_label() {
1267 let (tokens, input) = make_parser("MATCH HYPEREDGE (h)");
1268 let mut p = Parser::new(&tokens, &input);
1269 p.advance(); let mhc = p.parse_match_hyperedge_clause().expect("should parse");
1271
1272 assert_eq!(mhc.variable, Some("h".to_string()));
1273 assert!(mhc.labels.is_empty());
1274 }
1275
1276 #[test]
1278 fn query_create_hyperedge_full() {
1279 let q = parse_query("CREATE HYPEREDGE (h:GroupMigration) FROM (a, b) TO (c)")
1280 .expect("should parse");
1281 assert_eq!(q.clauses.len(), 1);
1282 assert!(matches!(&q.clauses[0], Clause::CreateHyperedge(_)));
1283 }
1284
1285 #[test]
1287 fn query_match_hyperedge_full() {
1288 let q =
1289 parse_query("MATCH HYPEREDGE (h:GroupMigration) RETURN h").expect("should parse");
1290 assert_eq!(q.clauses.len(), 2);
1291 assert!(matches!(&q.clauses[0], Clause::MatchHyperedge(_)));
1292 assert!(matches!(&q.clauses[1], Clause::Return(_)));
1293 }
1294 }
1295
1296 #[cfg(feature = "subgraph")]
1301 mod snapshot_tests {
1302 use super::*;
1303
1304 #[test]
1306 fn snapshot_basic() {
1307 let (tokens, input) =
1308 make_parser("CREATE SNAPSHOT (s:Snap) FROM MATCH (n:Person) RETURN n");
1309 let mut p = Parser::new(&tokens, &input);
1310 p.advance(); let sc = p.parse_create_snapshot_clause().expect("should parse");
1312
1313 assert_eq!(sc.variable, Some("s".to_string()));
1314 assert_eq!(sc.labels, vec!["Snap".to_string()]);
1315 assert!(sc.properties.is_none());
1316 assert!(sc.temporal_anchor.is_none());
1317 assert!(!sc.from_match.optional);
1319 assert_eq!(sc.from_match.pattern.chains.len(), 1);
1320 assert_eq!(sc.from_return.len(), 1);
1322 assert_eq!(
1323 sc.from_return[0].expr,
1324 Expression::Variable("n".to_string())
1325 );
1326 }
1327
1328 #[test]
1330 fn snapshot_with_properties() {
1331 let (tokens, input) = make_parser(
1332 "CREATE SNAPSHOT (s:Snap {name: 'test'}) FROM MATCH (n:Person) RETURN n",
1333 );
1334 let mut p = Parser::new(&tokens, &input);
1335 p.advance(); let sc = p.parse_create_snapshot_clause().expect("should parse");
1337
1338 assert_eq!(sc.variable, Some("s".to_string()));
1339 assert!(sc.properties.is_some());
1340 let props = sc.properties.expect("checked above");
1341 assert_eq!(props.len(), 1);
1342 assert_eq!(props[0].0, "name");
1343 }
1344
1345 #[test]
1347 fn snapshot_with_at_time() {
1348 let (tokens, input) =
1349 make_parser("CREATE SNAPSHOT (s:Snap) AT TIME 1000 FROM MATCH (n:Person) RETURN n");
1350 let mut p = Parser::new(&tokens, &input);
1351 p.advance(); let sc = p.parse_create_snapshot_clause().expect("should parse");
1353
1354 assert!(sc.temporal_anchor.is_some());
1355 assert_eq!(
1356 sc.temporal_anchor.expect("checked"),
1357 Expression::Literal(Literal::Integer(1000))
1358 );
1359 }
1360
1361 #[test]
1363 fn snapshot_with_where() {
1364 let (tokens, input) = make_parser(
1365 "CREATE SNAPSHOT (s:Snap) FROM MATCH (n:Person) WHERE n.age > 30 RETURN n",
1366 );
1367 let mut p = Parser::new(&tokens, &input);
1368 p.advance(); let sc = p.parse_create_snapshot_clause().expect("should parse");
1370
1371 assert!(sc.from_match.where_clause.is_some());
1372 }
1373
1374 #[test]
1376 fn snapshot_multiple_return_items() {
1377 let (tokens, input) = make_parser(
1378 "CREATE SNAPSHOT (s:Snap) FROM MATCH (n:Person)-[:KNOWS]->(m) RETURN n, m",
1379 );
1380 let mut p = Parser::new(&tokens, &input);
1381 p.advance(); let sc = p.parse_create_snapshot_clause().expect("should parse");
1383
1384 assert_eq!(sc.from_return.len(), 2);
1385 }
1386
1387 #[test]
1389 fn snapshot_no_variable() {
1390 let (tokens, input) =
1391 make_parser("CREATE SNAPSHOT (:Snap) FROM MATCH (n:Person) RETURN n");
1392 let mut p = Parser::new(&tokens, &input);
1393 p.advance(); let sc = p.parse_create_snapshot_clause().expect("should parse");
1395
1396 assert!(sc.variable.is_none());
1397 assert_eq!(sc.labels, vec!["Snap".to_string()]);
1398 }
1399
1400 #[test]
1402 fn snapshot_at_time_function_call() {
1403 let (tokens, input) = make_parser(
1404 "CREATE SNAPSHOT (s:Snap) AT TIME datetime('2024-01-15T00:00:00Z') FROM MATCH (n:Person) RETURN n",
1405 );
1406 let mut p = Parser::new(&tokens, &input);
1407 p.advance(); let sc = p.parse_create_snapshot_clause().expect("should parse");
1409
1410 assert!(sc.temporal_anchor.is_some());
1411 match sc.temporal_anchor.expect("checked") {
1412 Expression::FunctionCall { name, .. } => {
1413 assert_eq!(name, "datetime");
1414 }
1415 _ => panic!("expected function call for temporal anchor"),
1416 }
1417 }
1418 }
1419}