A Claude-written graph database in Rust. Use at your own risk.
1use nom::{
2 IResult,
3 branch::alt,
4 combinator::{map, opt, value},
5 multi::{many0, many1, separated_list0, separated_list1},
6 sequence::{preceded, terminated, tuple, delimited, pair},
7};
8use std::collections::HashMap;
9use crate::core::PropertyValue;
10use crate::cypher::ast::*;
11use crate::cypher::lexer::{Token, lex};
12
13pub fn parse_cypher(input: &str) -> Result<CypherQuery, String> {
14 let (_, tokens) = lex(input).map_err(|e| format!("Lexing error: {:?}", e))?;
15 let (remaining, query) = cypher_query(&tokens).map_err(|e| format!("Parse error: {:?}", e))?;
16
17 if !remaining.is_empty() {
18 return Err(format!("Unexpected tokens after query: {:?}", remaining));
19 }
20
21 Ok(query)
22}
23
24type Tokens<'a> = &'a [Token];
25
26fn cypher_query(input: Tokens) -> IResult<Tokens, CypherQuery> {
27 alt((
28 // Handle compound queries (MATCH ... RETURN ...)
29 map(
30 tuple((
31 match_clause,
32 opt(return_clause),
33 )),
34 |(match_clause, return_clause)| {
35 if let Some(return_clause) = return_clause {
36 CypherQuery::Compound(vec![
37 CypherQuery::Match(match_clause),
38 CypherQuery::Return(return_clause),
39 ])
40 } else {
41 CypherQuery::Match(match_clause)
42 }
43 }
44 ),
45 map(create_clause, CypherQuery::Create),
46 map(return_clause, CypherQuery::Return),
47 map(
48 separated_list1(
49 |i| expect_token(i, &Token::Semicolon),
50 alt((
51 map(match_clause, CypherQuery::Match),
52 map(create_clause, CypherQuery::Create),
53 map(return_clause, CypherQuery::Return),
54 ))
55 ),
56 CypherQuery::Compound
57 ),
58 ))(input)
59}
60
61fn match_clause(input: Tokens) -> IResult<Tokens, MatchClause> {
62 map(
63 tuple((
64 opt(|i| expect_token(i, &Token::Optional)),
65 |i| expect_token(i, &Token::Match),
66 pattern,
67 opt(where_clause),
68 )),
69 |(optional, _, pattern, where_clause)| MatchClause {
70 pattern,
71 where_clause,
72 optional: optional.is_some(),
73 },
74 )(input)
75}
76
77fn create_clause(input: Tokens) -> IResult<Tokens, CreateClause> {
78 map(
79 preceded(
80 |i| expect_token(i, &Token::Create),
81 pattern,
82 ),
83 |pattern| CreateClause { pattern },
84 )(input)
85}
86
87fn return_clause(input: Tokens) -> IResult<Tokens, ReturnClause> {
88 map(
89 tuple((
90 |i| expect_token(i, &Token::Return),
91 opt(|i| expect_token(i, &Token::Distinct)),
92 separated_list1(|i| expect_token(i, &Token::Comma), return_item),
93 opt(order_by),
94 opt(preceded(|i| expect_token(i, &Token::Skip), expression)),
95 opt(preceded(|i| expect_token(i, &Token::Limit), expression)),
96 )),
97 |(_, distinct, items, order_by, skip, limit)| ReturnClause {
98 distinct: distinct.is_some(),
99 items,
100 order_by,
101 skip,
102 limit,
103 },
104 )(input)
105}
106
107fn return_item(input: Tokens) -> IResult<Tokens, ReturnItem> {
108 map(
109 tuple((
110 expression,
111 opt(preceded(|i| expect_token(i, &Token::As), identifier)),
112 )),
113 |(expression, alias)| ReturnItem { expression, alias },
114 )(input)
115}
116
117fn order_by(input: Tokens) -> IResult<Tokens, Vec<OrderByItem>> {
118 preceded(
119 tuple((
120 |i| expect_token(i, &Token::Order),
121 |i| expect_token(i, &Token::By),
122 )),
123 separated_list1(|i| expect_token(i, &Token::Comma), order_by_item),
124 )(input)
125}
126
127fn order_by_item(input: Tokens) -> IResult<Tokens, OrderByItem> {
128 map(
129 tuple((
130 expression,
131 opt(alt((
132 value(true, |i| expect_token(i, &Token::Asc)),
133 value(false, |i| expect_token(i, &Token::Desc)),
134 ))),
135 )),
136 |(expression, ascending)| OrderByItem {
137 expression,
138 ascending: ascending.unwrap_or(true),
139 },
140 )(input)
141}
142
143fn where_clause(input: Tokens) -> IResult<Tokens, Expression> {
144 preceded(
145 |i| expect_token(i, &Token::Where),
146 expression,
147 )(input)
148}
149
150fn pattern(input: Tokens) -> IResult<Tokens, Pattern> {
151 map(
152 separated_list1(|i| expect_token(i, &Token::Comma), pattern_part),
153 |parts| Pattern {
154 elements: parts.into_iter().flatten().collect(),
155 },
156 )(input)
157}
158
159fn pattern_part(input: Tokens) -> IResult<Tokens, Vec<PatternElement>> {
160 many1(pattern_element)(input)
161}
162
163fn pattern_element(input: Tokens) -> IResult<Tokens, PatternElement> {
164 alt((
165 map(node_pattern, PatternElement::Node),
166 map(relationship_pattern, PatternElement::Relationship),
167 ))(input)
168}
169
170fn node_pattern(input: Tokens) -> IResult<Tokens, NodePattern> {
171 delimited(
172 |i| expect_token(i, &Token::LeftParen),
173 map(
174 tuple((
175 opt(identifier),
176 many0(preceded(|i| expect_token(i, &Token::Colon), identifier)),
177 opt(properties_map),
178 )),
179 |(variable, labels, properties)| NodePattern {
180 variable,
181 labels,
182 properties,
183 },
184 ),
185 |i| expect_token(i, &Token::RightParen),
186 )(input)
187}
188
189fn relationship_pattern(input: Tokens) -> IResult<Tokens, RelationshipPattern> {
190 alt((
191 map(
192 tuple((
193 |i| expect_token(i, &Token::Minus),
194 delimited(
195 |i| expect_token(i, &Token::LeftBracket),
196 relationship_detail,
197 |i| expect_token(i, &Token::RightBracket),
198 ),
199 |i| expect_token(i, &Token::Arrow),
200 )),
201 |(_, detail, _)| RelationshipPattern {
202 direction: RelationshipDirection::Outgoing,
203 ..detail
204 },
205 ),
206 map(
207 tuple((
208 |i| expect_token(i, &Token::LeftArrow),
209 delimited(
210 |i| expect_token(i, &Token::LeftBracket),
211 relationship_detail,
212 |i| expect_token(i, &Token::RightBracket),
213 ),
214 |i| expect_token(i, &Token::Minus),
215 )),
216 |(_, detail, _)| RelationshipPattern {
217 direction: RelationshipDirection::Incoming,
218 ..detail
219 },
220 ),
221 ))(input)
222}
223
224fn relationship_detail(input: Tokens) -> IResult<Tokens, RelationshipPattern> {
225 map(
226 tuple((
227 opt(identifier),
228 many0(preceded(|i| expect_token(i, &Token::Colon), identifier)),
229 opt(properties_map),
230 )),
231 |(variable, types, properties)| RelationshipPattern {
232 variable,
233 types,
234 properties,
235 direction: RelationshipDirection::Both,
236 length: None,
237 },
238 )(input)
239}
240
241fn properties_map(input: Tokens) -> IResult<Tokens, HashMap<String, Expression>> {
242 delimited(
243 |i| expect_token(i, &Token::LeftBrace),
244 map(
245 separated_list0(
246 |i| expect_token(i, &Token::Comma),
247 tuple((
248 identifier,
249 |i| expect_token(i, &Token::Colon),
250 expression,
251 )),
252 ),
253 |items| items.into_iter().map(|(k, _, v)| (k, v)).collect(),
254 ),
255 |i| expect_token(i, &Token::RightBrace),
256 )(input)
257}
258
259fn expression(input: Tokens) -> IResult<Tokens, Expression> {
260 or_expression(input)
261}
262
263fn or_expression(input: Tokens) -> IResult<Tokens, Expression> {
264 let (input, left) = and_expression(input)?;
265
266 let (input, rights) = many0(preceded(
267 |i| expect_token(i, &Token::Or),
268 and_expression,
269 ))(input)?;
270
271 Ok((input, rights.into_iter().fold(left, |acc, right| {
272 Expression::Or(Box::new(acc), Box::new(right))
273 })))
274}
275
276fn and_expression(input: Tokens) -> IResult<Tokens, Expression> {
277 let (input, left) = comparison_expression(input)?;
278
279 let (input, rights) = many0(preceded(
280 |i| expect_token(i, &Token::And),
281 comparison_expression,
282 ))(input)?;
283
284 Ok((input, rights.into_iter().fold(left, |acc, right| {
285 Expression::And(Box::new(acc), Box::new(right))
286 })))
287}
288
289fn comparison_expression(input: Tokens) -> IResult<Tokens, Expression> {
290 let (input, left) = additive_expression(input)?;
291
292 if let Ok((input, _)) = expect_token(input, &Token::Equal) {
293 let (input, right) = additive_expression(input)?;
294 return Ok((input, Expression::Equal(Box::new(left), Box::new(right))));
295 }
296
297 Ok((input, left))
298}
299
300fn additive_expression(input: Tokens) -> IResult<Tokens, Expression> {
301 let (input, left) = multiplicative_expression(input)?;
302
303 let (input, ops) = many0(tuple((
304 alt((
305 value(true, |i| expect_token(i, &Token::Plus)),
306 value(false, |i| expect_token(i, &Token::Minus)),
307 )),
308 multiplicative_expression,
309 )))(input)?;
310
311 Ok((input, ops.into_iter().fold(left, |acc, (is_add, right)| {
312 if is_add {
313 Expression::Add(Box::new(acc), Box::new(right))
314 } else {
315 Expression::Subtract(Box::new(acc), Box::new(right))
316 }
317 })))
318}
319
320fn multiplicative_expression(input: Tokens) -> IResult<Tokens, Expression> {
321 let (input, left) = primary_expression(input)?;
322 Ok((input, left))
323}
324
325fn primary_expression(input: Tokens) -> IResult<Tokens, Expression> {
326 alt((
327 // Try property access first (a.name)
328 map(
329 tuple((
330 identifier,
331 |i| expect_token(i, &Token::Dot),
332 identifier,
333 )),
334 |(obj, _, prop)| Expression::Property(PropertyExpression {
335 expression: Box::new(Expression::Variable(obj)),
336 property: prop,
337 }),
338 ),
339 // Then literals and simple variables
340 map(literal, |lit| Expression::Literal(lit)),
341 map(identifier, Expression::Variable),
342 delimited(
343 |i| expect_token(i, &Token::LeftParen),
344 expression,
345 |i| expect_token(i, &Token::RightParen),
346 ),
347 ))(input)
348}
349
350fn literal(input: Tokens) -> IResult<Tokens, PropertyValue> {
351 if input.is_empty() {
352 return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Eof)));
353 }
354
355 match &input[0] {
356 Token::Integer(n) => Ok((&input[1..], PropertyValue::Integer(*n))),
357 Token::Float(f) => Ok((&input[1..], PropertyValue::Float(*f))),
358 Token::String(s) => Ok((&input[1..], PropertyValue::String(s.clone()))),
359 Token::True => Ok((&input[1..], PropertyValue::Boolean(true))),
360 Token::False => Ok((&input[1..], PropertyValue::Boolean(false))),
361 Token::Null => Ok((&input[1..], PropertyValue::Null)),
362 _ => Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Tag))),
363 }
364}
365
366fn identifier(input: Tokens) -> IResult<Tokens, String> {
367 if input.is_empty() {
368 return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Eof)));
369 }
370
371 match &input[0] {
372 Token::Identifier(s) => Ok((&input[1..], s.clone())),
373 _ => Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Tag))),
374 }
375}
376
377fn expect_token<'a>(input: &'a [Token], expected: &Token) -> IResult<&'a [Token], &'a Token> {
378 if input.is_empty() {
379 return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Eof)));
380 }
381
382 if std::mem::discriminant(&input[0]) == std::mem::discriminant(expected) {
383 Ok((&input[1..], &input[0]))
384 } else {
385 Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Tag)))
386 }
387}