Skip to content

feat(parser): pattern parser — NodePattern, RelPattern, PathPattern#656

Merged
DecisionNerd merged 4 commits into
mainfrom
feature/651-pattern-parser
May 29, 2026
Merged

feat(parser): pattern parser — NodePattern, RelPattern, PathPattern#656
DecisionNerd merged 4 commits into
mainfrom
feature/651-pattern-parser

Conversation

@DecisionNerd
Copy link
Copy Markdown
Owner

@DecisionNerd DecisionNerd commented May 29, 2026

Summary

  • Implements crates/gf-cypher/src/parser/patterns.rs (chunk B of rust: implement recursive-descent + Pratt parser for gf-cypher #554)
  • parse_node_pattern: anonymous (), variable, labels, multi-label, inline property map
  • parse_rel_pattern: all 6 forms (-->, <--, --, -[r]->, <-[r]-, -[r]-), type lists, variable-length ranges, inline properties
  • parse_pattern: chained path + named path binding (p = ...)
  • parse_pattern_list: comma-separated pattern list
  • All property values delegate to parse_expr from chunk A
  • 24 unit tests; all 73 gf-cypher tests pass

Test plan

  • cargo test -p gf-cypher — 73 tests pass
  • cargo clippy -p gf-cypher -- -D warnings — clean
  • cargo fmt --all -- --check — clean

Closes #651
Part of #554

🤖 Generated with Claude Code

Note

Add pattern parser for NodePattern, RelPattern, and PathPattern in gf-cypher

  • Adds a new patterns submodule to the Cypher parser exposing parse_node_pattern, parse_pattern, and parse_pattern_list in patterns.rs.
  • parse_node_pattern handles (), (n), (:Label), and (n:Label {props}) forms, producing a NodePattern with var, labels, properties, and span.
  • parse_rel_pattern supports all relationship syntaxes (-->, <--, --, bracketed forms) with direction, optional variable, type list, hop ranges, and properties; invalid combinations like <-[r]-> produce structured errors.
  • parse_pattern composes node and relationship elements into a PathPattern, optionally with a named path prefix (var =); parse_pattern_list handles comma-separated lists of patterns.

Macroscope summarized 34b9a3b.

Summary by CodeRabbit

  • New Features

    • Publicly exposed pattern parsing: full path patterns, named paths, node labels, and property maps.
    • Support for directional relationships and variable-length hops (open or ranged).
    • Parsing of comma-separated pattern lists.
  • Tests

    • Added comprehensive tests for node/relationship parsing, hop ranges, chaining, named paths, and span coverage.

Review Change Stack

…651)

Implements crates/gf-cypher/src/parser/patterns.rs with the full
openCypher pattern grammar:

- NodePattern: anonymous, variable, label(s), properties
- RelPattern: all 6 syntactic forms (-->, <--, --, -[r]->, etc.)
  with optional variable, type list (pipe-separated), variable-length
  range (*1..3), and inline properties
- PathPattern: chained node+rel elements, optional named path (p = ...)
- parse_pattern_list: comma-separated patterns

24 unit tests, all passing. Delegates property parsing to parse_expr.

Closes #651

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 413c5e9d-8320-4ff9-9deb-328845966b9c

📥 Commits

Reviewing files that changed from the base of the PR and between 8665cd5 and 34b9a3b.

📒 Files selected for processing (1)
  • crates/gf-cypher/src/parser/patterns.rs

Walkthrough

Implements a Cypher path pattern parser: new public patterns module with parse_node_pattern, parse_pattern, parse_pattern_list; parses node patterns, relationship syntaxes (directions, types, variable-length hops), assembles chained PathPattern (optional named path), includes helpers and tests.

Changes

Pattern Parser Implementation

Layer / File(s) Summary
Module declaration and re-exports
crates/gf-cypher/src/parser/mod.rs
Parser module declares the public patterns submodule and re-exports the public entry points (parse_node_pattern, parse_pattern, parse_pattern_list) alongside existing parse_expr.
Public entry points
crates/gf-cypher/src/parser/patterns.rs (lines 1–57)
Defines the public functions: parse_pattern (single pattern, optional name) and parse_pattern_list (comma-separated patterns).
Node pattern parsing
crates/gf-cypher/src/parser/patterns.rs (lines 59–91)
Parses node patterns in (...): optional variable, zero or more labels, optional {...} property map (delegates to parse_expr), and span construction.
Relationship parsing and hop ranges
crates/gf-cypher/src/parser/patterns.rs (lines 97–299)
Parses relationship syntaxes (-->, <--, --, -[...] ->, etc.), direction, optional relationship variable/type lists, * variable-length forms (*, *N, *N..M, *..M, *N..), optional property maps; builds RelPattern spans.
Helpers: identifiers & labels
crates/gf-cypher/src/parser/patterns.rs (lines 304–395)
Utility functions to consume identifiers, accept keyword tokens as label/type names, and map tokens to label/type strings.
Test suite
crates/gf-cypher/src/parser/patterns.rs (lines 400–649)
Unit tests for node parsing variants, relationship directions/types/hop ranges, chained path assembly, named-path parsing, pattern-list parsing, and span sanity checks.

Sequence Diagram(s)

