Skip to main content

marsdb_query/
parser.rs

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
23fn parse_statement(pair: Pair<Rule>) -> Result<Statement, QueryError> {
24    let inner = pair.into_inner().next().expect("statement has one child");
25    match inner.as_rule() {
26        Rule::create_stmt => parse_create_stmt(inner),
27        Rule::match_stmt => parse_match_stmt(inner),
28        r => unreachable!("unexpected statement child rule {r:?}"),
29    }
30}
31
32fn parse_create_stmt(pair: Pair<Rule>) -> Result<Statement, QueryError> {
33    let patterns = pair
34        .into_inner()
35        .filter(|p| p.as_rule() == Rule::pattern)
36        .map(parse_pattern)
37        .collect::<Result<Vec<_>, _>>()?;
38    Ok(Statement::Create(patterns))
39}
40
41fn parse_match_stmt(pair: Pair<Rule>) -> Result<Statement, QueryError> {
42    let mut pattern = None;
43    let mut where_clause = None;
44    let mut tail = None;
45    let mut limit = None;
46    for p in pair.into_inner() {
47        match p.as_rule() {
48            Rule::pattern => pattern = Some(parse_pattern(p)?),
49            Rule::where_clause => {
50                let expr_pair = p.into_inner().next().expect("WHERE has an expr");
51                where_clause = Some(parse_expr(expr_pair)?);
52            }
53            Rule::tail_clause => tail = Some(parse_tail_clause(p)?),
54            Rule::limit_clause => {
55                let n_pair = p.into_inner().next().expect("LIMIT has an int_literal");
56                let n = n_pair
57                    .as_str()
58                    .parse::<i64>()
59                    .map_err(|_| QueryError::Parse("invalid LIMIT value".into()))?;
60                limit = Some(n);
61            }
62            r => unreachable!("unexpected match_stmt child rule {r:?}"),
63        }
64    }
65    Ok(Statement::Match {
66        pattern: pattern.ok_or_else(|| QueryError::Parse("MATCH requires a pattern".into()))?,
67        where_clause,
68        tail: tail
69            .ok_or_else(|| QueryError::Parse("MATCH requires RETURN/DELETE/SET".into()))?,
70        limit,
71    })
72}
73
74fn parse_tail_clause(pair: Pair<Rule>) -> Result<Tail, QueryError> {
75    let inner = pair.into_inner().next().expect("tail_clause has one child");
76    match inner.as_rule() {
77        Rule::return_clause => {
78            let items = inner
79                .into_inner()
80                .filter(|p| p.as_rule() == Rule::return_item)
81                .map(parse_return_item)
82                .collect::<Result<Vec<_>, _>>()?;
83            Ok(Tail::Return(items))
84        }
85        Rule::detach_delete_clause => {
86            let vars = inner
87                .into_inner()
88                .filter(|p| p.as_rule() == Rule::identifier)
89                .map(|p| p.as_str().to_string())
90                .collect();
91            Ok(Tail::DetachDelete(vars))
92        }
93        Rule::delete_clause => {
94            let vars = inner
95                .into_inner()
96                .filter(|p| p.as_rule() == Rule::identifier)
97                .map(|p| p.as_str().to_string())
98                .collect();
99            Ok(Tail::Delete(vars))
100        }
101        Rule::set_clause => {
102            let items = inner
103                .into_inner()
104                .filter(|p| p.as_rule() == Rule::set_item)
105                .map(parse_set_item)
106                .collect::<Result<Vec<_>, _>>()?;
107            Ok(Tail::Set(items))
108        }
109        r => unreachable!("unexpected tail_clause child rule {r:?}"),
110    }
111}
112
113fn parse_set_item(pair: Pair<Rule>) -> Result<(PropAccess, Literal), QueryError> {
114    let mut inner = pair.into_inner();
115    let prop_access_pair = inner.next().expect("set_item has a prop_access");
116    let literal_pair = inner.next().expect("set_item has a literal");
117    Ok((parse_prop_access(prop_access_pair), parse_literal(literal_pair)?))
118}
119
120fn parse_return_item(pair: Pair<Rule>) -> Result<ReturnItem, QueryError> {
121    let mut inner = pair.into_inner();
122    let expr_pair = inner.next().expect("return_item has a return_expr");
123    let expr = parse_return_expr(expr_pair)?;
124    let alias = inner.next().map(|p| p.as_str().to_string());
125    Ok(ReturnItem { expr, alias })
126}
127
128fn parse_return_expr(pair: Pair<Rule>) -> Result<ReturnExpr, QueryError> {
129    let inner = pair.into_inner().next().expect("return_expr has one child");
130    match inner.as_rule() {
131        Rule::prop_access => Ok(ReturnExpr::Prop(parse_prop_access(inner))),
132        Rule::identifier => Ok(ReturnExpr::Var(inner.as_str().to_string())),
133        Rule::literal => Ok(ReturnExpr::Lit(parse_literal(inner)?)),
134        r => unreachable!("unexpected return_expr child rule {r:?}"),
135    }
136}
137
138fn parse_prop_access(pair: Pair<Rule>) -> PropAccess {
139    let mut inner = pair.into_inner();
140    let var = inner.next().expect("prop_access has a var").as_str().to_string();
141    let prop = inner.next().expect("prop_access has a prop").as_str().to_string();
142    PropAccess { var, prop }
143}
144
145fn parse_pattern(pair: Pair<Rule>) -> Result<Pattern, QueryError> {
146    let mut inner = pair.into_inner();
147    let start = parse_node_pattern(inner.next().expect("pattern has a start node"))?;
148    let mut hops = Vec::new();
149    loop {
150        let Some(rel_pair) = inner.next() else { break };
151        let node_pair = inner
152            .next()
153            .ok_or_else(|| QueryError::Parse("dangling relationship in pattern".into()))?;
154        hops.push((parse_rel_pattern(rel_pair)?, parse_node_pattern(node_pair)?));
155    }
156    Ok(Pattern { start, hops })
157}
158
159fn parse_node_pattern(pair: Pair<Rule>) -> Result<NodePattern, QueryError> {
160    let mut var = None;
161    let mut label = None;
162    let mut props = Vec::new();
163    for p in pair.into_inner() {
164        match p.as_rule() {
165            Rule::node_var => var = Some(p.as_str().to_string()),
166            Rule::node_label => {
167                label = Some(p.into_inner().next().expect("node_label has an identifier").as_str().to_string())
168            }
169            Rule::prop_map => props = parse_prop_map(p)?,
170            r => unreachable!("unexpected node_pattern child rule {r:?}"),
171        }
172    }
173    Ok(NodePattern { var, label, props })
174}
175
176fn parse_rel_pattern(pair: Pair<Rule>) -> Result<RelPattern, QueryError> {
177    let inner = pair.into_inner().next().expect("rel_pattern has one child");
178    let direction = match inner.as_rule() {
179        Rule::rel_right => RelDirection::Right,
180        Rule::rel_left => RelDirection::Left,
181        r => unreachable!("unexpected rel_pattern child rule {r:?}"),
182    };
183    let mut var = None;
184    let mut rel_type = None;
185    let mut props = Vec::new();
186    for p in inner.into_inner() {
187        match p.as_rule() {
188            Rule::rel_var => var = Some(p.as_str().to_string()),
189            Rule::rel_type => {
190                rel_type = Some(p.into_inner().next().expect("rel_type has an identifier").as_str().to_string())
191            }
192            Rule::prop_map => props = parse_prop_map(p)?,
193            r => unreachable!("unexpected rel_right/rel_left child rule {r:?}"),
194        }
195    }
196    Ok(RelPattern {
197        var,
198        rel_type,
199        props,
200        direction,
201    })
202}
203
204fn parse_prop_map(pair: Pair<Rule>) -> Result<Vec<(String, Literal)>, QueryError> {
205    pair.into_inner()
206        .filter(|p| p.as_rule() == Rule::prop_kv)
207        .map(|p| {
208            let mut inner = p.into_inner();
209            let key = inner.next().expect("prop_kv has a key").as_str().to_string();
210            let value = parse_literal(inner.next().expect("prop_kv has a value"))?;
211            Ok((key, value))
212        })
213        .collect()
214}
215
216fn parse_literal(pair: Pair<Rule>) -> Result<Literal, QueryError> {
217    let inner = pair.into_inner().next().expect("literal has one child");
218    Ok(match inner.as_rule() {
219        Rule::int_literal => Literal::Int(
220            inner
221                .as_str()
222                .parse()
223                .map_err(|_| QueryError::Parse("invalid integer literal".into()))?,
224        ),
225        Rule::float_literal => Literal::Float(
226            inner
227                .as_str()
228                .parse()
229                .map_err(|_| QueryError::Parse("invalid float literal".into()))?,
230        ),
231        Rule::string_literal => {
232            let s = inner.as_str();
233            Literal::String(s[1..s.len() - 1].to_string())
234        }
235        Rule::bool_literal => Literal::Bool(inner.as_str().eq_ignore_ascii_case("true")),
236        Rule::null_literal => Literal::Null,
237        r => unreachable!("unexpected literal child rule {r:?}"),
238    })
239}
240
241fn parse_expr(pair: Pair<Rule>) -> Result<Expr, QueryError> {
242    // expr = { or_expr }
243    parse_or_expr(pair.into_inner().next().expect("expr has an or_expr"))
244}
245
246fn parse_or_expr(pair: Pair<Rule>) -> Result<Expr, QueryError> {
247    let mut parts = pair.into_inner();
248    let mut acc = parse_and_expr(parts.next().expect("or_expr has at least one and_expr"))?;
249    for rest in parts {
250        acc = Expr::Or(Box::new(acc), Box::new(parse_and_expr(rest)?));
251    }
252    Ok(acc)
253}
254
255fn parse_and_expr(pair: Pair<Rule>) -> Result<Expr, QueryError> {
256    let mut parts = pair.into_inner();
257    let mut acc = parse_unary_expr(parts.next().expect("and_expr has at least one unary_expr"))?;
258    for rest in parts {
259        acc = Expr::And(Box::new(acc), Box::new(parse_unary_expr(rest)?));
260    }
261    Ok(acc)
262}
263
264fn parse_unary_expr(pair: Pair<Rule>) -> Result<Expr, QueryError> {
265    let inner = pair.into_inner().next().expect("unary_expr has one child");
266    match inner.as_rule() {
267        Rule::unary_expr => Ok(Expr::Not(Box::new(parse_unary_expr(inner)?))),
268        Rule::comparison => parse_comparison(inner),
269        Rule::expr => parse_expr(inner),
270        r => unreachable!("unexpected unary_expr child rule {r:?}"),
271    }
272}
273
274fn parse_comparison(pair: Pair<Rule>) -> Result<Expr, QueryError> {
275    let mut inner = pair.into_inner();
276    let prop_access = parse_prop_access(inner.next().expect("comparison has a prop_access"));
277    let op_pair = inner.next().expect("comparison has a compare_op");
278    let op = match op_pair.as_str() {
279        "=" => CompareOp::Eq,
280        "<>" => CompareOp::Ne,
281        "<" => CompareOp::Lt,
282        "<=" => CompareOp::Le,
283        ">" => CompareOp::Gt,
284        ">=" => CompareOp::Ge,
285        other => unreachable!("unexpected compare_op {other:?}"),
286    };
287    let literal = parse_literal(inner.next().expect("comparison has a literal"))?;
288    Ok(Expr::Compare(prop_access, op, literal))
289}