1use pest::Parser;
2use pest::error::InputLocation;
3use pest_derive::Parser;
4
5use crate::error::{
6 CompilerError, ParseDiagnostic, Result, SourceSpan, decode_string_literal, render_span,
7};
8
9use super::ast::*;
10
11#[derive(Parser)]
12#[grammar = "query/query.pest"]
13struct QueryParser;
14
15pub fn parse_query(input: &str) -> Result<QueryFile> {
16 parse_query_diagnostic(input).map_err(|e| CompilerError::Parse(e.to_string()))
17}
18
19pub fn parse_query_diagnostic(input: &str) -> std::result::Result<QueryFile, ParseDiagnostic> {
20 let pairs = QueryParser::parse(Rule::query_file, input).map_err(pest_error_to_diagnostic)?;
21
22 let mut queries = Vec::new();
23 for pair in pairs {
24 if let Rule::query_file = pair.as_rule() {
25 for inner in pair.into_inner() {
26 if let Rule::query_decl = inner.as_rule() {
27 queries.push(parse_query_decl(inner).map_err(compiler_error_to_diagnostic)?);
28 }
29 }
30 }
31 }
32 Ok(QueryFile { queries })
33}
34
35fn pest_error_to_diagnostic(err: pest::error::Error<Rule>) -> ParseDiagnostic {
36 let span = match err.location {
37 InputLocation::Pos(pos) => Some(render_span(SourceSpan::new(pos, pos))),
38 InputLocation::Span((start, end)) => Some(render_span(SourceSpan::new(start, end))),
39 };
40 ParseDiagnostic::new(err.to_string(), span)
41}
42
43fn compiler_error_to_diagnostic(err: CompilerError) -> ParseDiagnostic {
44 ParseDiagnostic::new(err.to_string(), None)
45}
46
47fn parse_query_decl(pair: pest::iterators::Pair<Rule>) -> Result<QueryDecl> {
48 let mut inner = pair.into_inner();
49 let name = inner.next().unwrap().as_str().to_string();
50
51 let mut description = None;
52 let mut instruction = None;
53 let mut params = Vec::new();
54 let mut match_clause = Vec::new();
55 let mut return_clause = Vec::new();
56 let mut order_clause = Vec::new();
57 let mut limit = None;
58 let mut mutations = Vec::new();
59
60 for item in inner {
61 match item.as_rule() {
62 Rule::param_list => {
63 for p in item.into_inner() {
64 if let Rule::param = p.as_rule() {
65 params.push(parse_param(p)?);
66 }
67 }
68 }
69 Rule::query_annotation => {
70 let (annotation_name, value) = parse_query_annotation(item)?;
71 match annotation_name {
72 "description" => {
73 if description.replace(value).is_some() {
74 return Err(CompilerError::Parse(format!(
75 "query `{}` cannot include duplicate @description annotations",
76 name
77 )));
78 }
79 }
80 "instruction" => {
81 if instruction.replace(value).is_some() {
82 return Err(CompilerError::Parse(format!(
83 "query `{}` cannot include duplicate @instruction annotations",
84 name
85 )));
86 }
87 }
88 other => {
89 return Err(CompilerError::Parse(format!(
90 "unsupported query annotation: @{}",
91 other
92 )));
93 }
94 }
95 }
96 Rule::query_body => {
97 let body = item.into_inner().next().ok_or_else(|| {
98 CompilerError::Parse("query body cannot be empty".to_string())
99 })?;
100 match body.as_rule() {
101 Rule::read_query_body => {
102 for section in body.into_inner() {
103 match section.as_rule() {
104 Rule::match_clause => {
105 for c in section.into_inner() {
106 if let Rule::clause = c.as_rule() {
107 match_clause.push(parse_clause(c)?);
108 }
109 }
110 }
111 Rule::return_clause => {
112 for proj in section.into_inner() {
113 if let Rule::projection = proj.as_rule() {
114 return_clause.push(parse_projection(proj)?);
115 }
116 }
117 }
118 Rule::order_clause => {
119 for ord in section.into_inner() {
120 if let Rule::ordering = ord.as_rule() {
121 order_clause.push(parse_ordering(ord)?);
122 }
123 }
124 }
125 Rule::limit_clause => {
126 let int_pair = section.into_inner().next().unwrap();
127 limit =
128 Some(int_pair.as_str().parse::<u64>().map_err(|e| {
129 CompilerError::Parse(format!("invalid limit: {}", e))
130 })?);
131 }
132 _ => {}
133 }
134 }
135 }
136 Rule::mutation_body => {
137 for mutation_pair in body.into_inner() {
138 if let Rule::mutation_stmt = mutation_pair.as_rule() {
139 let stmt = mutation_pair.into_inner().next().ok_or_else(|| {
140 CompilerError::Parse(
141 "mutation statement cannot be empty".to_string(),
142 )
143 })?;
144 mutations.push(parse_mutation_stmt(stmt)?);
145 }
146 }
147 }
148 _ => {}
149 }
150 }
151 _ => {}
152 }
153 }
154
155 Ok(QueryDecl {
156 name,
157 description,
158 instruction,
159 params,
160 match_clause,
161 return_clause,
162 order_clause,
163 limit,
164 mutations,
165 })
166}
167
168fn parse_query_annotation(pair: pest::iterators::Pair<Rule>) -> Result<(&'static str, String)> {
169 let inner = pair
170 .into_inner()
171 .next()
172 .ok_or_else(|| CompilerError::Parse("query annotation cannot be empty".to_string()))?;
173 match inner.as_rule() {
174 Rule::description_annotation => {
175 let value = inner
176 .into_inner()
177 .next()
178 .ok_or_else(|| {
179 CompilerError::Parse("@description requires a string literal".to_string())
180 })
181 .map(|value| parse_string_lit(value.as_str()))??;
182 Ok(("description", value))
183 }
184 Rule::instruction_annotation => {
185 let value = inner
186 .into_inner()
187 .next()
188 .ok_or_else(|| {
189 CompilerError::Parse("@instruction requires a string literal".to_string())
190 })
191 .map(|value| parse_string_lit(value.as_str()))??;
192 Ok(("instruction", value))
193 }
194 other => Err(CompilerError::Parse(format!(
195 "unexpected query annotation rule: {:?}",
196 other
197 ))),
198 }
199}
200
201fn parse_param(pair: pest::iterators::Pair<Rule>) -> Result<Param> {
202 let mut inner = pair.into_inner();
203 let var = inner.next().unwrap().as_str();
204 let name = var.strip_prefix('$').unwrap_or(var).to_string();
205 let type_ref = inner.next().unwrap();
206 let nullable = type_ref.as_str().trim_end().ends_with('?');
207 let mut type_inner = type_ref.into_inner();
208 let core = type_inner
209 .next()
210 .ok_or_else(|| CompilerError::Parse("parameter type is missing".to_string()))?;
211 let base =
212 match core.as_rule() {
213 Rule::base_type => core.as_str().to_string(),
214 Rule::list_type => {
215 let inner = core.into_inner().next().ok_or_else(|| {
216 CompilerError::Parse("list type missing item type".to_string())
217 })?;
218 format!("[{}]", inner.as_str().trim())
219 }
220 Rule::vector_type => {
221 let vector = core.into_inner().next().ok_or_else(|| {
222 CompilerError::Parse("Vector type missing dimension".to_string())
223 })?;
224 format!("Vector({})", vector.as_str().trim())
225 }
226 other => {
227 return Err(CompilerError::Parse(format!(
228 "unexpected param type rule: {:?}",
229 other
230 )));
231 }
232 };
233
234 Ok(Param {
235 name,
236 type_name: base,
237 nullable,
238 })
239}
240
241fn parse_clause(pair: pest::iterators::Pair<Rule>) -> Result<Clause> {
242 let inner = pair.into_inner().next().unwrap();
243 match inner.as_rule() {
244 Rule::binding => Ok(Clause::Binding(parse_binding(inner)?)),
245 Rule::traversal => Ok(Clause::Traversal(parse_traversal(inner)?)),
246 Rule::filter => Ok(Clause::Filter(parse_filter(inner)?)),
247 Rule::text_search_clause => Ok(parse_text_search_clause(inner)?),
248 Rule::negation => {
249 let mut clauses = Vec::new();
250 for c in inner.into_inner() {
251 if let Rule::clause = c.as_rule() {
252 clauses.push(parse_clause(c)?);
253 }
254 }
255 Ok(Clause::Negation(clauses))
256 }
257 _ => Err(CompilerError::Parse(format!(
258 "unexpected clause rule: {:?}",
259 inner.as_rule()
260 ))),
261 }
262}
263
264fn parse_text_search_clause(pair: pest::iterators::Pair<Rule>) -> Result<Clause> {
265 let inner = pair
266 .into_inner()
267 .next()
268 .ok_or_else(|| CompilerError::Parse("text search clause cannot be empty".to_string()))?;
269 let expr = match inner.as_rule() {
270 Rule::search_call => parse_search_call(inner)?,
271 Rule::fuzzy_call => parse_fuzzy_call(inner)?,
272 Rule::match_text_call => parse_match_text_call(inner)?,
273 other => {
274 return Err(CompilerError::Parse(format!(
275 "unexpected text search clause rule: {:?}",
276 other
277 )));
278 }
279 };
280
281 Ok(Clause::Filter(Filter {
282 left: expr,
283 op: CompOp::Eq,
284 right: Expr::Literal(Literal::Bool(true)),
285 }))
286}
287
288fn parse_binding(pair: pest::iterators::Pair<Rule>) -> Result<Binding> {
289 let mut inner = pair.into_inner();
290 let var = inner.next().unwrap().as_str();
291 let variable = var.strip_prefix('$').unwrap_or(var).to_string();
292 let type_name = inner.next().unwrap().as_str().to_string();
293
294 let mut prop_matches = Vec::new();
295 for item in inner {
296 if let Rule::prop_match_list = item.as_rule() {
297 for pm in item.into_inner() {
298 if let Rule::prop_match = pm.as_rule() {
299 prop_matches.push(parse_prop_match(pm)?);
300 }
301 }
302 }
303 }
304
305 Ok(Binding {
306 variable,
307 type_name,
308 prop_matches,
309 })
310}
311
312fn parse_prop_match(pair: pest::iterators::Pair<Rule>) -> Result<PropMatch> {
313 let mut inner = pair.into_inner();
314 let prop_name = inner.next().unwrap().as_str().to_string();
315 let value_pair = inner.next().unwrap();
316 let value = parse_match_value(value_pair)?;
317
318 Ok(PropMatch { prop_name, value })
319}
320
321fn parse_mutation_stmt(pair: pest::iterators::Pair<Rule>) -> Result<Mutation> {
322 match pair.as_rule() {
323 Rule::insert_stmt => parse_insert_mutation(pair).map(Mutation::Insert),
324 Rule::update_stmt => parse_update_mutation(pair).map(Mutation::Update),
325 Rule::delete_stmt => parse_delete_mutation(pair).map(Mutation::Delete),
326 other => Err(CompilerError::Parse(format!(
327 "unexpected mutation statement rule: {:?}",
328 other
329 ))),
330 }
331}
332
333fn parse_insert_mutation(pair: pest::iterators::Pair<Rule>) -> Result<InsertMutation> {
334 let mut inner = pair.into_inner();
335 let type_name = inner.next().unwrap().as_str().to_string();
336 let mut assignments = Vec::new();
337 for item in inner {
338 if let Rule::mutation_assignment = item.as_rule() {
339 assignments.push(parse_mutation_assignment(item)?);
340 }
341 }
342 Ok(InsertMutation {
343 type_name,
344 assignments,
345 })
346}
347
348fn parse_update_mutation(pair: pest::iterators::Pair<Rule>) -> Result<UpdateMutation> {
349 let mut inner = pair.into_inner();
350 let type_name = inner.next().unwrap().as_str().to_string();
351
352 let mut assignments = Vec::new();
353 let mut predicate = None;
354
355 for item in inner {
356 match item.as_rule() {
357 Rule::mutation_assignment => assignments.push(parse_mutation_assignment(item)?),
358 Rule::mutation_predicate => predicate = Some(parse_mutation_predicate(item)?),
359 _ => {}
360 }
361 }
362
363 let predicate = predicate.ok_or_else(|| {
364 CompilerError::Parse("update mutation requires a where predicate".to_string())
365 })?;
366
367 Ok(UpdateMutation {
368 type_name,
369 assignments,
370 predicate,
371 })
372}
373
374fn parse_delete_mutation(pair: pest::iterators::Pair<Rule>) -> Result<DeleteMutation> {
375 let mut inner = pair.into_inner();
376 let type_name = inner.next().unwrap().as_str().to_string();
377 let predicate = inner
378 .next()
379 .ok_or_else(|| {
380 CompilerError::Parse("delete mutation requires a where predicate".to_string())
381 })
382 .and_then(parse_mutation_predicate)?;
383 Ok(DeleteMutation {
384 type_name,
385 predicate,
386 })
387}
388
389fn parse_mutation_assignment(pair: pest::iterators::Pair<Rule>) -> Result<MutationAssignment> {
390 let mut inner = pair.into_inner();
391 let property = inner.next().unwrap().as_str().to_string();
392 let value = parse_match_value(inner.next().unwrap())?;
393 Ok(MutationAssignment { property, value })
394}
395
396fn parse_mutation_predicate(pair: pest::iterators::Pair<Rule>) -> Result<MutationPredicate> {
397 let mut inner = pair.into_inner();
398 let property = inner.next().unwrap().as_str().to_string();
399 let op = parse_comp_op(inner.next().unwrap())?;
400 let value = parse_match_value(inner.next().unwrap())?;
401 Ok(MutationPredicate {
402 property,
403 op,
404 value,
405 })
406}
407
408fn parse_match_value(pair: pest::iterators::Pair<Rule>) -> Result<MatchValue> {
409 let value_inner = pair.into_inner().next().unwrap();
410 match value_inner.as_rule() {
411 Rule::variable => {
412 let v = value_inner.as_str();
413 Ok(MatchValue::Variable(
414 v.strip_prefix('$').unwrap_or(v).to_string(),
415 ))
416 }
417 Rule::now_call => Ok(MatchValue::Now),
418 Rule::literal => Ok(MatchValue::Literal(parse_literal(value_inner)?)),
419 _ => Err(CompilerError::Parse(format!(
420 "unexpected match value: {:?}",
421 value_inner.as_rule()
422 ))),
423 }
424}
425
426fn parse_traversal(pair: pest::iterators::Pair<Rule>) -> Result<Traversal> {
427 let mut inner = pair.into_inner();
428 let src_var = inner.next().unwrap().as_str();
429 let src = src_var.strip_prefix('$').unwrap_or(src_var).to_string();
430 let edge_name = inner.next().unwrap().as_str().to_string();
431 let mut min_hops = 1u32;
432 let mut max_hops = Some(1u32);
433
434 let next = inner.next().unwrap();
435 let dst_pair = if let Rule::traversal_bounds = next.as_rule() {
436 let (min, max) = parse_traversal_bounds(next)?;
437 min_hops = min;
438 max_hops = max;
439 inner.next().ok_or_else(|| {
440 CompilerError::Parse("traversal missing destination variable".to_string())
441 })?
442 } else {
443 next
444 };
445
446 let dst_var = dst_pair.as_str();
447 let dst = dst_var.strip_prefix('$').unwrap_or(dst_var).to_string();
448
449 Ok(Traversal {
450 src,
451 edge_name,
452 dst,
453 min_hops,
454 max_hops,
455 })
456}
457
458fn parse_traversal_bounds(pair: pest::iterators::Pair<Rule>) -> Result<(u32, Option<u32>)> {
459 let mut inner = pair.into_inner();
460 let min = inner
461 .next()
462 .ok_or_else(|| CompilerError::Parse("traversal bound missing min hop".to_string()))?
463 .as_str()
464 .parse::<u32>()
465 .map_err(|e| CompilerError::Parse(format!("invalid traversal min bound: {}", e)))?;
466 let max = inner
467 .next()
468 .map(|p| {
469 p.as_str()
470 .parse::<u32>()
471 .map_err(|e| CompilerError::Parse(format!("invalid traversal max bound: {}", e)))
472 })
473 .transpose()?;
474 Ok((min, max))
475}
476
477fn parse_filter(pair: pest::iterators::Pair<Rule>) -> Result<Filter> {
478 let mut inner = pair.into_inner();
479 let left = parse_expr(inner.next().unwrap())?;
480 let op = parse_filter_op(inner.next().unwrap())?;
481 let right = parse_expr(inner.next().unwrap())?;
482
483 Ok(Filter { left, op, right })
484}
485
486fn parse_expr(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
487 let inner = pair.into_inner().next().unwrap();
488 match inner.as_rule() {
489 Rule::now_call => Ok(Expr::Now),
490 Rule::prop_access => {
491 let mut parts = inner.into_inner();
492 let var = parts.next().unwrap().as_str();
493 let variable = var.strip_prefix('$').unwrap_or(var).to_string();
494 let property = parts.next().unwrap().as_str().to_string();
495 Ok(Expr::PropAccess { variable, property })
496 }
497 Rule::variable => {
498 let v = inner.as_str();
499 Ok(Expr::Variable(v.strip_prefix('$').unwrap_or(v).to_string()))
500 }
501 Rule::literal => Ok(Expr::Literal(parse_literal(inner)?)),
502 Rule::agg_call => {
503 let mut parts = inner.into_inner();
504 let func = match parts.next().unwrap().as_str() {
505 "count" => AggFunc::Count,
506 "sum" => AggFunc::Sum,
507 "avg" => AggFunc::Avg,
508 "min" => AggFunc::Min,
509 "max" => AggFunc::Max,
510 other => {
511 return Err(CompilerError::Parse(format!(
512 "unknown aggregate: {}",
513 other
514 )));
515 }
516 };
517 let arg = parse_expr(parts.next().unwrap())?;
518 Ok(Expr::Aggregate {
519 func,
520 arg: Box::new(arg),
521 })
522 }
523 Rule::search_call => parse_search_call(inner),
524 Rule::fuzzy_call => parse_fuzzy_call(inner),
525 Rule::match_text_call => parse_match_text_call(inner),
526 Rule::nearest_ordering => parse_nearest_ordering(inner),
527 Rule::bm25_call => parse_bm25_call(inner),
528 Rule::rrf_call => parse_rrf_call(inner),
529 Rule::ident => Ok(Expr::AliasRef(inner.as_str().to_string())),
530 _ => Err(CompilerError::Parse(format!(
531 "unexpected expr rule: {:?}",
532 inner.as_rule()
533 ))),
534 }
535}
536
537fn parse_search_call(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
538 let mut args = pair.into_inner();
539 let field = args
540 .next()
541 .ok_or_else(|| CompilerError::Parse("search() missing field argument".to_string()))?;
542 let query = args
543 .next()
544 .ok_or_else(|| CompilerError::Parse("search() missing query argument".to_string()))?;
545 if args.next().is_some() {
546 return Err(CompilerError::Parse(
547 "search() accepts exactly 2 arguments".to_string(),
548 ));
549 }
550 Ok(Expr::Search {
551 field: Box::new(parse_expr(field)?),
552 query: Box::new(parse_expr(query)?),
553 })
554}
555
556fn parse_fuzzy_call(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
557 let mut args = pair.into_inner();
558 let field = args
559 .next()
560 .ok_or_else(|| CompilerError::Parse("fuzzy() missing field argument".to_string()))?;
561 let query = args
562 .next()
563 .ok_or_else(|| CompilerError::Parse("fuzzy() missing query argument".to_string()))?;
564 let max_edits = args.next().map(parse_expr).transpose()?.map(Box::new);
565 if args.next().is_some() {
566 return Err(CompilerError::Parse(
567 "fuzzy() accepts at most 3 arguments".to_string(),
568 ));
569 }
570 Ok(Expr::Fuzzy {
571 field: Box::new(parse_expr(field)?),
572 query: Box::new(parse_expr(query)?),
573 max_edits,
574 })
575}
576
577fn parse_match_text_call(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
578 let mut args = pair.into_inner();
579 let field = args
580 .next()
581 .ok_or_else(|| CompilerError::Parse("match_text() missing field argument".to_string()))?;
582 let query = args
583 .next()
584 .ok_or_else(|| CompilerError::Parse("match_text() missing query argument".to_string()))?;
585 if args.next().is_some() {
586 return Err(CompilerError::Parse(
587 "match_text() accepts exactly 2 arguments".to_string(),
588 ));
589 }
590 Ok(Expr::MatchText {
591 field: Box::new(parse_expr(field)?),
592 query: Box::new(parse_expr(query)?),
593 })
594}
595
596fn parse_bm25_call(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
597 let mut args = pair.into_inner();
598 let field = args
599 .next()
600 .ok_or_else(|| CompilerError::Parse("bm25() missing field argument".to_string()))?;
601 let query = args
602 .next()
603 .ok_or_else(|| CompilerError::Parse("bm25() missing query argument".to_string()))?;
604 if args.next().is_some() {
605 return Err(CompilerError::Parse(
606 "bm25() accepts exactly 2 arguments".to_string(),
607 ));
608 }
609 Ok(Expr::Bm25 {
610 field: Box::new(parse_expr(field)?),
611 query: Box::new(parse_expr(query)?),
612 })
613}
614
615fn parse_rank_expr(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
616 let inner = if pair.as_rule() == Rule::rank_expr {
617 pair.into_inner()
618 .next()
619 .ok_or_else(|| CompilerError::Parse("rank expression cannot be empty".to_string()))?
620 } else {
621 pair
622 };
623 match inner.as_rule() {
624 Rule::nearest_ordering => parse_nearest_ordering(inner),
625 Rule::bm25_call => parse_bm25_call(inner),
626 other => Err(CompilerError::Parse(format!(
627 "rrf() rank expression must be nearest(...) or bm25(...), got {:?}",
628 other
629 ))),
630 }
631}
632
633fn parse_rrf_call(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
634 let mut args = pair.into_inner();
635 let primary = args
636 .next()
637 .ok_or_else(|| CompilerError::Parse("rrf() missing primary rank expression".to_string()))?;
638 let secondary = args.next().ok_or_else(|| {
639 CompilerError::Parse("rrf() missing secondary rank expression".to_string())
640 })?;
641 let k = args.next().map(parse_expr).transpose()?.map(Box::new);
642 if args.next().is_some() {
643 return Err(CompilerError::Parse(
644 "rrf() accepts at most 3 arguments".to_string(),
645 ));
646 }
647 Ok(Expr::Rrf {
648 primary: Box::new(parse_rank_expr(primary)?),
649 secondary: Box::new(parse_rank_expr(secondary)?),
650 k,
651 })
652}
653
654fn parse_comp_op(pair: pest::iterators::Pair<Rule>) -> Result<CompOp> {
655 match pair.as_str() {
656 "=" => Ok(CompOp::Eq),
657 "!=" => Ok(CompOp::Ne),
658 ">" => Ok(CompOp::Gt),
659 "<" => Ok(CompOp::Lt),
660 ">=" => Ok(CompOp::Ge),
661 "<=" => Ok(CompOp::Le),
662 other => Err(CompilerError::Parse(format!("unknown operator: {}", other))),
663 }
664}
665
666fn parse_filter_op(pair: pest::iterators::Pair<Rule>) -> Result<CompOp> {
667 match pair.as_str() {
668 "contains" => Ok(CompOp::Contains),
669 _ => parse_comp_op(pair),
670 }
671}
672
673fn parse_literal(pair: pest::iterators::Pair<Rule>) -> Result<Literal> {
674 let inner = pair.into_inner().next().unwrap();
675 match inner.as_rule() {
676 Rule::string_lit => Ok(Literal::String(parse_string_lit(inner.as_str())?)),
677 Rule::integer => {
678 let n: i64 = inner
679 .as_str()
680 .parse()
681 .map_err(|e| CompilerError::Parse(format!("invalid integer: {}", e)))?;
682 Ok(Literal::Integer(n))
683 }
684 Rule::float_lit => {
685 let f: f64 = inner
686 .as_str()
687 .parse()
688 .map_err(|e| CompilerError::Parse(format!("invalid float: {}", e)))?;
689 Ok(Literal::Float(f))
690 }
691 Rule::bool_lit => {
692 let b = match inner.as_str() {
693 "true" => true,
694 "false" => false,
695 other => {
696 return Err(CompilerError::Parse(format!(
697 "invalid boolean literal: {}",
698 other
699 )));
700 }
701 };
702 Ok(Literal::Bool(b))
703 }
704 Rule::date_lit => {
705 let date_str = inner
706 .into_inner()
707 .next()
708 .map(|s| parse_string_lit(s.as_str()))
709 .ok_or_else(|| {
710 CompilerError::Parse("date literal requires a string".to_string())
711 })?;
712 Ok(Literal::Date(date_str?))
713 }
714 Rule::datetime_lit => {
715 let dt_str = inner
716 .into_inner()
717 .next()
718 .map(|s| parse_string_lit(s.as_str()))
719 .ok_or_else(|| {
720 CompilerError::Parse("datetime literal requires a string".to_string())
721 })?;
722 Ok(Literal::DateTime(dt_str?))
723 }
724 Rule::list_lit => {
725 let mut items = Vec::new();
726 for item in inner.into_inner() {
727 if item.as_rule() == Rule::literal {
728 items.push(parse_literal(item)?);
729 }
730 }
731 Ok(Literal::List(items))
732 }
733 _ => Err(CompilerError::Parse(format!(
734 "unexpected literal: {:?}",
735 inner.as_rule()
736 ))),
737 }
738}
739
740fn parse_string_lit(raw: &str) -> Result<String> {
741 decode_string_literal(raw)
742}
743
744fn parse_projection(pair: pest::iterators::Pair<Rule>) -> Result<Projection> {
745 let mut inner = pair.into_inner();
746 let expr = parse_expr(inner.next().unwrap())?;
747 let alias = inner.next().map(|p| p.as_str().to_string());
748
749 Ok(Projection { expr, alias })
750}
751
752fn parse_ordering(pair: pest::iterators::Pair<Rule>) -> Result<Ordering> {
753 let mut inner = pair.into_inner();
754 let first = inner
755 .next()
756 .ok_or_else(|| CompilerError::Parse("ordering cannot be empty".to_string()))?;
757 let (expr, descending) = match first.as_rule() {
758 Rule::nearest_ordering => (parse_nearest_ordering(first)?, false),
759 Rule::expr => {
760 let expr = parse_expr(first)?;
761 let direction = inner.next().map(|p| p.as_str().to_string());
762 if matches!(expr, Expr::Nearest { .. }) && direction.is_some() {
763 return Err(CompilerError::Parse(
764 "nearest() ordering does not accept asc/desc modifiers".to_string(),
765 ));
766 }
767 let descending = matches!(direction.as_deref(), Some("desc"));
768 (expr, descending)
769 }
770 other => {
771 return Err(CompilerError::Parse(format!(
772 "unexpected ordering rule: {:?}",
773 other
774 )));
775 }
776 };
777
778 Ok(Ordering { expr, descending })
779}
780
781fn parse_nearest_ordering(pair: pest::iterators::Pair<Rule>) -> Result<Expr> {
782 let mut inner = pair.into_inner();
783 let prop = inner
784 .next()
785 .ok_or_else(|| CompilerError::Parse("nearest() missing property".to_string()))?;
786 let mut prop_parts = prop.into_inner();
787 let var = prop_parts
788 .next()
789 .ok_or_else(|| CompilerError::Parse("nearest() missing variable".to_string()))?
790 .as_str();
791 let variable = var.strip_prefix('$').unwrap_or(var).to_string();
792 let property = prop_parts
793 .next()
794 .ok_or_else(|| CompilerError::Parse("nearest() missing property name".to_string()))?
795 .as_str()
796 .to_string();
797
798 let query = inner
799 .next()
800 .ok_or_else(|| CompilerError::Parse("nearest() missing query expression".to_string()))?;
801 Ok(Expr::Nearest {
802 variable,
803 property,
804 query: Box::new(parse_expr(query)?),
805 })
806}
807
808#[cfg(test)]
809#[path = "parser_tests.rs"]
810mod tests;