1use crate::error::ParseError;
2use lora_ast::*;
3use pest::iterators::Pair;
4use pest::Parser;
5use smallvec::SmallVec;
6
7#[derive(pest_derive::Parser)]
8#[grammar = "cypher.pest"]
9struct LoraParser;
10
11pub fn parse_query(input: &str) -> Result<Document, ParseError> {
12 let mut pairs = LoraParser::parse(Rule::query, input)
13 .map_err(|e| ParseError::new(e.to_string(), 0, input.len()))?;
14
15 let pair = pairs
16 .next()
17 .ok_or_else(|| ParseError::new("expected query", 0, input.len()))?;
18
19 lower_query(pair)
20}
21
22fn lower_query(pair: Pair<Rule>) -> Result<Document, ParseError> {
23 let span = pair_span(&pair);
24 let mut inner = pair.into_inner();
25
26 let stmt = inner
27 .find(|p| p.as_rule() == Rule::statement)
28 .ok_or_else(|| ParseError::new("expected statement", span.start, span.end))?;
29
30 Ok(Document {
31 statement: lower_statement(stmt)?,
32 span,
33 })
34}
35
36fn lower_statement(pair: Pair<Rule>) -> Result<Statement, ParseError> {
37 let inner = single_inner(pair)?;
38 match inner.as_rule() {
39 Rule::regular_query => Ok(Statement::Query(Query::Regular(lower_regular_query(
40 inner,
41 )?))),
42 Rule::standalone_call => Ok(Statement::Query(Query::StandaloneCall(
43 lower_standalone_call(inner)?,
44 ))),
45 _ => Err(unexpected_rule("statement", inner)),
46 }
47}
48
49fn lower_regular_query(pair: Pair<Rule>) -> Result<RegularQuery, ParseError> {
50 let span = pair_span(&pair);
51 let mut inner = pair.into_inner();
52
53 let first = inner
54 .next()
55 .ok_or_else(|| ParseError::new("expected single query", span.start, span.end))?;
56
57 let head = lower_single_query(first)?;
58 let mut unions = Vec::new();
59
60 while let Some(union_pair) = inner.next() {
61 let uq = inner
62 .next()
63 .ok_or_else(|| ParseError::new("expected query after UNION", span.start, span.end))?;
64
65 let all = union_pair
66 .clone()
67 .into_inner()
68 .any(|p| p.as_rule() == Rule::ALL);
69 let union_span = Span::new(union_pair.as_span().start(), uq.as_span().end());
70
71 unions.push(UnionPart {
72 all,
73 query: lower_single_query(uq)?,
74 span: union_span,
75 });
76 }
77
78 Ok(RegularQuery { head, unions, span })
79}
80
81fn lower_single_query(pair: Pair<Rule>) -> Result<SingleQuery, ParseError> {
82 match pair.as_rule() {
83 Rule::single_query => lower_single_query(single_inner(pair)?),
84 Rule::single_part_query => Ok(SingleQuery::SinglePart(lower_single_part_query(pair)?)),
85 Rule::multi_part_query => Ok(SingleQuery::MultiPart(lower_multi_part_query(pair)?)),
86 _ => Err(unexpected_rule("single_query", pair)),
87 }
88}
89
90fn lower_multi_part_query(pair: Pair<Rule>) -> Result<MultiPartQuery, ParseError> {
91 let span = pair_span(&pair);
92 let mut parts = Vec::new();
93 let mut tail = None;
94
95 for p in pair.into_inner() {
96 match p.as_rule() {
97 Rule::query_part => parts.push(lower_query_part(p)?),
98 Rule::single_part_query => tail = Some(lower_single_part_query(p)?),
99 _ => {}
100 }
101 }
102
103 Ok(MultiPartQuery {
104 parts,
105 tail: Box::new(tail.ok_or_else(|| {
106 ParseError::new("expected single-part tail query", span.start, span.end)
107 })?),
108 span,
109 })
110}
111
112fn lower_query_part(pair: Pair<Rule>) -> Result<QueryPart, ParseError> {
113 let span = pair_span(&pair);
114 let mut reading_clauses = Vec::new();
115 let mut updating_clauses = Vec::new();
116 let mut with_clause = None;
117
118 for p in pair.into_inner() {
119 match p.as_rule() {
120 Rule::reading_clause => reading_clauses.push(lower_reading_clause(p)?),
121 Rule::updating_clause => updating_clauses.push(lower_updating_clause(p)?),
122 Rule::with_clause => with_clause = Some(lower_with_clause(p)?),
123 _ => {}
124 }
125 }
126
127 Ok(QueryPart {
128 reading_clauses,
129 updating_clauses,
130 with_clause: with_clause
131 .ok_or_else(|| ParseError::new("expected WITH clause", span.start, span.end))?,
132 span,
133 })
134}
135
136fn lower_single_part_query(pair: Pair<Rule>) -> Result<SinglePartQuery, ParseError> {
137 let span = pair_span(&pair);
138 let mut reading_clauses = Vec::new();
139 let mut updating_clauses = Vec::new();
140 let mut return_clause = None;
141
142 for p in pair.into_inner() {
143 match p.as_rule() {
144 Rule::reading_clause => reading_clauses.push(lower_reading_clause(p)?),
145 Rule::updating_clause => updating_clauses.push(lower_updating_clause(p)?),
146 Rule::return_clause => return_clause = Some(lower_return_clause(p)?),
147 _ => {}
148 }
149 }
150
151 Ok(SinglePartQuery {
152 reading_clauses,
153 updating_clauses,
154 return_clause,
155 span,
156 })
157}
158
159fn lower_reading_clause(pair: Pair<Rule>) -> Result<ReadingClause, ParseError> {
160 let inner = single_inner(pair)?;
161 match inner.as_rule() {
162 Rule::match_clause => Ok(ReadingClause::Match(lower_match(inner)?)),
163 Rule::unwind_clause => Ok(ReadingClause::Unwind(lower_unwind(inner)?)),
164 Rule::in_query_call => Ok(ReadingClause::InQueryCall(lower_in_query_call(inner)?)),
165 _ => Err(unexpected_rule("reading_clause", inner)),
166 }
167}
168
169fn lower_updating_clause(pair: Pair<Rule>) -> Result<UpdatingClause, ParseError> {
170 let inner = single_inner(pair)?;
171 match inner.as_rule() {
172 Rule::create_clause => Ok(UpdatingClause::Create(lower_create(inner)?)),
173 Rule::merge_clause => Ok(UpdatingClause::Merge(lower_merge(inner)?)),
174 Rule::delete_clause => Ok(UpdatingClause::Delete(lower_delete(inner)?)),
175 Rule::set_clause => Ok(UpdatingClause::Set(lower_set(inner)?)),
176 Rule::remove_clause => Ok(UpdatingClause::Remove(lower_remove(inner)?)),
177 _ => Err(unexpected_rule("updating_clause", inner)),
178 }
179}
180
181fn lower_match(pair: Pair<Rule>) -> Result<Match, ParseError> {
182 let span = pair_span(&pair);
183 let mut optional = false;
184 let mut pattern = None;
185 let mut where_ = None;
186
187 for p in pair.into_inner() {
188 match p.as_rule() {
189 Rule::OPTIONAL => optional = true,
190 Rule::pattern => pattern = Some(lower_pattern(p)?),
191 Rule::where_clause => where_ = Some(lower_where_clause(p)?),
192 _ => {}
193 }
194 }
195
196 Ok(Match {
197 optional,
198 pattern: pattern
199 .ok_or_else(|| ParseError::new("expected pattern", span.start, span.end))?,
200 where_,
201 span,
202 })
203}
204
205fn lower_unwind(pair: Pair<Rule>) -> Result<Unwind, ParseError> {
206 let span = pair_span(&pair);
207 let mut expr = None;
208 let mut alias = None;
209
210 for p in pair.into_inner() {
211 match p.as_rule() {
212 Rule::expression => expr = Some(lower_expression(p)?),
213 Rule::variable => alias = Some(lower_variable(p)?),
214 _ => {}
215 }
216 }
217
218 Ok(Unwind {
219 expr: expr.ok_or_else(|| ParseError::new("expected expression", span.start, span.end))?,
220 alias: alias.ok_or_else(|| ParseError::new("expected alias", span.start, span.end))?,
221 span,
222 })
223}
224
225fn lower_create(pair: Pair<Rule>) -> Result<Create, ParseError> {
226 let span = pair_span(&pair);
227 let pattern = pair
228 .into_inner()
229 .find(|p| p.as_rule() == Rule::pattern)
230 .ok_or_else(|| ParseError::new("expected pattern", span.start, span.end))?;
231
232 Ok(Create {
233 pattern: lower_pattern(pattern)?,
234 span,
235 })
236}
237
238fn lower_merge(pair: Pair<Rule>) -> Result<Merge, ParseError> {
239 let span = pair_span(&pair);
240 let mut pattern_part = None;
241 let mut actions = Vec::new();
242
243 for p in pair.into_inner() {
244 match p.as_rule() {
245 Rule::pattern_part => pattern_part = Some(lower_pattern_part(p)?),
246 Rule::merge_action => actions.push(lower_merge_action(p)?),
247 _ => {}
248 }
249 }
250
251 Ok(Merge {
252 pattern_part: pattern_part
253 .ok_or_else(|| ParseError::new("expected pattern part", span.start, span.end))?,
254 actions,
255 span,
256 })
257}
258
259fn lower_merge_action(pair: Pair<Rule>) -> Result<MergeAction, ParseError> {
260 let span = pair_span(&pair);
261 let mut on_match = false;
262 let mut set = None;
263
264 for p in pair.into_inner() {
265 match p.as_rule() {
266 Rule::MATCH => on_match = true,
267 Rule::CREATE => on_match = false,
268 Rule::set_clause => set = Some(lower_set(p)?),
269 _ => {}
270 }
271 }
272
273 Ok(MergeAction {
274 on_match,
275 set: set.ok_or_else(|| ParseError::new("expected SET clause", span.start, span.end))?,
276 span,
277 })
278}
279
280fn lower_delete(pair: Pair<Rule>) -> Result<Delete, ParseError> {
281 let span = pair_span(&pair);
282 let mut detach = false;
283 let mut expressions = Vec::new();
284
285 for p in pair.into_inner() {
286 match p.as_rule() {
287 Rule::DETACH => detach = true,
288 Rule::expression => expressions.push(lower_expression(p)?),
289 _ => {}
290 }
291 }
292
293 Ok(Delete {
294 detach,
295 expressions,
296 span,
297 })
298}
299
300fn lower_set(pair: Pair<Rule>) -> Result<Set, ParseError> {
301 let span = pair_span(&pair);
302 let mut items = Vec::new();
303
304 for p in pair.into_inner() {
305 if p.as_rule() == Rule::set_item {
306 items.push(lower_set_item(p)?);
307 }
308 }
309
310 Ok(Set { items, span })
311}
312fn lower_set_item(pair: Pair<Rule>) -> Result<SetItem, ParseError> {
313 let span = pair_span(&pair);
314 let inner: Vec<_> = pair.into_inner().collect();
315
316 match inner.as_slice() {
317 [var, labels]
318 if var.as_rule() == Rule::variable && labels.as_rule() == Rule::node_labels =>
319 {
320 let variable = lower_variable(var.clone())?;
321 let labels: Vec<String> = lower_node_labels(labels.clone())?
322 .into_iter()
323 .flat_map(|g| g.into_iter())
324 .collect();
325 Ok(SetItem::SetLabels {
326 variable,
327 labels,
328 span,
329 })
330 }
331
332 [var, op, value]
333 if var.as_rule() == Rule::variable
334 && op.as_rule() == Rule::plus_eq
335 && value.as_rule() == Rule::expression =>
336 {
337 let variable = lower_variable(var.clone())?;
338 let value = lower_expression(value.clone())?;
339 Ok(SetItem::MutateVariable {
340 variable,
341 value,
342 span,
343 })
344 }
345
346 [var, op, value]
347 if var.as_rule() == Rule::variable
348 && op.as_rule() == Rule::eq
349 && value.as_rule() == Rule::expression =>
350 {
351 let variable = lower_variable(var.clone())?;
352 let value = lower_expression(value.clone())?;
353 Ok(SetItem::SetVariable {
354 variable,
355 value,
356 span,
357 })
358 }
359
360 [target, op, value]
361 if target.as_rule() == Rule::property_set_target
362 && op.as_rule() == Rule::eq
363 && value.as_rule() == Rule::expression =>
364 {
365 let target = lower_property_set_target(target.clone())?;
366 let value = lower_expression(value.clone())?;
367 Ok(SetItem::SetProperty {
368 target,
369 value,
370 span,
371 })
372 }
373
374 _ => Err(ParseError::new("invalid SET item", span.start, span.end)),
375 }
376}
377
378fn lower_property_set_target(pair: Pair<Rule>) -> Result<Expr, ParseError> {
379 let span = pair_span(&pair);
380 let mut inner = pair.into_inner();
381
382 let first = inner
383 .next()
384 .ok_or_else(|| ParseError::new("expected variable", span.start, span.end))?;
385 let mut expr = Expr::Variable(lower_variable(first)?);
386
387 for p in inner {
388 if p.as_rule() == Rule::property_lookup {
389 let p_span = pair_span(&p);
390 let key_pair = p
391 .into_inner()
392 .find(|q| q.as_rule() == Rule::property_key_name)
393 .ok_or_else(|| {
394 ParseError::new("expected property key", p_span.start, p_span.end)
395 })?;
396
397 let key = lower_schema_name(key_pair)?;
398 let merged = merge_spans(expr.span(), p_span);
399
400 expr = Expr::Property {
401 expr: Box::new(expr),
402 key,
403 span: merged,
404 };
405 }
406 }
407
408 Ok(expr)
409}
410
411fn lower_remove(pair: Pair<Rule>) -> Result<Remove, ParseError> {
412 let span = pair_span(&pair);
413 let mut items = Vec::new();
414
415 for p in pair.into_inner() {
416 if p.as_rule() == Rule::remove_item {
417 items.push(lower_remove_item(p)?);
418 }
419 }
420
421 Ok(Remove { items, span })
422}
423
424fn lower_remove_item(pair: Pair<Rule>) -> Result<RemoveItem, ParseError> {
425 let span = pair_span(&pair);
426 let inner: Vec<_> = pair.into_inner().collect();
427
428 if inner.len() == 2
429 && inner[0].as_rule() == Rule::variable
430 && inner[1].as_rule() == Rule::node_labels
431 {
432 let variable = lower_variable(inner[0].clone())?;
433 let labels: Vec<String> = lower_node_labels(inner[1].clone())?
434 .into_iter()
435 .flat_map(|g| g.into_iter())
436 .collect();
437 return Ok(RemoveItem::Labels {
438 variable,
439 labels,
440 span,
441 });
442 }
443
444 if inner.len() == 1 {
445 return Ok(RemoveItem::Property {
446 expr: lower_expression(inner[0].clone())?,
447 span,
448 });
449 }
450
451 Err(ParseError::new("invalid REMOVE item", span.start, span.end))
452}
453
454fn lower_in_query_call(pair: Pair<Rule>) -> Result<InQueryCall, ParseError> {
455 let span = pair_span(&pair);
456 let mut procedure = None;
457 let mut yield_items = Vec::new();
458 let mut where_ = None;
459
460 for p in pair.into_inner() {
461 match p.as_rule() {
462 Rule::procedure_invocation => procedure = Some(lower_procedure_invocation(p)?),
463 Rule::yield_clause => {
464 let (items, _all) = lower_yield_clause(p)?;
465 yield_items = items;
466 }
467 Rule::where_clause => where_ = Some(lower_where_clause(p)?),
468 _ => {}
469 }
470 }
471
472 Ok(InQueryCall {
473 procedure: procedure.ok_or_else(|| {
474 ParseError::new("expected procedure invocation", span.start, span.end)
475 })?,
476 yield_items,
477 where_,
478 span,
479 })
480}
481
482fn lower_standalone_call(pair: Pair<Rule>) -> Result<StandaloneCall, ParseError> {
483 let span = pair_span(&pair);
484 let mut procedure = None;
485 let mut yield_items = Vec::new();
486 let mut yield_all = false;
487
488 for p in pair.into_inner() {
489 match p.as_rule() {
490 Rule::procedure_invocation => {
491 procedure = Some(ProcedureInvocationKind::Explicit(
492 lower_procedure_invocation(p)?,
493 ));
494 }
495 Rule::procedure_name => {
496 procedure = Some(ProcedureInvocationKind::Implicit(lower_procedure_name(p)?));
497 }
498 Rule::yield_clause => {
499 let (items, all) = lower_yield_clause(p)?;
500 yield_items = items;
501 yield_all = all;
502 }
503 _ => {}
504 }
505 }
506
507 Ok(StandaloneCall {
508 procedure: procedure
509 .ok_or_else(|| ParseError::new("expected procedure", span.start, span.end))?,
510 yield_items,
511 yield_all,
512 span,
513 })
514}
515
516fn lower_procedure_invocation(pair: Pair<Rule>) -> Result<ProcedureInvocation, ParseError> {
517 let span = pair_span(&pair);
518 let mut name = None;
519 let mut args = Vec::new();
520
521 for p in pair.into_inner() {
522 match p.as_rule() {
523 Rule::procedure_name => name = Some(lower_procedure_name(p)?),
524 Rule::expression => args.push(lower_expression(p)?),
525 _ => {}
526 }
527 }
528
529 Ok(ProcedureInvocation {
530 name: name
531 .ok_or_else(|| ParseError::new("expected procedure name", span.start, span.end))?,
532 args,
533 span,
534 })
535}
536
537fn lower_procedure_name(pair: Pair<Rule>) -> Result<ProcedureName, ParseError> {
538 let span = pair_span(&pair);
539 let parts = lower_name_parts(pair)?;
540 Ok(ProcedureName { parts, span })
541}
542
543fn lower_yield_clause(pair: Pair<Rule>) -> Result<(Vec<YieldItem>, bool), ParseError> {
544 let mut items = Vec::new();
545 let mut all = false;
546
547 for p in pair.into_inner() {
548 match p.as_rule() {
549 Rule::STAR => all = true,
550 Rule::yield_items => {
551 for q in p.into_inner() {
552 if q.as_rule() == Rule::yield_item {
553 items.push(lower_yield_item(q)?);
554 }
555 }
556 }
557 _ => {}
558 }
559 }
560
561 Ok((items, all))
562}
563
564fn lower_yield_item(pair: Pair<Rule>) -> Result<YieldItem, ParseError> {
565 let span = pair_span(&pair);
566 let mut symbolic = None;
567 let mut alias = None;
568
569 for p in pair.into_inner() {
570 match p.as_rule() {
571 Rule::symbolic_name => symbolic = Some(lower_symbolic_name(p)?),
572 Rule::variable => alias = Some(lower_variable(p)?),
573 _ => {}
574 }
575 }
576
577 match (symbolic, alias) {
578 (Some(field), Some(alias)) => Ok(YieldItem {
579 field: Some(field),
580 alias,
581 span,
582 }),
583 (Some(name), None) => Ok(YieldItem {
584 field: None,
585 alias: Variable { name, span },
586 span,
587 }),
588 _ => Err(ParseError::new("invalid YIELD item", span.start, span.end)),
589 }
590}
591
592fn lower_with_clause(pair: Pair<Rule>) -> Result<With, ParseError> {
593 let span = pair_span(&pair);
594 let mut body = None;
595 let mut where_ = None;
596
597 for p in pair.into_inner() {
598 match p.as_rule() {
599 Rule::projection_body => body = Some(lower_projection_body(p)?),
600 Rule::where_clause => where_ = Some(lower_where_clause(p)?),
601 _ => {}
602 }
603 }
604
605 Ok(With {
606 body: body
607 .ok_or_else(|| ParseError::new("expected projection body", span.start, span.end))?,
608 where_,
609 span,
610 })
611}
612
613fn lower_where_clause(pair: Pair<Rule>) -> Result<Expr, ParseError> {
614 let span = pair_span(&pair);
615
616 let expr = pair
617 .into_inner()
618 .find(|p| p.as_rule() == Rule::expression)
619 .ok_or_else(|| ParseError::new("expected expression", span.start, span.end))?;
620
621 lower_expression(expr)
622}
623
624fn lower_return_clause(pair: Pair<Rule>) -> Result<Return, ParseError> {
625 let span = pair_span(&pair);
626 let body = pair
627 .into_inner()
628 .find(|p| p.as_rule() == Rule::projection_body)
629 .ok_or_else(|| ParseError::new("expected projection body", span.start, span.end))?;
630
631 Ok(Return {
632 body: lower_projection_body(body)?,
633 span,
634 })
635}
636
637fn lower_projection_body(pair: Pair<Rule>) -> Result<ProjectionBody, ParseError> {
638 let span = pair_span(&pair);
639 let mut distinct = false;
640 let mut items = Vec::new();
641 let mut order = Vec::new();
642 let mut skip = None;
643 let mut limit = None;
644
645 for p in pair.into_inner() {
646 match p.as_rule() {
647 Rule::DISTINCT => distinct = true,
648 Rule::projection_items => items = lower_projection_items(p)?,
649 Rule::order_clause => order = lower_order_clause(p)?,
650 Rule::skip_clause => {
651 let expr = p
652 .into_inner()
653 .find(|q| q.as_rule() == Rule::expression)
654 .ok_or_else(|| {
655 ParseError::new("expected skip expression", span.start, span.end)
656 })?;
657 skip = Some(lower_expression(expr)?);
658 }
659 Rule::limit_clause => {
660 let expr = p
661 .into_inner()
662 .find(|q| q.as_rule() == Rule::expression)
663 .ok_or_else(|| {
664 ParseError::new("expected limit expression", span.start, span.end)
665 })?;
666 limit = Some(lower_expression(expr)?);
667 }
668 _ => {}
669 }
670 }
671
672 Ok(ProjectionBody {
673 distinct,
674 items,
675 order,
676 skip,
677 limit,
678 span,
679 })
680}
681
682fn lower_projection_items(pair: Pair<Rule>) -> Result<Vec<ProjectionItem>, ParseError> {
683 let mut out = Vec::new();
684 for p in pair.into_inner() {
685 if p.as_rule() == Rule::projection_item {
686 out.push(lower_projection_item(p)?);
687 }
688 }
689 Ok(out)
690}
691
692fn lower_projection_item(pair: Pair<Rule>) -> Result<ProjectionItem, ParseError> {
693 let span = pair_span(&pair);
694 let mut expr = None;
695 let mut alias = None;
696 let mut saw_star = false;
697
698 for p in pair.into_inner() {
699 match p.as_rule() {
700 Rule::star => saw_star = true,
701 Rule::expression => expr = Some(lower_expression(p)?),
702 Rule::variable => alias = Some(lower_variable(p)?),
703 _ => {}
704 }
705 }
706
707 if saw_star {
708 return Ok(ProjectionItem::Star { span });
709 }
710
711 Ok(ProjectionItem::Expr {
712 expr: expr.ok_or_else(|| ParseError::new("expected expression", span.start, span.end))?,
713 alias,
714 span,
715 })
716}
717
718fn lower_order_clause(pair: Pair<Rule>) -> Result<Vec<SortItem>, ParseError> {
719 let mut out = Vec::new();
720 for p in pair.into_inner() {
721 if p.as_rule() == Rule::sort_item {
722 out.push(lower_sort_item(p)?);
723 }
724 }
725 Ok(out)
726}
727
728fn lower_sort_item(pair: Pair<Rule>) -> Result<SortItem, ParseError> {
729 let span = pair_span(&pair);
730 let mut expr = None;
731 let mut direction = SortDirection::Asc;
732
733 for p in pair.into_inner() {
734 match p.as_rule() {
735 Rule::expression => expr = Some(lower_expression(p)?),
736 Rule::DESC | Rule::DESCENDING => direction = SortDirection::Desc,
737 Rule::ASC | Rule::ASCENDING => direction = SortDirection::Asc,
738 _ => {}
739 }
740 }
741
742 Ok(SortItem {
743 expr: expr.ok_or_else(|| ParseError::new("expected expression", span.start, span.end))?,
744 direction,
745 span,
746 })
747}
748
749fn lower_pattern(pair: Pair<Rule>) -> Result<Pattern, ParseError> {
750 let span = pair_span(&pair);
751 let mut parts = Vec::new();
752
753 for p in pair.into_inner() {
754 if p.as_rule() == Rule::pattern_part {
755 parts.push(lower_pattern_part(p)?);
756 }
757 }
758
759 Ok(Pattern { parts, span })
760}
761
762fn lower_pattern_part(pair: Pair<Rule>) -> Result<PatternPart, ParseError> {
763 let span = pair_span(&pair);
764 let mut binding = None;
765 let mut element = None;
766 let mut saw_eq = false;
767
768 for p in pair.into_inner() {
769 match p.as_rule() {
770 Rule::variable if !saw_eq && binding.is_none() => binding = Some(lower_variable(p)?),
771 Rule::eq => saw_eq = true,
772 Rule::anonymous_pattern_part => {
773 for inner in p.into_inner() {
774 match inner.as_rule() {
775 Rule::pattern_element => element = Some(lower_pattern_element(inner)?),
776 Rule::shortest_path_pattern => {
777 element = Some(lower_shortest_path_pattern(inner)?)
778 }
779 _ => {}
780 }
781 }
782 }
783 Rule::pattern_element => element = Some(lower_pattern_element(p)?),
784 _ => {}
785 }
786 }
787
788 Ok(PatternPart {
789 binding,
790 element: element
791 .ok_or_else(|| ParseError::new("expected pattern element", span.start, span.end))?,
792 span,
793 })
794}
795
796fn lower_shortest_path_pattern(pair: Pair<Rule>) -> Result<PatternElement, ParseError> {
797 let span = pair_span(&pair);
798 let mut all = false;
799 let mut inner_element = None;
800
801 for p in pair.into_inner() {
802 match p.as_rule() {
803 Rule::SHORTEST_PATH => all = false,
804 Rule::ALL_SHORTEST_PATHS => all = true,
805 Rule::pattern_element => inner_element = Some(lower_pattern_element(p)?),
806 _ => {}
807 }
808 }
809
810 Ok(PatternElement::ShortestPath {
811 all,
812 element: Box::new(inner_element.ok_or_else(|| {
813 ParseError::new(
814 "expected pattern element in shortestPath",
815 span.start,
816 span.end,
817 )
818 })?),
819 span,
820 })
821}
822
823fn lower_pattern_element(pair: Pair<Rule>) -> Result<PatternElement, ParseError> {
824 let span = pair_span(&pair);
825 let mut inners = pair.into_inner().peekable();
826
827 let first = inners
828 .next()
829 .ok_or_else(|| ParseError::new("expected pattern element", span.start, span.end))?;
830
831 match first.as_rule() {
832 Rule::node_pattern => {
833 let head = lower_node_pattern(first)?;
834 let mut chain = Vec::new();
835
836 for p in inners {
837 if p.as_rule() == Rule::pattern_element_chain {
838 chain.push(lower_pattern_element_chain(p)?);
839 }
840 }
841
842 Ok(PatternElement::NodeChain { head, chain, span })
843 }
844 Rule::pattern_element => {
845 let inner = lower_pattern_element(first)?;
846 Ok(PatternElement::Parenthesized(Box::new(inner), span))
847 }
848 _ => Err(unexpected_rule("pattern_element", first)),
849 }
850}
851
852fn lower_pattern_element_chain(pair: Pair<Rule>) -> Result<PatternElementChain, ParseError> {
853 let span = pair_span(&pair);
854 let mut relationship = None;
855 let mut node = None;
856
857 for p in pair.into_inner() {
858 match p.as_rule() {
859 Rule::relationship_pattern => relationship = Some(lower_relationship_pattern(p)?),
860 Rule::node_pattern => node = Some(lower_node_pattern(p)?),
861 _ => {}
862 }
863 }
864
865 Ok(PatternElementChain {
866 relationship: relationship.ok_or_else(|| {
867 ParseError::new("expected relationship pattern", span.start, span.end)
868 })?,
869 node: node.ok_or_else(|| ParseError::new("expected node pattern", span.start, span.end))?,
870 span,
871 })
872}
873
874fn lower_node_pattern(pair: Pair<Rule>) -> Result<NodePattern, ParseError> {
875 let span = pair_span(&pair);
876 let mut variable = None;
877 let mut labels = SmallVec::new();
878 let mut properties = None;
879
880 for p in pair.into_inner() {
881 match p.as_rule() {
882 Rule::variable => variable = Some(lower_variable(p)?),
883 Rule::node_labels => labels = lower_node_labels(p)?,
884 Rule::properties => properties = Some(lower_properties(p)?),
885 _ => {}
886 }
887 }
888
889 Ok(NodePattern {
890 variable,
891 labels,
892 properties,
893 span,
894 })
895}
896
897fn lower_node_labels(pair: Pair<Rule>) -> Result<SmallVec<SmallVec<String, 2>, 2>, ParseError> {
898 let mut out: SmallVec<SmallVec<String, 2>, 2> = SmallVec::new();
899 for p in pair.into_inner() {
900 if p.as_rule() == Rule::node_label_set {
901 let mut group = SmallVec::new();
902 for q in p.into_inner() {
903 if q.as_rule() == Rule::label_name {
904 group.push(lower_schema_name(q)?);
905 }
906 }
907 if !group.is_empty() {
908 out.push(group);
909 }
910 }
911 }
912 Ok(out)
913}
914
915fn lower_relationship_pattern(pair: Pair<Rule>) -> Result<RelationshipPattern, ParseError> {
916 let span = pair_span(&pair);
917 let mut left = false;
918 let mut right = false;
919 let mut detail = None;
920
921 for p in pair.into_inner() {
922 match p.as_rule() {
923 Rule::left_arrow => left = true,
924 Rule::right_arrow => right = true,
925 Rule::relationship_detail => detail = Some(lower_relationship_detail(p)?),
926 _ => {}
927 }
928 }
929
930 let direction = match (left, right) {
931 (true, false) => Direction::Left,
932 (false, true) => Direction::Right,
933 _ => Direction::Undirected,
934 };
935
936 Ok(RelationshipPattern {
937 direction,
938 detail,
939 span,
940 })
941}
942
943fn lower_relationship_detail(pair: Pair<Rule>) -> Result<RelationshipDetail, ParseError> {
944 let span = pair_span(&pair);
945 let mut variable = None;
946 let mut types = SmallVec::new();
947 let mut range = None;
948 let mut properties = None;
949
950 for p in pair.into_inner() {
951 match p.as_rule() {
952 Rule::variable => variable = Some(lower_variable(p)?),
953 Rule::relationship_types => types = lower_relationship_types(p)?,
954 Rule::range_literal => range = Some(lower_range_literal(p)?),
955 Rule::properties => properties = Some(lower_properties(p)?),
956 _ => {}
957 }
958 }
959
960 Ok(RelationshipDetail {
961 variable,
962 types,
963 range,
964 properties,
965 span,
966 })
967}
968
969fn lower_relationship_types(pair: Pair<Rule>) -> Result<SmallVec<String, 2>, ParseError> {
970 let mut out = SmallVec::new();
971 for p in pair.into_inner() {
972 if p.as_rule() == Rule::rel_type_name {
973 out.push(lower_schema_name(p)?);
974 }
975 }
976 Ok(out)
977}
978
979fn lower_range_literal(pair: Pair<Rule>) -> Result<RangeLiteral, ParseError> {
980 let span = pair_span(&pair);
981 let raw = pair.as_str().trim();
982 let body = raw.strip_prefix('*').unwrap_or(raw);
983
984 let (start, end) = if let Some((lhs, rhs)) = body.split_once("..") {
985 let start = if lhs.is_empty() {
986 None
987 } else {
988 Some(
989 lhs.parse::<u64>()
990 .map_err(|_| ParseError::new("invalid range start", span.start, span.end))?,
991 )
992 };
993 let end = if rhs.is_empty() {
994 None
995 } else {
996 Some(
997 rhs.parse::<u64>()
998 .map_err(|_| ParseError::new("invalid range end", span.start, span.end))?,
999 )
1000 };
1001 (start, end)
1002 } else if body.is_empty() {
1003 (None, None)
1004 } else {
1005 (
1006 Some(
1007 body.parse::<u64>()
1008 .map_err(|_| ParseError::new("invalid range bound", span.start, span.end))?,
1009 ),
1010 None,
1011 )
1012 };
1013
1014 Ok(RangeLiteral { start, end, span })
1015}
1016
1017fn lower_properties(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1018 let inner = single_inner(pair)?;
1019 match inner.as_rule() {
1020 Rule::map_literal => lower_map_literal(inner),
1021 Rule::parameter => lower_parameter(inner),
1022 _ => Err(unexpected_rule("properties", inner)),
1023 }
1024}
1025
1026fn lower_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1027 match pair.as_rule() {
1028 Rule::expression => lower_expression(single_inner(pair)?),
1029 Rule::case_expression => lower_expression(single_inner(pair)?),
1030 Rule::simple_case_expression => lower_simple_case_expression(pair),
1031 Rule::generic_case_expression => lower_generic_case_expression(pair),
1032 Rule::or_expression => {
1033 lower_left_assoc(pair, lower_expression, &[(Rule::OR, BinaryOp::Or)])
1034 }
1035 Rule::xor_expression => {
1036 lower_left_assoc(pair, lower_expression, &[(Rule::XOR, BinaryOp::Xor)])
1037 }
1038 Rule::and_expression => {
1039 lower_left_assoc(pair, lower_expression, &[(Rule::AND, BinaryOp::And)])
1040 }
1041 Rule::not_expression => lower_not_expression(pair),
1042 Rule::comparison_expression => lower_comparison_expression(pair),
1043 Rule::add_expression => lower_add_expression(pair),
1044 Rule::mul_expression => lower_mul_expression(pair),
1045 Rule::pow_expression => lower_pow_expression(pair),
1046 Rule::unary_expression => lower_unary_expression(pair),
1047 Rule::postfix_expression => lower_postfix_expression(pair),
1048 Rule::atom => lower_expression(single_inner(pair)?),
1049 Rule::list_predicate => lower_list_predicate(pair),
1050 Rule::reduce_expression => lower_reduce_expression(pair),
1051 Rule::variable => Ok(Expr::Variable(lower_variable(pair)?)),
1052 Rule::literal => lower_literal(single_inner(pair)?),
1053 Rule::parameter => lower_parameter(pair),
1054 Rule::function_invocation => lower_function_invocation(pair),
1055 Rule::parenthesized_expression => {
1056 let span = pair_span(&pair);
1057 let expr = pair
1058 .into_inner()
1059 .find(|p| p.as_rule() == Rule::expression)
1060 .ok_or_else(|| ParseError::new("expected expression", span.start, span.end))?;
1061 lower_expression(expr)
1062 }
1063 Rule::exists_subquery => lower_exists_subquery(pair),
1064 _ => Err(unexpected_rule("expression", pair)),
1065 }
1066}
1067
1068fn lower_left_assoc(
1069 pair: Pair<Rule>,
1070 recurse: fn(Pair<Rule>) -> Result<Expr, ParseError>,
1071 ops: &[(Rule, BinaryOp)],
1072) -> Result<Expr, ParseError> {
1073 let span = pair_span(&pair);
1074 let mut inner = pair.into_inner();
1075 let first = inner
1076 .next()
1077 .ok_or_else(|| ParseError::new("expected lhs", span.start, span.end))?;
1078 let mut expr = recurse(first)?;
1079
1080 while let Some(op_pair) = inner.next() {
1081 let rhs_pair = inner
1082 .next()
1083 .ok_or_else(|| ParseError::new("expected rhs", span.start, span.end))?;
1084 let op = ops
1085 .iter()
1086 .find_map(|(r, op)| {
1087 if *r == op_pair.as_rule() {
1088 Some(*op)
1089 } else {
1090 None
1091 }
1092 })
1093 .ok_or_else(|| {
1094 ParseError::new(
1095 "unknown operator",
1096 op_pair.as_span().start(),
1097 op_pair.as_span().end(),
1098 )
1099 })?;
1100
1101 let rhs = recurse(rhs_pair)?;
1102 let merged = merge_spans(expr.span(), rhs.span());
1103 expr = Expr::Binary {
1104 lhs: Box::new(expr),
1105 op,
1106 rhs: Box::new(rhs),
1107 span: merged,
1108 };
1109 }
1110
1111 Ok(expr)
1112}
1113
1114fn lower_not_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1115 let span = pair_span(&pair);
1116 let mut not_count = 0usize;
1117 let mut tail = None;
1118
1119 for p in pair.into_inner() {
1120 match p.as_rule() {
1121 Rule::NOT => not_count += 1,
1122 _ => tail = Some(p),
1123 }
1124 }
1125
1126 let mut expr = lower_expression(
1127 tail.ok_or_else(|| ParseError::new("expected expression", span.start, span.end))?,
1128 )?;
1129
1130 for _ in 0..not_count {
1131 expr = Expr::Unary {
1132 op: UnaryOp::Not,
1133 expr: Box::new(expr),
1134 span,
1135 };
1136 }
1137
1138 Ok(expr)
1139}
1140
1141fn lower_comparison_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1142 let span = pair_span(&pair);
1143 let mut inner = pair.into_inner();
1144
1145 let first = inner
1146 .next()
1147 .ok_or_else(|| ParseError::new("expected lhs", span.start, span.end))?;
1148 let mut expr = lower_expression(first)?;
1149
1150 for tail in inner {
1151 if tail.as_rule() != Rule::comparison_tail {
1152 continue;
1153 }
1154
1155 let tail_span = pair_span(&tail);
1156 let parts: Vec<_> = tail.into_inner().collect();
1157
1158 let (op, rhs): (BinaryOp, Option<Expr>) = match parts.as_slice() {
1159 [p0, p1] if p0.as_rule() == Rule::comparison_op => {
1160 let op = match p0.as_str() {
1161 "=" => BinaryOp::Eq,
1162 "<>" => BinaryOp::Ne,
1163 "<" => BinaryOp::Lt,
1164 ">" => BinaryOp::Gt,
1165 "<=" => BinaryOp::Le,
1166 ">=" => BinaryOp::Ge,
1167 _ => {
1168 return Err(ParseError::new(
1169 "unknown comparison operator",
1170 tail_span.start,
1171 tail_span.end,
1172 ))
1173 }
1174 };
1175 (op, Some(lower_expression(p1.clone())?))
1176 }
1177 [p0, p1] if p0.as_rule() == Rule::IN => {
1178 (BinaryOp::In, Some(lower_expression(p1.clone())?))
1179 }
1180 [p0, p1, p2] if p0.as_rule() == Rule::STARTS && p1.as_rule() == Rule::WITH => {
1181 (BinaryOp::StartsWith, Some(lower_expression(p2.clone())?))
1182 }
1183 [p0, p1, p2] if p0.as_rule() == Rule::ENDS && p1.as_rule() == Rule::WITH => {
1184 (BinaryOp::EndsWith, Some(lower_expression(p2.clone())?))
1185 }
1186 [p0, p1] if p0.as_rule() == Rule::CONTAINS => {
1187 (BinaryOp::Contains, Some(lower_expression(p1.clone())?))
1188 }
1189 [p0, p1] if p0.as_rule() == Rule::IS && p1.as_rule() == Rule::NULL => {
1190 (BinaryOp::IsNull, None)
1191 }
1192 [p0, p1, p2]
1193 if p0.as_rule() == Rule::IS
1194 && p1.as_rule() == Rule::NOT
1195 && p2.as_rule() == Rule::NULL =>
1196 {
1197 (BinaryOp::IsNotNull, None)
1198 }
1199 [p0, p1] if p0.as_rule() == Rule::regex_match => {
1200 (BinaryOp::RegexMatch, Some(lower_expression(p1.clone())?))
1201 }
1202 _ => {
1203 return Err(ParseError::new(
1204 "invalid comparison tail",
1205 tail_span.start,
1206 tail_span.end,
1207 ))
1208 }
1209 };
1210
1211 match rhs {
1212 Some(rhs) => {
1213 let merged = merge_spans(expr.span(), rhs.span());
1214 expr = Expr::Binary {
1215 lhs: Box::new(expr),
1216 op,
1217 rhs: Box::new(rhs),
1218 span: merged,
1219 };
1220 }
1221 None => {
1222 let rhs = Expr::Null(tail_span);
1223 let merged = merge_spans(expr.span(), rhs.span());
1224 expr = Expr::Binary {
1225 lhs: Box::new(expr),
1226 op,
1227 rhs: Box::new(rhs),
1228 span: merged,
1229 };
1230 }
1231 }
1232 }
1233
1234 Ok(expr)
1235}
1236
1237fn lower_add_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1238 let span = pair_span(&pair);
1239 let mut inner = pair.into_inner();
1240 let first = inner
1241 .next()
1242 .ok_or_else(|| ParseError::new("expected lhs", span.start, span.end))?;
1243 let mut expr = lower_expression(first)?;
1244
1245 while let Some(op_pair) = inner.next() {
1246 let rhs_pair = inner
1247 .next()
1248 .ok_or_else(|| ParseError::new("expected rhs", span.start, span.end))?;
1249 let op = match op_pair.as_rule() {
1250 Rule::add => BinaryOp::Add,
1251 Rule::sub => BinaryOp::Sub,
1252 _ => return Err(unexpected_rule("add/sub op", op_pair)),
1253 };
1254
1255 let rhs = lower_expression(rhs_pair)?;
1256 let merged = merge_spans(expr.span(), rhs.span());
1257 expr = Expr::Binary {
1258 lhs: Box::new(expr),
1259 op,
1260 rhs: Box::new(rhs),
1261 span: merged,
1262 };
1263 }
1264
1265 Ok(expr)
1266}
1267
1268fn lower_mul_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1269 let span = pair_span(&pair);
1270 let mut inner = pair.into_inner();
1271 let first = inner
1272 .next()
1273 .ok_or_else(|| ParseError::new("expected lhs", span.start, span.end))?;
1274 let mut expr = lower_expression(first)?;
1275
1276 while let Some(op_pair) = inner.next() {
1277 let rhs_pair = inner
1278 .next()
1279 .ok_or_else(|| ParseError::new("expected rhs", span.start, span.end))?;
1280 let op = match op_pair.as_rule() {
1281 Rule::mul => BinaryOp::Mul,
1282 Rule::div => BinaryOp::Div,
1283 Rule::modulo => BinaryOp::Mod,
1284 _ => return Err(unexpected_rule("mul/div/mod op", op_pair)),
1285 };
1286
1287 let rhs = lower_expression(rhs_pair)?;
1288 let merged = merge_spans(expr.span(), rhs.span());
1289 expr = Expr::Binary {
1290 lhs: Box::new(expr),
1291 op,
1292 rhs: Box::new(rhs),
1293 span: merged,
1294 };
1295 }
1296
1297 Ok(expr)
1298}
1299
1300fn lower_pow_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1301 let span = pair_span(&pair);
1302 let mut inner = pair.into_inner();
1303 let first = inner
1304 .next()
1305 .ok_or_else(|| ParseError::new("expected lhs", span.start, span.end))?;
1306 let mut expr = lower_expression(first)?;
1307
1308 while let Some(_pow) = inner.next() {
1309 let rhs_pair = inner
1310 .next()
1311 .ok_or_else(|| ParseError::new("expected rhs", span.start, span.end))?;
1312 let rhs = lower_expression(rhs_pair)?;
1313 let merged = merge_spans(expr.span(), rhs.span());
1314 expr = Expr::Binary {
1315 lhs: Box::new(expr),
1316 op: BinaryOp::Pow,
1317 rhs: Box::new(rhs),
1318 span: merged,
1319 };
1320 }
1321
1322 Ok(expr)
1323}
1324
1325fn lower_unary_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1326 let span = pair_span(&pair);
1327 let mut ops = Vec::new();
1328 let mut tail = None;
1329
1330 for p in pair.into_inner() {
1331 match p.as_rule() {
1332 Rule::add => ops.push(UnaryOp::Pos),
1333 Rule::sub => ops.push(UnaryOp::Neg),
1334 _ => tail = Some(p),
1335 }
1336 }
1337
1338 let mut expr = lower_expression(
1339 tail.ok_or_else(|| ParseError::new("expected expression", span.start, span.end))?,
1340 )?;
1341
1342 for op in ops.into_iter().rev() {
1343 expr = Expr::Unary {
1344 op,
1345 expr: Box::new(expr),
1346 span,
1347 };
1348 }
1349
1350 Ok(expr)
1351}
1352
1353fn lower_postfix_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1354 let span = pair_span(&pair);
1355 let mut inner = pair.into_inner();
1356
1357 let atom = inner
1358 .next()
1359 .ok_or_else(|| ParseError::new("expected atom", span.start, span.end))?;
1360
1361 let mut expr = lower_expression(atom)?;
1362
1363 for p in inner {
1364 match p.as_rule() {
1365 Rule::postfix_op => {
1366 let inner_pair = single_inner(p)?;
1367 match inner_pair.as_rule() {
1368 Rule::property_lookup => {
1369 let p_span = pair_span(&inner_pair);
1370 let key_pair = inner_pair
1371 .into_inner()
1372 .find(|q| q.as_rule() == Rule::property_key_name)
1373 .ok_or_else(|| {
1374 ParseError::new("expected property key", p_span.start, p_span.end)
1375 })?;
1376 let key = lower_schema_name(key_pair)?;
1377 let merged = merge_spans(expr.span(), p_span);
1378 expr = Expr::Property {
1379 expr: Box::new(expr),
1380 key,
1381 span: merged,
1382 };
1383 }
1384 Rule::map_projection_postfix => {
1385 let mp_span = pair_span(&inner_pair);
1386 let mut selectors = Vec::new();
1387 for sel_pair in inner_pair.into_inner() {
1388 if sel_pair.as_rule() == Rule::map_projection_selector {
1389 selectors.push(lower_map_projection_selector(sel_pair)?);
1390 }
1391 }
1392 let merged = merge_spans(expr.span(), mp_span);
1393 expr = Expr::MapProjection {
1394 base: Box::new(expr),
1395 selectors,
1396 span: merged,
1397 };
1398 }
1399 Rule::index_or_slice => {
1400 let is_span = pair_span(&inner_pair);
1401 let inner_op = single_inner(inner_pair)?;
1402 match inner_op.as_rule() {
1403 Rule::slice_op => {
1404 let parts: Vec<_> = inner_op.into_inner().collect();
1405 let mut from_expr = None;
1406 let mut to_expr = None;
1407 let mut seen_dots = false;
1408 for p in parts {
1409 if p.as_rule() == Rule::slice_dots {
1410 seen_dots = true;
1411 } else if p.as_rule() == Rule::expression {
1412 if !seen_dots {
1413 from_expr = Some(Box::new(lower_expression(p)?));
1414 } else {
1415 to_expr = Some(Box::new(lower_expression(p)?));
1416 }
1417 }
1418 }
1419 let merged = merge_spans(expr.span(), is_span);
1420 expr = Expr::Slice {
1421 expr: Box::new(expr),
1422 from: from_expr,
1423 to: to_expr,
1424 span: merged,
1425 };
1426 }
1427 Rule::index_op => {
1428 let idx_pair = inner_op
1429 .into_inner()
1430 .find(|p| p.as_rule() == Rule::expression);
1431 if let Some(idx) = idx_pair {
1432 let merged = merge_spans(expr.span(), is_span);
1433 expr = Expr::Index {
1434 expr: Box::new(expr),
1435 index: Box::new(lower_expression(idx)?),
1436 span: merged,
1437 };
1438 }
1439 }
1440 _ => {}
1441 }
1442 }
1443 _ => {}
1444 }
1445 }
1446 Rule::property_lookup => {
1447 let p_span = pair_span(&p);
1448 let key_pair = p
1449 .into_inner()
1450 .find(|q| q.as_rule() == Rule::property_key_name)
1451 .ok_or_else(|| {
1452 ParseError::new("expected property key", p_span.start, p_span.end)
1453 })?;
1454 let key = lower_schema_name(key_pair)?;
1455 let merged = merge_spans(expr.span(), p_span);
1456 expr = Expr::Property {
1457 expr: Box::new(expr),
1458 key,
1459 span: merged,
1460 };
1461 }
1462 _ => {}
1463 }
1464 }
1465
1466 Ok(expr)
1467}
1468
1469fn lower_map_projection_selector(pair: Pair<Rule>) -> Result<MapProjectionSelector, ParseError> {
1470 let parts: Vec<_> = pair.into_inner().collect();
1471
1472 if parts.len() == 1 && parts[0].as_rule() == Rule::STAR {
1474 return Ok(MapProjectionSelector::AllProperties);
1475 }
1476
1477 if parts.len() == 1 && parts[0].as_rule() == Rule::property_key_name {
1479 let name = lower_schema_name(parts[0].clone())?;
1480 return Ok(MapProjectionSelector::Property(name));
1481 }
1482
1483 if parts.len() >= 2 && parts[0].as_rule() == Rule::property_key_name {
1485 let key = lower_schema_name(parts[0].clone())?;
1486 let expr_pair = parts.into_iter().find(|p| p.as_rule() == Rule::expression);
1487 if let Some(ep) = expr_pair {
1488 return Ok(MapProjectionSelector::Literal(key, lower_expression(ep)?));
1489 }
1490 }
1491
1492 Err(ParseError::new("invalid map projection selector", 0, 0))
1493}
1494
1495fn lower_simple_case_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1496 let span = pair_span(&pair);
1497 let mut inner = pair.into_inner();
1498
1499 let first = inner
1500 .next()
1501 .ok_or_else(|| ParseError::new("expected CASE", span.start, span.end))?;
1502 if first.as_rule() != Rule::CASE {
1503 return Err(unexpected_rule("CASE", first));
1504 }
1505
1506 let input_pair = inner
1507 .next()
1508 .ok_or_else(|| ParseError::new("expected CASE input expression", span.start, span.end))?;
1509 let input = lower_expression(input_pair)?;
1510
1511 let mut alternatives = Vec::new();
1512 let mut else_expr = None;
1513
1514 while let Some(p) = inner.next() {
1515 match p.as_rule() {
1516 Rule::WHEN => {
1517 let when_expr = inner
1518 .next()
1519 .ok_or_else(|| {
1520 ParseError::new("expected WHEN expression", span.start, span.end)
1521 })
1522 .and_then(lower_expression)?;
1523
1524 let then_kw = inner
1525 .next()
1526 .ok_or_else(|| ParseError::new("expected THEN", span.start, span.end))?;
1527 if then_kw.as_rule() != Rule::THEN {
1528 return Err(unexpected_rule("THEN", then_kw));
1529 }
1530
1531 let then_expr = inner
1532 .next()
1533 .ok_or_else(|| {
1534 ParseError::new("expected THEN expression", span.start, span.end)
1535 })
1536 .and_then(lower_expression)?;
1537
1538 alternatives.push((when_expr, then_expr));
1539 }
1540 Rule::ELSE => {
1541 let expr = inner
1542 .next()
1543 .ok_or_else(|| {
1544 ParseError::new("expected ELSE expression", span.start, span.end)
1545 })
1546 .and_then(lower_expression)?;
1547 else_expr = Some(Box::new(expr));
1548 }
1549 Rule::END => {}
1550 _ => {}
1551 }
1552 }
1553
1554 Ok(Expr::Case {
1555 input: Some(Box::new(input)),
1556 alternatives,
1557 else_expr,
1558 span,
1559 })
1560}
1561
1562fn lower_generic_case_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1563 let span = pair_span(&pair);
1564 let mut inner = pair.into_inner();
1565
1566 let mut alternatives = Vec::new();
1567 let mut else_expr = None;
1568
1569 while let Some(p) = inner.next() {
1570 match p.as_rule() {
1571 Rule::WHEN => {
1572 let when_expr = inner
1573 .next()
1574 .ok_or_else(|| {
1575 ParseError::new("expected WHEN expression", span.start, span.end)
1576 })
1577 .and_then(lower_expression)?;
1578
1579 let then_kw = inner
1580 .next()
1581 .ok_or_else(|| ParseError::new("expected THEN", span.start, span.end))?;
1582 if then_kw.as_rule() != Rule::THEN {
1583 return Err(unexpected_rule("THEN", then_kw));
1584 }
1585
1586 let then_expr = inner
1587 .next()
1588 .ok_or_else(|| {
1589 ParseError::new("expected THEN expression", span.start, span.end)
1590 })
1591 .and_then(lower_expression)?;
1592
1593 alternatives.push((when_expr, then_expr));
1594 }
1595 Rule::ELSE => {
1596 let expr = inner
1597 .next()
1598 .ok_or_else(|| {
1599 ParseError::new("expected ELSE expression", span.start, span.end)
1600 })
1601 .and_then(lower_expression)?;
1602 else_expr = Some(Box::new(expr));
1603 }
1604 Rule::END => {}
1605 _ => {}
1606 }
1607 }
1608
1609 Ok(Expr::Case {
1610 input: None,
1611 alternatives,
1612 else_expr,
1613 span,
1614 })
1615}
1616
1617fn lower_literal(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1618 match pair.as_rule() {
1619 Rule::number_literal => lower_literal(single_inner(pair)?),
1620 Rule::integer_literal => {
1621 let v = lower_integer_literal(pair.clone())?;
1622 Ok(Expr::Integer(v, pair_span(&pair)))
1623 }
1624 Rule::double_literal => {
1625 let s = pair.as_str();
1626 let v: f64 = s.parse().map_err(|_| {
1627 ParseError::new(
1628 "invalid float",
1629 pair.as_span().start(),
1630 pair.as_span().end(),
1631 )
1632 })?;
1633 Ok(Expr::Float(v, pair_span(&pair)))
1634 }
1635 Rule::string_literal => lower_string_literal(pair),
1636 Rule::boolean_literal => {
1637 let s = pair.as_str();
1638 Ok(Expr::Bool(s.eq_ignore_ascii_case("true"), pair_span(&pair)))
1639 }
1640 Rule::null_literal => Ok(Expr::Null(pair_span(&pair))),
1641 Rule::map_literal => lower_map_literal(pair),
1642 Rule::list_literal => lower_list_literal(pair),
1643 _ => Err(unexpected_rule("literal", pair)),
1644 }
1645}
1646
1647fn lower_string_literal(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1648 let span = pair_span(&pair);
1649 let inner_pair = single_inner(pair)?;
1650 let raw = inner_pair.as_str();
1651
1652 let inner = &raw[1..raw.len() - 1];
1653 let mut out = String::new();
1654 let mut chars = inner.chars();
1655
1656 while let Some(ch) = chars.next() {
1657 if ch == '\\' {
1658 let esc = chars
1659 .next()
1660 .ok_or_else(|| ParseError::new("invalid escape", span.start, span.end))?;
1661 match esc {
1662 '\\' => out.push('\\'),
1663 '\'' => out.push('\''),
1664 '"' => out.push('"'),
1665 'n' => out.push('\n'),
1666 'r' => out.push('\r'),
1667 't' => out.push('\t'),
1668 other => out.push(other),
1669 }
1670 } else {
1671 out.push(ch);
1672 }
1673 }
1674
1675 Ok(Expr::String(out, span))
1676}
1677
1678fn lower_map_literal(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1679 let span = pair_span(&pair);
1680 let mut items = Vec::new();
1681 let mut key: Option<String> = None;
1682
1683 for p in pair.into_inner() {
1684 match p.as_rule() {
1685 Rule::property_key_name => key = Some(lower_schema_name(p)?),
1686 Rule::expression => {
1687 let k = key
1688 .take()
1689 .ok_or_else(|| ParseError::new("expected map key", span.start, span.end))?;
1690 items.push((k, lower_expression(p)?));
1691 }
1692 _ => {}
1693 }
1694 }
1695
1696 Ok(Expr::Map(items, span))
1697}
1698
1699fn lower_list_literal(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1700 let span = pair_span(&pair);
1701 let inner: Vec<_> = pair.into_inner().collect();
1702
1703 if inner.len() == 1 && inner[0].as_rule() == Rule::list_comprehension {
1704 return lower_list_comprehension(inner.into_iter().next().unwrap(), span);
1705 }
1706
1707 if inner.len() == 1 && inner[0].as_rule() == Rule::pattern_comprehension {
1708 return lower_pattern_comprehension(inner.into_iter().next().unwrap(), span);
1709 }
1710
1711 let mut items = Vec::new();
1712 for p in inner {
1713 if p.as_rule() == Rule::expression {
1714 items.push(lower_expression(p)?);
1715 }
1716 }
1717
1718 Ok(Expr::List(items, span))
1719}
1720
1721fn lower_pattern_comprehension(pair: Pair<Rule>, outer_span: Span) -> Result<Expr, ParseError> {
1722 let mut pattern = None;
1723 let mut where_ = None;
1724 let mut map_expr = None;
1725
1726 let mut inner = pair.into_inner().peekable();
1727 while let Some(p) = inner.next() {
1728 match p.as_rule() {
1729 Rule::pattern_element => {
1730 pattern = Some(lower_pattern_element(p)?);
1731 }
1732 Rule::WHERE => {
1733 let expr_pair = inner.next().ok_or_else(|| {
1734 ParseError::new(
1735 "expected WHERE expression",
1736 outer_span.start,
1737 outer_span.end,
1738 )
1739 })?;
1740 where_ = Some(Box::new(lower_expression(expr_pair)?));
1741 }
1742 Rule::expression => {
1743 map_expr = Some(Box::new(lower_expression(p)?));
1744 }
1745 _ => {}
1746 }
1747 }
1748
1749 Ok(Expr::PatternComprehension {
1750 pattern: Box::new(pattern.ok_or_else(|| {
1751 ParseError::new(
1752 "expected pattern in comprehension",
1753 outer_span.start,
1754 outer_span.end,
1755 )
1756 })?),
1757 where_,
1758 map_expr: map_expr.ok_or_else(|| {
1759 ParseError::new(
1760 "expected map expression after |",
1761 outer_span.start,
1762 outer_span.end,
1763 )
1764 })?,
1765 span: outer_span,
1766 })
1767}
1768
1769fn lower_list_comprehension(pair: Pair<Rule>, outer_span: Span) -> Result<Expr, ParseError> {
1770 let mut inner = pair.into_inner();
1771
1772 let var_pair = inner
1773 .next()
1774 .ok_or_else(|| ParseError::new("expected variable", outer_span.start, outer_span.end))?;
1775 let variable = lower_variable(var_pair)?;
1776
1777 let _in_kw = inner.next();
1779
1780 let list_pair = inner.next().ok_or_else(|| {
1781 ParseError::new("expected list expression", outer_span.start, outer_span.end)
1782 })?;
1783 let list = lower_expression(list_pair)?;
1784
1785 let mut filter = None;
1786 let mut map_expr = None;
1787
1788 while let Some(p) = inner.next() {
1790 match p.as_rule() {
1791 Rule::WHERE => {
1792 let filter_pair = inner.next().ok_or_else(|| {
1793 ParseError::new(
1794 "expected filter expression",
1795 outer_span.start,
1796 outer_span.end,
1797 )
1798 })?;
1799 filter = Some(Box::new(lower_expression(filter_pair)?));
1800 }
1801 Rule::pipe => {
1802 let map_pair = inner.next().ok_or_else(|| {
1803 ParseError::new("expected map expression", outer_span.start, outer_span.end)
1804 })?;
1805 map_expr = Some(Box::new(lower_expression(map_pair)?));
1806 }
1807 _ if p.as_rule() == Rule::expression => {
1808 map_expr = Some(Box::new(lower_expression(p)?));
1810 }
1811 _ => {}
1812 }
1813 }
1814
1815 Ok(Expr::ListComprehension {
1816 variable,
1817 list: Box::new(list),
1818 filter,
1819 map_expr,
1820 span: outer_span,
1821 })
1822}
1823
1824fn lower_parameter(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1825 let span = pair_span(&pair);
1826 let raw = pair.as_str();
1827 Ok(Expr::Parameter(raw[1..].to_string(), span))
1828}
1829
1830fn lower_list_predicate(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1831 let span = pair_span(&pair);
1832 let mut inner = pair.into_inner();
1833
1834 let kind_pair = inner
1835 .next()
1836 .ok_or_else(|| ParseError::new("expected list predicate kind", span.start, span.end))?;
1837 let kind = match kind_pair.as_rule() {
1838 Rule::ANY_ => ListPredicateKind::Any,
1839 Rule::ALL => ListPredicateKind::All,
1840 Rule::NONE => ListPredicateKind::None,
1841 Rule::SINGLE => ListPredicateKind::Single,
1842 _ => {
1843 return Err(ParseError::new(
1844 "expected ANY/ALL/NONE/SINGLE",
1845 span.start,
1846 span.end,
1847 ))
1848 }
1849 };
1850
1851 let var_pair = inner
1852 .next()
1853 .ok_or_else(|| ParseError::new("expected variable", span.start, span.end))?;
1854 let variable = lower_variable(var_pair)?;
1855
1856 let _in_kw = inner.next();
1858
1859 let list_pair = inner
1860 .next()
1861 .ok_or_else(|| ParseError::new("expected list expression", span.start, span.end))?;
1862 let list = lower_expression(list_pair)?;
1863
1864 let _where_kw = inner.next();
1866
1867 let pred_pair = inner
1868 .next()
1869 .ok_or_else(|| ParseError::new("expected predicate expression", span.start, span.end))?;
1870 let predicate = lower_expression(pred_pair)?;
1871
1872 Ok(Expr::ListPredicate {
1873 kind,
1874 variable,
1875 list: Box::new(list),
1876 predicate: Box::new(predicate),
1877 span,
1878 })
1879}
1880
1881fn lower_reduce_expression(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1882 let span = pair_span(&pair);
1883 let mut inner = pair.into_inner();
1884
1885 let _reduce_kw = inner.next();
1887
1888 let acc_pair = inner
1889 .next()
1890 .ok_or_else(|| ParseError::new("expected accumulator variable", span.start, span.end))?;
1891 let accumulator = lower_variable(acc_pair)?;
1892
1893 let _eq = inner.next();
1895
1896 let init_pair = inner
1897 .next()
1898 .ok_or_else(|| ParseError::new("expected init expression", span.start, span.end))?;
1899 let init = lower_expression(init_pair)?;
1900
1901 let var_pair = inner
1904 .next()
1905 .ok_or_else(|| ParseError::new("expected variable", span.start, span.end))?;
1906 let variable = lower_variable(var_pair)?;
1907
1908 let _in_kw = inner.next();
1910
1911 let list_pair = inner
1912 .next()
1913 .ok_or_else(|| ParseError::new("expected list expression", span.start, span.end))?;
1914 let list = lower_expression(list_pair)?;
1915
1916 let expr_pair = inner
1919 .next()
1920 .ok_or_else(|| ParseError::new("expected reduce expression", span.start, span.end))?;
1921 let expr = lower_expression(expr_pair)?;
1922
1923 Ok(Expr::Reduce {
1924 accumulator,
1925 init: Box::new(init),
1926 variable,
1927 list: Box::new(list),
1928 expr: Box::new(expr),
1929 span,
1930 })
1931}
1932
1933fn lower_exists_subquery(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1934 let span = pair_span(&pair);
1935 let mut pattern = None;
1936 let mut where_ = None;
1937
1938 for p in pair.into_inner() {
1939 match p.as_rule() {
1940 Rule::pattern => pattern = Some(lower_pattern(p)?),
1941 Rule::where_clause => {
1942 let inner = p
1943 .into_inner()
1944 .find(|q| q.as_rule() == Rule::expression)
1945 .ok_or_else(|| ParseError::new("expected expression", span.start, span.end))?;
1946 where_ = Some(Box::new(lower_expression(inner)?));
1947 }
1948 _ => {}
1949 }
1950 }
1951
1952 Ok(Expr::ExistsSubquery {
1953 pattern: pattern
1954 .ok_or_else(|| ParseError::new("expected pattern in EXISTS", span.start, span.end))?,
1955 where_,
1956 span,
1957 })
1958}
1959
1960fn lower_function_invocation(pair: Pair<Rule>) -> Result<Expr, ParseError> {
1961 let span = pair_span(&pair);
1962 let mut name = Vec::new();
1963 let mut distinct = false;
1964 let mut args = Vec::new();
1965 let mut star = false;
1966
1967 for p in pair.into_inner() {
1968 match p.as_rule() {
1969 Rule::function_name => name = lower_name_parts(p)?,
1970 Rule::DISTINCT => distinct = true,
1971 Rule::STAR => star = true,
1972 Rule::expression => args.push(lower_expression(p)?),
1973 _ => {}
1974 }
1975 }
1976
1977 if star && !name.iter().any(|n| n.eq_ignore_ascii_case("count")) {
1982 return Err(ParseError::new(
1983 format!(
1984 "* is only valid as an argument to count(), not {}()",
1985 name.join(".")
1986 ),
1987 span.start,
1988 span.end,
1989 ));
1990 }
1991
1992 Ok(Expr::FunctionCall {
1993 name,
1994 distinct,
1995 args,
1996 span,
1997 })
1998}
1999
2000fn lower_name_parts(pair: Pair<Rule>) -> Result<Vec<String>, ParseError> {
2001 let mut parts = Vec::new();
2002 for p in pair.into_inner() {
2003 match p.as_rule() {
2004 Rule::namespace => {
2005 for q in p.into_inner() {
2006 if q.as_rule() == Rule::symbolic_name {
2007 parts.push(lower_symbolic_name(q)?);
2008 }
2009 }
2010 }
2011 Rule::symbolic_name => parts.push(lower_symbolic_name(p)?),
2012 _ => {}
2013 }
2014 }
2015 Ok(parts)
2016}
2017
2018fn lower_variable(pair: Pair<Rule>) -> Result<Variable, ParseError> {
2019 let span = pair_span(&pair);
2020 let name_pair = single_inner(pair)?;
2021 Ok(Variable {
2022 name: lower_symbolic_name(name_pair)?,
2023 span,
2024 })
2025}
2026
2027fn lower_symbolic_name(pair: Pair<Rule>) -> Result<String, ParseError> {
2028 match pair.as_rule() {
2029 Rule::symbolic_name => lower_symbolic_name(single_inner(pair)?),
2030 Rule::unescaped_symbolic_name => Ok(pair.as_str().to_string()),
2031 Rule::escaped_symbolic_name => {
2032 let s = pair.as_str();
2033 Ok(s[1..s.len() - 1].to_string())
2034 }
2035 Rule::COUNT | Rule::ANY_ | Rule::NONE | Rule::SINGLE => Ok(pair.as_str().to_string()),
2036 _ => Err(unexpected_rule("symbolic_name", pair)),
2037 }
2038}
2039
2040fn lower_schema_name(pair: Pair<Rule>) -> Result<String, ParseError> {
2041 match pair.as_rule() {
2042 Rule::schema_name => lower_schema_name(single_inner(pair)?),
2043 Rule::symbolic_name => lower_symbolic_name(pair),
2044 Rule::reserved_word => Ok(pair.as_str().to_string()),
2045 Rule::label_name | Rule::rel_type_name | Rule::property_key_name => {
2046 lower_schema_name(single_inner(pair)?)
2047 }
2048 _ => Err(unexpected_rule("schema_name", pair)),
2049 }
2050}
2051
2052fn lower_integer_literal(pair: Pair<Rule>) -> Result<i64, ParseError> {
2053 match pair.as_rule() {
2054 Rule::integer_literal => lower_integer_literal(single_inner(pair)?),
2055 Rule::decimal_integer => pair.as_str().parse::<i64>().map_err(|_| {
2056 ParseError::new(
2057 "invalid decimal integer",
2058 pair.as_span().start(),
2059 pair.as_span().end(),
2060 )
2061 }),
2062 Rule::hex_integer => i64::from_str_radix(&pair.as_str()[2..], 16).map_err(|_| {
2063 ParseError::new(
2064 "invalid hex integer",
2065 pair.as_span().start(),
2066 pair.as_span().end(),
2067 )
2068 }),
2069 Rule::octal_integer => i64::from_str_radix(&pair.as_str()[1..], 8).map_err(|_| {
2070 ParseError::new(
2071 "invalid octal integer",
2072 pair.as_span().start(),
2073 pair.as_span().end(),
2074 )
2075 }),
2076 _ => Err(unexpected_rule("integer_literal", pair)),
2077 }
2078}
2079
2080fn single_inner(pair: Pair<Rule>) -> Result<Pair<Rule>, ParseError> {
2081 let span = pair.as_span();
2082 pair.into_inner()
2083 .next()
2084 .ok_or_else(|| ParseError::new("expected inner rule", span.start(), span.end()))
2085}
2086
2087fn pair_span(pair: &Pair<Rule>) -> Span {
2088 Span::new(pair.as_span().start(), pair.as_span().end())
2089}
2090
2091fn merge_spans(a: Span, b: Span) -> Span {
2092 Span {
2093 start: a.start.min(b.start),
2094 end: a.end.max(b.end),
2095 }
2096}
2097
2098fn unexpected_rule(expected: &str, pair: Pair<Rule>) -> ParseError {
2099 ParseError::new(
2100 format!("expected {expected}, got {:?}", pair.as_rule()),
2101 pair.as_span().start(),
2102 pair.as_span().end(),
2103 )
2104}
2105
2106#[cfg(test)]
2107mod tests {
2108 use super::*;
2109
2110 fn as_regular_single_part(doc: Document) -> SinglePartQuery {
2111 let Statement::Query(Query::Regular(rq)) = doc.statement else {
2112 panic!("expected regular query");
2113 };
2114 let SingleQuery::SinglePart(sp) = rq.head else {
2115 panic!("expected single-part query");
2116 };
2117 sp
2118 }
2119
2120 fn as_regular_multi_part(doc: Document) -> MultiPartQuery {
2121 let Statement::Query(Query::Regular(rq)) = doc.statement else {
2122 panic!("expected regular query");
2123 };
2124 let SingleQuery::MultiPart(mp) = rq.head else {
2125 panic!("expected multi-part query");
2126 };
2127 mp
2128 }
2129
2130 fn as_standalone_call(doc: Document) -> StandaloneCall {
2131 let Statement::Query(Query::StandaloneCall(call)) = doc.statement else {
2132 panic!("expected standalone CALL");
2133 };
2134 call
2135 }
2136
2137 fn first_match_clause(sp: &SinglePartQuery) -> &Match {
2138 let Some(first) = sp.reading_clauses.first() else {
2139 panic!("expected at least one reading clause");
2140 };
2141 let ReadingClause::Match(m) = first else {
2142 panic!("expected first reading clause to be MATCH");
2143 };
2144 m
2145 }
2146
2147 fn first_return_expr(sp: &SinglePartQuery) -> &Expr {
2148 let ret = sp.return_clause.as_ref().expect("expected RETURN clause");
2149 let Some(first) = ret.body.items.first() else {
2150 panic!("expected at least one projection item");
2151 };
2152 let ProjectionItem::Expr { expr, .. } = first else {
2153 panic!("expected first projection item to be an expression");
2154 };
2155 expr
2156 }
2157
2158 #[test]
2159 fn parse_basic_match_return() {
2160 let doc = parse_query("MATCH (n) RETURN n").unwrap();
2161 let sp = as_regular_single_part(doc);
2162
2163 assert_eq!(sp.reading_clauses.len(), 1);
2164 assert!(sp.updating_clauses.is_empty());
2165 assert!(sp.return_clause.is_some());
2166
2167 let m = first_match_clause(&sp);
2168 assert!(!m.optional);
2169 assert_eq!(m.pattern.parts.len(), 1);
2170 assert!(m.where_.is_none());
2171
2172 match first_return_expr(&sp) {
2173 Expr::Variable(v) => assert_eq!(v.name, "n"),
2174 other => panic!("expected RETURN variable n, got {other:?}"),
2175 }
2176 }
2177
2178 #[test]
2179 fn parse_match_with_label() {
2180 let doc = parse_query("MATCH (n:User) RETURN n").unwrap();
2181 let sp = as_regular_single_part(doc);
2182
2183 let m = first_match_clause(&sp);
2184 let PatternElement::NodeChain { head, chain, .. } = &m.pattern.parts[0].element else {
2185 panic!("expected node chain");
2186 };
2187
2188 assert!(chain.is_empty());
2189 assert_eq!(head.variable.as_ref().map(|v| v.name.as_str()), Some("n"));
2190 assert_eq!(head.labels.len(), 1);
2191 assert_eq!(head.labels[0][0], "User");
2192 assert!(head.properties.is_none());
2193 }
2194
2195 #[test]
2196 fn parse_match_multiple_labels() {
2197 let doc = parse_query("MATCH (n:User:Admin) RETURN n").unwrap();
2198 let sp = as_regular_single_part(doc);
2199
2200 let m = first_match_clause(&sp);
2201 let PatternElement::NodeChain { head, .. } = &m.pattern.parts[0].element else {
2202 panic!("expected node chain");
2203 };
2204
2205 assert_eq!(head.labels.len(), 2);
2206 assert_eq!(head.labels[0][0], "User");
2207 assert_eq!(head.labels[1][0], "Admin");
2208 }
2209
2210 #[test]
2211 fn parse_optional_match() {
2212 let doc = parse_query("OPTIONAL MATCH (n) RETURN n").unwrap();
2213 let sp = as_regular_single_part(doc);
2214
2215 let m = first_match_clause(&sp);
2216 assert!(m.optional);
2217 assert!(m.where_.is_none());
2218 }
2219
2220 #[test]
2221 fn parse_match_relationship() {
2222 let doc = parse_query("MATCH (a)-[:FOLLOWS]->(b) RETURN a, b").unwrap();
2223 let sp = as_regular_single_part(doc);
2224
2225 let m = first_match_clause(&sp);
2226 let PatternElement::NodeChain { head, chain, .. } = &m.pattern.parts[0].element else {
2227 panic!("expected node chain");
2228 };
2229
2230 assert_eq!(head.variable.as_ref().map(|v| v.name.as_str()), Some("a"));
2231 assert_eq!(chain.len(), 1);
2232
2233 let rel = chain[0]
2234 .relationship
2235 .detail
2236 .as_ref()
2237 .expect("expected relationship detail");
2238
2239 assert_eq!(rel.types.len(), 1);
2240 assert_eq!(rel.types[0], "FOLLOWS");
2241 assert!(matches!(chain[0].relationship.direction, Direction::Right));
2242 assert_eq!(
2243 chain[0].node.variable.as_ref().map(|v| v.name.as_str()),
2244 Some("b")
2245 );
2246 }
2247
2248 #[test]
2249 fn parse_match_left_relationship() {
2250 let doc = parse_query("MATCH (a)<-[:FOLLOWS]-(b) RETURN a, b").unwrap();
2251 let sp = as_regular_single_part(doc);
2252
2253 let m = first_match_clause(&sp);
2254 let PatternElement::NodeChain { chain, .. } = &m.pattern.parts[0].element else {
2255 panic!("expected node chain");
2256 };
2257
2258 assert_eq!(chain.len(), 1);
2259 assert!(matches!(chain[0].relationship.direction, Direction::Left));
2260 }
2261
2262 #[test]
2263 fn parse_match_undirected_relationship() {
2264 let doc = parse_query("MATCH (a)-[:KNOWS]-(b) RETURN a, b").unwrap();
2265 let sp = as_regular_single_part(doc);
2266
2267 let m = first_match_clause(&sp);
2268 let PatternElement::NodeChain { chain, .. } = &m.pattern.parts[0].element else {
2269 panic!("expected node chain");
2270 };
2271
2272 assert_eq!(chain.len(), 1);
2273 assert!(matches!(
2274 chain[0].relationship.direction,
2275 Direction::Undirected
2276 ));
2277 }
2278
2279 #[test]
2280 fn parse_relationship_with_variable_and_range() {
2281 let doc = parse_query("MATCH (a)-[r:FOLLOWS*1..3]->(b) RETURN r").unwrap();
2282 let sp = as_regular_single_part(doc);
2283
2284 let m = first_match_clause(&sp);
2285 let PatternElement::NodeChain { chain, .. } = &m.pattern.parts[0].element else {
2286 panic!("expected node chain");
2287 };
2288
2289 let rel = chain[0]
2290 .relationship
2291 .detail
2292 .as_ref()
2293 .expect("expected relationship detail");
2294
2295 assert_eq!(rel.variable.as_ref().map(|v| v.name.as_str()), Some("r"));
2296 assert_eq!(rel.types.len(), 1);
2297 assert_eq!(rel.types[0], "FOLLOWS");
2298
2299 let range = rel.range.as_ref().expect("expected range");
2300 assert_eq!(range.start, Some(1));
2301 assert_eq!(range.end, Some(3));
2302 }
2303
2304 #[test]
2305 fn parse_relationship_range_upper_only() {
2306 let doc = parse_query("MATCH (a)-[:FOLLOWS*..3]->(b) RETURN a").unwrap();
2307 let sp = as_regular_single_part(doc);
2308
2309 let m = first_match_clause(&sp);
2310 let PatternElement::NodeChain { chain, .. } = &m.pattern.parts[0].element else {
2311 panic!("expected node chain");
2312 };
2313
2314 let range = chain[0]
2315 .relationship
2316 .detail
2317 .as_ref()
2318 .and_then(|d| d.range.as_ref())
2319 .expect("expected range");
2320
2321 assert_eq!(range.start, None);
2322 assert_eq!(range.end, Some(3));
2323 }
2324
2325 #[test]
2326 fn parse_relationship_range_lower_only() {
2327 let doc = parse_query("MATCH (a)-[:FOLLOWS*3..]->(b) RETURN a").unwrap();
2328 let sp = as_regular_single_part(doc);
2329
2330 let m = first_match_clause(&sp);
2331 let PatternElement::NodeChain { chain, .. } = &m.pattern.parts[0].element else {
2332 panic!("expected node chain");
2333 };
2334
2335 let range = chain[0]
2336 .relationship
2337 .detail
2338 .as_ref()
2339 .and_then(|d| d.range.as_ref())
2340 .expect("expected range");
2341
2342 assert_eq!(range.start, Some(3));
2343 assert_eq!(range.end, None);
2344 }
2345
2346 #[test]
2347 fn parse_relationship_range_unbounded() {
2348 let doc = parse_query("MATCH (a)-[:FOLLOWS*]->(b) RETURN a").unwrap();
2349 let sp = as_regular_single_part(doc);
2350
2351 let m = first_match_clause(&sp);
2352 let PatternElement::NodeChain { chain, .. } = &m.pattern.parts[0].element else {
2353 panic!("expected node chain");
2354 };
2355
2356 let range = chain[0]
2357 .relationship
2358 .detail
2359 .as_ref()
2360 .and_then(|d| d.range.as_ref())
2361 .expect("expected range");
2362
2363 assert_eq!(range.start, None);
2364 assert_eq!(range.end, None);
2365 }
2366
2367 #[test]
2368 fn parse_pattern_binding() {
2369 let doc = parse_query("MATCH p = (a)-[:FOLLOWS]->(b) RETURN p").unwrap();
2370 let sp = as_regular_single_part(doc);
2371
2372 let m = first_match_clause(&sp);
2373 assert_eq!(m.pattern.parts.len(), 1);
2374 assert_eq!(
2375 m.pattern.parts[0].binding.as_ref().map(|v| v.name.as_str()),
2376 Some("p")
2377 );
2378
2379 match first_return_expr(&sp) {
2380 Expr::Variable(v) => assert_eq!(v.name, "p"),
2381 other => panic!("expected RETURN variable p, got {other:?}"),
2382 }
2383 }
2384
2385 #[test]
2386 fn parse_parenthesized_pattern_element() {
2387 let doc = parse_query("MATCH ((n)) RETURN n").unwrap();
2388 let sp = as_regular_single_part(doc);
2389
2390 let m = first_match_clause(&sp);
2391 match &m.pattern.parts[0].element {
2392 PatternElement::Parenthesized(inner, _) => match inner.as_ref() {
2393 PatternElement::NodeChain { head, chain, .. } => {
2394 assert!(chain.is_empty());
2395 assert_eq!(head.variable.as_ref().map(|v| v.name.as_str()), Some("n"));
2396 }
2397 other => panic!("expected node chain inside parentheses, got {other:?}"),
2398 },
2399 other => panic!("expected parenthesized pattern element, got {other:?}"),
2400 }
2401 }
2402
2403 #[test]
2404 fn parse_where_expression() {
2405 let doc = parse_query("MATCH (n) WHERE 1 + 2 > 2 RETURN n").unwrap();
2406 let sp = as_regular_single_part(doc);
2407
2408 let m = first_match_clause(&sp);
2409 let where_ = m.where_.as_ref().expect("expected WHERE clause");
2410
2411 match where_ {
2412 Expr::Binary { lhs, op, rhs, .. } => {
2413 assert!(matches!(op, BinaryOp::Gt));
2414
2415 match lhs.as_ref() {
2416 Expr::Binary {
2417 lhs: add_lhs,
2418 op: add_op,
2419 rhs: add_rhs,
2420 ..
2421 } => {
2422 assert!(matches!(add_op, BinaryOp::Add));
2423 assert!(matches!(add_lhs.as_ref(), Expr::Integer(1, _)));
2424 assert!(matches!(add_rhs.as_ref(), Expr::Integer(2, _)));
2425 }
2426 other => panic!("expected lhs to be addition expression, got {other:?}"),
2427 }
2428
2429 assert!(matches!(rhs.as_ref(), Expr::Integer(2, _)));
2430 }
2431 other => panic!("expected binary WHERE expression, got {other:?}"),
2432 }
2433 }
2434
2435 #[test]
2436 fn parse_where_boolean_precedence() {
2437 let doc = parse_query("MATCH (n) WHERE NOT n.active AND n.age >= 18 RETURN n").unwrap();
2438 let sp = as_regular_single_part(doc);
2439
2440 let m = first_match_clause(&sp);
2441 let where_ = m.where_.as_ref().expect("expected WHERE clause");
2442
2443 match where_ {
2444 Expr::Binary { lhs, op, rhs, .. } => {
2445 assert!(matches!(op, BinaryOp::And));
2446
2447 match lhs.as_ref() {
2448 Expr::Unary {
2449 op: UnaryOp::Not,
2450 expr,
2451 ..
2452 } => match expr.as_ref() {
2453 Expr::Property { key, .. } => assert_eq!(key, "active"),
2454 other => panic!("expected property under NOT, got {other:?}"),
2455 },
2456 other => panic!("expected NOT expression on lhs, got {other:?}"),
2457 }
2458
2459 match rhs.as_ref() {
2460 Expr::Binary {
2461 lhs: cmp_lhs,
2462 op: cmp_op,
2463 rhs: cmp_rhs,
2464 ..
2465 } => {
2466 assert!(matches!(cmp_op, BinaryOp::Ge));
2467 assert!(matches!(cmp_rhs.as_ref(), Expr::Integer(18, _)));
2468 match cmp_lhs.as_ref() {
2469 Expr::Property { key, .. } => assert_eq!(key, "age"),
2470 other => panic!("expected property on comparison lhs, got {other:?}"),
2471 }
2472 }
2473 other => panic!("expected comparison expression on rhs, got {other:?}"),
2474 }
2475 }
2476 other => panic!("expected binary WHERE expression, got {other:?}"),
2477 }
2478 }
2479
2480 #[test]
2481 fn parse_where_parenthesized_expression() {
2482 let doc = parse_query("MATCH (n) WHERE (1 + 2) * 3 > 5 RETURN n").unwrap();
2483 let sp = as_regular_single_part(doc);
2484
2485 let m = first_match_clause(&sp);
2486 let where_ = m.where_.as_ref().expect("expected WHERE clause");
2487
2488 match where_ {
2489 Expr::Binary { lhs, op, rhs, .. } => {
2490 assert!(matches!(op, BinaryOp::Gt));
2491 assert!(matches!(rhs.as_ref(), Expr::Integer(5, _)));
2492
2493 match lhs.as_ref() {
2494 Expr::Binary {
2495 lhs: mul_lhs,
2496 op: mul_op,
2497 rhs: mul_rhs,
2498 ..
2499 } => {
2500 assert!(matches!(mul_op, BinaryOp::Mul));
2501 assert!(matches!(mul_rhs.as_ref(), Expr::Integer(3, _)));
2502
2503 match mul_lhs.as_ref() {
2504 Expr::Binary {
2505 lhs: add_lhs,
2506 op: add_op,
2507 rhs: add_rhs,
2508 ..
2509 } => {
2510 assert!(matches!(add_op, BinaryOp::Add));
2511 assert!(matches!(add_lhs.as_ref(), Expr::Integer(1, _)));
2512 assert!(matches!(add_rhs.as_ref(), Expr::Integer(2, _)));
2513 }
2514 other => {
2515 panic!("expected addition under multiplication, got {other:?}")
2516 }
2517 }
2518 }
2519 other => panic!("expected multiplication lhs, got {other:?}"),
2520 }
2521 }
2522 other => panic!("expected binary WHERE expression, got {other:?}"),
2523 }
2524 }
2525
2526 #[test]
2527 fn parse_where_in_operator() {
2528 let doc = parse_query("MATCH (n) WHERE n.age IN [1, 2, 3] RETURN n").unwrap();
2529 let sp = as_regular_single_part(doc);
2530
2531 let where_ = first_match_clause(&sp).where_.as_ref().unwrap();
2532 match where_ {
2533 Expr::Binary { lhs, op, rhs, .. } => {
2534 assert!(matches!(op, BinaryOp::In));
2535 assert!(matches!(lhs.as_ref(), Expr::Property { key, .. } if key == "age"));
2536 assert!(matches!(rhs.as_ref(), Expr::List(_, _)));
2537 }
2538 other => panic!("expected IN expression, got {other:?}"),
2539 }
2540 }
2541
2542 #[test]
2543 fn parse_where_contains_operator() {
2544 let doc = parse_query("MATCH (n) WHERE n.name CONTAINS 'al' RETURN n").unwrap();
2545 let sp = as_regular_single_part(doc);
2546
2547 let where_ = first_match_clause(&sp).where_.as_ref().unwrap();
2548 match where_ {
2549 Expr::Binary { op, rhs, .. } => {
2550 assert!(matches!(op, BinaryOp::Contains));
2551 assert!(matches!(rhs.as_ref(), Expr::String(s, _) if s == "al"));
2552 }
2553 other => panic!("expected CONTAINS expression, got {other:?}"),
2554 }
2555 }
2556
2557 #[test]
2558 fn parse_where_starts_with_operator() {
2559 let doc = parse_query("MATCH (n) WHERE n.name STARTS WITH 'a' RETURN n").unwrap();
2560 let sp = as_regular_single_part(doc);
2561
2562 let where_ = first_match_clause(&sp).where_.as_ref().unwrap();
2563 match where_ {
2564 Expr::Binary { op, rhs, .. } => {
2565 assert!(matches!(op, BinaryOp::StartsWith));
2566 assert!(matches!(rhs.as_ref(), Expr::String(s, _) if s == "a"));
2567 }
2568 other => panic!("expected STARTS WITH expression, got {other:?}"),
2569 }
2570 }
2571
2572 #[test]
2573 fn parse_where_ends_with_operator() {
2574 let doc = parse_query("MATCH (n) WHERE n.name ENDS WITH 'z' RETURN n").unwrap();
2575 let sp = as_regular_single_part(doc);
2576
2577 let where_ = first_match_clause(&sp).where_.as_ref().unwrap();
2578 match where_ {
2579 Expr::Binary { op, rhs, .. } => {
2580 assert!(matches!(op, BinaryOp::EndsWith));
2581 assert!(matches!(rhs.as_ref(), Expr::String(s, _) if s == "z"));
2582 }
2583 other => panic!("expected ENDS WITH expression, got {other:?}"),
2584 }
2585 }
2586
2587 #[test]
2588 fn parse_where_is_null() {
2589 let doc = parse_query("MATCH (n) WHERE n.name IS NULL RETURN n").unwrap();
2590 let sp = as_regular_single_part(doc);
2591
2592 let where_ = first_match_clause(&sp).where_.as_ref().unwrap();
2593 match where_ {
2594 Expr::Binary { lhs, op, rhs, .. } => {
2595 assert!(matches!(op, BinaryOp::IsNull));
2596 assert!(matches!(lhs.as_ref(), Expr::Property { key, .. } if key == "name"));
2597 assert!(matches!(rhs.as_ref(), Expr::Null(_)));
2598 }
2599 other => panic!("expected IS NULL expression, got {other:?}"),
2600 }
2601 }
2602
2603 #[test]
2604 fn parse_where_is_not_null() {
2605 let doc = parse_query("MATCH (n) WHERE n.name IS NOT NULL RETURN n").unwrap();
2606 let sp = as_regular_single_part(doc);
2607
2608 let where_ = first_match_clause(&sp).where_.as_ref().unwrap();
2609 match where_ {
2610 Expr::Binary { lhs, op, rhs, .. } => {
2611 assert!(matches!(op, BinaryOp::IsNotNull));
2612 assert!(matches!(lhs.as_ref(), Expr::Property { key, .. } if key == "name"));
2613 assert!(matches!(rhs.as_ref(), Expr::Null(_)));
2614 }
2615 other => panic!("expected IS NOT NULL expression, got {other:?}"),
2616 }
2617 }
2618
2619 #[test]
2620 fn parse_limit() {
2621 let doc = parse_query("MATCH (n) RETURN n LIMIT 10").unwrap();
2622 let sp = as_regular_single_part(doc);
2623
2624 let ret = sp.return_clause.expect("expected RETURN clause");
2625 assert!(matches!(ret.body.limit, Some(Expr::Integer(10, _))));
2626 assert!(ret.body.skip.is_none());
2627 }
2628
2629 #[test]
2630 fn parse_distinct_return() {
2631 let doc = parse_query("MATCH (n) RETURN DISTINCT n").unwrap();
2632 let sp = as_regular_single_part(doc);
2633
2634 let ret = sp.return_clause.expect("expected RETURN clause");
2635 assert!(ret.body.distinct);
2636 assert_eq!(ret.body.items.len(), 1);
2637 }
2638
2639 #[test]
2640 fn parse_return_star() {
2641 let doc = parse_query("MATCH (n) RETURN *").unwrap();
2642 let sp = as_regular_single_part(doc);
2643
2644 let ret = sp.return_clause.expect("expected RETURN clause");
2645 assert_eq!(ret.body.items.len(), 1);
2646 assert!(matches!(ret.body.items[0], ProjectionItem::Star { .. }));
2647 }
2648
2649 #[test]
2650 fn parse_return_star_and_expr() {
2651 let doc = parse_query("MATCH (n) RETURN *, n.name").unwrap();
2652 let sp = as_regular_single_part(doc);
2653
2654 let ret = sp.return_clause.expect("expected RETURN clause");
2655 assert_eq!(ret.body.items.len(), 2);
2656 assert!(matches!(ret.body.items[0], ProjectionItem::Star { .. }));
2657 assert!(matches!(
2658 &ret.body.items[1],
2659 ProjectionItem::Expr {
2660 expr: Expr::Property { key, .. },
2661 ..
2662 } if key == "name"
2663 ));
2664 }
2665
2666 #[test]
2667 fn parse_multiple_projection_items() {
2668 let doc = parse_query("MATCH (n) RETURN n, n.name AS name").unwrap();
2669 let sp = as_regular_single_part(doc);
2670
2671 let ret = sp.return_clause.expect("expected RETURN clause");
2672 assert_eq!(ret.body.items.len(), 2);
2673
2674 match &ret.body.items[0] {
2675 ProjectionItem::Expr { expr, alias, .. } => {
2676 assert!(alias.is_none());
2677 assert!(matches!(expr, Expr::Variable(_)));
2678 }
2679 other => panic!("expected projection expr, got {other:?}"),
2680 }
2681
2682 match &ret.body.items[1] {
2683 ProjectionItem::Expr { expr, alias, .. } => {
2684 assert_eq!(alias.as_ref().map(|v| v.name.as_str()), Some("name"));
2685 match expr {
2686 Expr::Property { key, .. } => assert_eq!(key, "name"),
2687 other => panic!("expected property projection, got {other:?}"),
2688 }
2689 }
2690 other => panic!("expected projection expr, got {other:?}"),
2691 }
2692 }
2693
2694 #[test]
2695 fn parse_order_skip_limit() {
2696 let doc = parse_query("MATCH (n) RETURN n ORDER BY n.name DESC SKIP 5 LIMIT 10").unwrap();
2697 let sp = as_regular_single_part(doc);
2698
2699 let ret = sp.return_clause.expect("expected RETURN clause");
2700 assert_eq!(ret.body.order.len(), 1);
2701 assert!(matches!(ret.body.order[0].direction, SortDirection::Desc));
2702 assert!(matches!(ret.body.skip, Some(Expr::Integer(5, _))));
2703 assert!(matches!(ret.body.limit, Some(Expr::Integer(10, _))));
2704
2705 match &ret.body.order[0].expr {
2706 Expr::Property { key, .. } => assert_eq!(key, "name"),
2707 other => panic!("expected ORDER BY property lookup, got {other:?}"),
2708 }
2709 }
2710
2711 #[test]
2712 fn parse_order_multiple_sort_items() {
2713 let doc = parse_query("MATCH (n) RETURN n ORDER BY n.last ASC, n.first DESC").unwrap();
2714 let sp = as_regular_single_part(doc);
2715
2716 let ret = sp.return_clause.expect("expected RETURN clause");
2717 assert_eq!(ret.body.order.len(), 2);
2718 assert!(matches!(ret.body.order[0].direction, SortDirection::Asc));
2719 assert!(matches!(ret.body.order[1].direction, SortDirection::Desc));
2720 }
2721
2722 #[test]
2723 fn parse_create_clause() {
2724 let doc = parse_query("CREATE (n:User {name: 'alice'}) RETURN n").unwrap();
2725 let sp = as_regular_single_part(doc);
2726
2727 assert_eq!(sp.updating_clauses.len(), 1);
2728
2729 let UpdatingClause::Create(create) = &sp.updating_clauses[0] else {
2730 panic!("expected CREATE clause");
2731 };
2732 let PatternElement::NodeChain { head, chain, .. } = &create.pattern.parts[0].element else {
2733 panic!("expected node chain");
2734 };
2735
2736 assert!(chain.is_empty());
2737 assert_eq!(head.labels[0][0], "User");
2738 assert!(head.properties.is_some());
2739 }
2740
2741 #[test]
2742 fn parse_create_without_return() {
2743 let doc = parse_query("CREATE (n:User)").unwrap();
2744 let sp = as_regular_single_part(doc);
2745
2746 assert_eq!(sp.updating_clauses.len(), 1);
2747 assert!(sp.return_clause.is_none());
2748 }
2749
2750 #[test]
2751 fn parse_merge_clause() {
2752 let doc = parse_query("MERGE (n:User {id: 1}) RETURN n").unwrap();
2753 let sp = as_regular_single_part(doc);
2754
2755 assert_eq!(sp.updating_clauses.len(), 1);
2756 let UpdatingClause::Merge(merge) = &sp.updating_clauses[0] else {
2757 panic!("expected MERGE clause");
2758 };
2759
2760 let PatternElement::NodeChain { head, chain, .. } = &merge.pattern_part.element else {
2761 panic!("expected node chain");
2762 };
2763 assert!(chain.is_empty());
2764 assert_eq!(head.labels[0][0], "User");
2765 assert!(head.properties.is_some());
2766 }
2767
2768 #[test]
2769 fn parse_merge_with_actions() {
2770 let doc = parse_query(
2771 "MERGE (n:User {id: 1}) ON MATCH SET n.name = 'alice' ON CREATE SET n:New RETURN n",
2772 )
2773 .unwrap();
2774 let sp = as_regular_single_part(doc);
2775
2776 let UpdatingClause::Merge(merge) = &sp.updating_clauses[0] else {
2777 panic!("expected MERGE clause");
2778 };
2779
2780 assert_eq!(merge.actions.len(), 2);
2781 assert!(merge.actions[0].on_match);
2782 assert!(!merge.actions[1].on_match);
2783 assert_eq!(merge.actions[0].set.items.len(), 1);
2784 assert_eq!(merge.actions[1].set.items.len(), 1);
2785 }
2786
2787 #[test]
2788 fn parse_delete_clause() {
2789 let doc = parse_query("MATCH (n) DELETE n").unwrap();
2790 let sp = as_regular_single_part(doc);
2791
2792 let UpdatingClause::Delete(delete) = &sp.updating_clauses[0] else {
2793 panic!("expected DELETE clause");
2794 };
2795
2796 assert!(!delete.detach);
2797 assert_eq!(delete.expressions.len(), 1);
2798 assert!(matches!(delete.expressions[0], Expr::Variable(_)));
2799 }
2800
2801 #[test]
2802 fn parse_detach_delete_clause() {
2803 let doc = parse_query("MATCH (n) DETACH DELETE n").unwrap();
2804 let sp = as_regular_single_part(doc);
2805
2806 let UpdatingClause::Delete(delete) = &sp.updating_clauses[0] else {
2807 panic!("expected DELETE clause");
2808 };
2809
2810 assert!(delete.detach);
2811 assert_eq!(delete.expressions.len(), 1);
2812 }
2813
2814 #[test]
2815 fn parse_set_variable_clause() {
2816 let doc = parse_query("MATCH (n) SET n = {name: 'alice'} RETURN n").unwrap();
2817 let sp = as_regular_single_part(doc);
2818
2819 let UpdatingClause::Set(set) = &sp.updating_clauses[0] else {
2820 panic!("expected SET clause");
2821 };
2822
2823 assert_eq!(set.items.len(), 1);
2824 match &set.items[0] {
2825 SetItem::SetVariable {
2826 variable, value, ..
2827 } => {
2828 assert_eq!(variable.name, "n");
2829 assert!(matches!(value, Expr::Map(_, _)));
2830 }
2831 other => panic!("expected SetVariable, got {other:?}"),
2832 }
2833 }
2834
2835 #[test]
2836 fn parse_set_property_clause() {
2837 let doc = parse_query("MATCH (n) SET n.name = 'alice' RETURN n").unwrap();
2838 let sp = as_regular_single_part(doc);
2839
2840 let UpdatingClause::Set(set) = &sp.updating_clauses[0] else {
2841 panic!("expected SET clause");
2842 };
2843
2844 assert_eq!(set.items.len(), 1);
2845 match &set.items[0] {
2846 SetItem::SetProperty { target, value, .. } => {
2847 assert!(matches!(target, Expr::Property { key, .. } if key == "name"));
2848 assert!(matches!(value, Expr::String(s, _) if s == "alice"));
2849 }
2850 other => panic!("expected SetProperty, got {other:?}"),
2851 }
2852 }
2853
2854 #[test]
2855 fn parse_set_mutate_variable_clause() {
2856 let doc = parse_query("MATCH (n) SET n += {age: 42} RETURN n").unwrap();
2857 let sp = as_regular_single_part(doc);
2858
2859 let UpdatingClause::Set(set) = &sp.updating_clauses[0] else {
2860 panic!("expected SET clause");
2861 };
2862
2863 assert_eq!(set.items.len(), 1);
2864 match &set.items[0] {
2865 SetItem::MutateVariable {
2866 variable, value, ..
2867 } => {
2868 assert_eq!(variable.name, "n");
2869 assert!(matches!(value, Expr::Map(_, _)));
2870 }
2871 other => panic!("expected MutateVariable, got {other:?}"),
2872 }
2873 }
2874
2875 #[test]
2876 fn parse_set_labels_clause() {
2877 let doc = parse_query("MATCH (n) SET n:User:Admin RETURN n").unwrap();
2878 let sp = as_regular_single_part(doc);
2879
2880 let UpdatingClause::Set(set) = &sp.updating_clauses[0] else {
2881 panic!("expected SET clause");
2882 };
2883
2884 assert_eq!(set.items.len(), 1);
2885 match &set.items[0] {
2886 SetItem::SetLabels {
2887 variable, labels, ..
2888 } => {
2889 assert_eq!(variable.name, "n");
2890 assert_eq!(labels, &vec!["User".to_string(), "Admin".to_string()]);
2891 }
2892 other => panic!("expected SetLabels, got {other:?}"),
2893 }
2894 }
2895
2896 #[test]
2897 fn parse_remove_labels_clause() {
2898 let doc = parse_query("MATCH (n) REMOVE n:User:Admin RETURN n").unwrap();
2899 let sp = as_regular_single_part(doc);
2900
2901 let UpdatingClause::Remove(remove) = &sp.updating_clauses[0] else {
2902 panic!("expected REMOVE clause");
2903 };
2904
2905 assert_eq!(remove.items.len(), 1);
2906 match &remove.items[0] {
2907 RemoveItem::Labels {
2908 variable, labels, ..
2909 } => {
2910 assert_eq!(variable.name, "n");
2911 assert_eq!(labels, &vec!["User".to_string(), "Admin".to_string()]);
2912 }
2913 other => panic!("expected RemoveItem::Labels, got {other:?}"),
2914 }
2915 }
2916
2917 #[test]
2918 fn parse_remove_property_clause() {
2919 let doc = parse_query("MATCH (n) REMOVE n.name RETURN n").unwrap();
2920 let sp = as_regular_single_part(doc);
2921
2922 let UpdatingClause::Remove(remove) = &sp.updating_clauses[0] else {
2923 panic!("expected REMOVE clause");
2924 };
2925
2926 assert_eq!(remove.items.len(), 1);
2927 match &remove.items[0] {
2928 RemoveItem::Property { expr, .. } => {
2929 assert!(matches!(expr, Expr::Property { key, .. } if key == "name"));
2930 }
2931 other => panic!("expected RemoveItem::Property, got {other:?}"),
2932 }
2933 }
2934
2935 #[test]
2936 fn parse_node_properties_map() {
2937 let doc = parse_query("MATCH (n:User {name: 'alice', age: 42}) RETURN n").unwrap();
2938 let sp = as_regular_single_part(doc);
2939
2940 let m = first_match_clause(&sp);
2941 let PatternElement::NodeChain { head, .. } = &m.pattern.parts[0].element else {
2942 panic!("expected node chain");
2943 };
2944
2945 match head.properties.as_ref().expect("expected node properties") {
2946 Expr::Map(items, _) => {
2947 assert_eq!(items.len(), 2);
2948 assert_eq!(items[0].0, "name");
2949 assert!(matches!(items[0].1, Expr::String(_, _)));
2950 assert_eq!(items[1].0, "age");
2951 assert!(matches!(items[1].1, Expr::Integer(42, _)));
2952 }
2953 other => panic!("expected map literal properties, got {other:?}"),
2954 }
2955 }
2956
2957 #[test]
2958 fn parse_relationship_properties_map() {
2959 let doc = parse_query("MATCH (a)-[:FOLLOWS {since: 2020}]->(b) RETURN a").unwrap();
2960 let sp = as_regular_single_part(doc);
2961
2962 let m = first_match_clause(&sp);
2963 let PatternElement::NodeChain { chain, .. } = &m.pattern.parts[0].element else {
2964 panic!("expected node chain");
2965 };
2966
2967 let rel = chain[0]
2968 .relationship
2969 .detail
2970 .as_ref()
2971 .expect("expected relationship detail");
2972
2973 match rel
2974 .properties
2975 .as_ref()
2976 .expect("expected relationship properties")
2977 {
2978 Expr::Map(items, _) => {
2979 assert_eq!(items.len(), 1);
2980 assert_eq!(items[0].0, "since");
2981 assert!(matches!(items[0].1, Expr::Integer(2020, _)));
2982 }
2983 other => panic!("expected relationship map properties, got {other:?}"),
2984 }
2985 }
2986
2987 #[test]
2988 fn parse_unwind_clause() {
2989 let doc = parse_query("UNWIND [1, 2, 3] AS n RETURN n").unwrap();
2990 let sp = as_regular_single_part(doc);
2991
2992 assert_eq!(sp.reading_clauses.len(), 1);
2993
2994 let ReadingClause::Unwind(unwind) = &sp.reading_clauses[0] else {
2995 panic!("expected UNWIND clause");
2996 };
2997
2998 assert_eq!(unwind.alias.name, "n");
2999
3000 match &unwind.expr {
3001 Expr::List(items, _) => {
3002 assert_eq!(items.len(), 3);
3003 assert!(matches!(items[0], Expr::Integer(1, _)));
3004 assert!(matches!(items[1], Expr::Integer(2, _)));
3005 assert!(matches!(items[2], Expr::Integer(3, _)));
3006 }
3007 other => panic!("expected list expression, got {other:?}"),
3008 }
3009 }
3010
3011 #[test]
3012 fn parse_unary_operators() {
3013 let doc = parse_query("RETURN -1, +2").unwrap();
3014 let sp = as_regular_single_part(doc);
3015
3016 let ret = sp.return_clause.expect("expected RETURN clause");
3017 assert_eq!(ret.body.items.len(), 2);
3018
3019 match &ret.body.items[0] {
3020 ProjectionItem::Expr { expr, .. } => match expr {
3021 Expr::Unary {
3022 op: UnaryOp::Neg,
3023 expr,
3024 ..
3025 } => assert!(matches!(expr.as_ref(), Expr::Integer(1, _))),
3026 other => panic!("expected unary negation, got {other:?}"),
3027 },
3028 other => panic!("expected projection expr, got {other:?}"),
3029 }
3030
3031 match &ret.body.items[1] {
3032 ProjectionItem::Expr { expr, .. } => match expr {
3033 Expr::Unary {
3034 op: UnaryOp::Pos,
3035 expr,
3036 ..
3037 } => assert!(matches!(expr.as_ref(), Expr::Integer(2, _))),
3038 other => panic!("expected unary positive, got {other:?}"),
3039 },
3040 other => panic!("expected projection expr, got {other:?}"),
3041 }
3042 }
3043
3044 #[test]
3045 fn parse_power_operator() {
3046 let doc = parse_query("RETURN 2 ^ 3 ^ 4").unwrap();
3047 let sp = as_regular_single_part(doc);
3048
3049 match first_return_expr(&sp) {
3050 Expr::Binary { lhs, op, rhs, .. } => {
3051 assert!(matches!(op, BinaryOp::Pow));
3052 assert!(matches!(rhs.as_ref(), Expr::Integer(4, _)));
3053 assert!(matches!(
3054 lhs.as_ref(),
3055 Expr::Binary {
3056 op: BinaryOp::Pow,
3057 ..
3058 }
3059 ));
3060 }
3061 other => panic!("expected power expression, got {other:?}"),
3062 }
3063 }
3064
3065 #[test]
3066 fn parse_function_call_and_alias() {
3067 let doc = parse_query("MATCH (n) RETURN count(n) AS c").unwrap();
3068 let sp = as_regular_single_part(doc);
3069
3070 let ret = sp.return_clause.expect("expected RETURN clause");
3071 assert_eq!(ret.body.items.len(), 1);
3072
3073 let ProjectionItem::Expr { expr, alias, .. } = &ret.body.items[0] else {
3074 panic!("expected projection expr");
3075 };
3076
3077 assert_eq!(alias.as_ref().map(|v| v.name.as_str()), Some("c"));
3078
3079 match expr {
3080 Expr::FunctionCall {
3081 name,
3082 distinct,
3083 args,
3084 ..
3085 } => {
3086 assert_eq!(name, &vec!["count".to_string()]);
3087 assert!(!distinct);
3088 assert_eq!(args.len(), 1);
3089 assert!(matches!(args[0], Expr::Variable(_)));
3090 }
3091 other => panic!("expected function call, got {other:?}"),
3092 }
3093 }
3094
3095 #[test]
3096 fn parse_distinct_function_call() {
3097 let doc = parse_query("MATCH (n) RETURN count(DISTINCT n) AS c").unwrap();
3098 let sp = as_regular_single_part(doc);
3099
3100 let ret = sp.return_clause.expect("expected RETURN clause");
3101 let ProjectionItem::Expr { expr, .. } = &ret.body.items[0] else {
3102 panic!("expected projection expr");
3103 };
3104
3105 match expr {
3106 Expr::FunctionCall {
3107 name,
3108 distinct,
3109 args,
3110 ..
3111 } => {
3112 assert_eq!(name, &vec!["count".to_string()]);
3113 assert!(*distinct);
3114 assert_eq!(args.len(), 1);
3115 }
3116 other => panic!("expected function call, got {other:?}"),
3117 }
3118 }
3119
3120 #[test]
3121 fn parse_namespaced_function_call() {
3122 let doc = parse_query("RETURN my.ns.func(1, 2)").unwrap();
3123 let sp = as_regular_single_part(doc);
3124
3125 match first_return_expr(&sp) {
3126 Expr::FunctionCall { name, args, .. } => {
3127 assert_eq!(
3128 name,
3129 &vec!["my".to_string(), "ns".to_string(), "func".to_string()]
3130 );
3131 assert_eq!(args.len(), 2);
3132 assert!(matches!(args[0], Expr::Integer(1, _)));
3133 assert!(matches!(args[1], Expr::Integer(2, _)));
3134 }
3135 other => panic!("expected namespaced function call, got {other:?}"),
3136 }
3137 }
3138
3139 #[test]
3140 fn parse_parameter_and_property_lookup() {
3141 let doc = parse_query("MATCH (n) WHERE n.age >= $minAge RETURN n.name").unwrap();
3142 let sp = as_regular_single_part(doc);
3143
3144 let m = first_match_clause(&sp);
3145 let where_ = m.where_.as_ref().expect("expected WHERE clause");
3146
3147 match where_ {
3148 Expr::Binary { lhs, op, rhs, .. } => {
3149 assert!(matches!(op, BinaryOp::Ge));
3150
3151 match lhs.as_ref() {
3152 Expr::Property { key, .. } => assert_eq!(key, "age"),
3153 other => panic!("expected property lookup on lhs, got {other:?}"),
3154 }
3155
3156 match rhs.as_ref() {
3157 Expr::Parameter(name, _) => assert_eq!(name, "minAge"),
3158 other => panic!("expected parameter on rhs, got {other:?}"),
3159 }
3160 }
3161 other => panic!("expected binary WHERE expression, got {other:?}"),
3162 }
3163
3164 let ret = sp.return_clause.expect("expected RETURN clause");
3165 let ProjectionItem::Expr { expr, .. } = &ret.body.items[0] else {
3166 panic!("expected projection expr");
3167 };
3168
3169 match expr {
3170 Expr::Property { key, .. } => assert_eq!(key, "name"),
3171 other => panic!("expected property lookup in RETURN, got {other:?}"),
3172 }
3173 }
3174
3175 #[test]
3176 fn parse_numeric_parameter() {
3177 let doc = parse_query("RETURN $1").unwrap();
3178 let sp = as_regular_single_part(doc);
3179
3180 match first_return_expr(&sp) {
3181 Expr::Parameter(name, _) => assert_eq!(name, "1"),
3182 other => panic!("expected numeric parameter, got {other:?}"),
3183 }
3184 }
3185
3186 #[test]
3187 fn parse_map_and_list_literals() {
3188 let doc = parse_query("RETURN {name: 'alice', nums: [1, 2, 3]}").unwrap();
3189 let sp = as_regular_single_part(doc);
3190
3191 match first_return_expr(&sp) {
3192 Expr::Map(items, _) => {
3193 assert_eq!(items.len(), 2);
3194 assert_eq!(items[0].0, "name");
3195 assert!(matches!(items[0].1, Expr::String(_, _)));
3196
3197 assert_eq!(items[1].0, "nums");
3198 match &items[1].1 {
3199 Expr::List(values, _) => {
3200 assert_eq!(values.len(), 3);
3201 assert!(matches!(values[0], Expr::Integer(1, _)));
3202 assert!(matches!(values[1], Expr::Integer(2, _)));
3203 assert!(matches!(values[2], Expr::Integer(3, _)));
3204 }
3205 other => panic!("expected nested list, got {other:?}"),
3206 }
3207 }
3208 other => panic!("expected map literal, got {other:?}"),
3209 }
3210 }
3211
3212 #[test]
3213 fn parse_case_expression() {
3214 let doc = parse_query("RETURN CASE WHEN 1 = 1 THEN 'yes' ELSE 'no' END").unwrap();
3215 let sp = as_regular_single_part(doc);
3216
3217 match first_return_expr(&sp) {
3218 Expr::Case {
3219 input,
3220 alternatives,
3221 else_expr,
3222 ..
3223 } => {
3224 assert!(input.is_none());
3225 assert_eq!(alternatives.len(), 1);
3226 assert!(else_expr.is_some());
3227 }
3228 other => panic!("expected CASE expression, got {other:?}"),
3229 }
3230 }
3231
3232 #[test]
3233 fn parse_case_expression_with_input() {
3234 let doc =
3235 parse_query("RETURN CASE n.age WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'other' END")
3236 .unwrap();
3237 let sp = as_regular_single_part(doc);
3238
3239 match first_return_expr(&sp) {
3240 Expr::Case {
3241 input,
3242 alternatives,
3243 else_expr,
3244 ..
3245 } => {
3246 assert!(input.is_some());
3247 assert_eq!(alternatives.len(), 2);
3248 assert!(else_expr.is_some());
3249 }
3250 other => panic!("expected CASE expression, got {other:?}"),
3251 }
3252 }
3253
3254 #[test]
3255 fn parse_literals() {
3256 let doc = parse_query("RETURN 42, 3.14, true, false, null, 'x'").unwrap();
3257 let sp = as_regular_single_part(doc);
3258
3259 let ret = sp.return_clause.expect("expected RETURN clause");
3260 assert_eq!(ret.body.items.len(), 6);
3261
3262 match &ret.body.items[0] {
3263 ProjectionItem::Expr { expr, .. } => assert!(matches!(expr, Expr::Integer(42, _))),
3264 other => panic!("expected projection expr, got {other:?}"),
3265 }
3266 match &ret.body.items[1] {
3267 ProjectionItem::Expr { expr, .. } => assert!(matches!(expr, Expr::Float(_, _))),
3268 other => panic!("expected projection expr, got {other:?}"),
3269 }
3270 match &ret.body.items[2] {
3271 ProjectionItem::Expr { expr, .. } => assert!(matches!(expr, Expr::Bool(true, _))),
3272 other => panic!("expected projection expr, got {other:?}"),
3273 }
3274 match &ret.body.items[3] {
3275 ProjectionItem::Expr { expr, .. } => assert!(matches!(expr, Expr::Bool(false, _))),
3276 other => panic!("expected projection expr, got {other:?}"),
3277 }
3278 match &ret.body.items[4] {
3279 ProjectionItem::Expr { expr, .. } => assert!(matches!(expr, Expr::Null(_))),
3280 other => panic!("expected projection expr, got {other:?}"),
3281 }
3282 match &ret.body.items[5] {
3283 ProjectionItem::Expr { expr, .. } => match expr {
3284 Expr::String(s, _) => assert_eq!(s, "x"),
3285 other => panic!("expected string literal, got {other:?}"),
3286 },
3287 other => panic!("expected projection expr, got {other:?}"),
3288 }
3289 }
3290
3291 #[test]
3292 fn parse_string_escapes() {
3293 let doc = parse_query(r#"RETURN "a\nb", 'it\'s', "\\""#).unwrap();
3294 let sp = as_regular_single_part(doc);
3295
3296 let ret = sp.return_clause.expect("expected RETURN clause");
3297 assert_eq!(ret.body.items.len(), 3);
3298
3299 match &ret.body.items[0] {
3300 ProjectionItem::Expr {
3301 expr: Expr::String(s, _),
3302 ..
3303 } => assert_eq!(s, "a\nb"),
3304 other => panic!("expected escaped string, got {other:?}"),
3305 }
3306 match &ret.body.items[1] {
3307 ProjectionItem::Expr {
3308 expr: Expr::String(s, _),
3309 ..
3310 } => assert_eq!(s, "it's"),
3311 other => panic!("expected escaped string, got {other:?}"),
3312 }
3313 match &ret.body.items[2] {
3314 ProjectionItem::Expr {
3315 expr: Expr::String(s, _),
3316 ..
3317 } => assert_eq!(s, "\\"),
3318 other => panic!("expected escaped string, got {other:?}"),
3319 }
3320 }
3321
3322 #[test]
3323 fn parse_union_query() {
3324 let doc = parse_query("MATCH (a) RETURN a UNION MATCH (b) RETURN b").unwrap();
3325
3326 let Statement::Query(Query::Regular(rq)) = doc.statement else {
3327 panic!("expected regular query");
3328 };
3329
3330 assert_eq!(rq.unions.len(), 1);
3331 assert!(!rq.unions[0].all);
3332
3333 let SingleQuery::SinglePart(head) = rq.head else {
3334 panic!("expected single-part head");
3335 };
3336 let head_ret = head.return_clause.expect("expected head return");
3337 assert_eq!(head_ret.body.items.len(), 1);
3338
3339 let SingleQuery::SinglePart(union_q) = &rq.unions[0].query else {
3340 panic!("expected single-part union");
3341 };
3342 let union_ret = union_q
3343 .return_clause
3344 .as_ref()
3345 .expect("expected union return");
3346 assert_eq!(union_ret.body.items.len(), 1);
3347 }
3348
3349 #[test]
3350 fn parse_union_all_query() {
3351 let doc = parse_query("MATCH (a) RETURN a UNION ALL MATCH (b) RETURN b").unwrap();
3352
3353 let Statement::Query(Query::Regular(rq)) = doc.statement else {
3354 panic!("expected regular query");
3355 };
3356
3357 assert_eq!(rq.unions.len(), 1);
3358 assert!(rq.unions[0].all);
3359 }
3360
3361 #[test]
3362 fn parse_with_clause_in_multi_part_query() {
3363 let doc = parse_query("MATCH (n) WITH n RETURN n").unwrap();
3364 let mp = as_regular_multi_part(doc);
3365
3366 assert_eq!(mp.parts.len(), 1);
3367 assert_eq!(mp.parts[0].reading_clauses.len(), 1);
3368 assert!(mp.parts[0].updating_clauses.is_empty());
3369 assert_eq!(mp.parts[0].with_clause.body.items.len(), 1);
3370 assert!(mp.parts[0].with_clause.where_.is_none());
3371 assert!(mp.tail.return_clause.is_some());
3372 }
3373
3374 #[test]
3375 fn parse_with_where_clause_in_multi_part_query() {
3376 let doc = parse_query("MATCH (n) WITH n WHERE n.age >= 18 RETURN n").unwrap();
3377 let mp = as_regular_multi_part(doc);
3378
3379 let where_ = mp.parts[0]
3380 .with_clause
3381 .where_
3382 .as_ref()
3383 .expect("expected WITH WHERE clause");
3384 match where_ {
3385 Expr::Binary { op, .. } => assert!(matches!(op, BinaryOp::Ge)),
3386 other => panic!("expected binary expression, got {other:?}"),
3387 }
3388 }
3389
3390 #[test]
3391 fn parse_multi_part_with_update() {
3392 let doc = parse_query("MATCH (n) SET n:Seen WITH n RETURN n").unwrap();
3393 let mp = as_regular_multi_part(doc);
3394
3395 assert_eq!(mp.parts.len(), 1);
3396 assert_eq!(mp.parts[0].reading_clauses.len(), 1);
3397 assert_eq!(mp.parts[0].updating_clauses.len(), 1);
3398 assert!(mp.tail.return_clause.is_some());
3399 }
3400
3401 #[test]
3402 fn parse_standalone_call_explicit() {
3403 let doc = parse_query("CALL db.labels()").unwrap();
3404 let call = as_standalone_call(doc);
3405
3406 match call.procedure {
3407 ProcedureInvocationKind::Explicit(proc_) => {
3408 assert_eq!(
3409 proc_.name.parts,
3410 vec!["db".to_string(), "labels".to_string()]
3411 );
3412 assert!(proc_.args.is_empty());
3413 }
3414 other => panic!("expected explicit procedure invocation, got {other:?}"),
3415 }
3416
3417 assert!(call.yield_items.is_empty());
3418 assert!(!call.yield_all);
3419 }
3420
3421 #[test]
3422 fn parse_standalone_call_implicit() {
3423 let doc = parse_query("CALL db.labels").unwrap();
3424 let call = as_standalone_call(doc);
3425
3426 match call.procedure {
3427 ProcedureInvocationKind::Implicit(name) => {
3428 assert_eq!(name.parts, vec!["db".to_string(), "labels".to_string()]);
3429 }
3430 other => panic!("expected implicit procedure name, got {other:?}"),
3431 }
3432 }
3433
3434 #[test]
3435 fn parse_standalone_call_yield_all() {
3436 let doc = parse_query("CALL db.labels() YIELD *").unwrap();
3437 let call = as_standalone_call(doc);
3438
3439 assert!(call.yield_all);
3440 assert!(call.yield_items.is_empty());
3441 }
3442
3443 #[test]
3444 fn parse_standalone_call_yield_items() {
3445 let doc = parse_query("CALL db.labels() YIELD label, value AS v").unwrap();
3446 let call = as_standalone_call(doc);
3447
3448 assert!(!call.yield_all);
3449 assert_eq!(call.yield_items.len(), 2);
3450
3451 assert_eq!(call.yield_items[0].field, None);
3452 assert_eq!(call.yield_items[0].alias.name, "label");
3453
3454 assert_eq!(call.yield_items[1].field.as_deref(), Some("value"));
3455 assert_eq!(call.yield_items[1].alias.name, "v");
3456 }
3457
3458 #[test]
3459 fn parse_in_query_call() {
3460 let doc = parse_query("CALL db.labels() YIELD label RETURN label").unwrap();
3461 let sp = as_regular_single_part(doc);
3462
3463 assert_eq!(sp.reading_clauses.len(), 1);
3464 let ReadingClause::InQueryCall(call) = &sp.reading_clauses[0] else {
3465 panic!("expected in-query CALL");
3466 };
3467
3468 assert_eq!(
3469 call.procedure.name.parts,
3470 vec!["db".to_string(), "labels".to_string()]
3471 );
3472 assert_eq!(call.yield_items.len(), 1);
3473 assert!(call.where_.is_none());
3474 }
3475
3476 #[test]
3477 fn parse_in_query_call_with_where() {
3478 let doc = parse_query("CALL db.labels() YIELD label WHERE label IS NOT NULL RETURN label")
3479 .unwrap();
3480 let sp = as_regular_single_part(doc);
3481
3482 let ReadingClause::InQueryCall(call) = &sp.reading_clauses[0] else {
3483 panic!("expected in-query CALL");
3484 };
3485 let where_ = call.where_.as_ref().expect("expected WHERE on CALL");
3486
3487 match where_ {
3488 Expr::Binary { op, .. } => assert!(matches!(op, BinaryOp::IsNotNull)),
3489 other => panic!("expected binary WHERE expression, got {other:?}"),
3490 }
3491 }
3492
3493 #[test]
3494 fn parse_semicolon() {
3495 let doc = parse_query("MATCH (n) RETURN n;").unwrap();
3496 let sp = as_regular_single_part(doc);
3497 assert!(sp.return_clause.is_some());
3498 }
3499}