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