linkrs_query/parser/parsing/
clause_parser.rs1use crate::parser::ast::stmt::*;
6use crate::parser::ast::types::{LimitClause, OrderDirection, SkipClause};
7use crate::parser::core::error::{ParseError, ParseErrorKind};
8use crate::parser::parsing::expr_parser::parse_expression_with_context;
9use crate::parser::parsing::parse_context::ParseContext;
10use crate::parser::TokenKind;
11use linkrs_core::types::expr::contextual::ContextualExpression;
12use linkrs_core::types::expr::Expression as CoreExpression;
13use linkrs_core::types::graph_schema::EdgeDirection;
14
15pub struct ClauseParser;
17
18impl ClauseParser {
19 pub fn new() -> Self {
20 Self
21 }
22
23 pub fn parse_return_clause(
25 &mut self,
26 ctx: &mut ParseContext,
27 ) -> Result<ReturnClause, ParseError> {
28 let span = ctx.current_span();
29
30 let distinct = ctx.match_token(TokenKind::Distinct);
31
32 let mut items = Vec::new();
33
34 if ctx.match_token(TokenKind::Star) {
36 let expr = CoreExpression::variable("*");
37 let expr_meta = linkrs_core::types::expr::ExpressionMeta::new(expr);
38 let id = ctx.expression_context().register_expression(expr_meta);
39 let ctx_expr = ContextualExpression::new(id, ctx.expression_context_clone());
40 items.push(ReturnItem::Expression {
41 expression: ctx_expr,
42 alias: None,
43 });
44 } else {
45 loop {
46 let Some(item) = ctx.recover_clause(
47 |_| Ok(None),
48 |c| {
49 let expr = self.parse_expression(c)?;
50 let alias = if c.match_token(TokenKind::As) {
51 Some(c.expect_identifier()?)
52 } else {
53 None
54 };
55 Ok(Some(ReturnItem::Expression {
56 expression: expr,
57 alias,
58 }))
59 },
60 )?
61 else {
62 break;
63 };
64 items.push(item);
65 if !ctx.match_token(TokenKind::Comma) {
66 break;
67 }
68 }
69 }
70
71 let mut order_by = if ctx.match_token(TokenKind::Order) {
73 ctx.recover_clause(
74 |_| Ok(None),
75 |c| {
76 c.expect_token(TokenKind::By)?;
77 self.parse_order_by_clause(c).map(Some)
78 },
79 )?
80 } else {
81 None
82 };
83
84 let mut limit: Option<LimitClause> = None;
86 let mut skip: Option<SkipClause> = None;
87
88 if ctx.match_token(TokenKind::Skip) {
90 skip = ctx.recover_clause(|_| Ok(None), |c| self.parse_skip_clause(c))?;
91 }
92
93 if ctx.match_token(TokenKind::Limit) {
95 limit = ctx.recover_clause(|_| Ok(None), |c| self.parse_limit_clause(c))?;
96 }
97
98 if skip.is_none() && ctx.match_token(TokenKind::Skip) {
100 skip = ctx.recover_clause(|_| Ok(None), |c| self.parse_skip_clause(c))?;
101 }
102
103 if ctx.match_token(TokenKind::Group) {
106 ctx.recover_clause(|_| Ok(()), |c| self.parse_return_group_by(c))?;
107 }
108
109 if order_by.is_none() && ctx.match_token(TokenKind::Order) {
111 order_by = ctx.recover_clause(
112 |_| Ok(None),
113 |c| {
114 c.expect_token(TokenKind::By)?;
115 self.parse_order_by_clause(c).map(Some)
116 },
117 )?;
118 }
119
120 let sample = if ctx.match_token(TokenKind::Sample) {
122 ctx.recover_clause(
123 |_| Ok(None),
124 |c| {
125 let count = c.expect_integer_literal()? as usize;
126 Ok(Some(crate::parser::ast::types::SampleClause {
127 span: c.current_span(),
128 count,
129 percentage: None,
130 }))
131 },
132 )?
133 } else {
134 None
135 };
136
137 let having_clause = if ctx.match_token(TokenKind::Having) {
139 ctx.recover_clause(|_| Ok(None), |c| self.parse_expression(c).map(Some))?
140 } else {
141 None
142 };
143
144 Ok(ReturnClause {
145 span,
146 items,
147 distinct,
148 order_by,
149 limit,
150 skip,
151 sample,
152 having_clause,
153 })
154 }
155
156 pub fn parse_yield_clause(
160 &mut self,
161 ctx: &mut ParseContext,
162 ) -> Result<YieldClause, ParseError> {
163 let start_span = ctx.current_span();
164
165 let distinct = ctx.match_token(TokenKind::Distinct);
166
167 let mut items = Vec::new();
168
169 if ctx.match_token(TokenKind::Star) {
171 } else {
173 loop {
174 let Some(item) = ctx.recover_clause(
175 |_| Ok(None),
176 |c| {
177 let expr = self.parse_expression(c)?;
178 let alias = if c.match_token(TokenKind::As) {
179 Some(c.expect_identifier()?)
180 } else {
181 None
182 };
183 Ok(Some(YieldItem {
184 expression: expr,
185 alias,
186 }))
187 },
188 )?
189 else {
190 break;
191 };
192 items.push(item);
193 if !ctx.match_token(TokenKind::Comma) {
194 break;
195 }
196 }
197 }
198
199 let where_clause = if ctx.match_token(TokenKind::Where) {
201 ctx.recover_clause(|_| Ok(None), |c| self.parse_expression(c).map(Some))?
202 } else {
203 None
204 };
205
206 let order_by = if ctx.match_token(TokenKind::Order) {
208 ctx.recover_clause(
209 |_| Ok(None),
210 |c| {
211 c.expect_token(TokenKind::By)?;
212 self.parse_order_by_clause(c).map(Some)
213 },
214 )?
215 } else {
216 None
217 };
218
219 let limit = if ctx.match_token(TokenKind::Limit) {
221 ctx.recover_clause(|_| Ok(None), |c| self.parse_limit_clause(c))?
222 } else {
223 None
224 };
225
226 let skip = if ctx.match_token(TokenKind::Skip) {
228 ctx.recover_clause(|_| Ok(None), |c| self.parse_skip_clause(c))?
229 } else {
230 None
231 };
232
233 let sample = if ctx.match_token(TokenKind::Sample) {
235 ctx.recover_clause(
236 |_| Ok(None),
237 |c| {
238 let count = c.expect_integer_literal()? as usize;
239 Ok(Some(crate::parser::ast::types::SampleClause {
240 span: c.current_span(),
241 count,
242 percentage: None,
243 }))
244 },
245 )?
246 } else {
247 None
248 };
249
250 let end_span = ctx.current_span();
251
252 Ok(YieldClause {
253 span: ctx.merge_span(start_span.start, end_span.end),
254 distinct,
255 items,
256 where_clause,
257 order_by,
258 limit,
259 skip,
260 sample,
261 })
262 }
263
264 pub fn parse_set_clause(&mut self, ctx: &mut ParseContext) -> Result<SetClause, ParseError> {
266 let span = ctx.current_span();
267 let assignments = self.parse_set_assignments(ctx)?;
268 Ok(SetClause { span, assignments })
269 }
270
271 pub fn parse_set_assignments(
273 &mut self,
274 ctx: &mut ParseContext,
275 ) -> Result<Vec<Assignment>, ParseError> {
276 let mut assignments = Vec::new();
277 loop {
278 let Some(assignment) =
279 ctx.recover_clause(|_| Ok(None), |c| self.parse_assignment(c).map(Some))?
280 else {
281 break;
282 };
283 assignments.push(assignment);
284 if !ctx.match_token(TokenKind::Comma) {
285 break;
286 }
287 }
288 Ok(assignments)
289 }
290
291 fn parse_assignment(&mut self, ctx: &mut ParseContext) -> Result<Assignment, ParseError> {
293 let property_expr = crate::parser::parsing::expr_parser::parse_property_path_with_context(
296 ctx,
297 ctx.expression_context_clone(),
298 )?;
299 ctx.expect_token(TokenKind::Assign)?;
300 let value = self.parse_expression(ctx)?;
301
302 let (property, target, is_bare_variable) = match property_expr.expression() {
303 Some(expr) => match expr.inner() {
304 CoreExpression::Property { object, property } => {
305 let target = match object.as_ref() {
307 CoreExpression::Literal(_) => Some(property_expr.clone()),
308 CoreExpression::Variable(_) => None, _ => Some(property_expr.clone()),
310 };
311 (property.clone(), target, false)
312 }
313 CoreExpression::Variable(name) => (name.clone(), None, true),
314 _ => {
315 return Err(ParseError::new(
316 ParseErrorKind::SyntaxError,
317 "SET assignment requires a property path (e.g., p.age)".to_string(),
318 ctx.current_position(),
319 ));
320 }
321 },
322 None => {
323 return Err(ParseError::new(
324 ParseErrorKind::SyntaxError,
325 "Expression not registered in context".to_string(),
326 ctx.current_position(),
327 ));
328 }
329 };
330
331 let is_map_overwrite = if !is_bare_variable {
332 false
333 } else if let Some(value_meta) = value.expression() {
334 matches!(value_meta.inner(), CoreExpression::Map(_))
335 } else {
336 false
337 };
338
339 Ok(Assignment {
340 property,
341 value,
342 target,
343 object: None,
344 is_map_overwrite,
345 })
346 }
347
348 pub fn parse_over_clause(&mut self, ctx: &mut ParseContext) -> Result<OverClause, ParseError> {
350 let span = ctx.current_span();
351
352 ctx.recover_clause(
353 |c| {
354 Ok(OverClause {
355 span: c.current_span(),
356 edge_types: Vec::new(),
357 direction: EdgeDirection::Out,
358 })
359 },
360 |c| {
361 let edge_types = self.parse_edge_types(c)?;
362
363 let direction =
365 if c.match_token(TokenKind::In) || c.match_token(TokenKind::Reversely) {
366 EdgeDirection::In
367 } else if c.match_token(TokenKind::Bidirect) {
368 EdgeDirection::Both
369 } else {
370 EdgeDirection::Out
371 };
372
373 Ok(OverClause {
374 span,
375 edge_types,
376 direction,
377 })
378 },
379 )
380 }
381
382 fn parse_edge_types(&mut self, ctx: &mut ParseContext) -> Result<Vec<String>, ParseError> {
384 let mut types = Vec::new();
385 types.push(ctx.expect_identifier()?);
386 while ctx.match_token(TokenKind::Comma) {
387 types.push(ctx.expect_identifier()?);
388 }
389 Ok(types)
390 }
391
392 fn parse_skip_clause(
394 &mut self,
395 ctx: &mut ParseContext,
396 ) -> Result<Option<SkipClause>, ParseError> {
397 let count = ctx.expect_integer_literal()? as usize;
398 Ok(Some(SkipClause {
399 span: ctx.current_span(),
400 count,
401 }))
402 }
403
404 fn parse_limit_clause(
406 &mut self,
407 ctx: &mut ParseContext,
408 ) -> Result<Option<LimitClause>, ParseError> {
409 let count = ctx.expect_integer_literal()? as usize;
410 Ok(Some(LimitClause {
411 span: ctx.current_span(),
412 count,
413 }))
414 }
415
416 fn parse_return_group_by(&mut self, ctx: &mut ParseContext) -> Result<(), ParseError> {
419 ctx.expect_token(TokenKind::By)?;
420
421 if ctx.match_token(TokenKind::Rollup) {
422 ctx.expect_token(TokenKind::LParen)?;
424 loop {
425 self.parse_expression(ctx)?;
426 if !ctx.match_token(TokenKind::Comma) {
427 break;
428 }
429 }
430 ctx.expect_token(TokenKind::RParen)?;
431 } else if ctx.match_token(TokenKind::Cube) {
432 ctx.expect_token(TokenKind::LParen)?;
434 loop {
435 self.parse_expression(ctx)?;
436 if !ctx.match_token(TokenKind::Comma) {
437 break;
438 }
439 }
440 ctx.expect_token(TokenKind::RParen)?;
441 } else if ctx.match_token(TokenKind::Grouping) {
442 ctx.expect_token(TokenKind::Sets)?;
444 ctx.expect_token(TokenKind::LParen)?;
445 loop {
446 ctx.expect_token(TokenKind::LParen)?;
447 loop {
448 self.parse_expression(ctx)?;
449 if !ctx.match_token(TokenKind::Comma) {
450 break;
451 }
452 }
453 ctx.expect_token(TokenKind::RParen)?;
454 if !ctx.match_token(TokenKind::Comma) {
455 break;
456 }
457 }
458 ctx.expect_token(TokenKind::RParen)?;
459 } else {
460 loop {
462 self.parse_expression(ctx)?;
463 if !ctx.match_token(TokenKind::Comma) {
464 break;
465 }
466 }
467 }
468 Ok(())
469 }
470
471 fn parse_order_by_clause(
473 &mut self,
474 ctx: &mut ParseContext,
475 ) -> Result<OrderByClause, ParseError> {
476 let span = ctx.current_span();
477 let mut items = Vec::new();
478
479 loop {
480 let expr = self.parse_expression(ctx)?;
481 let direction = if ctx.match_token(TokenKind::Asc) {
482 OrderDirection::Asc
483 } else if ctx.match_token(TokenKind::Desc) {
484 OrderDirection::Desc
485 } else {
486 OrderDirection::Asc
487 };
488 items.push(OrderByItem {
489 expression: expr,
490 direction,
491 });
492 if !ctx.match_token(TokenKind::Comma) {
493 break;
494 }
495 }
496
497 Ok(OrderByClause { span, items })
498 }
499
500 fn parse_expression(
502 &mut self,
503 ctx: &mut ParseContext,
504 ) -> Result<ContextualExpression, ParseError> {
505 parse_expression_with_context(ctx, ctx.expression_context_clone())
506 }
507}
508
509impl Default for ClauseParser {
510 fn default() -> Self {
511 Self::new()
512 }
513}