1use pest::iterators::Pair;
2use pest::Parser;
3use pest_derive::Parser;
4
5use crate::ast::*;
6use crate::error::QueryError;
7
8#[derive(Parser)]
9#[grammar = "cypher.pest"]
10struct CypherParser;
11
12pub fn parse(input: &str) -> Result<Statement, QueryError> {
13 let mut pairs = CypherParser::parse(Rule::query, input)
14 .map_err(|e| QueryError::Parse(e.to_string()))?;
15 let query_pair = pairs.next().expect("query rule always produces one pair");
16 let statement_pair = query_pair
17 .into_inner()
18 .find(|p| p.as_rule() == Rule::statement)
19 .expect("query grammar guarantees a statement");
20 parse_statement(statement_pair)
21}
22
23pub fn parse_many(input: &str) -> Result<Vec<Statement>, QueryError> {
27 let mut pairs = CypherParser::parse(Rule::queries, input)
28 .map_err(|e| QueryError::Parse(e.to_string()))?;
29 let queries_pair = pairs.next().expect("queries rule always produces one pair");
30 queries_pair
31 .into_inner()
32 .filter(|p| p.as_rule() == Rule::statement)
33 .map(parse_statement)
34 .collect()
35}
36
37fn parse_statement(pair: Pair<Rule>) -> Result<Statement, QueryError> {
38 let inner = pair.into_inner().next().expect("statement has one child");
39 match inner.as_rule() {
40 Rule::create_stmt => parse_create_stmt(inner),
41 Rule::match_stmt => parse_match_stmt(inner),
42 r => unreachable!("unexpected statement child rule {r:?}"),
43 }
44}
45
46fn parse_create_stmt(pair: Pair<Rule>) -> Result<Statement, QueryError> {
47 Ok(Statement::Create(parse_create_patterns(pair)?))
48}
49
50fn parse_create_patterns(pair: Pair<Rule>) -> Result<Vec<Pattern>, QueryError> {
55 pair.into_inner()
56 .filter(|p| p.as_rule() == Rule::pattern)
57 .map(parse_pattern)
58 .collect()
59}
60
61fn parse_match_stmt(pair: Pair<Rule>) -> Result<Statement, QueryError> {
62 let mut clauses = Vec::new();
63 let mut tail = None;
64 let mut order_by = None;
65 let mut limit = None;
66 for p in pair.into_inner() {
67 match p.as_rule() {
68 Rule::clause => clauses.push(parse_clause(p)?),
69 Rule::tail_clause => tail = Some(parse_tail_clause(p)?),
70 Rule::order_by_clause => order_by = Some(parse_order_by_clause(p)?),
71 Rule::limit_clause => limit = Some(parse_limit_clause(p)?),
72 r => unreachable!("unexpected match_stmt child rule {r:?}"),
73 }
74 }
75
76 let with_count = clauses.iter().filter(|c| clause_with(c).is_some()).count();
87 if with_count > 1 {
88 return Err(QueryError::Parse(
89 "chaining past one WITH boundary in a single MATCH isn't supported yet".into(),
90 ));
91 }
92 for i in 0..clauses.len() {
93 let (QueryClause::Match(part), Some(QueryClause::Match(next))) = (&clauses[i], clauses.get(i + 1)) else {
94 continue;
95 };
96 if part.with.is_none() && !next.optional {
97 return Err(QueryError::Parse(
98 "multiple MATCH clauses must be separated by WITH".into(),
99 ));
100 }
101 }
102
103 if tail.is_none() && !clauses.iter().any(|c| matches!(c, QueryClause::Merge(_))) {
109 return Err(QueryError::Parse(
110 "a query needs a RETURN/DELETE/SET tail, unless it has a MERGE clause with nothing after it".into(),
111 ));
112 }
113
114 Ok(Statement::Match {
115 clauses,
116 tail,
117 order_by,
118 limit,
119 })
120}
121
122fn clause_with(clause: &QueryClause) -> Option<&WithClause> {
123 match clause {
124 QueryClause::Match(part) => part.with.as_ref(),
125 QueryClause::Unwind(u) => u.with.as_ref(),
126 QueryClause::Merge(m) => m.with.as_ref(),
127 }
128}
129
130fn parse_clause(pair: Pair<Rule>) -> Result<QueryClause, QueryError> {
131 let inner = pair.into_inner().next().expect("clause has one child");
132 match inner.as_rule() {
133 Rule::match_part => Ok(QueryClause::Match(parse_match_part(inner)?)),
134 Rule::unwind_clause => Ok(QueryClause::Unwind(parse_unwind_clause(inner)?)),
135 Rule::merge_clause => Ok(QueryClause::Merge(parse_merge_clause(inner)?)),
136 r => unreachable!("unexpected clause child rule {r:?}"),
137 }
138}
139
140fn parse_merge_clause(pair: Pair<Rule>) -> Result<MergeClause, QueryError> {
145 let mut inner = pair.into_inner();
146 let pattern = parse_pattern(inner.next().expect("merge_clause has a pattern"))?;
147 if pattern.hops.len() > 1 {
148 return Err(QueryError::Parse(
149 "MERGE with more than one relationship hop isn't supported yet — split it into a MATCH \
150 for the already-known part and a MERGE for one new hop"
151 .into(),
152 ));
153 }
154 let mut on_create = Vec::new();
155 let mut on_match = Vec::new();
156 let mut with = None;
157 for p in inner {
158 match p.as_rule() {
159 Rule::on_create_clause => {
160 on_create = p.into_inner().filter(|p| p.as_rule() == Rule::set_item).map(parse_set_item).collect::<Result<_, _>>()?;
161 }
162 Rule::on_match_clause => {
163 on_match = p.into_inner().filter(|p| p.as_rule() == Rule::set_item).map(parse_set_item).collect::<Result<_, _>>()?;
164 }
165 Rule::with_clause => with = Some(parse_with_clause(p)?),
166 r => unreachable!("unexpected merge_clause child rule {r:?}"),
167 }
168 }
169 Ok(MergeClause {
170 pattern,
171 on_create,
172 on_match,
173 with,
174 })
175}
176
177fn parse_unwind_clause(pair: Pair<Rule>) -> Result<UnwindClause, QueryError> {
178 let mut inner = pair.into_inner();
179 let source = parse_unwind_source(inner.next().expect("unwind_clause has an unwind_source"))?;
180 let var = inner.next().expect("unwind_clause has an AS identifier").as_str().to_string();
181 let mut where_clause = None;
182 let mut with = None;
183 for p in inner {
184 match p.as_rule() {
185 Rule::with_where_clause => {
186 let expr_pair = p.into_inner().next().expect("WHERE has a with_expr");
187 where_clause = Some(parse_with_expr(expr_pair)?);
188 }
189 Rule::with_clause => with = Some(parse_with_clause(p)?),
190 r => unreachable!("unexpected unwind_clause child rule {r:?}"),
191 }
192 }
193 Ok(UnwindClause {
194 source,
195 var,
196 where_clause,
197 with,
198 })
199}
200
201fn parse_unwind_source(pair: Pair<Rule>) -> Result<UnwindSource, QueryError> {
202 let inner = pair.into_inner().next().expect("unwind_source has one child");
203 match inner.as_rule() {
204 Rule::list_literal => Ok(UnwindSource::List(
205 inner
206 .into_inner()
207 .filter(|p| p.as_rule() == Rule::literal)
208 .map(parse_literal)
209 .collect::<Result<Vec<_>, _>>()?,
210 )),
211 Rule::identifier => Ok(UnwindSource::Var(inner.as_str().to_string())),
212 r => unreachable!("unexpected unwind_source child rule {r:?}"),
213 }
214}
215
216fn parse_match_part(pair: Pair<Rule>) -> Result<QueryPart, QueryError> {
217 let mut optional = false;
218 let mut path_var = None;
219 let mut shortest_path = false;
220 let mut patterns = Vec::new();
221 let mut where_clause = None;
222 let mut with = None;
223 for p in pair.into_inner() {
224 match p.as_rule() {
225 Rule::match_keyword => {
226 optional = p.as_str().to_ascii_uppercase().starts_with("OPTIONAL");
227 }
228 Rule::path_pattern => {
229 let (var, is_shortest, pattern) = parse_path_pattern(p)?;
230 path_var = var;
231 shortest_path = is_shortest;
232 patterns.push(pattern);
233 }
234 Rule::pattern => patterns.push(parse_pattern(p)?),
235 Rule::where_clause => {
236 let expr_pair = p.into_inner().next().expect("WHERE has an expr");
237 where_clause = Some(parse_expr(expr_pair)?);
238 }
239 Rule::with_clause => with = Some(parse_with_clause(p)?),
240 r => unreachable!("unexpected match_part child rule {r:?}"),
241 }
242 }
243 let pattern = splice_patterns(patterns)?;
244 if shortest_path {
245 validate_shortest_path_pattern(&pattern)?;
246 } else if path_var.is_some() {
247 validate_named_path_pattern(&pattern)?;
248 }
249 Ok(QueryPart {
250 optional,
251 path_var,
252 shortest_path,
253 pattern,
254 where_clause,
255 with,
256 })
257}
258
259fn parse_path_pattern(pair: Pair<Rule>) -> Result<(Option<String>, bool, Pattern), QueryError> {
260 let mut var = None;
261 let mut shortest_path = false;
262 let mut pattern = None;
263 for p in pair.into_inner() {
264 match p.as_rule() {
265 Rule::identifier => var = Some(p.as_str().to_string()),
266 Rule::shortest_path_wrapper => {
267 shortest_path = true;
268 let inner_pattern = p.into_inner().next().expect("shortest_path_wrapper has a pattern");
269 pattern = Some(parse_pattern(inner_pattern)?);
270 }
271 Rule::pattern => pattern = Some(parse_pattern(p)?),
272 r => unreachable!("unexpected path_pattern child rule {r:?}"),
273 }
274 }
275 Ok((var, shortest_path, pattern.expect("path_pattern always has a pattern or shortest_path_wrapper")))
276}
277
278fn validate_shortest_path_pattern(pattern: &Pattern) -> Result<(), QueryError> {
284 if pattern.hops.len() != 1 || pattern.hops[0].0.hop_range.is_none() {
285 return Err(QueryError::Parse(
286 "shortestPath() requires exactly one variable-length relationship pattern (e.g. (a)-[:TYPE*..5]-(b))"
287 .into(),
288 ));
289 }
290 Ok(())
291}
292
293fn validate_named_path_pattern(pattern: &Pattern) -> Result<(), QueryError> {
297 if pattern.hops.iter().any(|(rel, _)| rel.hop_range.is_some()) {
298 return Err(QueryError::Parse(
299 "named-path capture (`p = ...`) over a variable-length relationship pattern isn't supported yet \
300 — use shortestPath() instead, or drop the path variable"
301 .into(),
302 ));
303 }
304 Ok(())
305}
306
307fn parse_with_clause(pair: Pair<Rule>) -> Result<WithClause, QueryError> {
308 let mut items = Vec::new();
309 let mut where_clause = None;
310 let mut order_by = None;
311 let mut limit = None;
312 for p in pair.into_inner() {
313 match p.as_rule() {
314 Rule::return_item => items.push(parse_return_item(p)?),
315 Rule::with_where_clause => {
316 let expr_pair = p.into_inner().next().expect("WITH...WHERE has a with_expr");
317 where_clause = Some(parse_with_expr(expr_pair)?);
318 }
319 Rule::order_by_clause => order_by = Some(parse_order_by_clause(p)?),
320 Rule::limit_clause => limit = Some(parse_limit_clause(p)?),
321 r => unreachable!("unexpected with_clause child rule {r:?}"),
322 }
323 }
324 Ok(WithClause {
325 items,
326 where_clause,
327 order_by,
328 limit,
329 })
330}
331
332fn parse_with_expr(pair: Pair<Rule>) -> Result<WithExpr, QueryError> {
333 parse_with_or_expr(pair.into_inner().next().expect("with_expr has a with_or_expr"))
335}
336
337fn parse_with_or_expr(pair: Pair<Rule>) -> Result<WithExpr, QueryError> {
338 let mut parts = pair.into_inner();
339 let mut acc = parse_with_and_expr(parts.next().expect("with_or_expr has at least one with_and_expr"))?;
340 for rest in parts {
341 acc = WithExpr::Or(Box::new(acc), Box::new(parse_with_and_expr(rest)?));
342 }
343 Ok(acc)
344}
345
346fn parse_with_and_expr(pair: Pair<Rule>) -> Result<WithExpr, QueryError> {
347 let mut parts = pair.into_inner();
348 let mut acc = parse_with_unary_expr(parts.next().expect("with_and_expr has at least one with_unary_expr"))?;
349 for rest in parts {
350 acc = WithExpr::And(Box::new(acc), Box::new(parse_with_unary_expr(rest)?));
351 }
352 Ok(acc)
353}
354
355fn parse_with_unary_expr(pair: Pair<Rule>) -> Result<WithExpr, QueryError> {
356 let inner = pair.into_inner().next().expect("with_unary_expr has one child");
357 match inner.as_rule() {
358 Rule::with_unary_expr => Ok(WithExpr::Not(Box::new(parse_with_unary_expr(inner)?))),
359 Rule::with_comparison => parse_with_comparison(inner),
360 Rule::with_expr => parse_with_expr(inner),
361 r => unreachable!("unexpected with_unary_expr child rule {r:?}"),
362 }
363}
364
365fn parse_with_comparison(pair: Pair<Rule>) -> Result<WithExpr, QueryError> {
366 let mut inner = pair.into_inner();
367 let lhs = parse_return_expr(inner.next().expect("with_comparison has a return_expr"))?;
368 let op_pair = inner.next().expect("with_comparison has a compare_op");
369 let op = parse_compare_op(op_pair);
370 let literal = parse_literal(inner.next().expect("with_comparison has a literal"))?;
371 Ok(WithExpr::Compare(lhs, op, literal))
372}
373
374fn parse_order_by_clause(pair: Pair<Rule>) -> Result<Vec<(ReturnExpr, SortDir)>, QueryError> {
375 pair.into_inner()
376 .filter(|c| c.as_rule() == Rule::sort_item)
377 .map(parse_sort_item)
378 .collect()
379}
380
381fn parse_limit_clause(pair: Pair<Rule>) -> Result<i64, QueryError> {
382 let n_pair = pair.into_inner().next().expect("LIMIT has an int_literal");
383 n_pair
384 .as_str()
385 .parse::<i64>()
386 .map_err(|_| QueryError::Parse("invalid LIMIT value".into()))
387}
388
389fn splice_patterns(mut patterns: Vec<Pattern>) -> Result<Pattern, QueryError> {
399 if patterns.is_empty() {
400 return Err(QueryError::Parse("MATCH requires a pattern".into()));
401 }
402 let mut combined = patterns.remove(0);
403 for next in patterns {
404 let Some(start_var) = next.start.var.clone() else {
405 return Err(QueryError::Parse(
406 "a comma-separated MATCH pattern must start from a named variable".into(),
407 ));
408 };
409 let last_var = combined
410 .hops
411 .last()
412 .map(|(_, n)| n.var.clone())
413 .unwrap_or_else(|| combined.start.var.clone());
414 if last_var.as_deref() != Some(start_var.as_str()) {
415 return Err(QueryError::Parse(format!(
416 "comma-separated MATCH pattern must continue from the previous pattern's last \
417 variable ('{}'), not '{start_var}' — general cross-joins aren't supported",
418 last_var.unwrap_or_default()
419 )));
420 }
421 let target = match combined.hops.last_mut() {
422 Some((_, node)) => node,
423 None => &mut combined.start,
424 };
425 target.labels.extend(next.start.labels);
426 target.props.extend(next.start.props);
427 combined.hops.extend(next.hops);
428 }
429 Ok(combined)
430}
431
432fn parse_sort_item(pair: Pair<Rule>) -> Result<(ReturnExpr, SortDir), QueryError> {
433 let mut inner = pair.into_inner();
434 let expr = parse_return_expr(inner.next().expect("sort_item has a return_expr"))?;
435 let dir = match inner.next() {
436 Some(d) if d.as_str().eq_ignore_ascii_case("desc") => SortDir::Desc,
437 _ => SortDir::Asc,
438 };
439 Ok((expr, dir))
440}
441
442fn parse_tail_clause(pair: Pair<Rule>) -> Result<Tail, QueryError> {
443 let inner = pair.into_inner().next().expect("tail_clause has one child");
444 match inner.as_rule() {
445 Rule::return_clause => {
446 let items = inner
447 .into_inner()
448 .filter(|p| p.as_rule() == Rule::return_item)
449 .map(parse_return_item)
450 .collect::<Result<Vec<_>, _>>()?;
451 Ok(Tail::Return(items))
452 }
453 Rule::detach_delete_clause => {
454 let vars = inner
455 .into_inner()
456 .filter(|p| p.as_rule() == Rule::identifier)
457 .map(|p| p.as_str().to_string())
458 .collect();
459 Ok(Tail::DetachDelete(vars))
460 }
461 Rule::delete_clause => {
462 let vars = inner
463 .into_inner()
464 .filter(|p| p.as_rule() == Rule::identifier)
465 .map(|p| p.as_str().to_string())
466 .collect();
467 Ok(Tail::Delete(vars))
468 }
469 Rule::set_clause => {
470 let items = inner
471 .into_inner()
472 .filter(|p| p.as_rule() == Rule::set_item)
473 .map(parse_set_item)
474 .collect::<Result<Vec<_>, _>>()?;
475 Ok(Tail::Set(items))
476 }
477 Rule::create_stmt => Ok(Tail::Create(parse_create_patterns(inner)?)),
478 r => unreachable!("unexpected tail_clause child rule {r:?}"),
479 }
480}
481
482fn parse_set_item(pair: Pair<Rule>) -> Result<(PropAccess, Literal), QueryError> {
483 let mut inner = pair.into_inner();
484 let prop_access_pair = inner.next().expect("set_item has a prop_access");
485 let literal_pair = inner.next().expect("set_item has a literal");
486 Ok((parse_prop_access(prop_access_pair), parse_literal(literal_pair)?))
487}
488
489fn parse_return_item(pair: Pair<Rule>) -> Result<ReturnItem, QueryError> {
490 let mut inner = pair.into_inner();
491 let expr_pair = inner.next().expect("return_item has a return_expr");
492 let expr = parse_return_expr(expr_pair)?;
493 let alias = inner.next().map(|p| p.as_str().to_string());
494 Ok(ReturnItem { expr, alias })
495}
496
497fn parse_return_expr(pair: Pair<Rule>) -> Result<ReturnExpr, QueryError> {
498 let inner = pair.into_inner().next().expect("return_expr has one child");
499 match inner.as_rule() {
500 Rule::case_expr => parse_case_expr(inner),
501 Rule::function_call => parse_function_call(inner),
502 Rule::prop_access => Ok(ReturnExpr::Prop(parse_prop_access(inner))),
503 Rule::literal => Ok(ReturnExpr::Lit(parse_literal(inner)?)),
504 Rule::identifier => Ok(ReturnExpr::Var(inner.as_str().to_string())),
505 r => unreachable!("unexpected return_expr child rule {r:?}"),
506 }
507}
508
509fn parse_case_expr(pair: Pair<Rule>) -> Result<ReturnExpr, QueryError> {
510 let mut inner = pair.into_inner();
511 let test = parse_return_expr(inner.next().expect("case_expr has a test expr"))?;
512 let mut whens = Vec::new();
513 let mut else_ = None;
514 for p in inner {
515 match p.as_rule() {
516 Rule::case_when => {
517 let mut when_inner = p.into_inner();
518 let when = parse_return_expr(when_inner.next().expect("case_when has a WHEN expr"))?;
519 let then = parse_return_expr(when_inner.next().expect("case_when has a THEN expr"))?;
520 whens.push((when, then));
521 }
522 _ => else_ = Some(Box::new(parse_return_expr(p)?)),
524 }
525 }
526 Ok(ReturnExpr::Case {
527 test: Some(Box::new(test)),
528 whens,
529 else_,
530 })
531}
532
533fn parse_function_call(pair: Pair<Rule>) -> Result<ReturnExpr, QueryError> {
534 let mut inner = pair.into_inner();
535 let name = inner.next().expect("function_call has a name").as_str().to_string();
536 let call_args = inner.next().expect("function_call has call_args");
537 let is_star = call_args.as_str().trim() == "*";
538 if is_star {
539 if !name.eq_ignore_ascii_case("count") {
540 return Err(QueryError::Parse(format!(
541 "'{name}(*)' isn't valid — '*' is only meaningful for count(*)"
542 )));
543 }
544 return Ok(ReturnExpr::CountStar);
545 }
546 let mut distinct = false;
547 let mut args = Vec::new();
548 for p in call_args.into_inner() {
549 match p.as_rule() {
550 Rule::distinct_kw => distinct = true,
551 _ => args.push(parse_return_expr(p)?),
552 }
553 }
554 if distinct && !is_aggregate_name(&name) {
555 return Err(QueryError::Parse(format!(
556 "'{name}(DISTINCT ...)' isn't valid — DISTINCT is only meaningful inside an aggregate function"
557 )));
558 }
559 Ok(ReturnExpr::Call { name, args, distinct })
560}
561
562fn parse_prop_access(pair: Pair<Rule>) -> PropAccess {
563 let mut inner = pair.into_inner();
564 let var = inner.next().expect("prop_access has a var").as_str().to_string();
565 let prop = inner.next().expect("prop_access has a prop").as_str().to_string();
566 PropAccess { var, prop }
567}
568
569fn parse_pattern(pair: Pair<Rule>) -> Result<Pattern, QueryError> {
570 let mut inner = pair.into_inner();
571 let start = parse_node_pattern(inner.next().expect("pattern has a start node"))?;
572 let mut hops = Vec::new();
573 loop {
574 let Some(rel_pair) = inner.next() else { break };
575 let node_pair = inner
576 .next()
577 .ok_or_else(|| QueryError::Parse("dangling relationship in pattern".into()))?;
578 hops.push((parse_rel_pattern(rel_pair)?, parse_node_pattern(node_pair)?));
579 }
580 Ok(Pattern { start, hops })
581}
582
583fn parse_node_pattern(pair: Pair<Rule>) -> Result<NodePattern, QueryError> {
584 let mut var = None;
585 let mut labels = Vec::new();
586 let mut props = Vec::new();
587 for p in pair.into_inner() {
588 match p.as_rule() {
589 Rule::node_var => var = Some(p.as_str().to_string()),
590 Rule::node_label => {
591 labels.push(p.into_inner().next().expect("node_label has an identifier").as_str().to_string())
592 }
593 Rule::prop_map => props = parse_prop_map(p)?,
594 r => unreachable!("unexpected node_pattern child rule {r:?}"),
595 }
596 }
597 Ok(NodePattern { var, labels, props })
598}
599
600fn parse_rel_pattern(pair: Pair<Rule>) -> Result<RelPattern, QueryError> {
601 let inner = pair.into_inner().next().expect("rel_pattern has one child");
602 let direction = match inner.as_rule() {
603 Rule::rel_right => RelDirection::Right,
604 Rule::rel_left => RelDirection::Left,
605 Rule::rel_either => RelDirection::Either,
606 r => unreachable!("unexpected rel_pattern child rule {r:?}"),
607 };
608 let mut var = None;
609 let mut rel_type = None;
610 let mut props = Vec::new();
611 let mut hop_range = None;
612 for p in inner.into_inner() {
613 match p.as_rule() {
614 Rule::rel_var => var = Some(p.as_str().to_string()),
615 Rule::rel_type => {
616 rel_type = Some(p.into_inner().next().expect("rel_type has an identifier").as_str().to_string())
617 }
618 Rule::rel_range => hop_range = Some(parse_rel_range(p.as_str())?),
619 Rule::prop_map => props = parse_prop_map(p)?,
620 r => unreachable!("unexpected rel_right/rel_left/rel_either child rule {r:?}"),
621 }
622 }
623 Ok(RelPattern {
624 var,
625 rel_type,
626 props,
627 direction,
628 hop_range,
629 })
630}
631
632fn parse_rel_range(text: &str) -> Result<(u32, Option<u32>), QueryError> {
637 let rest = &text[1..]; if rest.is_empty() {
639 return Ok((0, None));
640 }
641 if let Some(idx) = rest.find("..") {
642 let min_str = &rest[..idx];
643 let max_str = &rest[idx + 2..];
644 let min = if min_str.is_empty() {
645 0
646 } else {
647 min_str
648 .parse()
649 .map_err(|_| QueryError::Parse("invalid variable-length min hop count".into()))?
650 };
651 let max = if max_str.is_empty() {
652 None
653 } else {
654 Some(
655 max_str
656 .parse()
657 .map_err(|_| QueryError::Parse("invalid variable-length max hop count".into()))?,
658 )
659 };
660 Ok((min, max))
661 } else {
662 let n: u32 = rest
663 .parse()
664 .map_err(|_| QueryError::Parse("invalid variable-length hop count".into()))?;
665 Ok((n, Some(n)))
666 }
667}
668
669fn parse_prop_map(pair: Pair<Rule>) -> Result<Vec<(String, Literal)>, QueryError> {
670 pair.into_inner()
671 .filter(|p| p.as_rule() == Rule::prop_kv)
672 .map(|p| {
673 let mut inner = p.into_inner();
674 let key = inner.next().expect("prop_kv has a key").as_str().to_string();
675 let value = parse_literal(inner.next().expect("prop_kv has a value"))?;
676 Ok((key, value))
677 })
678 .collect()
679}
680
681fn unescape_string(s: &str) -> Result<String, QueryError> {
690 if !s.contains('\\') {
691 return Ok(s.to_string());
692 }
693 let mut out = String::with_capacity(s.len());
694 let mut chars = s.chars();
695 while let Some(c) = chars.next() {
696 if c != '\\' {
697 out.push(c);
698 continue;
699 }
700 match chars.next() {
701 Some('\\') => out.push('\\'),
702 Some('\'') => out.push('\''),
703 Some('"') => out.push('"'),
704 Some('n') => out.push('\n'),
705 Some('r') => out.push('\r'),
706 Some('t') => out.push('\t'),
707 Some('b') => out.push('\u{8}'),
708 Some('f') => out.push('\u{c}'),
709 Some(other) => {
710 return Err(QueryError::Parse(format!("unrecognized string escape '\\{other}'")))
711 }
712 None => return Err(QueryError::Parse("string ends with a trailing '\\'".into())),
713 }
714 }
715 Ok(out)
716}
717
718fn parse_literal(pair: Pair<Rule>) -> Result<Literal, QueryError> {
719 let inner = pair.into_inner().next().expect("literal has one child");
720 Ok(match inner.as_rule() {
721 Rule::int_literal => Literal::Int(
722 inner
723 .as_str()
724 .parse()
725 .map_err(|_| QueryError::Parse("invalid integer literal".into()))?,
726 ),
727 Rule::float_literal => Literal::Float(
728 inner
729 .as_str()
730 .parse()
731 .map_err(|_| QueryError::Parse("invalid float literal".into()))?,
732 ),
733 Rule::string_literal => {
734 let s = inner.as_str();
735 Literal::String(unescape_string(&s[1..s.len() - 1])?)
736 }
737 Rule::bool_literal => Literal::Bool(inner.as_str().eq_ignore_ascii_case("true")),
738 Rule::null_literal => Literal::Null,
739 Rule::param => {
740 let name = inner.into_inner().next().expect("param has an identifier").as_str().to_string();
741 Literal::Param(name)
742 }
743 r => unreachable!("unexpected literal child rule {r:?}"),
744 })
745}
746
747fn parse_expr(pair: Pair<Rule>) -> Result<Expr, QueryError> {
748 parse_or_expr(pair.into_inner().next().expect("expr has an or_expr"))
750}
751
752fn parse_or_expr(pair: Pair<Rule>) -> Result<Expr, QueryError> {
753 let mut parts = pair.into_inner();
754 let mut acc = parse_and_expr(parts.next().expect("or_expr has at least one and_expr"))?;
755 for rest in parts {
756 acc = Expr::Or(Box::new(acc), Box::new(parse_and_expr(rest)?));
757 }
758 Ok(acc)
759}
760
761fn parse_and_expr(pair: Pair<Rule>) -> Result<Expr, QueryError> {
762 let mut parts = pair.into_inner();
763 let mut acc = parse_unary_expr(parts.next().expect("and_expr has at least one unary_expr"))?;
764 for rest in parts {
765 acc = Expr::And(Box::new(acc), Box::new(parse_unary_expr(rest)?));
766 }
767 Ok(acc)
768}
769
770fn parse_unary_expr(pair: Pair<Rule>) -> Result<Expr, QueryError> {
771 let inner = pair.into_inner().next().expect("unary_expr has one child");
772 match inner.as_rule() {
773 Rule::unary_expr => Ok(Expr::Not(Box::new(parse_unary_expr(inner)?))),
774 Rule::comparison => parse_comparison(inner),
775 Rule::expr => parse_expr(inner),
776 r => unreachable!("unexpected unary_expr child rule {r:?}"),
777 }
778}
779
780fn parse_comparison(pair: Pair<Rule>) -> Result<Expr, QueryError> {
781 let mut inner = pair.into_inner();
782 let prop_access = parse_prop_access(inner.next().expect("comparison has a prop_access"));
783 let op = parse_compare_op(inner.next().expect("comparison has a compare_op"));
784 let literal = parse_literal(inner.next().expect("comparison has a literal"))?;
785 Ok(Expr::Compare(prop_access, op, literal))
786}
787
788fn parse_compare_op(pair: Pair<Rule>) -> CompareOp {
789 match pair.as_str() {
790 "=" => CompareOp::Eq,
791 "<>" => CompareOp::Ne,
792 "<" => CompareOp::Lt,
793 "<=" => CompareOp::Le,
794 ">" => CompareOp::Gt,
795 ">=" => CompareOp::Ge,
796 other => unreachable!("unexpected compare_op {other:?}"),
797 }
798}