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