Skip to main content

kyu_parser/parser/
statement.rs

1//! Top-level statement parser dispatching to clause and DDL parsers.
2
3use chumsky::prelude::*;
4
5use crate::ast::*;
6use crate::span::Spanned;
7use crate::token::Token;
8
9use super::clause::{
10    reading_clause, return_clause, standalone_call, transaction_statement, updating_clause,
11    with_clause,
12};
13use super::ddl::{
14    alter_table, copy_from, create_node_table, create_rel_table, drop_statement, load_from,
15};
16
17type ParserError = Simple<Token>;
18
19/// Parse a complete query (reading/updating clauses + RETURN).
20fn query_parser() -> impl Parser<Token, Query, Error = ParserError> + Clone {
21    // A query part is: reading clauses + updating clauses + (WITH | RETURN)
22    // Multiple parts are chained by WITH clauses.
23
24    let query_part = reading_clause()
25        .repeated()
26        .then(updating_clause().repeated())
27        .then(
28            return_clause()
29                .map(|proj| (proj, true))
30                .or(with_clause().map(|(proj, _where)| (proj, false))),
31        )
32        .map(|((reading, updating), (projection, is_return))| QueryPart {
33            reading_clauses: reading,
34            updating_clauses: updating,
35            projection: Some(projection),
36            is_return,
37        });
38
39    // A single-clause query with no RETURN (e.g., just CREATE)
40    let update_only = reading_clause()
41        .repeated()
42        .then(updating_clause().repeated().at_least(1))
43        .map(|(reading, updating)| QueryPart {
44            reading_clauses: reading,
45            updating_clauses: updating,
46            projection: None,
47            is_return: false,
48        });
49
50    let parts = query_part
51        .repeated()
52        .at_least(1)
53        .or(update_only.map(|p| vec![p]));
54
55    parts.map(|parts| Query {
56        parts,
57        union_all: vec![],
58    })
59}
60
61/// Parse a top-level statement.
62pub fn statement_parser() -> impl Parser<Token, Spanned<Statement>, Error = ParserError> {
63    let explain = just(Token::Explain)
64        .ignore_then(
65            query_parser()
66                .map(Statement::Query)
67                .map_with_span(|s, span| (s, span)),
68        )
69        .map_with_span(|(inner, _), span| (Statement::Explain(Box::new(inner)), span));
70
71    let profile = just(Token::Profile)
72        .ignore_then(
73            query_parser()
74                .map(Statement::Query)
75                .map_with_span(|s, span| (s, span)),
76        )
77        .map_with_span(|(inner, _), span| (Statement::Profile(Box::new(inner)), span));
78
79    let query = query_parser()
80        .map(Statement::Query)
81        .map_with_span(|s, span| (s, span));
82
83    let create_node = create_node_table()
84        .map(Statement::CreateNodeTable)
85        .map_with_span(|s, span| (s, span));
86
87    let create_rel = create_rel_table()
88        .map(Statement::CreateRelTable)
89        .map_with_span(|s, span| (s, span));
90
91    let drop = drop_statement()
92        .map(Statement::Drop)
93        .map_with_span(|s, span| (s, span));
94
95    let alter = alter_table()
96        .map(Statement::AlterTable)
97        .map_with_span(|s, span| (s, span));
98
99    let copy = copy_from()
100        .map(Statement::CopyFrom)
101        .map_with_span(|s, span| (s, span));
102
103    let load = load_from()
104        .map(Statement::LoadFrom)
105        .map_with_span(|s, span| (s, span));
106
107    let call = standalone_call()
108        .map(Statement::StandaloneCall)
109        .map_with_span(|s, span| (s, span));
110
111    let txn = transaction_statement()
112        .map(Statement::Transaction)
113        .map_with_span(|s, span| (s, span));
114
115    choice((
116        explain,
117        profile,
118        create_node,
119        create_rel,
120        drop,
121        alter,
122        copy,
123        load,
124        call,
125        txn,
126        query,
127    ))
128    .then_ignore(just(Token::Semicolon).or_not())
129    .then_ignore(end())
130}