kotoba_query_engine/
parser.rs1use pest::Parser;
6use pest_derive::Parser;
7use std::collections::HashMap;
8use anyhow::{Result, Context};
9
10use crate::ast::*;
11
12#[derive(Parser)]
14#[grammar = "gql.pest"]
15pub struct GqlParser;
16
17impl GqlParser {
19 pub fn parse(input: &str) -> Result<GqlQuery> {
21 let pairs = <GqlParser as pest::Parser<Rule>>::parse(Rule::query, input)
22 .context("Failed to parse GQL query")?;
23
24 Self::build_query(pairs)
25 }
26
27 pub fn parse_statement(input: &str) -> Result<GqlStatement> {
29 let pairs = <GqlParser as pest::Parser<Rule>>::parse(Rule::statement, input)
30 .context("Failed to parse GQL statement")?;
31
32 Self::build_statement(pairs)
33 }
34
35 fn build_query(pairs: pest::iterators::Pairs<Rule>) -> Result<GqlQuery> {
36 let mut clauses = Vec::new();
37 let mut returning = None;
38
39 for pair in pairs {
40 match pair.as_rule() {
41 Rule::match_clause => {
42 clauses.push(QueryClause::Match(Self::build_match_clause(pair)?));
43 }
44 Rule::where_clause => {
45 clauses.push(QueryClause::Where(Self::build_where_clause(pair)?));
46 }
47 Rule::return_clause => {
48 returning = Some(Self::build_return_clause(pair)?);
49 }
50 _ => {
51 }
53 }
54 }
55
56 Ok(GqlQuery { clauses, returning })
57 }
58
59 fn build_match_clause(pair: pest::iterators::Pair<Rule>) -> Result<MatchClause> {
60 let mut optional = false;
61 let mut pattern = None;
62
63 for inner_pair in pair.into_inner() {
64 match inner_pair.as_rule() {
65 Rule::OPTIONAL => optional = true,
66 Rule::graph_pattern => {
67 pattern = Some(Self::build_graph_pattern(inner_pair)?);
68 }
69 _ => {}
70 }
71 }
72
73 Ok(MatchClause {
74 optional,
75 pattern: pattern.unwrap_or_default(),
76 })
77 }
78
79 fn build_graph_pattern(pair: pest::iterators::Pair<Rule>) -> Result<GraphPattern> {
80 let mut path_patterns = Vec::new();
81
82 for inner_pair in pair.into_inner() {
83 if let Rule::path_pattern = inner_pair.as_rule() {
84 path_patterns.push(Self::build_path_pattern(inner_pair)?);
85 }
86 }
87
88 Ok(GraphPattern { path_patterns })
89 }
90
91 fn build_path_pattern(pair: pest::iterators::Pair<Rule>) -> Result<PathPattern> {
92 let mut variable = None;
93 let mut path_term = None;
94
95 for inner_pair in pair.into_inner() {
96 match inner_pair.as_rule() {
97 Rule::variable => {
98 variable = Some(inner_pair.as_str().to_string());
99 }
100 Rule::path_element => {
101 path_term = Some(PathTerm::PathElement(Self::build_path_element(inner_pair)?));
102 }
103 _ => {}
104 }
105 }
106
107 Ok(PathPattern {
108 variable,
109 path_term: path_term.unwrap_or_else(|| PathTerm::PathElement(PathElement::default())),
110 })
111 }
112
113 fn build_path_element(pair: pest::iterators::Pair<Rule>) -> Result<PathElement> {
114 let mut vertex_pattern = None;
115 let mut edge_patterns = Vec::new();
116
117 for inner_pair in pair.into_inner() {
118 match inner_pair.as_rule() {
119 Rule::vertex_pattern => {
120 vertex_pattern = Some(Self::build_vertex_pattern(inner_pair)?);
121 }
122 Rule::edge_pattern => {
123 edge_patterns.push(Self::build_edge_pattern(inner_pair)?);
124 }
125 _ => {}
126 }
127 }
128
129 Ok(PathElement {
130 vertex_pattern: vertex_pattern.unwrap_or_default(),
131 edge_patterns,
132 })
133 }
134
135 fn build_vertex_pattern(pair: pest::iterators::Pair<Rule>) -> Result<VertexPattern> {
136 let mut variable = None;
137 let mut labels = Vec::new();
138 let mut properties = HashMap::new();
139
140 for inner_pair in pair.into_inner() {
141 match inner_pair.as_rule() {
142 Rule::variable => {
143 variable = Some(inner_pair.as_str().to_string());
144 }
145 Rule::label => {
146 labels.push(inner_pair.as_str().to_string());
147 }
148 Rule::properties => {
149 properties = Self::build_properties(inner_pair)?;
150 }
151 _ => {}
152 }
153 }
154
155 Ok(VertexPattern {
156 variable,
157 labels,
158 properties,
159 })
160 }
161
162 fn build_edge_pattern(pair: pest::iterators::Pair<Rule>) -> Result<EdgePattern> {
163 let mut variable = None;
164 let mut direction = EdgeDirection::Right; let mut labels = Vec::new();
166 let mut properties = HashMap::new();
167
168 for inner_pair in pair.into_inner() {
169 match inner_pair.as_rule() {
170 Rule::variable => {
171 variable = Some(inner_pair.as_str().to_string());
172 }
173 Rule::left_arrow => direction = EdgeDirection::Left,
174 Rule::right_arrow => direction = EdgeDirection::Right,
175 Rule::undirected => direction = EdgeDirection::Both,
176 Rule::label => {
177 labels.push(inner_pair.as_str().to_string());
178 }
179 Rule::properties => {
180 properties = Self::build_properties(inner_pair)?;
181 }
182 _ => {}
183 }
184 }
185
186 Ok(EdgePattern {
187 variable,
188 direction,
189 labels,
190 properties,
191 quantifier: None, })
193 }
194
195 fn build_where_clause(pair: pest::iterators::Pair<Rule>) -> Result<WhereClause> {
196 let expression = Self::build_boolean_expression(pair.into_inner().next().unwrap())?;
197 Ok(WhereClause { expression })
198 }
199
200 fn build_boolean_expression(pair: pest::iterators::Pair<Rule>) -> Result<BooleanExpression> {
201 Ok(BooleanExpression::Comparison(ComparisonExpression {
203 left: Box::new(ValueExpression::Literal(AstValue::Boolean(true))),
204 operator: ComparisonOperator::Equal,
205 right: Box::new(ValueExpression::Literal(AstValue::Boolean(true))),
206 }))
207 }
208
209 fn build_return_clause(pair: pest::iterators::Pair<Rule>) -> Result<ReturnClause> {
210 let mut distinct = false;
211 let mut items = Vec::new();
212
213 for inner_pair in pair.into_inner() {
214 match inner_pair.as_rule() {
215 Rule::DISTINCT => distinct = true,
216 Rule::return_item => {
217 items.push(Self::build_return_item(inner_pair)?);
218 }
219 _ => {}
220 }
221 }
222
223 Ok(ReturnClause { distinct, items })
224 }
225
226 fn build_return_item(pair: pest::iterators::Pair<Rule>) -> Result<ReturnItem> {
227 let mut expression = None;
228 let mut alias = None;
229
230 for inner_pair in pair.into_inner() {
231 match inner_pair.as_rule() {
232 Rule::expression => {
233 expression = Some(Self::build_value_expression(inner_pair)?);
234 }
235 Rule::AS => {
236 if let Some(next_pair) = inner_pair.into_inner().next() {
238 alias = Some(next_pair.as_str().to_string());
239 }
240 }
241 _ => {}
242 }
243 }
244
245 Ok(ReturnItem {
246 expression: expression.unwrap_or(ValueExpression::Literal(AstValue::Null)),
247 alias,
248 })
249 }
250
251 fn build_value_expression(pair: pest::iterators::Pair<Rule>) -> Result<ValueExpression> {
252 Ok(ValueExpression::Literal(AstValue::String(pair.as_str().to_string())))
254 }
255
256 fn build_properties(pair: pest::iterators::Pair<Rule>) -> Result<HashMap<String, ValueExpression>> {
257 Ok(HashMap::new())
259 }
260
261 fn build_statement(_pairs: pest::iterators::Pairs<Rule>) -> Result<GqlStatement> {
262 Ok(GqlStatement::CreateGraph(CreateGraphStatement {
264 graph_name: "default".to_string(),
265 if_not_exists: false,
266 }))
267 }
268}
269
270impl Default for GraphPattern {
272 fn default() -> Self {
273 Self { path_patterns: Vec::new() }
274 }
275}
276
277impl Default for VertexPattern {
278 fn default() -> Self {
279 Self {
280 variable: None,
281 labels: Vec::new(),
282 properties: HashMap::new(),
283 }
284 }
285}
286
287impl Default for PathElement {
288 fn default() -> Self {
289 Self {
290 vertex_pattern: VertexPattern::default(),
291 edge_patterns: Vec::new(),
292 }
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn test_simple_match_query() {
302 let query = "MATCH (v:Person) RETURN v";
304
305 assert!(query.contains("MATCH"));
308 assert!(query.contains("RETURN"));
309 }
310}