Skip to main content

akar_parser/parser/
mod.rs

1//! Parser implementation — converts Cypher query text to AST using pest.rs PEG grammar.
2//!
3//! This module has been modularized. The actual parsing functions are in:
4//! - `parser/ddl.rs` — DDL statements (CREATE/DROP TABLE, INDEX, SEQUENCE, ALTER, COPY, etc.)
5//! - `parser/dml.rs` — DML statements (MATCH, RETURN, WHERE, CREATE, DELETE, SET, MERGE, FOREACH, etc.)
6//! - `parser/expression.rs` — Expression parsing
7
8use crate::ast::*;
9use pest::Parser;
10
11#[derive(pest_derive::Parser)]
12#[grammar = "cypher.pest"]
13pub struct CypherParser;
14
15pub fn parse(input: &str) -> Result<Statement, String> {
16    let trimmed = input.trim();
17
18    // Handle EXPLAIN prefix before PEG parsing to avoid grammar recursion
19    if let Some(rest) = trimmed.strip_prefix("EXPLAIN").map(|s| s.trim()) {
20        let (explain_type, inner_sql) = if let Some(rest) = rest.strip_prefix("LOGICAL").map(|s| s.trim()) {
21            (ExplainType::LogicalPlan, rest)
22        } else if let Some(rest) = rest.strip_prefix("PROFILE").map(|s| s.trim()) {
23            (ExplainType::Profile, rest)
24        } else {
25            (ExplainType::PhysicalPlan, rest)
26        };
27        let inner_stmt = parse(inner_sql)?;
28        return Ok(Statement::Explain(ExplainStatement::new(inner_stmt, explain_type)));
29    }
30
31    let mut pairs = CypherParser::parse(Rule::akar_query, trimmed).map_err(|e| format!("Parse error: {e}"))?;
32    let akar_query_pair = pairs.next().ok_or("Empty input")?;
33    let statement_pair = akar_query_pair.into_inner().next().ok_or("No statement in query")?;
34    parse_statement(statement_pair)
35}
36
37fn parse_statement(pair: pest::iterators::Pair<Rule>) -> Result<Statement, String> {
38    // Unwrap the outer statement rule to its inner content
39    let inner = if pair.as_rule() == Rule::statement {
40        pair.into_inner().next().ok_or("Empty statement")?
41    } else {
42        pair
43    };
44    match inner.as_rule() {
45        Rule::ddl_statement => {
46            let ddl_inner = inner.into_inner().next().ok_or("Empty DDL")?;
47            ddl::parse_ddl(ddl_inner)
48        }
49        Rule::create_dml_statement => {
50            let inner_clone = inner.clone();
51            let patterns = dml::parse_patterns(inner_clone)?;
52            Ok(Statement::CreateDml(CreateClause { patterns }))
53        }
54        Rule::query_statement => {
55            let inner_clone = inner.clone();
56            let child_rules: Vec<_> = inner_clone.into_inner().map(|c| c.as_rule()).collect();
57            if child_rules.contains(&Rule::merge_clause) {
58                let merge = dml::parse_merge(inner)?;
59                Ok(merge)
60            } else {
61                let query = dml::parse_query_pairs(inner)?;
62                Ok(Statement::Query(query))
63            }
64        }
65        Rule::union_statement => {
66            let inner_clone = inner.clone();
67            ddl::parse_ddl(inner_clone)
68        }
69        Rule::call_statement => {
70            let call = dml::parse_call(inner)?;
71            Ok(Statement::StandaloneCall(call))
72        }
73        Rule::export_database => ddl::parse_export_database(inner),
74        Rule::import_database => ddl::parse_import_database(inner),
75        Rule::analyze_statement => ddl::parse_analyze(inner),
76        Rule::transaction_statement => ddl::parse_transaction(inner),
77        Rule::extension_statement => ddl::parse_extension(inner),
78        Rule::multi_db_statement => {
79            let db_inner = inner.into_inner().next().ok_or("Empty multi-DB statement")?;
80            ddl::parse_multi_db(db_inner)
81        }
82        _ => Err(format!("Unexpected rule: {:?}", inner.as_rule())),
83    }
84}
85
86mod ddl;
87mod dml;
88pub mod expression;