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