1use std::fmt;
4
5use lalrpop_util::ParseError;
6
7use crate::ast::{
8 BinaryOperator, Clause, ClauseKind, ErrorNode, Expr, ExprKind, Identifier, IntegerLiteral,
9 IntegerRadix, IsPredicate, LabelExpression, ListComprehension, Literal, LiteralKind,
10 MapProjection, MapProjectionItem, MatchClause, Name, Node, Parameter, ParameterName,
11 PathFactor, PathFactorKind, Pattern, PatternComprehension, Program, QualifiedName, Quantifier,
12 Query, QueryKind, QueryStatement, QuoteStyle, RegularQuery, RelationshipDirection,
13 RelationshipPattern, SingleQuery, SingleQueryKind, StatementKind, StringLiteral, UnionBranch,
14 UnionOperator,
15};
16use crate::diagnostic::{Diagnostic, DiagnosticCode, ParseErrors};
17use crate::lexer;
18use crate::span::Span;
19use crate::token::{Keyword, Token, TokenKind};
20
21#[cfg_attr(mutants, mutants::skip)]
28#[path = "generated/cypher.rs"]
29#[allow(clippy::all)]
30mod generated;
31
32const MAX_DIAGNOSTICS: usize = 32;
33
34#[derive(Clone, Debug, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct ParsedProgram {
38 pub program: Program,
40 pub tokens: Vec<Token>,
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub struct ParseOutcome<T> {
48 pub value: Option<T>,
54 pub diagnostics: Vec<Diagnostic>,
56}
57
58pub fn parse(source: &str) -> Result<ParsedProgram, ParseErrors> {
60 let outcome = parse_recovering(source);
61 if outcome.diagnostics.iter().any(Diagnostic::is_error) {
62 return Err(ParseErrors::new(outcome.diagnostics));
63 }
64
65 outcome.value.ok_or_else(|| {
66 ParseErrors::new(vec![Diagnostic::error(
67 DiagnosticCode::Internal,
68 "the parser did not produce a program",
69 Span::empty(source.len()),
70 )])
71 })
72}
73
74#[must_use]
82pub fn parse_recovering(source: &str) -> ParseOutcome<ParsedProgram> {
83 let lexed = lexer::lex(source);
84 let mut diagnostics = lexed.diagnostics.clone();
85 let significant = parser_tokens(&lexed.tokens);
86
87 let parsed = parse_program_with_contextual_names(source, &significant);
88 let program = match parsed {
89 Ok(program) => Some(program),
90 Err(error) => {
91 diagnostics.push(parse_diagnostic(source, error));
92 None
93 }
94 };
95
96 if program.is_none() || !diagnostics.is_empty() {
97 collect_delimiter_diagnostics(source, &lexed.tokens, &mut diagnostics);
98 }
99
100 normalize_diagnostics(source.len(), &mut diagnostics);
101
102 let program = program.unwrap_or_else(|| {
103 recover_clauses(source, &significant)
104 .unwrap_or_else(|| recovered_program(source, &lexed.tokens))
105 });
106 ParseOutcome {
107 value: Some(ParsedProgram {
108 program,
109 tokens: lexed.tokens,
110 }),
111 diagnostics,
112 }
113}
114
115type SpannedParserToken = (usize, ParserToken, usize);
116
117fn parse_program_with_contextual_names(
118 source: &str,
119 tokens: &[SpannedParserToken],
120) -> Result<Program, LalrpopError> {
121 parse_tokens_with_contextual_names(tokens, |contextual| parse_program(source, contextual))
122}
123
124fn parse_tokens_with_contextual_names<T>(
125 tokens: &[SpannedParserToken],
126 mut parse_tokens: impl FnMut(&[SpannedParserToken]) -> Result<T, LalrpopError>,
127) -> Result<T, LalrpopError> {
128 let mut contextual = tokens.to_vec();
129 let mut resolved = vec![false; tokens.len()];
130 for index in 0..tokens.len() {
131 let follows_name_marker = index
132 .checked_sub(1)
133 .and_then(|previous| tokens.get(previous))
134 .is_some_and(|token| matches!(token.1, ParserToken::As | ParserToken::Dot));
135 if is_aliased_bare_expression(tokens, index) && tokens[index].1.is_keyword_literal() {
136 resolved[index] = true;
139 continue;
140 }
141 if tokens[index].1.is_contextual_name_candidate()
142 && ((follows_name_marker
143 && !(tokens[index].1 == ParserToken::As && tokens[index - 1].1 == ParserToken::As))
144 || is_solo_projection_keyword(tokens, index)
145 || is_aliased_bare_expression(tokens, index))
146 {
147 resolved[index] = true;
148 contextual[index].1 = ParserToken::Identifier;
149 }
150 }
151
152 let initial_error = match parse_tokens(&contextual) {
153 Ok(value) => return Ok(value),
154 Err(error) => error,
155 };
156
157 let mut best_error = initial_error.clone();
158 let mut error = initial_error;
159
160 loop {
172 if error_location(&error) >= error_location(&best_error) {
173 best_error = error.clone();
174 }
175
176 let location = error_location(&error);
177 let candidate = tokens
178 .iter()
179 .enumerate()
180 .filter(|(index, (_, token, _))| {
181 token.is_contextual_name_candidate() && !resolved[*index]
182 })
183 .map(|(index, (start, _, end))| {
184 let distance = if location < *start {
185 *start - location
186 } else {
187 location.saturating_sub(*end)
188 };
189 let exact = (*start..*end).contains(&location);
190 let followed_by_as = tokens
191 .get(index + 1)
192 .is_some_and(|token| token.1 == ParserToken::As);
193 let priority = if exact && (tokens[index].1 != ParserToken::As || followed_by_as) {
194 0
195 } else if followed_by_as && tokens[index].1 != ParserToken::As {
196 1
197 } else if exact {
198 2
199 } else {
200 3
201 };
202 (priority, distance, index)
203 })
204 .min();
205 let Some((_, _, index)) = candidate else {
206 break;
207 };
208
209 let signature = contextual_name_signature(tokens, index);
210 let mut reclassified = Vec::new();
211 for peer in 0..tokens.len() {
212 if !resolved[peer]
213 && tokens[peer].1.is_contextual_name_candidate()
214 && contextual_name_signature(tokens, peer) == signature
215 {
216 resolved[peer] = true;
217 contextual[peer].1 = ParserToken::Identifier;
218 reclassified.push(peer);
219 }
220 }
221 let next_error = match parse_tokens(&contextual) {
222 Ok(value) => return Ok(value),
223 Err(next_error) => next_error,
224 };
225 let next_location = error_location(&next_error);
230 let lands_on_reclassified = reclassified
231 .iter()
232 .any(|&peer| (tokens[peer].0..tokens[peer].2).contains(&next_location));
233 if next_location > location && !lands_on_reclassified {
234 error = next_error;
235 } else {
236 break;
237 }
238 }
239
240 Err(best_error)
241}
242
243fn is_aliased_bare_expression(tokens: &[SpannedParserToken], index: usize) -> bool {
244 index
249 .checked_sub(1)
250 .and_then(|previous| tokens.get(previous))
251 .is_some_and(|token| {
252 matches!(
253 token.1,
254 ParserToken::Return | ParserToken::With | ParserToken::Yield | ParserToken::Comma
255 )
256 })
257 && tokens
258 .get(index + 1)
259 .is_some_and(|token| token.1 == ParserToken::As)
260}
261
262fn is_solo_projection_keyword(tokens: &[SpannedParserToken], index: usize) -> bool {
263 if !matches!(tokens[index].1, ParserToken::SetAll | ParserToken::Distinct)
264 || !index
265 .checked_sub(1)
266 .and_then(|previous| tokens.get(previous))
267 .is_some_and(|token| matches!(token.1, ParserToken::Return | ParserToken::With))
268 {
269 return false;
270 }
271
272 tokens.get(index + 1).is_none_or(|token| {
273 matches!(
274 token.1,
275 ParserToken::As
276 | ParserToken::Comma
277 | ParserToken::Order
278 | ParserToken::Offset
279 | ParserToken::Skip
280 | ParserToken::Limit
281 | ParserToken::Where
282 | ParserToken::Union
283 | ParserToken::Semicolon
284 ) || clause_starts(token.1)
285 })
286}
287
288fn contextual_name_signature(
289 tokens: &[SpannedParserToken],
290 index: usize,
291) -> (Option<ParserToken>, ParserToken, Option<ParserToken>) {
292 (
293 index
294 .checked_sub(1)
295 .and_then(|previous| tokens.get(previous))
296 .map(|token| token.1),
297 tokens[index].1,
298 tokens.get(index + 1).map(|token| token.1),
299 )
300}
301
302fn parse_program(source: &str, tokens: &[SpannedParserToken]) -> Result<Program, LalrpopError> {
303 if tokens.is_empty() {
304 return Ok(Program::new(Vec::new(), Span::empty(0)));
305 }
306
307 let (query_tokens, terminator) = match tokens.last() {
308 Some((start, ParserToken::Semicolon, end)) => {
309 (&tokens[..tokens.len() - 1], Some(Span::new(*start, *end)))
310 }
311 _ => (tokens, None),
312 };
313
314 if query_tokens.is_empty() {
315 return Err(ParseError::UnrecognizedEof {
316 location: terminator.map_or(0, |span| span.start),
317 expected: vec!["query clause".to_owned()],
318 });
319 }
320
321 let (head, unions) = match parse_regular_query(source, query_tokens) {
322 Ok(query) => query,
323 Err(regular_error) => {
324 let input = query_tokens.iter().copied().map(Ok);
325 let clause = match generated::StandaloneCallRootParser::new().parse(source, input) {
326 Ok(clause) => clause,
327 Err(_) => return Err(regular_error),
328 };
329 let span = clause.span;
330 (
331 Node::new(
332 SingleQueryKind {
333 clauses: vec![clause],
334 },
335 span,
336 ),
337 Vec::new(),
338 )
339 }
340 };
341 let query_span = Span::new(
342 query_tokens.first().expect("non-empty query").0,
343 query_tokens.last().expect("non-empty query").2,
344 );
345 let query = Node::new(
346 QueryKind::Regular(RegularQuery { head, unions }),
347 query_span,
348 );
349 let statement_span = terminator.map_or(query_span, |span| query_span.cover(span));
350 let statement = Node::new(
351 StatementKind::Query(QueryStatement {
352 mode: None,
353 query,
354 terminator,
355 }),
356 statement_span,
357 );
358 Ok(Program::new(vec![statement], statement_span))
359}
360
361fn parse_regular_query(
362 source: &str,
363 tokens: &[SpannedParserToken],
364) -> Result<(SingleQuery, Vec<UnionBranch>), LalrpopError> {
365 let unsplit_error = match parse_single_query(source, tokens) {
366 Ok(query) => return Ok((query, Vec::new())),
367 Err(error) => error,
368 };
369
370 for union_index in top_level_indices(tokens, |token| token == ParserToken::Union) {
371 let Ok(head) = parse_single_query(source, &tokens[..union_index]) else {
372 continue;
373 };
374
375 let mut right_start = union_index + 1;
376 let operator = match tokens.get(right_start).map(|token| token.1) {
377 Some(ParserToken::All) => {
378 right_start += 1;
379 UnionOperator::All
380 }
381 Some(ParserToken::Distinct) => {
382 right_start += 1;
383 UnionOperator::Distinct
384 }
385 _ => UnionOperator::Default,
386 };
387 if right_start >= tokens.len() {
388 continue;
389 }
390
391 let Ok((right, mut tail)) = parse_regular_query(source, &tokens[right_start..]) else {
392 continue;
393 };
394 let operator_end = tokens[right_start - 1].2;
395 let operator = Node::new(operator, Span::new(tokens[union_index].0, operator_end));
396 let mut unions = Vec::with_capacity(1 + tail.len());
397 unions.push(UnionBranch {
398 operator,
399 query: right,
400 });
401 unions.append(&mut tail);
402 return Ok((head, unions));
403 }
404
405 Err(unsplit_error)
406}
407
408fn parse_single_query(
409 source: &str,
410 tokens: &[SpannedParserToken],
411) -> Result<SingleQuery, LalrpopError> {
412 if tokens.is_empty() {
413 return Err(ParseError::UnrecognizedEof {
414 location: 0,
415 expected: vec!["query clause".to_owned()],
416 });
417 }
418
419 let mut clauses: Vec<Clause> = Vec::new();
420 let mut cursor = 0;
421 while cursor < tokens.len() {
422 let mut candidate_ends = top_level_indices(&tokens[cursor + 1..], clause_starts)
423 .into_iter()
424 .map(|index| cursor + 1 + index)
425 .collect::<Vec<_>>();
426 candidate_ends.push(tokens.len());
427 candidate_ends.sort_unstable();
428 candidate_ends.dedup();
429
430 let mut best_error = None;
431 let mut parsed = None;
432 for end in candidate_ends {
433 let input = tokens[cursor..end].iter().copied().map(Ok);
434 match generated::ClauseRootParser::new().parse(source, input) {
435 Ok(clause) => {
436 if matches!(&clause.kind, ClauseKind::Return(_)) && end < tokens.len() {
437 return Err(ParseError::ExtraToken { token: tokens[end] });
438 }
439 parsed = Some((clause, end));
440 break;
441 }
442 Err(error) => retain_farthest_error(&mut best_error, error),
443 }
444 }
445
446 let Some((clause, end)) = parsed else {
447 return Err(best_error.unwrap_or(ParseError::UnrecognizedEof {
448 location: tokens[cursor].0,
449 expected: vec!["query clause".to_owned()],
450 }));
451 };
452 clauses.push(clause);
453 cursor = end;
454 }
455
456 let span = Span::new(
457 tokens.first().expect("non-empty query").0,
458 tokens.last().unwrap().2,
459 );
460 Ok(Node::new(SingleQueryKind { clauses }, span))
461}
462
463fn retain_farthest_error(slot: &mut Option<LalrpopError>, candidate: LalrpopError) {
464 let candidate_location = error_location(&candidate);
465 if slot
466 .as_ref()
467 .is_none_or(|current| candidate_location >= error_location(current))
468 {
469 *slot = Some(candidate);
470 }
471}
472
473fn error_location(error: &LalrpopError) -> usize {
474 match error {
475 ParseError::InvalidToken { location } | ParseError::UnrecognizedEof { location, .. } => {
476 *location
477 }
478 ParseError::UnrecognizedToken { token, .. } | ParseError::ExtraToken { token } => token.0,
479 ParseError::User { error: location } => *location,
480 }
481}
482
483fn top_level_indices(
484 tokens: &[SpannedParserToken],
485 predicate: impl Fn(ParserToken) -> bool,
486) -> Vec<usize> {
487 let mut depth = 0usize;
488 let mut indices = Vec::new();
489 for (index, (_, token, _)) in tokens.iter().copied().enumerate() {
490 if depth == 0 && predicate(token) {
491 indices.push(index);
492 }
493 match token {
494 ParserToken::LeftParen | ParserToken::LeftBracket | ParserToken::LeftBrace => {
495 depth = depth.saturating_add(1);
496 }
497 ParserToken::RightParen | ParserToken::RightBracket | ParserToken::RightBrace => {
498 depth = depth.saturating_sub(1);
499 }
500 _ => {}
501 }
502 }
503 indices
504}
505
506fn clause_starts(token: ParserToken) -> bool {
507 matches!(
508 token,
509 ParserToken::Match
510 | ParserToken::Optional
511 | ParserToken::Unwind
512 | ParserToken::With
513 | ParserToken::Return
514 | ParserToken::Create
515 | ParserToken::Merge
516 | ParserToken::Set
517 | ParserToken::Remove
518 | ParserToken::Delete
519 | ParserToken::Detach
520 | ParserToken::Call
521 )
522}
523
524fn normalize_diagnostics(source_len: usize, diagnostics: &mut Vec<Diagnostic>) {
525 diagnostics.sort_by(|left, right| {
526 left.primary_span
527 .start
528 .cmp(&right.primary_span.start)
529 .then(left.primary_span.end.cmp(&right.primary_span.end))
530 .then(left.code.cmp(&right.code))
531 .then(left.message.cmp(&right.message))
532 });
533 diagnostics.dedup_by(|left, right| {
534 left.code == right.code
535 && left.primary_span == right.primary_span
536 && left.message == right.message
537 });
538
539 if diagnostics.len() > MAX_DIAGNOSTICS {
540 diagnostics.truncate(MAX_DIAGNOSTICS - 1);
541 diagnostics.push(Diagnostic::error(
542 DiagnosticCode::TooManyErrors,
543 "additional parser diagnostics were suppressed",
544 Span::empty(source_len),
545 ));
546 }
547}
548
549fn recovered_program(source: &str, tokens: &[Token]) -> Program {
550 let span = significant_span(tokens).unwrap_or_else(|| Span::empty(source.len()));
551 let statements = if span.is_empty() {
552 Vec::new()
553 } else {
554 vec![Node::new(StatementKind::Error(ErrorNode), span)]
555 };
556 Program::new(statements, span)
557}
558
559fn significant_span(tokens: &[Token]) -> Option<Span> {
560 let mut significant = tokens.iter().filter(|token| !token.is_trivia());
561 let first = significant.next()?;
562 let mut span = first.span;
563 for token in significant {
564 span = span.cover(token.span);
565 }
566 Some(span)
567}
568
569fn collect_delimiter_diagnostics(
570 source: &str,
571 tokens: &[Token],
572 diagnostics: &mut Vec<Diagnostic>,
573) {
574 let mut stack: Vec<(TokenKind, Span)> = Vec::new();
575
576 for token in tokens.iter().filter(|token| !token.is_trivia()) {
577 match token.kind {
578 TokenKind::LeftParen | TokenKind::LeftBracket | TokenKind::LeftBrace => {
579 stack.push((token.kind, token.span));
580 }
581 TokenKind::RightParen | TokenKind::RightBracket | TokenKind::RightBrace => {
582 let expected_open = match token.kind {
583 TokenKind::RightParen => TokenKind::LeftParen,
584 TokenKind::RightBracket => TokenKind::LeftBracket,
585 TokenKind::RightBrace => TokenKind::LeftBrace,
586 _ => unreachable!(),
587 };
588 match stack.pop() {
589 Some((open, _)) if open == expected_open => {}
590 Some((open, open_span)) => diagnostics.push(
591 Diagnostic::error(
592 DiagnosticCode::MismatchedDelimiter,
593 format!(
594 "mismatched delimiter: {} does not close {}",
595 token.kind, open
596 ),
597 token.span,
598 )
599 .with_label(open_span, "this delimiter was opened here"),
600 ),
601 None => diagnostics.push(Diagnostic::error(
602 DiagnosticCode::MismatchedDelimiter,
603 format!("unexpected closing delimiter {}", token.kind),
604 token.span,
605 )),
606 }
607 }
608 _ => {}
609 }
610 }
611
612 for (open, span) in stack {
613 diagnostics.push(
614 Diagnostic::error(
615 DiagnosticCode::UnclosedDelimiter,
616 format!("unclosed delimiter {open}"),
617 span,
618 )
619 .with_help(format!(
620 "add the matching delimiter before byte {}",
621 source.len()
622 )),
623 );
624 }
625}
626
627type LalrpopError = ParseError<usize, ParserToken, usize>;
629
630fn parse_diagnostic(source: &str, error: LalrpopError) -> Diagnostic {
631 match error {
632 ParseError::InvalidToken { location } => Diagnostic::error(
633 DiagnosticCode::InvalidToken,
634 "invalid token",
635 Span::empty(location.min(source.len())),
636 ),
637 ParseError::UnrecognizedEof { location, expected } => {
638 let expected = normalized_expected(expected);
639 let message = if expected.is_empty() {
640 "unexpected end of input".to_owned()
641 } else {
642 format!("unexpected end of input; expected {expected}")
643 };
644 Diagnostic::error(
645 DiagnosticCode::UnexpectedEof,
646 message,
647 Span::empty(location.min(source.len())),
648 )
649 }
650 ParseError::UnrecognizedToken {
651 token: (start, token, end),
652 expected,
653 } => {
654 let expected = normalized_expected(expected);
655 let message = if expected.is_empty() {
656 format!("unexpected token {token}")
657 } else {
658 format!("unexpected token {token}; expected {expected}")
659 };
660 Diagnostic::error(
661 DiagnosticCode::UnexpectedToken,
662 message,
663 Span::new(start, end),
664 )
665 }
666 ParseError::ExtraToken {
667 token: (start, token, end),
668 } => Diagnostic::error(
669 DiagnosticCode::ExtraToken,
670 format!("extra token {token} after the end of the statement"),
671 Span::new(start, end),
672 ),
673 ParseError::User { error: location } => Diagnostic::error(
674 DiagnosticCode::UnexpectedToken,
675 "input does not satisfy the selected syntax production",
676 Span::empty(location.min(source.len())),
677 ),
678 }
679}
680
681fn normalized_expected(mut expected: Vec<String>) -> String {
682 for item in &mut expected {
683 *item = item.trim_matches('"').to_owned();
684 }
685 expected.sort();
686 expected.dedup();
687 match expected.as_slice() {
688 [] => String::new(),
689 [only] => format!("`{only}`"),
690 [head @ .., last] => {
691 let head = head
692 .iter()
693 .map(|item| format!("`{item}`"))
694 .collect::<Vec<_>>()
695 .join(", ");
696 format!("{head}, or `{last}`")
697 }
698 }
699}
700
701fn parser_tokens(tokens: &[Token]) -> Vec<SpannedParserToken> {
702 let significant = tokens
703 .iter()
704 .filter(|token| !token.is_trivia())
705 .collect::<Vec<_>>();
706 let mut result = Vec::with_capacity(significant.len());
707 let mut index = 0;
708 while index < significant.len() {
709 if let Some(end) = escaped_parameter_end(&significant, index) {
710 result.push((
711 significant[index].span.start,
712 ParserToken::Parameter,
713 significant[end].span.end,
714 ));
715 index = end + 1;
716 continue;
717 }
718 if let Some(end) = pattern_comprehension_end(&significant, index) {
719 result.push((
720 significant[index].span.start,
721 ParserToken::PatternComprehension,
722 significant[end].span.end,
723 ));
724 index = end + 1;
725 continue;
726 }
727 if significant[index].kind == TokenKind::LeftParen
728 && is_pattern_expression_context(&significant, index)
729 && let Some(end) = pattern_expression_end(&significant, index)
730 {
731 result.push((
732 significant[index].span.start,
733 ParserToken::PatternExpression,
734 significant[end].span.end,
735 ));
736 index = end + 1;
737 continue;
738 }
739 if is_relationship_label_start(&significant, index)
740 && let Some(end) = relationship_label_end(&significant, index)
741 {
742 result.push((
743 significant[index].span.start,
744 ParserToken::RelationshipLabelExpression,
745 significant[end].span.end,
746 ));
747 index = end + 1;
748 continue;
749 }
750 if is_label_predicate_context(&significant, index)
751 && let Some(end) = label_predicate_end(&significant, index)
752 {
753 result.push((
754 significant[index].span.start,
755 ParserToken::LabelPredicate,
756 significant[end].span.end,
757 ));
758 index = end + 1;
759 continue;
760 }
761 if !is_procedure_name_start(&significant, index)
762 && let Some(end) = qualified_function_name_end(&significant, index)
763 {
764 result.push((
765 significant[index].span.start,
766 ParserToken::QualifiedFunctionName,
767 significant[end].span.end,
768 ));
769 index = end + 1;
770 continue;
771 }
772 let token = significant[index];
773 let mut parser_token = ParserToken::from(token.kind);
774 if parser_token == ParserToken::All && is_explicit_set_all(&significant, index) {
775 parser_token = ParserToken::SetAll;
776 }
777 result.push((token.span.start, parser_token, token.span.end));
778 index += 1;
779 }
780 result
781}
782
783fn escaped_parameter_end(tokens: &[&Token], start: usize) -> Option<usize> {
784 (tokens.get(start)?.kind == TokenKind::Dollar
785 && tokens.get(start + 1).is_some_and(|token| {
786 token.kind == TokenKind::EscapedIdentifier && tokens[start].span.end == token.span.start
787 }))
788 .then_some(start + 1)
789}
790
791fn qualified_function_name_end(tokens: &[&Token], start: usize) -> Option<usize> {
792 if !is_symbolic_name_kind(tokens.get(start)?.kind) {
793 return None;
794 }
795
796 let mut end = start;
797 let mut components = 1usize;
798 while tokens
799 .get(end + 1)
800 .is_some_and(|token| token.kind == TokenKind::Dot)
801 && tokens
802 .get(end + 2)
803 .is_some_and(|token| is_symbolic_name_kind(token.kind))
804 {
805 end += 2;
806 components += 1;
807 }
808
809 (components > 1
810 && tokens
811 .get(end + 1)
812 .is_some_and(|token| token.kind == TokenKind::LeftParen))
813 .then_some(end)
814}
815
816fn is_procedure_name_start(tokens: &[&Token], start: usize) -> bool {
817 let mut first_component = start;
818 while first_component >= 2
819 && tokens[first_component - 1].kind == TokenKind::Dot
820 && is_symbolic_name_kind(tokens[first_component - 2].kind)
821 {
822 first_component -= 2;
823 }
824
825 first_component
826 .checked_sub(1)
827 .and_then(|index| tokens.get(index))
828 .is_some_and(|token| token.kind == TokenKind::Keyword(Keyword::Call))
829}
830
831fn is_symbolic_name_kind(kind: TokenKind) -> bool {
832 matches!(
833 kind,
834 TokenKind::Identifier | TokenKind::EscapedIdentifier | TokenKind::Keyword(_)
835 )
836}
837
838fn is_explicit_set_all(tokens: &[&Token], index: usize) -> bool {
839 if tokens
840 .get(index + 1)
841 .is_some_and(|token| token.kind == TokenKind::LeftParen)
842 {
843 return false;
844 }
845
846 let Some(previous) = index.checked_sub(1).and_then(|index| tokens.get(index)) else {
847 return false;
848 };
849 if matches!(
850 previous.kind,
851 TokenKind::Keyword(Keyword::Return | Keyword::With)
852 ) {
853 return true;
854 }
855 if previous.kind != TokenKind::LeftParen {
856 return false;
857 }
858
859 index
860 .checked_sub(2)
861 .and_then(|index| tokens.get(index))
862 .is_some_and(|token| {
863 matches!(
864 ParserToken::from(token.kind),
865 ParserToken::Identifier
866 | ParserToken::EscapedIdentifier
867 | ParserToken::Count
868 | ParserToken::Match
869 | ParserToken::Path
870 | ParserToken::Return
871 )
872 })
873}
874
875fn pattern_comprehension_end(tokens: &[&Token], start: usize) -> Option<usize> {
876 if tokens.get(start)?.kind != TokenKind::LeftBracket {
877 return None;
878 }
879
880 let mut brackets = 0usize;
887 let mut parentheses = 0usize;
888 let mut braces = 0usize;
889 let mut pipe_at = None;
890 let mut where_seen = false;
891 let mut arrow = false;
892 let mut top_level_nodes = 0usize;
893 let mut top_level_minuses = 0usize;
894 for (index, token) in tokens.iter().enumerate().skip(start) {
895 let in_pattern_region = pipe_at.is_none() && !where_seen;
896 match token.kind {
897 TokenKind::LeftBracket => brackets += 1,
898 TokenKind::RightBracket => {
899 if brackets == 1 && parentheses == 0 && braces == 0 {
900 let has_projection = pipe_at.is_some_and(|pipe| index > pipe + 1);
901 let undirected_relationship = top_level_nodes >= 2 && top_level_minuses >= 2;
902 return (has_projection && (arrow || undirected_relationship)).then_some(index);
903 }
904 brackets = brackets.saturating_sub(1);
905 }
906 TokenKind::LeftParen => {
907 let call_or_grouping = index > start
908 && (matches!(
909 tokens[index - 1].kind,
910 TokenKind::RightParen | TokenKind::RightBracket | TokenKind::RightBrace
911 ) || is_symbolic_name_kind(tokens[index - 1].kind));
912 if in_pattern_region
913 && brackets == 1
914 && parentheses == 0
915 && braces == 0
916 && !call_or_grouping
917 {
918 top_level_nodes += 1;
919 }
920 parentheses += 1;
921 }
922 TokenKind::RightParen => parentheses = parentheses.saturating_sub(1),
923 TokenKind::LeftBrace => braces += 1,
924 TokenKind::RightBrace => braces = braces.saturating_sub(1),
925 TokenKind::Pipe if brackets == 1 && parentheses == 0 && braces == 0 => {
926 pipe_at = Some(index)
927 }
928 TokenKind::Keyword(Keyword::Where)
931 if in_pattern_region
932 && brackets == 1
933 && parentheses == 0
934 && braces == 0
935 && tokens
936 .get(index + 1)
937 .is_none_or(|next| next.kind != TokenKind::Equal) =>
938 {
939 where_seen = true;
940 }
941 TokenKind::LeftArrow | TokenKind::RightArrow if in_pattern_region && brackets == 1 => {
942 arrow = true;
943 }
944 TokenKind::Minus
945 if in_pattern_region && brackets == 1 && parentheses == 0 && braces == 0 =>
946 {
947 top_level_minuses += 1;
948 }
949 _ => {}
950 }
951 }
952 None
953}
954
955fn is_pattern_expression_context(tokens: &[&Token], start: usize) -> bool {
956 if start >= 2
957 && tokens[start - 1].kind == TokenKind::LeftParen
958 && matches!(
959 tokens[start - 2].kind,
960 TokenKind::Keyword(Keyword::ShortestPath | Keyword::AllShortestPaths)
961 )
962 {
963 return false;
964 }
965 if start >= 2
966 && tokens[start - 1].kind == TokenKind::LeftBrace
967 && matches!(
968 tokens[start - 2].kind,
969 TokenKind::Keyword(Keyword::Exists | Keyword::Count | Keyword::Collect)
970 )
971 {
972 return false;
973 }
974 if is_in_map_entry_value(tokens, start) {
975 return true;
976 }
977
978 let mut nested_brackets = 0usize;
983 let mut nested_braces = 0usize;
984 for (index, token) in tokens[..start].iter().enumerate().rev() {
985 match token.kind {
986 TokenKind::RightBracket => {
987 nested_brackets = nested_brackets.saturating_add(1);
988 continue;
989 }
990 TokenKind::LeftBracket if nested_brackets != 0 => {
991 nested_brackets = nested_brackets.saturating_sub(1);
992 continue;
993 }
994 TokenKind::RightBrace => {
995 nested_braces = nested_braces.saturating_add(1);
996 continue;
997 }
998 TokenKind::LeftBrace if nested_braces != 0 => {
999 nested_braces = nested_braces.saturating_sub(1);
1000 continue;
1001 }
1002 _ if nested_brackets != 0 || nested_braces != 0 => continue,
1003 _ => {}
1004 }
1005 if tokens
1006 .get(index + 1)
1007 .is_some_and(|next| next.kind == TokenKind::Equal)
1008 {
1009 continue;
1010 }
1011 match token.kind {
1012 TokenKind::Pipe => return true,
1013 TokenKind::Keyword(
1014 Keyword::Where
1015 | Keyword::Return
1016 | Keyword::With
1017 | Keyword::Unwind
1018 | Keyword::Set
1019 | Keyword::Delete
1020 | Keyword::Then
1021 | Keyword::Else
1022 | Keyword::When
1023 | Keyword::Case,
1024 ) => return true,
1025 TokenKind::Keyword(
1026 Keyword::Match | Keyword::Create | Keyword::Merge | Keyword::Call | Keyword::Remove,
1027 ) => return false,
1028 _ => {}
1029 }
1030 }
1031 false
1032}
1033
1034fn pattern_expression_end(tokens: &[&Token], start: usize) -> Option<usize> {
1035 if tokens.get(start)?.kind != TokenKind::LeftParen {
1036 return None;
1037 }
1038
1039 let mut node_end =
1040 matching_delimiter(tokens, start, TokenKind::LeftParen, TokenKind::RightParen)?;
1041 let mut relationships = 0usize;
1042 while let Some(after_relationship) = relationship_syntax_end(tokens, node_end + 1) {
1043 let mut next_node = after_relationship;
1044 if matches!(
1045 tokens.get(next_node).map(|token| token.kind),
1046 Some(TokenKind::Star | TokenKind::Plus | TokenKind::Question)
1047 ) {
1048 next_node += 1;
1049 } else if tokens
1050 .get(next_node)
1051 .is_some_and(|token| token.kind == TokenKind::LeftBrace)
1052 {
1053 next_node = matching_delimiter(
1054 tokens,
1055 next_node,
1056 TokenKind::LeftBrace,
1057 TokenKind::RightBrace,
1058 )? + 1;
1059 }
1060 if !tokens
1061 .get(next_node)
1062 .is_some_and(|token| token.kind == TokenKind::LeftParen)
1063 {
1064 break;
1065 }
1066 node_end = matching_delimiter(
1067 tokens,
1068 next_node,
1069 TokenKind::LeftParen,
1070 TokenKind::RightParen,
1071 )?;
1072 relationships += 1;
1073 }
1074
1075 (relationships > 0).then_some(node_end)
1076}
1077
1078fn relationship_syntax_end(tokens: &[&Token], start: usize) -> Option<usize> {
1079 let mut cursor = match (
1080 tokens.get(start).map(|token| token.kind),
1081 tokens.get(start + 1).map(|token| token.kind),
1082 ) {
1083 (Some(TokenKind::Minus | TokenKind::LeftArrow), _) => start + 1,
1084 (Some(TokenKind::Less), Some(TokenKind::Minus)) => start + 2,
1085 _ => return None,
1086 };
1087 if tokens
1088 .get(cursor)
1089 .is_some_and(|token| token.kind == TokenKind::LeftBracket)
1090 {
1091 cursor = matching_delimiter(
1092 tokens,
1093 cursor,
1094 TokenKind::LeftBracket,
1095 TokenKind::RightBracket,
1096 )? + 1;
1097 }
1098 match (
1099 tokens.get(cursor).map(|token| token.kind),
1100 tokens.get(cursor + 1).map(|token| token.kind),
1101 ) {
1102 (Some(TokenKind::Minus), Some(TokenKind::Greater)) => Some(cursor + 2),
1103 (Some(TokenKind::Minus | TokenKind::RightArrow), _) => Some(cursor + 1),
1104 _ => None,
1105 }
1106}
1107
1108fn matching_delimiter(
1109 tokens: &[&Token],
1110 start: usize,
1111 open: TokenKind,
1112 close: TokenKind,
1113) -> Option<usize> {
1114 if tokens.get(start)?.kind != open {
1115 return None;
1116 }
1117 let mut depth = 0usize;
1118 for (index, token) in tokens.iter().enumerate().skip(start) {
1119 if token.kind == open {
1120 depth += 1;
1121 } else if token.kind == close {
1122 depth = depth.saturating_sub(1);
1123 if depth == 0 {
1124 return Some(index);
1125 }
1126 }
1127 }
1128 None
1129}
1130
1131fn is_relationship_label_start(tokens: &[&Token], start: usize) -> bool {
1132 if tokens
1133 .get(start)
1134 .is_none_or(|token| token.kind != TokenKind::Colon)
1135 {
1136 return false;
1137 }
1138 match start.checked_sub(1).and_then(|index| tokens.get(index)) {
1139 Some(token) if token.kind == TokenKind::LeftBracket => true,
1140 Some(token) if is_symbolic_name_kind(token.kind) => {
1141 start >= 2 && tokens[start - 2].kind == TokenKind::LeftBracket
1142 }
1143 _ => false,
1144 }
1145}
1146
1147fn relationship_label_end(tokens: &[&Token], start: usize) -> Option<usize> {
1148 let end = label_predicate_end(tokens, start)?;
1149 if !(start + 1..=end).any(|index| tokens[index].kind == TokenKind::Colon) {
1150 return Some(end);
1151 }
1152
1153 let mut cursor = start + 1;
1154 loop {
1155 if cursor > end || !is_symbolic_name_kind(tokens[cursor].kind) {
1156 return None;
1157 }
1158 cursor += 1;
1159 if cursor > end {
1160 return Some(end);
1161 }
1162 if tokens[cursor].kind != TokenKind::Pipe
1163 || tokens
1164 .get(cursor + 1)
1165 .is_none_or(|token| token.kind != TokenKind::Colon)
1166 {
1167 return None;
1168 }
1169 cursor += 2;
1170 }
1171}
1172
1173fn is_label_predicate_context(tokens: &[&Token], start: usize) -> bool {
1174 if !matches!(
1175 tokens.get(start).map(|token| token.kind),
1176 Some(TokenKind::Colon | TokenKind::Keyword(Keyword::Is))
1177 ) {
1178 return false;
1179 }
1180 if tokens[start].kind == TokenKind::Keyword(Keyword::Is)
1181 && tokens
1182 .get(start + 1)
1183 .is_some_and(|token| token.kind == TokenKind::Keyword(Keyword::As))
1184 && tokens
1185 .get(start + 2)
1186 .is_some_and(|token| is_symbolic_name_kind(token.kind))
1187 {
1188 return false;
1189 }
1190 if tokens[start].kind == TokenKind::Colon
1191 && start >= 2
1192 && is_symbolic_name_kind(tokens[start - 1].kind)
1193 && matches!(tokens[start - 2].kind, TokenKind::LeftBrace)
1194 {
1195 return false;
1196 }
1197 if is_in_map_entry_value(tokens, start) {
1198 return true;
1199 }
1200 if tokens[start].kind == TokenKind::Colon
1201 && start >= 2
1202 && is_symbolic_name_kind(tokens[start - 1].kind)
1203 && tokens[start - 2].kind == TokenKind::Comma
1204 && is_inside_braces(tokens, start)
1205 {
1206 return false;
1207 }
1208
1209 let mut nested_brackets = 0usize;
1215 let mut nested_braces = 0usize;
1216 for (index, token) in tokens[..start].iter().enumerate().rev() {
1217 match token.kind {
1218 TokenKind::RightBracket => {
1219 nested_brackets = nested_brackets.saturating_add(1);
1220 continue;
1221 }
1222 TokenKind::LeftBracket if nested_brackets != 0 => {
1223 nested_brackets = nested_brackets.saturating_sub(1);
1224 continue;
1225 }
1226 TokenKind::RightBrace => {
1227 nested_braces = nested_braces.saturating_add(1);
1228 continue;
1229 }
1230 TokenKind::LeftBrace if nested_braces != 0 => {
1231 nested_braces = nested_braces.saturating_sub(1);
1232 continue;
1233 }
1234 _ if nested_brackets != 0 || nested_braces != 0 => continue,
1235 _ => {}
1236 }
1237 if tokens
1238 .get(index + 1)
1239 .is_some_and(|next| next.kind == TokenKind::Equal)
1240 {
1241 continue;
1242 }
1243 match token.kind {
1244 TokenKind::Pipe => return true,
1245 TokenKind::Keyword(
1246 Keyword::Where
1247 | Keyword::Return
1248 | Keyword::With
1249 | Keyword::Unwind
1250 | Keyword::Delete
1251 | Keyword::Then
1252 | Keyword::Else
1253 | Keyword::When
1254 | Keyword::Case,
1255 ) => return true,
1256 TokenKind::Keyword(
1257 Keyword::Match
1258 | Keyword::Create
1259 | Keyword::Merge
1260 | Keyword::Call
1261 | Keyword::Set
1262 | Keyword::Remove,
1263 ) => return false,
1264 _ => {}
1265 }
1266 }
1267 false
1268}
1269
1270fn is_inside_braces(tokens: &[&Token], start: usize) -> bool {
1271 let mut nested = 0usize;
1272 for token in tokens[..start].iter().rev() {
1273 match token.kind {
1274 TokenKind::RightBrace => nested += 1,
1275 TokenKind::LeftBrace if nested == 0 => return true,
1276 TokenKind::LeftBrace => nested = nested.saturating_sub(1),
1277 _ => {}
1278 }
1279 }
1280 false
1281}
1282
1283fn is_in_map_entry_value(tokens: &[&Token], start: usize) -> bool {
1284 let mut open_braces = Vec::new();
1285 for (index, token) in tokens.iter().enumerate().take(start) {
1286 match token.kind {
1287 TokenKind::LeftBrace => open_braces.push(index),
1288 TokenKind::RightBrace => {
1289 let _ = open_braces.pop();
1290 }
1291 _ => {}
1292 }
1293 }
1294 let Some(open) = open_braces.last().copied() else {
1295 return false;
1296 };
1297
1298 let mut depth = 0usize;
1299 let mut after_separator = false;
1300 for token in &tokens[open + 1..start] {
1301 match token.kind {
1302 TokenKind::LeftParen | TokenKind::LeftBracket | TokenKind::LeftBrace => {
1303 depth += 1;
1304 }
1305 TokenKind::RightParen | TokenKind::RightBracket | TokenKind::RightBrace => {
1306 depth = depth.saturating_sub(1);
1307 }
1308 TokenKind::Comma if depth == 0 => after_separator = false,
1309 TokenKind::Colon if depth == 0 => after_separator = true,
1310 _ => {}
1311 }
1312 }
1313 after_separator
1314}
1315
1316fn label_predicate_end(tokens: &[&Token], start: usize) -> Option<usize> {
1317 let expression_start = match tokens.get(start)?.kind {
1318 TokenKind::Colon => start + 1,
1319 TokenKind::Keyword(Keyword::Is) => {
1320 if (matches!(
1321 tokens.get(start + 1).map(|token| token.kind),
1322 Some(TokenKind::Keyword(Keyword::Null))
1323 ) && !matches!(
1324 tokens.get(start + 2).map(|token| token.kind),
1325 Some(TokenKind::Pipe | TokenKind::Ampersand)
1326 )) || matches!(
1327 (
1328 tokens.get(start + 1).map(|token| token.kind),
1329 tokens.get(start + 2).map(|token| token.kind),
1330 ),
1331 (
1332 Some(TokenKind::Keyword(Keyword::Not)),
1333 Some(TokenKind::Keyword(Keyword::Null)),
1334 )
1335 ) {
1336 return None;
1337 }
1338 start + 1
1339 }
1340 _ => return None,
1341 };
1342 let end = label_or_end(tokens, expression_start)?;
1343 Some(end.saturating_sub(1))
1344}
1345
1346fn label_or_end(tokens: &[&Token], start: usize) -> Option<usize> {
1347 let mut cursor = label_and_end(tokens, start)?;
1348 while tokens
1349 .get(cursor)
1350 .is_some_and(|token| token.kind == TokenKind::Pipe)
1351 {
1352 cursor += 1;
1353 if tokens
1354 .get(cursor)
1355 .is_some_and(|token| token.kind == TokenKind::Colon)
1356 {
1357 cursor += 1;
1358 }
1359 cursor = label_and_end(tokens, cursor)?;
1360 }
1361 Some(cursor)
1362}
1363
1364fn label_and_end(tokens: &[&Token], start: usize) -> Option<usize> {
1365 let mut cursor = label_primary_end(tokens, start)?;
1366 while matches!(
1367 tokens.get(cursor).map(|token| token.kind),
1368 Some(TokenKind::Ampersand | TokenKind::Colon)
1369 ) {
1370 cursor = label_primary_end(tokens, cursor + 1)?;
1371 }
1372 Some(cursor)
1373}
1374
1375fn label_primary_end(tokens: &[&Token], start: usize) -> Option<usize> {
1376 let mut cursor = start;
1377 if tokens
1378 .get(cursor)
1379 .is_some_and(|token| token.kind == TokenKind::Bang)
1380 {
1381 cursor += 1;
1382 }
1383 match tokens.get(cursor)?.kind {
1384 TokenKind::Percent => Some(cursor + 1),
1385 kind if is_symbolic_name_kind(kind) => Some(cursor + 1),
1386 TokenKind::LeftParen => {
1387 let end = label_or_end(tokens, cursor + 1)?;
1388 tokens
1389 .get(end)
1390 .is_some_and(|token| token.kind == TokenKind::RightParen)
1391 .then_some(end + 1)
1392 }
1393 _ => None,
1394 }
1395}
1396
1397#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1398pub(crate) enum ParserToken {
1399 Identifier,
1400 EscapedIdentifier,
1401 Parameter,
1402 Integer,
1403 HexInteger,
1404 OctalInteger,
1405 Float,
1406 String,
1407 PatternComprehension,
1408 PatternExpression,
1409 LabelPredicate,
1410 RelationshipLabelExpression,
1411 QualifiedFunctionName,
1412 All,
1413 SetAll,
1414 AllShortestPaths,
1415 And,
1416 Any,
1417 As,
1418 Asc,
1419 Ascending,
1420 By,
1421 Call,
1422 Case,
1423 Contains,
1424 Collect,
1425 Count,
1426 Create,
1427 Delete,
1428 Desc,
1429 Descending,
1430 Detach,
1431 Distinct,
1432 Else,
1433 End,
1434 Ends,
1435 Exists,
1436 False,
1437 Group,
1438 Groups,
1439 In,
1440 Inf,
1441 Infinity,
1442 Is,
1443 Limit,
1444 Match,
1445 Merge,
1446 Nan,
1447 None,
1448 Not,
1449 Null,
1450 Offset,
1451 On,
1452 Optional,
1453 Or,
1454 Order,
1455 Path,
1456 Paths,
1457 Reduce,
1458 Remove,
1459 Return,
1460 Set,
1461 Shortest,
1462 ShortestPath,
1463 Simple,
1464 Single,
1465 Skip,
1466 Starts,
1467 Then,
1468 Trail,
1469 Trim,
1470 True,
1471 Union,
1472 Unwind,
1473 Walk,
1474 When,
1475 Where,
1476 With,
1477 Xor,
1478 Yield,
1479 Acyclic,
1480 LeftParen,
1481 RightParen,
1482 LeftBracket,
1483 RightBracket,
1484 LeftBrace,
1485 RightBrace,
1486 Comma,
1487 Dot,
1488 DotDot,
1489 Colon,
1490 DoubleColon,
1491 Semicolon,
1492 Pipe,
1493 DoublePipe,
1494 Ampersand,
1495 Question,
1496 Dollar,
1497 Plus,
1498 Minus,
1499 Star,
1500 Slash,
1501 Percent,
1502 Caret,
1503 Bang,
1504 Equal,
1505 NotEqual,
1506 Less,
1507 LessEqual,
1508 Greater,
1509 GreaterEqual,
1510 PlusEqual,
1511 FatArrow,
1512 RegexMatch,
1513 LeftArrow,
1514 RightArrow,
1515 Invalid,
1516}
1517
1518impl fmt::Display for ParserToken {
1519 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1520 write!(formatter, "`{}`", self.name())
1521 }
1522}
1523
1524impl ParserToken {
1525 const fn is_keyword_literal(self) -> bool {
1526 matches!(
1527 self,
1528 Self::False | Self::Inf | Self::Infinity | Self::Nan | Self::Null | Self::True
1529 )
1530 }
1531
1532 const fn is_contextual_name_candidate(self) -> bool {
1533 !matches!(
1534 self,
1535 Self::Identifier
1536 | Self::EscapedIdentifier
1537 | Self::Parameter
1538 | Self::Integer
1539 | Self::HexInteger
1540 | Self::OctalInteger
1541 | Self::Float
1542 | Self::String
1543 | Self::PatternComprehension
1544 | Self::PatternExpression
1545 | Self::LabelPredicate
1546 | Self::RelationshipLabelExpression
1547 | Self::QualifiedFunctionName
1548 | Self::LeftParen
1549 | Self::RightParen
1550 | Self::LeftBracket
1551 | Self::RightBracket
1552 | Self::LeftBrace
1553 | Self::RightBrace
1554 | Self::Comma
1555 | Self::Dot
1556 | Self::DotDot
1557 | Self::Colon
1558 | Self::DoubleColon
1559 | Self::Semicolon
1560 | Self::Pipe
1561 | Self::DoublePipe
1562 | Self::Ampersand
1563 | Self::Question
1564 | Self::Dollar
1565 | Self::Plus
1566 | Self::Minus
1567 | Self::Star
1568 | Self::Slash
1569 | Self::Percent
1570 | Self::Caret
1571 | Self::Bang
1572 | Self::Equal
1573 | Self::NotEqual
1574 | Self::Less
1575 | Self::LessEqual
1576 | Self::Greater
1577 | Self::GreaterEqual
1578 | Self::PlusEqual
1579 | Self::FatArrow
1580 | Self::RegexMatch
1581 | Self::LeftArrow
1582 | Self::RightArrow
1583 | Self::Invalid
1584 )
1585 }
1586
1587 const fn name(self) -> &'static str {
1588 match self {
1589 Self::Identifier => "identifier",
1590 Self::EscapedIdentifier => "escaped identifier",
1591 Self::Parameter => "parameter",
1592 Self::Integer => "integer",
1593 Self::HexInteger => "hex integer",
1594 Self::OctalInteger => "octal integer",
1595 Self::Float => "float",
1596 Self::String => "string",
1597 Self::PatternComprehension => "pattern comprehension",
1598 Self::PatternExpression => "pattern expression",
1599 Self::LabelPredicate => "label predicate",
1600 Self::RelationshipLabelExpression => "relationship label expression",
1601 Self::QualifiedFunctionName => "qualified function name",
1602 Self::All => "ALL",
1603 Self::SetAll => "ALL",
1604 Self::AllShortestPaths => "ALLSHORTESTPATHS",
1605 Self::And => "AND",
1606 Self::Any => "ANY",
1607 Self::As => "AS",
1608 Self::Asc => "ASC",
1609 Self::Ascending => "ASCENDING",
1610 Self::By => "BY",
1611 Self::Call => "CALL",
1612 Self::Case => "CASE",
1613 Self::Contains => "CONTAINS",
1614 Self::Collect => "COLLECT",
1615 Self::Count => "COUNT",
1616 Self::Create => "CREATE",
1617 Self::Delete => "DELETE",
1618 Self::Desc => "DESC",
1619 Self::Descending => "DESCENDING",
1620 Self::Detach => "DETACH",
1621 Self::Distinct => "DISTINCT",
1622 Self::Else => "ELSE",
1623 Self::End => "END",
1624 Self::Ends => "ENDS",
1625 Self::Exists => "EXISTS",
1626 Self::False => "FALSE",
1627 Self::Group => "GROUP",
1628 Self::Groups => "GROUPS",
1629 Self::In => "IN",
1630 Self::Inf => "INF",
1631 Self::Infinity => "INFINITY",
1632 Self::Is => "IS",
1633 Self::Limit => "LIMIT",
1634 Self::Match => "MATCH",
1635 Self::Merge => "MERGE",
1636 Self::Nan => "NAN",
1637 Self::None => "NONE",
1638 Self::Not => "NOT",
1639 Self::Null => "NULL",
1640 Self::Offset => "OFFSET",
1641 Self::On => "ON",
1642 Self::Optional => "OPTIONAL",
1643 Self::Or => "OR",
1644 Self::Order => "ORDER",
1645 Self::Path => "PATH",
1646 Self::Paths => "PATHS",
1647 Self::Reduce => "REDUCE",
1648 Self::Remove => "REMOVE",
1649 Self::Return => "RETURN",
1650 Self::Set => "SET",
1651 Self::Shortest => "SHORTEST",
1652 Self::ShortestPath => "SHORTESTPATH",
1653 Self::Simple => "SIMPLE",
1654 Self::Single => "SINGLE",
1655 Self::Skip => "SKIP",
1656 Self::Starts => "STARTS",
1657 Self::Then => "THEN",
1658 Self::Trail => "TRAIL",
1659 Self::Trim => "TRIM",
1660 Self::True => "TRUE",
1661 Self::Union => "UNION",
1662 Self::Unwind => "UNWIND",
1663 Self::Walk => "WALK",
1664 Self::When => "WHEN",
1665 Self::Where => "WHERE",
1666 Self::With => "WITH",
1667 Self::Xor => "XOR",
1668 Self::Yield => "YIELD",
1669 Self::Acyclic => "ACYCLIC",
1670 Self::LeftParen => "(",
1671 Self::RightParen => ")",
1672 Self::LeftBracket => "[",
1673 Self::RightBracket => "]",
1674 Self::LeftBrace => "{",
1675 Self::RightBrace => "}",
1676 Self::Comma => ",",
1677 Self::Dot => ".",
1678 Self::DotDot => "..",
1679 Self::Colon => ":",
1680 Self::DoubleColon => "::",
1681 Self::Semicolon => ";",
1682 Self::Pipe => "|",
1683 Self::DoublePipe => "||",
1684 Self::Ampersand => "&",
1685 Self::Question => "?",
1686 Self::Dollar => "$",
1687 Self::Plus => "+",
1688 Self::Minus => "-",
1689 Self::Star => "*",
1690 Self::Slash => "/",
1691 Self::Percent => "%",
1692 Self::Caret => "^",
1693 Self::Bang => "!",
1694 Self::Equal => "=",
1695 Self::NotEqual => "<>",
1696 Self::Less => "<",
1697 Self::LessEqual => "<=",
1698 Self::Greater => ">",
1699 Self::GreaterEqual => ">=",
1700 Self::PlusEqual => "+=",
1701 Self::FatArrow => "=>",
1702 Self::RegexMatch => "=~",
1703 Self::LeftArrow => "<-",
1704 Self::RightArrow => "->",
1705 Self::Invalid => "invalid token",
1706 }
1707 }
1708}
1709
1710impl From<TokenKind> for ParserToken {
1711 fn from(kind: TokenKind) -> Self {
1712 match kind {
1713 TokenKind::Keyword(keyword) => keyword_token(keyword),
1714 TokenKind::Identifier => Self::Identifier,
1715 TokenKind::EscapedIdentifier => Self::EscapedIdentifier,
1716 TokenKind::Parameter => Self::Parameter,
1717 TokenKind::Integer => Self::Integer,
1718 TokenKind::HexInteger => Self::HexInteger,
1719 TokenKind::OctalInteger => Self::OctalInteger,
1720 TokenKind::Float => Self::Float,
1721 TokenKind::String => Self::String,
1722 TokenKind::LeftParen => Self::LeftParen,
1723 TokenKind::RightParen => Self::RightParen,
1724 TokenKind::LeftBracket => Self::LeftBracket,
1725 TokenKind::RightBracket => Self::RightBracket,
1726 TokenKind::LeftBrace => Self::LeftBrace,
1727 TokenKind::RightBrace => Self::RightBrace,
1728 TokenKind::Comma => Self::Comma,
1729 TokenKind::Dot => Self::Dot,
1730 TokenKind::DotDot => Self::DotDot,
1731 TokenKind::Colon => Self::Colon,
1732 TokenKind::DoubleColon => Self::DoubleColon,
1733 TokenKind::Semicolon => Self::Semicolon,
1734 TokenKind::Pipe => Self::Pipe,
1735 TokenKind::DoublePipe => Self::DoublePipe,
1736 TokenKind::Ampersand => Self::Ampersand,
1737 TokenKind::Question => Self::Question,
1738 TokenKind::Dollar => Self::Dollar,
1739 TokenKind::Plus => Self::Plus,
1740 TokenKind::Minus => Self::Minus,
1741 TokenKind::Star => Self::Star,
1742 TokenKind::Slash => Self::Slash,
1743 TokenKind::Percent => Self::Percent,
1744 TokenKind::Caret => Self::Caret,
1745 TokenKind::Bang => Self::Bang,
1746 TokenKind::Equal => Self::Equal,
1747 TokenKind::NotEqual => Self::NotEqual,
1748 TokenKind::Less => Self::Less,
1749 TokenKind::LessEqual => Self::LessEqual,
1750 TokenKind::Greater => Self::Greater,
1751 TokenKind::GreaterEqual => Self::GreaterEqual,
1752 TokenKind::PlusEqual => Self::PlusEqual,
1753 TokenKind::FatArrow => Self::FatArrow,
1754 TokenKind::RegexMatch => Self::RegexMatch,
1755 TokenKind::LeftArrow => Self::LeftArrow,
1756 TokenKind::RightArrow => Self::RightArrow,
1757 TokenKind::Invalid => Self::Invalid,
1758 TokenKind::Whitespace | TokenKind::LineComment | TokenKind::BlockComment => {
1759 unreachable!("trivia is filtered before parser token conversion")
1760 }
1761 }
1762 }
1763}
1764
1765fn keyword_token(keyword: Keyword) -> ParserToken {
1766 match keyword {
1767 Keyword::All => ParserToken::All,
1768 Keyword::AllShortestPaths => ParserToken::AllShortestPaths,
1769 Keyword::And => ParserToken::And,
1770 Keyword::Any => ParserToken::Any,
1771 Keyword::As => ParserToken::As,
1772 Keyword::Asc => ParserToken::Asc,
1773 Keyword::Ascending => ParserToken::Ascending,
1774 Keyword::By => ParserToken::By,
1775 Keyword::Call => ParserToken::Call,
1776 Keyword::Case => ParserToken::Case,
1777 Keyword::Contains => ParserToken::Contains,
1778 Keyword::Collect => ParserToken::Collect,
1779 Keyword::Count => ParserToken::Count,
1780 Keyword::Create => ParserToken::Create,
1781 Keyword::Delete => ParserToken::Delete,
1782 Keyword::Desc => ParserToken::Desc,
1783 Keyword::Descending => ParserToken::Descending,
1784 Keyword::Detach => ParserToken::Detach,
1785 Keyword::Distinct => ParserToken::Distinct,
1786 Keyword::Else => ParserToken::Else,
1787 Keyword::End => ParserToken::End,
1788 Keyword::Ends => ParserToken::Ends,
1789 Keyword::Exists => ParserToken::Exists,
1790 Keyword::False => ParserToken::False,
1791 Keyword::Group => ParserToken::Group,
1792 Keyword::Groups => ParserToken::Groups,
1793 Keyword::In => ParserToken::In,
1794 Keyword::Inf => ParserToken::Inf,
1795 Keyword::Infinity => ParserToken::Infinity,
1796 Keyword::Is => ParserToken::Is,
1797 Keyword::Limit => ParserToken::Limit,
1798 Keyword::Match => ParserToken::Match,
1799 Keyword::Merge => ParserToken::Merge,
1800 Keyword::Nan => ParserToken::Nan,
1801 Keyword::None => ParserToken::None,
1802 Keyword::Not => ParserToken::Not,
1803 Keyword::Null => ParserToken::Null,
1804 Keyword::Offset => ParserToken::Offset,
1805 Keyword::On => ParserToken::On,
1806 Keyword::Optional => ParserToken::Optional,
1807 Keyword::Or => ParserToken::Or,
1808 Keyword::Order => ParserToken::Order,
1809 Keyword::Path => ParserToken::Path,
1810 Keyword::Paths => ParserToken::Paths,
1811 Keyword::Reduce => ParserToken::Reduce,
1812 Keyword::Remove => ParserToken::Remove,
1813 Keyword::Return => ParserToken::Return,
1814 Keyword::Set => ParserToken::Set,
1815 Keyword::Shortest => ParserToken::Shortest,
1816 Keyword::ShortestPath => ParserToken::ShortestPath,
1817 Keyword::Simple => ParserToken::Simple,
1818 Keyword::Single => ParserToken::Single,
1819 Keyword::Skip => ParserToken::Skip,
1820 Keyword::Starts => ParserToken::Starts,
1821 Keyword::Then => ParserToken::Then,
1822 Keyword::Trail => ParserToken::Trail,
1823 Keyword::Trim => ParserToken::Trim,
1824 Keyword::True => ParserToken::True,
1825 Keyword::Union => ParserToken::Union,
1826 Keyword::Unwind => ParserToken::Unwind,
1827 Keyword::Walk => ParserToken::Walk,
1828 Keyword::When => ParserToken::When,
1829 Keyword::Where => ParserToken::Where,
1830 Keyword::With => ParserToken::With,
1831 Keyword::Xor => ParserToken::Xor,
1832 Keyword::Yield => ParserToken::Yield,
1833 Keyword::Acyclic => ParserToken::Acyclic,
1834 }
1835}
1836
1837pub(crate) fn name(source: &str, start: usize, end: usize, escaped: bool) -> Name {
1838 let span = Span::new(start, end);
1839 let raw = &source[start..end];
1840 let text = if escaped {
1841 decode_delimited(raw, '`')
1842 } else {
1843 raw.to_owned()
1844 };
1845 Node::new(Identifier::new(text, escaped), span)
1846}
1847
1848pub(crate) fn qualified_function_name(source: &str, start: usize, end: usize) -> QualifiedName {
1849 let fragment = source.get(start..end).unwrap_or_default();
1850 let lexed = lexer::lex(fragment);
1851 let parts = lexed
1852 .tokens
1853 .iter()
1854 .filter(|token| !token.is_trivia() && token.kind != TokenKind::Dot)
1855 .map(|token| {
1856 let part_start = start + token.span.start;
1857 let part_end = start + token.span.end;
1858 name(
1859 source,
1860 part_start,
1861 part_end,
1862 token.kind == TokenKind::EscapedIdentifier,
1863 )
1864 })
1865 .collect();
1866 QualifiedName {
1867 parts,
1868 span: Span::new(start, end),
1869 }
1870}
1871
1872pub(crate) fn parameter(source: &str, start: usize, end: usize) -> Parameter {
1873 let raw = &source[start + 1..end];
1874 let name = if raw.starts_with('`') && raw.ends_with('`') {
1875 ParameterName::Named(decode_delimited(raw, '`'))
1876 } else if raw.bytes().all(|byte| byte.is_ascii_digit()) {
1877 ParameterName::Positional(raw.to_owned())
1878 } else {
1879 ParameterName::Named(raw.to_owned())
1880 };
1881 Parameter { name }
1882}
1883
1884pub(crate) fn string_literal(source: &str, start: usize, end: usize) -> StringLiteral {
1885 let raw = &source[start..end];
1886 let delimiter = raw.chars().next().unwrap_or('\'');
1887 let quote = if delimiter == '\'' {
1888 QuoteStyle::Single
1889 } else {
1890 QuoteStyle::Double
1891 };
1892 StringLiteral {
1893 value: decode_delimited(raw, delimiter),
1894 quote,
1895 }
1896}
1897
1898fn decode_delimited(raw: &str, delimiter: char) -> String {
1899 let delimiter_len = delimiter.len_utf8();
1900 let inner = raw
1901 .get(delimiter_len..raw.len().saturating_sub(delimiter_len))
1902 .unwrap_or_default();
1903 let mut decoded = String::with_capacity(inner.len());
1904 let mut characters = inner.chars().peekable();
1905 while let Some(character) = characters.next() {
1906 if character == delimiter && characters.peek() == Some(&delimiter) {
1907 let _ = characters.next();
1908 decoded.push(delimiter);
1909 continue;
1910 }
1911 if character != '\\' {
1912 decoded.push(character);
1913 continue;
1914 }
1915 let Some(escaped) = characters.next() else {
1916 decoded.push('\\');
1917 break;
1918 };
1919 match escaped {
1920 't' => decoded.push('\t'),
1921 'b' => decoded.push('\u{0008}'),
1922 'n' => decoded.push('\n'),
1923 'r' => decoded.push('\r'),
1924 'f' => decoded.push('\u{000c}'),
1925 '\\' => decoded.push('\\'),
1926 '\'' => decoded.push('\''),
1927 '"' => decoded.push('"'),
1928 '`' => decoded.push('`'),
1929 'u' | 'U' => {
1930 let digits = if escaped == 'u' { 4 } else { 6 };
1931 let mut scalar = 0u32;
1932 let mut complete = true;
1933 for _ in 0..digits {
1934 match characters.next().and_then(|digit| digit.to_digit(16)) {
1935 Some(digit) => scalar = scalar * 16 + digit,
1936 None => {
1937 complete = false;
1938 break;
1939 }
1940 }
1941 }
1942 if complete && let Some(character) = char::from_u32(scalar) {
1943 decoded.push(character);
1944 }
1945 }
1946 other => {
1947 decoded.push('\\');
1950 decoded.push(other);
1951 }
1952 }
1953 }
1954 decoded
1955}
1956
1957pub(crate) fn integer_literal(
1958 source: &str,
1959 start: usize,
1960 end: usize,
1961 radix: IntegerRadix,
1962) -> Literal {
1963 let span = Span::new(start, end);
1964 Node::new(
1965 LiteralKind::Integer(IntegerLiteral {
1966 text: source[start..end].to_owned(),
1967 radix,
1968 }),
1969 span,
1970 )
1971}
1972
1973pub(crate) fn list_singleton_or_comprehension(start: usize, end: usize, expression: Expr) -> Expr {
1974 let span = Span::new(start, end);
1975 match split_comprehension_source(expression) {
1976 ComprehensionSource::Comprehension { variable, list } => Node::new(
1977 ExprKind::ListComprehension(ListComprehension {
1978 variable,
1979 list: Box::new(list),
1980 predicate: None,
1981 projection: None,
1982 }),
1983 span,
1984 ),
1985 ComprehensionSource::Expression(expression) => {
1986 Node::new(ExprKind::List(vec![expression]), span)
1987 }
1988 }
1989}
1990
1991pub(crate) fn list_comprehension_expr(
1992 start: usize,
1993 end: usize,
1994 source_expression: Expr,
1995 predicate: Option<Expr>,
1996 projection: Option<Expr>,
1997) -> Result<Expr, LalrpopError> {
1998 let ComprehensionSource::Comprehension { variable, list } =
1999 split_comprehension_source(source_expression)
2000 else {
2001 return Err(user_parse_error(start));
2002 };
2003 Ok(Node::new(
2004 ExprKind::ListComprehension(ListComprehension {
2005 variable,
2006 list: Box::new(list),
2007 predicate: predicate.map(Box::new),
2008 projection: projection.map(Box::new),
2009 }),
2010 Span::new(start, end),
2011 ))
2012}
2013
2014enum ComprehensionSource {
2015 Comprehension { variable: Name, list: Expr },
2016 Expression(Expr),
2017}
2018
2019fn split_comprehension_source(expression: Expr) -> ComprehensionSource {
2020 let is_source = matches!(
2021 &expression.kind,
2022 ExprKind::Binary { left, operator, .. }
2023 if operator.kind == BinaryOperator::In
2024 && matches!(&left.kind, ExprKind::Variable(_))
2025 );
2026 if !is_source {
2027 return ComprehensionSource::Expression(expression);
2028 }
2029
2030 let ExprKind::Binary {
2031 left,
2032 operator: _,
2033 right,
2034 } = expression.kind
2035 else {
2036 unreachable!("the source shape was checked above")
2037 };
2038 let ExprKind::Variable(variable) = left.kind else {
2039 unreachable!("the source variable shape was checked above")
2040 };
2041 ComprehensionSource::Comprehension {
2042 variable,
2043 list: *right,
2044 }
2045}
2046
2047pub(crate) fn pattern_comprehension(
2048 source: &str,
2049 start: usize,
2050 end: usize,
2051) -> Result<Expr, LalrpopError> {
2052 let Some(fragment) = source.get(start..end) else {
2053 return Err(user_parse_error(start));
2054 };
2055 let lexed = lexer::lex(fragment);
2056 if !lexed.diagnostics.is_empty() {
2057 return Err(user_parse_error(start));
2058 }
2059 let mut best_error = None;
2068 for pipe_index in top_level_pipe_candidates(&lexed.tokens) {
2069 let tokens = nested_parser_tokens(&lexed.tokens, start, Some(pipe_index));
2070 match parse_comprehension_tokens(source, start, end, &tokens) {
2071 Ok(expression) => return Ok(expression),
2072 Err(error) => retain_farthest_error(&mut best_error, error),
2073 }
2074 }
2075 Err(best_error.unwrap_or_else(|| user_parse_error(start)))
2076}
2077
2078fn top_level_pipe_candidates(tokens: &[Token]) -> Vec<usize> {
2079 let mut candidates = Vec::new();
2080 let mut brackets = 0usize;
2081 let mut parentheses = 0usize;
2082 let mut braces = 0usize;
2083 for (index, token) in tokens.iter().enumerate() {
2084 if token.is_trivia() {
2085 continue;
2086 }
2087 match token.kind {
2088 TokenKind::LeftBracket => brackets += 1,
2089 TokenKind::RightBracket => brackets = brackets.saturating_sub(1),
2090 TokenKind::LeftParen => parentheses += 1,
2091 TokenKind::RightParen => parentheses = parentheses.saturating_sub(1),
2092 TokenKind::LeftBrace => braces += 1,
2093 TokenKind::RightBrace => braces = braces.saturating_sub(1),
2094 TokenKind::Pipe if brackets == 1 && parentheses == 0 && braces == 0 => {
2095 candidates.push(index);
2096 }
2097 _ => {}
2098 }
2099 }
2100 candidates
2101}
2102
2103fn parse_comprehension_tokens(
2104 source: &str,
2105 start: usize,
2106 end: usize,
2107 tokens: &[SpannedParserToken],
2108) -> Result<Expr, LalrpopError> {
2109 let outer_start = tokens
2110 .iter()
2111 .position(|token| token.0 == start && token.1 == ParserToken::LeftBracket)
2112 .ok_or_else(|| user_parse_error(start))?;
2113 let outer_end = tokens
2114 .iter()
2115 .rposition(|token| token.2 == end && token.1 == ParserToken::RightBracket)
2116 .ok_or_else(|| user_parse_error(start))?;
2117
2118 let pipe = find_top_level_token(tokens, outer_start + 1, outer_end, ParserToken::Pipe)
2119 .ok_or_else(|| user_parse_error(start))?;
2120
2121 let mut pattern_start = outer_start + 1;
2122 let binding = if pattern_start + 1 < pipe
2123 && tokens[pattern_start + 1].1 == ParserToken::Equal
2124 && (is_name_token(tokens[pattern_start].1)
2125 || tokens[pattern_start].1.is_contextual_name_candidate())
2126 {
2127 let token = tokens[pattern_start];
2128 pattern_start += 2;
2129 Some(name(
2130 source,
2131 token.0,
2132 token.2,
2133 token.1 == ParserToken::EscapedIdentifier,
2134 ))
2135 } else {
2136 None
2137 };
2138
2139 let where_index = find_top_level_token(tokens, pattern_start, pipe, ParserToken::Where);
2140 let pattern_end = where_index.unwrap_or(pipe);
2141 if pattern_start >= pattern_end {
2142 return Err(user_parse_error(tokens[pattern_end].0));
2143 }
2144 if pipe + 1 >= outer_end {
2145 return Err(user_parse_error(tokens[outer_end].0));
2146 }
2147 let pattern = parse_pattern_fragment(source, &tokens[pattern_start..pattern_end])?;
2148 let predicate = match where_index {
2149 Some(index) if index + 1 < pipe => Some(Box::new(parse_expr_fragment(
2150 source,
2151 &tokens[index + 1..pipe],
2152 )?)),
2153 Some(_) => return Err(user_parse_error(tokens[pipe].0)),
2154 None => None,
2155 };
2156 let projection = parse_expr_fragment(source, &tokens[pipe + 1..outer_end])?;
2157
2158 Ok(Node::new(
2159 ExprKind::PatternComprehension(PatternComprehension {
2160 binding,
2161 pattern,
2162 predicate,
2163 projection: Box::new(projection),
2164 }),
2165 Span::new(start, end),
2166 ))
2167}
2168
2169pub(crate) fn pattern_expression(
2170 source: &str,
2171 start: usize,
2172 end: usize,
2173) -> Result<Expr, LalrpopError> {
2174 let Some(fragment) = source.get(start..end) else {
2175 return Err(user_parse_error(start));
2176 };
2177 let lexed = lexer::lex(fragment);
2178 if !lexed.diagnostics.is_empty() {
2179 return Err(user_parse_error(start));
2180 }
2181 let tokens = nested_parser_tokens(&lexed.tokens, start, None);
2182 let pattern = parse_pattern_fragment(source, &tokens)?;
2183 Ok(Node::new(ExprKind::Pattern(pattern), Span::new(start, end)))
2184}
2185
2186fn parse_pattern_fragment(
2187 source: &str,
2188 tokens: &[SpannedParserToken],
2189) -> Result<Pattern, LalrpopError> {
2190 parse_tokens_with_contextual_names(tokens, |contextual| {
2191 generated::PatternFragmentParser::new().parse(source, contextual.iter().copied().map(Ok))
2192 })
2193}
2194
2195fn parse_expr_fragment(source: &str, tokens: &[SpannedParserToken]) -> Result<Expr, LalrpopError> {
2196 parse_tokens_with_contextual_names(tokens, |contextual| {
2197 generated::ExprFragmentParser::new().parse(source, contextual.iter().copied().map(Ok))
2198 })
2199}
2200
2201pub(crate) fn label_comparison_suffix(
2202 source: &str,
2203 start: usize,
2204 end: usize,
2205) -> Result<ComparisonSuffix, LalrpopError> {
2206 Ok(ComparisonSuffix::Is {
2207 negated: false,
2208 predicate: IsPredicate::Label(parse_label_expression(source, start, end, true)?),
2209 end,
2210 })
2211}
2212
2213pub(crate) fn relationship_label_expression(
2214 source: &str,
2215 start: usize,
2216 end: usize,
2217) -> Result<LabelExpression, LalrpopError> {
2218 parse_label_expression(source, start, end, true)
2219}
2220
2221fn parse_label_expression(
2222 source: &str,
2223 start: usize,
2224 end: usize,
2225 normalize_legacy_relationship: bool,
2226) -> Result<LabelExpression, LalrpopError> {
2227 let Some(fragment) = source.get(start..end) else {
2228 return Err(user_parse_error(start));
2229 };
2230 let lexed = lexer::lex(fragment);
2231 if !lexed.diagnostics.is_empty() {
2232 return Err(user_parse_error(start));
2233 }
2234 let mut tokens = lexed
2235 .tokens
2236 .iter()
2237 .filter(|token| !token.is_trivia())
2238 .map(|token| {
2239 (
2240 start + token.span.start,
2241 ParserToken::from(token.kind),
2242 start + token.span.end,
2243 )
2244 })
2245 .collect::<Vec<_>>();
2246 if normalize_legacy_relationship && !legacy_label_tokens_are_consistent(&tokens) {
2247 return Err(user_parse_error(start));
2248 }
2249 let mut normalized = Vec::with_capacity(tokens.len());
2250 for (index, mut token) in tokens.drain(..).enumerate() {
2251 if normalize_legacy_relationship
2252 && token.1 == ParserToken::Colon
2253 && normalized
2254 .last()
2255 .is_some_and(|previous: &SpannedParserToken| previous.1 == ParserToken::Pipe)
2256 {
2257 continue;
2258 }
2259 if index > 0 && token.1.is_contextual_name_candidate() {
2260 token.1 = ParserToken::Identifier;
2261 }
2262 normalized.push(token);
2263 }
2264 generated::LabelPredicateFragmentParser::new().parse(source, normalized.into_iter().map(Ok))
2265}
2266
2267fn legacy_label_tokens_are_consistent(tokens: &[SpannedParserToken]) -> bool {
2268 let has_internal_colon = tokens
2269 .iter()
2270 .enumerate()
2271 .skip(1)
2272 .any(|(_, token)| token.1 == ParserToken::Colon);
2273 if !has_internal_colon {
2274 return true;
2275 }
2276 if tokens
2277 .first()
2278 .is_none_or(|token| token.1 != ParserToken::Colon)
2279 {
2280 return false;
2281 }
2282
2283 let mut cursor = 1usize;
2284 if tokens
2285 .get(cursor)
2286 .is_none_or(|token| !is_name_token(token.1) && !token.1.is_contextual_name_candidate())
2287 {
2288 return false;
2289 }
2290 cursor += 1;
2291 let pipe_separated = tokens
2292 .get(cursor)
2293 .is_some_and(|token| token.1 == ParserToken::Pipe);
2294 while cursor < tokens.len() {
2295 if pipe_separated {
2296 if tokens
2297 .get(cursor)
2298 .is_none_or(|token| token.1 != ParserToken::Pipe)
2299 || tokens
2300 .get(cursor + 1)
2301 .is_none_or(|token| token.1 != ParserToken::Colon)
2302 {
2303 return false;
2304 }
2305 cursor += 2;
2306 } else {
2307 if tokens
2308 .get(cursor)
2309 .is_none_or(|token| token.1 != ParserToken::Colon)
2310 {
2311 return false;
2312 }
2313 cursor += 1;
2314 }
2315 if tokens
2316 .get(cursor)
2317 .is_none_or(|token| !is_name_token(token.1) && !token.1.is_contextual_name_candidate())
2318 {
2319 return false;
2320 }
2321 cursor += 1;
2322 }
2323 true
2324}
2325
2326fn user_parse_error(location: usize) -> LalrpopError {
2327 ParseError::User { error: location }
2328}
2329
2330fn nested_parser_tokens(
2335 tokens: &[Token],
2336 base: usize,
2337 reserved_pipe: Option<usize>,
2338) -> Vec<SpannedParserToken> {
2339 let significant = tokens
2340 .iter()
2341 .filter(|token| !token.is_trivia())
2342 .collect::<Vec<_>>();
2343 let boundary = reserved_pipe.map(|raw| {
2344 tokens[..raw]
2345 .iter()
2346 .filter(|token| !token.is_trivia())
2347 .count()
2348 });
2349 let mut result = Vec::with_capacity(significant.len());
2350 let mut index = 0;
2351 while index < significant.len() {
2352 let scan: &[&Token] = match boundary {
2353 Some(boundary) if index < boundary => &significant[..boundary],
2354 _ => &significant,
2355 };
2356 if let Some(end) = escaped_parameter_end(scan, index) {
2357 result.push((
2358 base + significant[index].span.start,
2359 ParserToken::Parameter,
2360 base + significant[end].span.end,
2361 ));
2362 index = end + 1;
2363 continue;
2364 }
2365 if index != 0 {
2366 if let Some(end) = pattern_comprehension_end(scan, index) {
2367 result.push((
2368 base + significant[index].span.start,
2369 ParserToken::PatternComprehension,
2370 base + significant[end].span.end,
2371 ));
2372 index = end + 1;
2373 continue;
2374 }
2375 if significant[index].kind == TokenKind::LeftParen
2376 && is_pattern_expression_context(&significant, index)
2377 && let Some(end) = pattern_expression_end(scan, index)
2378 {
2379 result.push((
2380 base + significant[index].span.start,
2381 ParserToken::PatternExpression,
2382 base + significant[end].span.end,
2383 ));
2384 index = end + 1;
2385 continue;
2386 }
2387 if is_relationship_label_start(&significant, index)
2388 && let Some(end) = relationship_label_end(scan, index)
2389 {
2390 result.push((
2391 base + significant[index].span.start,
2392 ParserToken::RelationshipLabelExpression,
2393 base + significant[end].span.end,
2394 ));
2395 index = end + 1;
2396 continue;
2397 }
2398 if is_label_predicate_context(&significant, index)
2399 && let Some(end) = label_predicate_end(scan, index)
2400 {
2401 result.push((
2402 base + significant[index].span.start,
2403 ParserToken::LabelPredicate,
2404 base + significant[end].span.end,
2405 ));
2406 index = end + 1;
2407 continue;
2408 }
2409 }
2410
2411 if !is_procedure_name_start(&significant, index)
2412 && let Some(end) = qualified_function_name_end(scan, index)
2413 {
2414 result.push((
2415 base + significant[index].span.start,
2416 ParserToken::QualifiedFunctionName,
2417 base + significant[end].span.end,
2418 ));
2419 index = end + 1;
2420 continue;
2421 }
2422
2423 let token = significant[index];
2424 let mut parser_token = ParserToken::from(token.kind);
2425 if parser_token == ParserToken::All && is_explicit_set_all(&significant, index) {
2426 parser_token = ParserToken::SetAll;
2427 }
2428 result.push((base + token.span.start, parser_token, base + token.span.end));
2429 index += 1;
2430 }
2431 result
2432}
2433
2434fn find_top_level_token(
2435 tokens: &[SpannedParserToken],
2436 start: usize,
2437 end: usize,
2438 needle: ParserToken,
2439) -> Option<usize> {
2440 let mut parentheses = 0usize;
2441 let mut brackets = 0usize;
2442 let mut braces = 0usize;
2443 for (index, (_, token, _)) in tokens.iter().copied().enumerate().take(end).skip(start) {
2444 if parentheses == 0 && brackets == 0 && braces == 0 && token == needle {
2445 return Some(index);
2446 }
2447 match token {
2448 ParserToken::LeftParen => parentheses += 1,
2449 ParserToken::RightParen => parentheses = parentheses.saturating_sub(1),
2450 ParserToken::LeftBracket => brackets += 1,
2451 ParserToken::RightBracket => brackets = brackets.saturating_sub(1),
2452 ParserToken::LeftBrace => braces += 1,
2453 ParserToken::RightBrace => braces = braces.saturating_sub(1),
2454 _ => {}
2455 }
2456 }
2457 None
2458}
2459
2460fn is_name_token(token: ParserToken) -> bool {
2461 matches!(
2462 token,
2463 ParserToken::Identifier
2464 | ParserToken::EscapedIdentifier
2465 | ParserToken::Match
2466 | ParserToken::Path
2467 | ParserToken::Return
2468 )
2469}
2470
2471pub(crate) fn query_from_pattern(pattern: Pattern, where_clause: Option<Expr>) -> Query {
2472 let span = pattern.span;
2473 let clause = Node::new(
2474 ClauseKind::Match(MatchClause {
2475 optional: false,
2476 mode: None,
2477 pattern,
2478 hints: Vec::new(),
2479 where_clause,
2480 }),
2481 span,
2482 );
2483 let head = Node::new(
2484 SingleQueryKind {
2485 clauses: vec![clause],
2486 },
2487 span,
2488 );
2489 Node::new(
2490 QueryKind::Regular(RegularQuery {
2491 head,
2492 unions: Vec::new(),
2493 }),
2494 span,
2495 )
2496}
2497
2498pub(crate) type RelationshipDetail = (
2499 Option<Name>,
2500 Option<crate::ast::LabelExpression>,
2501 Option<Quantifier>,
2502 Option<crate::ast::Expr>,
2503 Option<crate::ast::Expr>,
2504);
2505
2506pub(crate) fn relationship_factor(
2507 start: usize,
2508 end: usize,
2509 direction: RelationshipDirection,
2510 detail: Option<RelationshipDetail>,
2511 outer_quantifier: Option<Quantifier>,
2512) -> PathFactor {
2513 let (variable, labels, inner_quantifier, properties, where_clause) =
2514 detail.unwrap_or((None, None, None, None, None));
2515
2516 Node::new(
2517 PathFactorKind::Relationship(RelationshipPattern {
2518 direction: Node::new(direction, Span::new(start, end)),
2519 variable,
2520 labels,
2521 legacy_quantifier: inner_quantifier,
2522 graph_quantifier: outer_quantifier,
2523 properties: properties.map(Box::new),
2524 where_clause: where_clause.map(Box::new),
2525 }),
2526 Span::new(start, end),
2527 )
2528}
2529
2530pub(crate) fn fold_binary(mut expression: Expr, tail: Vec<(Node<BinaryOperator>, Expr)>) -> Expr {
2531 for (operator, right) in tail {
2532 let span = expression.span.cover(right.span);
2533 expression = Node::new(
2534 ExprKind::Binary {
2535 left: Box::new(expression),
2536 operator,
2537 right: Box::new(right),
2538 },
2539 span,
2540 );
2541 }
2542 expression
2543}
2544
2545pub(crate) enum ComparisonSuffix {
2546 Binary(Node<BinaryOperator>, Expr),
2547 Is {
2548 negated: bool,
2549 predicate: IsPredicate,
2550 end: usize,
2551 },
2552}
2553
2554pub(crate) fn fold_comparison(mut expression: Expr, tail: Vec<ComparisonSuffix>) -> Expr {
2555 for suffix in tail {
2556 match suffix {
2557 ComparisonSuffix::Binary(operator, right) => {
2558 let span = expression.span.cover(right.span);
2559 expression = Node::new(
2560 ExprKind::Binary {
2561 left: Box::new(expression),
2562 operator,
2563 right: Box::new(right),
2564 },
2565 span,
2566 );
2567 }
2568 ComparisonSuffix::Is {
2569 negated,
2570 predicate,
2571 end,
2572 } => {
2573 let span = Span::new(expression.span.start, end);
2574 expression = Node::new(
2575 ExprKind::Is {
2576 expression: Box::new(expression),
2577 negated,
2578 predicate,
2579 },
2580 span,
2581 );
2582 }
2583 }
2584 }
2585 expression
2586}
2587
2588pub(crate) enum PostfixSuffix {
2589 Property(Name),
2590 MapProjection {
2591 items: Vec<MapProjectionItem>,
2592 end: usize,
2593 },
2594 Index {
2595 index: Expr,
2596 end: usize,
2597 },
2598 Slice {
2599 lower: Option<Expr>,
2600 upper: Option<Expr>,
2601 end: usize,
2602 },
2603}
2604
2605pub(crate) fn fold_postfix(mut expression: Expr, suffixes: Vec<PostfixSuffix>) -> Expr {
2606 for suffix in suffixes {
2607 expression = match suffix {
2608 PostfixSuffix::Property(key) => {
2609 let span = expression.span.cover(key.span);
2610 Node::new(
2611 ExprKind::Property {
2612 expression: Box::new(expression),
2613 key,
2614 },
2615 span,
2616 )
2617 }
2618 PostfixSuffix::MapProjection { items, end } => {
2619 let span = Span::new(expression.span.start, end);
2620 Node::new(
2621 ExprKind::MapProjection(MapProjection {
2622 base: Box::new(expression),
2623 items,
2624 }),
2625 span,
2626 )
2627 }
2628 PostfixSuffix::Index { index, end } => {
2629 let span = Span::new(expression.span.start, end);
2630 Node::new(
2631 ExprKind::Index {
2632 expression: Box::new(expression),
2633 index: Box::new(index),
2634 },
2635 span,
2636 )
2637 }
2638 PostfixSuffix::Slice { lower, upper, end } => {
2639 let span = Span::new(expression.span.start, end);
2640 Node::new(
2641 ExprKind::Slice {
2642 expression: Box::new(expression),
2643 lower: lower.map(Box::new),
2644 upper: upper.map(Box::new),
2645 },
2646 span,
2647 )
2648 }
2649 };
2650 }
2651 expression
2652}
2653
2654fn recover_clauses(source: &str, tokens: &[SpannedParserToken]) -> Option<Program> {
2658 let (tokens, terminator) = match tokens.last() {
2659 Some((start, ParserToken::Semicolon, end)) => {
2660 (&tokens[..tokens.len() - 1], Some(Span::new(*start, *end)))
2661 }
2662 _ => (tokens, None),
2663 };
2664 let first = tokens.first()?;
2665 let last = tokens.last()?;
2666 if !top_level_indices(tokens, |token| {
2669 matches!(token, ParserToken::Union | ParserToken::Semicolon)
2670 })
2671 .is_empty()
2672 {
2673 return None;
2674 }
2675 let mut boundaries = top_level_indices(tokens, clause_starts);
2676 boundaries.push(tokens.len());
2677 let mut clauses = Vec::new();
2678 let mut cursor = 0;
2679 let mut skipped = None;
2680 let mut retained = false;
2681 let mut returned = false;
2682 let mut attempts = 128usize;
2686 while cursor < tokens.len() {
2687 let mut parsed = None;
2688 if !returned {
2689 for &end in boundaries.iter().filter(|&&end| end > cursor) {
2690 if attempts == 0 {
2691 break;
2692 }
2693 attempts -= 1;
2694 let candidate = parse_tokens_with_contextual_names(&tokens[cursor..end], |input| {
2695 generated::ClauseRootParser::new().parse(source, input.iter().copied().map(Ok))
2696 });
2697 if let Ok(clause) = candidate {
2698 parsed = Some((clause, end));
2701 }
2702 }
2703 }
2704 if let Some((clause, end)) = parsed {
2705 if let Some(start) = skipped.take() {
2706 clauses.push(Node::new(
2707 ClauseKind::Error(ErrorNode),
2708 Span::new(start, tokens[cursor - 1].2),
2709 ));
2710 }
2711 returned = matches!(clause.kind, ClauseKind::Return(_));
2712 clauses.push(clause);
2713 retained = true;
2714 cursor = end;
2715 } else {
2716 skipped.get_or_insert(tokens[cursor].0);
2717 cursor = if returned || attempts == 0 {
2718 tokens.len()
2719 } else {
2720 *boundaries.iter().find(|&&end| end > cursor)?
2721 };
2722 }
2723 }
2724 if !retained {
2725 return None;
2726 }
2727 if let Some(start) = skipped {
2728 clauses.push(Node::new(
2729 ClauseKind::Error(ErrorNode),
2730 Span::new(start, last.2),
2731 ));
2732 }
2733 let query_span = Span::new(first.0, last.2);
2734 let head = Node::new(SingleQueryKind { clauses }, query_span);
2735 let query = Node::new(
2736 QueryKind::Regular(RegularQuery {
2737 head,
2738 unions: Vec::new(),
2739 }),
2740 query_span,
2741 );
2742 let span = terminator.map_or(query_span, |end| query_span.cover(end));
2743 let statement = Node::new(
2744 StatementKind::Query(QueryStatement {
2745 mode: None,
2746 query,
2747 terminator,
2748 }),
2749 span,
2750 );
2751 Some(Program::new(vec![statement], span))
2752}