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 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}