linkrs_query/parser/parsing/traversal_parser/
match.rs1use crate::parser::ast::hint::JoinHintAst;
2use crate::parser::ast::pattern::Pattern;
3use crate::parser::ast::stmt::*;
4use crate::parser::core::error::{ParseError, ParseErrorKind};
5use crate::parser::parsing::clause_parser::ClauseParser;
6use crate::parser::parsing::parse_context::ParseContext;
7use crate::parser::TokenKind;
8
9use super::{ParsedMatchClause, TraversalParser};
10
11impl TraversalParser {
12 pub fn parse_match_statement(&mut self, ctx: &mut ParseContext) -> Result<Stmt, ParseError> {
14 let start_span = ctx.current_span();
15
16 let mut optional = ctx.match_token(TokenKind::Optional);
17
18 ctx.expect_token(TokenKind::Match)?;
19
20 let first = self.parse_match_clause(ctx)?;
21 let mut patterns = first.patterns;
22 let mut join_hint = first.join_hint;
23 let mut where_clause = first.where_clause;
24 let mut where_explicit = first.where_explicit;
25 let mut return_clause = first.return_clause;
26 let mut delete_clause = first.delete_clause;
27
28 while !optional
29 && return_clause.is_none()
30 && delete_clause.is_none()
31 && (ctx.check_token(TokenKind::Match)
32 || (ctx.check_token(TokenKind::Optional)
33 && ctx.peek_token().kind == TokenKind::Match))
34 {
35 if ctx.check_token(TokenKind::Optional) {
36 ctx.expect_token(TokenKind::Optional)?;
37 optional = true;
38 }
39 ctx.expect_token(TokenKind::Match)?;
40 let next = self.parse_match_clause(ctx)?;
41 patterns.extend(next.patterns);
42
43 match (where_explicit, next.where_explicit) {
44 (false, true) => where_clause = next.where_clause,
45 (true, true) => {
46 if let (Some(left), Some(right)) = (&where_clause, &next.where_clause) {
47 if let Some(combined) = ctx.expression_context().and(left, right) {
48 where_clause = Some(combined);
49 }
50 }
51 }
52 _ => {}
53 }
54 where_explicit |= next.where_explicit;
55
56 if let Some(rc) = next.return_clause {
57 return_clause = Some(rc);
58 }
59 if let Some(dc) = next.delete_clause {
60 delete_clause = Some(dc);
61 }
62 if next.join_hint.is_some() {
63 join_hint = next.join_hint;
64 }
65 }
66
67 let (order_by, limit, skip) = if let Some(ref rc) = return_clause {
68 (rc.order_by.clone(), rc.limit.clone(), rc.skip.clone())
69 } else {
70 (None, None, None)
71 };
72
73 let span = ctx.span_since(start_span.start);
74
75 Ok(Stmt::Match(MatchStmt {
76 span,
77 patterns,
78 join_hint,
79 where_clause,
80 return_clause,
81 order_by,
82 limit,
83 skip,
84 optional,
85 delete_clause,
86 }))
87 }
88
89 pub(crate) fn parse_match_clause(
90 &mut self,
91 ctx: &mut ParseContext,
92 ) -> Result<ParsedMatchClause, ParseError> {
93 let mut patterns = Vec::new();
94 loop {
95 let Some(pattern) = ctx.recover_clause(
96 |c| {
97 Ok(Some(Pattern::Variable(VariablePattern {
98 span: c.current_span(),
99 name: String::new(),
100 })))
101 },
102 |c| self.parse_pattern(c).map(Some),
103 )?
104 else {
105 break;
106 };
107 patterns.push(pattern);
108 if !ctx.match_token(TokenKind::Comma) {
109 break;
110 }
111 }
112
113 let join_hint = if ctx.check_keyword_sequence(&["USING", "JOIN"]) {
114 Some(self.parse_join_hint(ctx)?)
115 } else {
116 None
117 };
118
119 let (mut where_clause, mut where_explicit) = if ctx.match_token(TokenKind::Where) {
120 (
121 Some(
122 ctx.recover_clause(Self::create_true_expression, |c| self.parse_expression(c))?,
123 ),
124 true,
125 )
126 } else {
127 (Some(Self::create_true_expression(ctx)?), false)
128 };
129
130 let return_clause = if ctx.match_token(TokenKind::Return) {
131 ctx.recover_clause(
132 |_| Ok(None),
133 |c| ClauseParser::new().parse_return_clause(c).map(Some),
134 )?
135 } else {
136 None
137 };
138
139 let delete_clause = if ctx.match_token(TokenKind::Delete) {
140 ctx.recover_clause(
141 |_| Ok(None),
142 |c| self.parse_match_delete_clause(c).map(Some),
143 )?
144 } else {
145 None
146 };
147
148 if ctx.match_token(TokenKind::Where) {
149 let post =
150 ctx.recover_clause(Self::create_true_expression, |c| self.parse_expression(c))?;
151 if let Some(ref pre) = where_clause {
152 if let Some(combined) = ctx.expression_context().and(pre, &post) {
153 where_clause = Some(combined);
154 } else {
155 where_clause = Some(post);
156 }
157 } else {
158 where_clause = Some(post);
159 }
160 where_explicit = true;
161 }
162
163 Ok(ParsedMatchClause {
164 patterns,
165 join_hint,
166 where_clause,
167 where_explicit,
168 return_clause,
169 delete_clause,
170 })
171 }
172
173 fn parse_join_hint(&mut self, ctx: &mut ParseContext) -> Result<JoinHintAst, ParseError> {
174 ctx.consume_keyword("USING")?;
175 ctx.consume_keyword("JOIN")?;
176 if ctx.check_keyword("BINARY") {
177 ctx.consume_keyword("BINARY")?;
178 ctx.expect_token(TokenKind::LParen)?;
179 let left = ctx.expect_identifier()?;
180 ctx.expect_token(TokenKind::Comma)?;
181 let right = ctx.expect_identifier()?;
182 ctx.expect_token(TokenKind::RParen)?;
183 Ok(JoinHintAst::Binary { left, right })
184 } else if ctx.check_keyword("MULTIWAY") {
185 ctx.consume_keyword("MULTIWAY")?;
186 ctx.expect_token(TokenKind::LParen)?;
187 let probe = ctx.expect_identifier()?;
188 let mut builds = Vec::new();
189 while ctx.match_token(TokenKind::Comma) {
190 builds.push(ctx.expect_identifier()?);
191 }
192 ctx.expect_token(TokenKind::RParen)?;
193 if builds.is_empty() {
194 let pos = ctx.current_position();
195 return Err(ParseError::new(
196 ParseErrorKind::UnexpectedToken,
197 "MULTIWAY needs at least a probe and one build variable".to_string(),
198 pos,
199 ));
200 }
201 Ok(JoinHintAst::Multiway { probe, builds })
202 } else {
203 let pos = ctx.current_position();
204 Err(ParseError::new(
205 ParseErrorKind::UnexpectedToken,
206 "Expected BINARY or MULTIWAY after USING JOIN".to_string(),
207 pos,
208 )
209 .with_expected_tokens(vec!["BINARY".to_string(), "MULTIWAY".to_string()]))
210 }
211 }
212
213 fn parse_match_delete_clause(
214 &mut self,
215 ctx: &mut ParseContext,
216 ) -> Result<MatchDeleteClause, ParseError> {
217 ctx.with_edge_syntax_mode(|ctx| {
218 let start_span = ctx.current_span();
219
220 let target = if ctx.match_token(TokenKind::Vertex) {
221 let vertex_ids = self.parse_expression_list(ctx)?;
222 MatchDeleteTarget::Vertices(vertex_ids)
223 } else if ctx.match_token(TokenKind::Edge) {
224 let first_expr = self.parse_expression(ctx)?;
225 if ctx.check_token(TokenKind::Arrow) {
226 let mut edge_refs = Vec::new();
227 let mut current_src = first_expr;
228 loop {
229 ctx.expect_token(TokenKind::Arrow)?;
230 let dst = self.parse_expression(ctx)?;
231 let rank = if ctx.match_token(TokenKind::At) {
232 Some(self.parse_expression(ctx)?)
233 } else {
234 None
235 };
236 edge_refs.push((current_src, dst, rank));
237 if ctx.match_token(TokenKind::Comma) {
238 current_src = self.parse_expression(ctx)?;
239 } else {
240 break;
241 }
242 }
243 MatchDeleteTarget::EdgeRefs(edge_refs)
244 } else {
245 let mut edge_refs = vec![first_expr];
246 while ctx.match_token(TokenKind::Comma) {
247 edge_refs.push(self.parse_expression(ctx)?);
248 }
249 MatchDeleteTarget::Edges(edge_refs)
250 }
251 } else {
252 return Err(ParseError::new(
253 ParseErrorKind::UnexpectedToken,
254 "Expected VERTEX or EDGE after DELETE".to_string(),
255 ctx.current_position(),
256 ));
257 };
258
259 let with_edge = if ctx.match_token(TokenKind::With) {
260 ctx.expect_token(TokenKind::Edge)?;
261 true
262 } else {
263 false
264 };
265
266 let span = ctx.span_since(start_span.start);
267
268 Ok(MatchDeleteClause {
269 span,
270 target,
271 with_edge,
272 })
273 })
274 }
275}