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