sequenceDiagram
  participant TokenStream
  participant parse_pattern
  participant parse_node_pattern
  participant parse_rel_interior
  participant parse_expr
  TokenStream->>parse_pattern: provide tokens for pattern (maybe "p = (a)-[:K]->(b)")
  parse_pattern->>parse_node_pattern: parse initial node `( ... )`
  parse_node_pattern->>parse_expr: parse `{...}` property map (if present)
  parse_pattern->>parse_rel_interior: parse relationship `-[]->` or `--`
  parse_rel_interior->>parse_expr: parse rel property map (if present)
  parse_pattern->>parse_node_pattern: parse next node
  parse_pattern->>TokenStream: return PathPattern AST (with name/span)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: implementing a pattern parser with NodePattern, RelPattern, and PathPattern support.
Linked Issues check ✅ Passed The PR successfully implements all requirements from #651: parse_node_pattern, parse_pattern, parse_pattern_list public APIs; node/rel pattern parsing with all forms; named paths; 24 unit tests; all 73 tests passing.
Out of Scope Changes check ✅ Passed All changes are scoped to the pattern parser implementation. The PR adds patterns.rs with pattern parsing logic and updates mod.rs to re-export public functions, both directly aligned with #651 and #554 objectives.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The pull request description is detailed and comprehensive, covering all major sections of the template including summary, type of change, related issues, and test plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/651-pattern-parser

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.09%. Comparing base (1a4f1cc) to head (34b9a3b).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #656   +/-   ##
=======================================
  Coverage   84.09%   84.09%           
=======================================
  Files          49       49           
  Lines       12921    12921           
  Branches     3628     3628           
=======================================
  Hits        10866    10866           
  Misses       1270     1270           
  Partials      785      785           
Flag Coverage Δ
full-coverage 84.09% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
parser 92.93% <ø> (ø)
planner 79.90% <ø> (ø)
executor 75.46% <ø> (ø)
storage 98.68% <ø> (ø)
ast 97.51% <ø> (ø)
types 90.66% <ø> (ø)

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a4f1cc...34b9a3b. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread crates/gf-cypher/src/parser/patterns.rs Outdated
Comment thread crates/gf-cypher/src/parser/patterns.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/gf-cypher/src/parser/patterns.rs`:
- Around line 109-124: The span for RelPattern currently ends at r_brace which
excludes the trailing direction token; update the span to include the consumed
direction token by advancing the end position to the token you matched when
setting direction (i.e., when ts.eat_if(&Tok::RightArrow) or
ts.eat_if(&Tok::Minus)); locate the block that builds RelPattern (uses vars var,
types, direction, min_hops, max_hops, properties, span: Span::new(start,
r_brace)) and replace the span end with the end position of the consumed
direction token (use the token's span/end returned by the eat/eat_if call or
capture the token node/end and pass that instead of r_brace) so spans for -[r]->
and -[r]- include the final arrow/dash.
- Around line 134-140: After parsing the `]` the code currently treats a
consumed Tok::RightArrow as Direction::Undirected (commenting "<-[r]-> means
both directions"); instead reject the two-headed arrow instead of mapping it to
Direction::Undirected. Replace the branch that returns Direction::Undirected on
ts.eat_if(&Tok::RightArrow) with a return Err(...) from the parser (using ts.err
with a clear message like "`<-[...]->` is not allowed"), keep the Tok::Minus
branch returning Direction::In, and leave the existing fallback error unchanged
so only the allowed `-[...]-`, `-...->`, and `->...-` forms are accepted.
- Around line 244-273: parse_hop_range currently returns (None, None) for a bare
`*`, which collides with the same tuple used for non-variable relationships;
change the default arm so a bare `*` is represented distinctly (e.g., return
(Some(0), None) to mean variable-length any hops) and adjust the function
docstring accordingly so callers can distinguish variable-length (`*`) from
non-variable edges; update parse_hop_range (and any related comments) to use
this new representation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2df7a196-a4de-4983-aa3b-e11e9ce50560

📥 Commits

Reviewing files that changed from the base of the PR and between 1a4f1cc and 165682a.

📒 Files selected for processing (2)
  • crates/gf-cypher/src/parser/mod.rs
  • crates/gf-cypher/src/parser/patterns.rs

Comment thread crates/gf-cypher/src/parser/patterns.rs Outdated
Comment thread crates/gf-cypher/src/parser/patterns.rs Outdated
Comment thread crates/gf-cypher/src/parser/patterns.rs
…s.rs

- RelOpen span now extends past direction token (end at current_pos)
- Reject <-[r]-> with a clear error (not a valid Cypher form)
- Bare * now encodes as (min=Some(1), max=None) per openCypher semantics
  instead of (None, None) which is indistinguishable from non-variable-length
- Hop count literals use u32::try_from to reject out-of-range values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread crates/gf-cypher/src/parser/patterns.rs Outdated
DecisionNerd and others added 2 commits May 29, 2026 15:08
`ts.err()` uses `current_pos()` which is past the already-consumed
integer literal. Capture the span before advancing and pass it to
`ParseError::new` with `ParseErrorKind::InvalidNumericLiteral`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@DecisionNerd DecisionNerd merged commit 2aafe67 into main May 29, 2026
42 checks passed
@DecisionNerd DecisionNerd deleted the feature/651-pattern-parser branch May 29, 2026 21:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

parser: pattern parser — NodePattern, RelPattern, PathPattern (src/parser/patterns.rs)

1 participant