linkrs_query/parser/parsing/
traversal_parser.rs1use crate::parser::ast::hint::JoinHintAst;
6use crate::parser::ast::pattern::{
7 EdgePattern, EdgeRange, NodePattern, PathElement, PathPattern, Pattern, VariablePattern,
8};
9use crate::parser::ast::stmt::*;
10use crate::parser::core::error::{ParseError, ParseErrorKind};
11use crate::parser::parsing::expr_parser::parse_expression_with_context;
12use crate::parser::parsing::parse_context::ParseContext;
13use crate::parser::parsing::pattern_common;
14use crate::parser::TokenKind;
15use linkrs_core::types::expr::contextual::ContextualExpression;
16use linkrs_core::types::expr::Expression as CoreExpression;
17
18mod find_path;
19mod get_subgraph;
20mod go;
21mod r#match;
22
23pub struct TraversalParser;
25
26pub(crate) struct ParsedMatchClause {
28 patterns: Vec<Pattern>,
29 join_hint: Option<JoinHintAst>,
30 where_clause: Option<ContextualExpression>,
31 where_explicit: bool,
32 return_clause: Option<ReturnClause>,
33 delete_clause: Option<MatchDeleteClause>,
34}
35
36impl TraversalParser {
37 pub fn new() -> Self {
38 Self
39 }
40
41 pub fn parse_pattern(&mut self, ctx: &mut ParseContext) -> Result<Pattern, ParseError> {
43 let start_span = ctx.current_span();
44
45 if let TokenKind::Identifier(_) = ctx.current_token().kind {
46 let ckpt = ctx.checkpoint();
47 if let Ok(candidate) = ctx.expect_identifier() {
48 if ctx.match_token(TokenKind::Assign) {
49 return self.parse_named_path(ctx, candidate, start_span);
50 }
51 }
52 ctx.restore(ckpt);
53 }
54
55 self.parse_unnamed_pattern(ctx, start_span)
56 }
57
58 fn parse_named_path(
60 &mut self,
61 ctx: &mut ParseContext,
62 name: String,
63 start_span: crate::parser::ast::types::Span,
64 ) -> Result<Pattern, ParseError> {
65 match self.parse_unnamed_pattern(ctx, ctx.current_span())? {
66 Pattern::Path(mut path) => {
67 if path.name.is_some() {
68 return Err(ParseError::new(
69 ParseErrorKind::SyntaxError,
70 "Duplicate path name in named path binding".to_string(),
71 ctx.current_position(),
72 ));
73 }
74 path.name = Some(name);
75 Ok(Pattern::Path(path))
76 }
77 Pattern::Node(node) => {
78 let span = ctx.span_since(start_span.start);
79 Ok(Pattern::Path(PathPattern::with_name(
80 vec![PathElement::Node(node)],
81 span,
82 name,
83 )))
84 }
85 _ => Err(ParseError::new(
86 ParseErrorKind::SyntaxError,
87 "Expected a node or path pattern after '=' in named path binding".to_string(),
88 ctx.current_position(),
89 )),
90 }
91 }
92
93 fn parse_unnamed_pattern(
95 &mut self,
96 ctx: &mut ParseContext,
97 start_span: crate::parser::ast::types::Span,
98 ) -> Result<Pattern, ParseError> {
99 if ctx.match_token(TokenKind::LParen) {
100 let node = self.parse_node_pattern(ctx, start_span)?;
101
102 if ctx.check_token(TokenKind::LeftArrow)
103 || ctx.check_token(TokenKind::RightArrow)
104 || ctx.check_token(TokenKind::Minus)
105 || ctx.check_token(TokenKind::Arrow)
106 || ctx.check_token(TokenKind::BackArrow)
107 {
108 return self.parse_path_pattern(ctx, node);
109 }
110
111 return Ok(Pattern::Node(node));
112 }
113
114 if let TokenKind::Identifier(ref name) = ctx.current_token().kind.clone() {
115 let name = name.clone();
116 let span = ctx.current_span();
117 ctx.next_token();
118 return Ok(Pattern::Variable(VariablePattern { span, name }));
119 }
120
121 Err(ParseError::new(
122 ParseErrorKind::SyntaxError,
123 "Expected pattern (node or path)".to_string(),
124 ctx.current_position(),
125 ))
126 }
127
128 fn parse_node_pattern(
130 &mut self,
131 ctx: &mut ParseContext,
132 start_span: crate::parser::ast::types::Span,
133 ) -> Result<NodePattern, ParseError> {
134 let mut variable = None;
135 let mut labels = Vec::new();
136 let mut properties = None;
137
138 if let TokenKind::Identifier(ref name) = ctx.current_token().kind.clone() {
139 let name = name.clone();
140 ctx.next_token();
141 variable = Some(name);
142 }
143
144 if ctx.match_token(TokenKind::Colon) {
145 loop {
146 let label = ctx.expect_identifier()?;
147 pattern_common::reject_qualified_reference(ctx, &label)?;
148 labels.push(label);
149 if !ctx.check_token(TokenKind::Colon) {
150 break;
151 }
152 ctx.next_token();
153 }
154 }
155
156 if ctx.match_token(TokenKind::LBrace) {
157 properties = Some(self.parse_properties_expr(ctx)?);
158 ctx.expect_token(TokenKind::RBrace)?;
159 }
160
161 ctx.expect_token(TokenKind::RParen)?;
162
163 let span = ctx.span_since(start_span.start);
164
165 Ok(NodePattern {
166 span,
167 variable,
168 labels,
169 properties,
170 predicates: Vec::new(),
171 })
172 }
173
174 fn parse_path_pattern(
176 &mut self,
177 ctx: &mut ParseContext,
178 start_node: NodePattern,
179 ) -> Result<Pattern, ParseError> {
180 let start_span = start_node.span;
181 let mut elements = vec![PathElement::Node(start_node)];
182
183 while ctx.check_token(TokenKind::LeftArrow)
184 || ctx.check_token(TokenKind::RightArrow)
185 || ctx.check_token(TokenKind::Minus)
186 || ctx.check_token(TokenKind::Arrow)
187 || ctx.check_token(TokenKind::BackArrow)
188 {
189 let edge = self.parse_edge_pattern(ctx)?;
190 elements.push(PathElement::Edge(edge));
191
192 if ctx.match_token(TokenKind::LParen) {
193 let node_span = ctx.current_span();
194 let node = self.parse_node_pattern(ctx, node_span)?;
195 elements.push(PathElement::Node(node));
196 } else {
197 break;
198 }
199 }
200
201 let span = ctx.span_since(start_span.start);
202
203 Ok(Pattern::Path(PathPattern {
204 span,
205 elements,
206 name: None,
207 }))
208 }
209
210 fn parse_edge_pattern(&mut self, ctx: &mut ParseContext) -> Result<EdgePattern, ParseError> {
212 let start_span = ctx.current_span();
213 let incoming =
214 ctx.match_token(TokenKind::BackArrow) || ctx.match_token(TokenKind::LeftArrow);
215
216 ctx.expect_token(TokenKind::Minus)?;
217
218 let mut variable = None;
219 let mut edge_types = Vec::new();
220 let mut properties = None;
221 let mut range = None;
222 let mut path_semantic = None;
223
224 if ctx.match_token(TokenKind::LBracket) {
225 if let TokenKind::Identifier(ref name) = ctx.current_token().kind.clone() {
226 let name = name.clone();
227 ctx.next_token();
228
229 if ctx.check_token(TokenKind::Colon) {
230 variable = Some(name);
231 } else {
232 pattern_common::reject_qualified_reference(ctx, &name)?;
233 edge_types.push(name);
234 }
235 }
236
237 if ctx.match_token(TokenKind::Colon) {
238 loop {
239 ctx.match_token(TokenKind::Colon);
240 let edge_type = ctx.expect_identifier()?;
241 pattern_common::reject_qualified_reference(ctx, &edge_type)?;
242 edge_types.push(edge_type);
243 if !ctx.match_token(TokenKind::Pipe) {
244 break;
245 }
246 }
247 }
248
249 if ctx.match_token(TokenKind::LBrace) {
250 properties = Some(self.parse_properties_expr(ctx)?);
251 ctx.expect_token(TokenKind::RBrace)?;
252 }
253
254 if ctx.match_token(TokenKind::Star) {
255 if ctx.match_token(TokenKind::Trail) {
256 path_semantic = Some(PathSemantic::Trail);
257 range = Some(EdgeRange::any());
258 } else if ctx.match_token(TokenKind::Acyclic) {
259 path_semantic = Some(PathSemantic::Acyclic);
260 range = Some(EdgeRange::any());
261 } else if ctx.match_token(TokenKind::Shortest) {
262 path_semantic = Some(PathSemantic::Shortest);
263 range = Some(EdgeRange::any());
264 } else if ctx.match_token(TokenKind::AllShortestPaths)
265 || (ctx.match_token(TokenKind::All) && ctx.match_token(TokenKind::Shortest))
266 {
267 path_semantic = Some(PathSemantic::AllShortest);
268 range = Some(EdgeRange::any());
269 } else if ctx.match_token(TokenKind::Weighted) {
270 ctx.expect_token(TokenKind::LParen)?;
271 let weight_prop = ctx.expect_identifier()?;
272 ctx.expect_token(TokenKind::RParen)?;
273 path_semantic = Some(PathSemantic::WeightedShortest(weight_prop));
274 range = Some(EdgeRange::any());
275 } else if ctx.match_token(TokenKind::LParen) {
276 return Err(ParseError::new(
277 ParseErrorKind::UnsupportedFeature,
278 "Recursive comprehension patterns are not supported".to_string(),
279 ctx.current_position(),
280 ));
281 } else if ctx.match_token(TokenKind::LBracket) {
282 let min = if matches!(ctx.current_token().kind, TokenKind::IntegerLiteral(_)) {
283 let n = ctx.expect_integer_literal()? as usize;
284 Some(n)
285 } else {
286 None
287 };
288
289 if ctx.match_token(TokenKind::DotDot) {
290 let max =
291 if matches!(ctx.current_token().kind, TokenKind::IntegerLiteral(_)) {
292 let n = ctx.expect_integer_literal()? as usize;
293 Some(n)
294 } else {
295 None
296 };
297 range = Some(EdgeRange::new(min, max));
298 } else if let Some(min_val) = min {
299 range = Some(EdgeRange::fixed(min_val));
300 } else {
301 range = Some(EdgeRange::any());
302 }
303
304 ctx.expect_token(TokenKind::RBracket)?;
305 } else if matches!(ctx.current_token().kind, TokenKind::IntegerLiteral(_)) {
306 let min = ctx.expect_integer_literal()? as usize;
307 if ctx.match_token(TokenKind::DotDot) {
308 let max =
309 if matches!(ctx.current_token().kind, TokenKind::IntegerLiteral(_)) {
310 let n = ctx.expect_integer_literal()? as usize;
311 Some(n)
312 } else {
313 None
314 };
315 range = Some(EdgeRange::new(Some(min), max));
316 } else {
317 range = Some(EdgeRange::fixed(min));
318 }
319 } else {
320 range = Some(EdgeRange::any());
321 }
322 }
323
324 ctx.expect_token(TokenKind::RBracket)?;
325 }
326
327 let closing = ctx.current_token().kind.clone();
329 let direction =
330 pattern_common::resolve_edge_direction(incoming, &closing).ok_or_else(|| {
331 ParseError::new(
332 ParseErrorKind::SyntaxError,
333 "Expected '-', '->', or '<-' after edge pattern".to_string(),
334 ctx.current_position(),
335 )
336 })?;
337 ctx.next_token();
338
339 let span = ctx.span_since(start_span.start);
340
341 Ok(EdgePattern {
342 span,
343 variable,
344 edge_types,
345 properties,
346 predicates: Vec::new(),
347 direction,
348 range,
349 path_semantic,
350 recursive_comprehension: ctx.take_recursive_comprehension(),
351 })
352 }
353
354 fn parse_steps(&mut self, ctx: &mut ParseContext) -> Result<Steps, ParseError> {
356 let token = ctx.current_token();
357 match token.kind {
358 TokenKind::IntegerLiteral(n) => {
359 ctx.next_token();
360 Ok(Steps::Fixed(n as usize))
361 }
362 TokenKind::Step | TokenKind::From => Ok(Steps::Fixed(1)),
363 _ => Err(ParseError::new(
364 ParseErrorKind::UnsupportedFeature,
365 "GO step count must be an integer literal".to_string(),
366 ctx.current_position(),
367 )),
368 }
369 }
370
371 fn parse_expression_list(
373 &mut self,
374 ctx: &mut ParseContext,
375 ) -> Result<Vec<ContextualExpression>, ParseError> {
376 let mut expressions = Vec::new();
377
378 loop {
379 expressions.push(self.parse_expression(ctx)?);
380 if !ctx.match_token(TokenKind::Comma) {
381 break;
382 }
383 }
384
385 Ok(expressions)
386 }
387
388 fn parse_properties_expr(
390 &mut self,
391 ctx: &mut ParseContext,
392 ) -> Result<ContextualExpression, ParseError> {
393 let mut properties = Vec::new();
394
395 while !ctx.check_token(TokenKind::RBrace) {
396 let key = ctx.expect_identifier()?;
397 ctx.expect_token(TokenKind::Colon)?;
398 let value = self.parse_expression(ctx)?;
399 properties.push((key, value));
400 if !ctx.match_token(TokenKind::Comma) {
401 break;
402 }
403 }
404
405 let mut mapped_properties = Vec::new();
406 for (k, v) in properties {
407 let v_expr = v
408 .expression()
409 .ok_or_else(|| {
410 ParseError::new_simple(
411 "Expression not registered in context".to_string(),
412 ctx.current_position(),
413 )
414 })?
415 .inner()
416 .clone();
417 mapped_properties.push((k, v_expr));
418 }
419
420 let expr = CoreExpression::map(mapped_properties);
421
422 let expr_meta = linkrs_core::types::expr::ExpressionMeta::new(expr);
423 let id = ctx.expression_context().register_expression(expr_meta);
424 Ok(ContextualExpression::new(
425 id,
426 ctx.expression_context_clone(),
427 ))
428 }
429
430 fn create_true_expression(ctx: &mut ParseContext) -> Result<ContextualExpression, ParseError> {
432 let expr = CoreExpression::literal(true);
433 let expr_meta = linkrs_core::types::expr::ExpressionMeta::new(expr);
434 let id = ctx.expression_context().register_expression(expr_meta);
435 Ok(ContextualExpression::new(
436 id,
437 ctx.expression_context_clone(),
438 ))
439 }
440
441 fn parse_expression(
443 &mut self,
444 ctx: &mut ParseContext,
445 ) -> Result<ContextualExpression, ParseError> {
446 parse_expression_with_context(ctx, ctx.expression_context_clone())
447 }
448}
449
450impl Default for TraversalParser {
451 fn default() -> Self {
452 Self::new()
453 }
454}