Skip to main content

kyu_parser/
ast.rs

1//! AST node types produced by the Cypher parser.
2
3use crate::span::{Span, Spanned};
4use smol_str::SmolStr;
5
6// =============================================================================
7// Top-Level Statement
8// =============================================================================
9
10/// A complete parsed statement.
11#[derive(Clone, Debug)]
12pub enum Statement {
13    Query(Query),
14    CreateNodeTable(CreateNodeTable),
15    CreateRelTable(CreateRelTable),
16    Drop(DropStatement),
17    AlterTable(AlterTable),
18    CopyFrom(CopyFrom),
19    CopyTo(CopyTo),
20    LoadFrom(LoadFrom),
21    StandaloneCall(StandaloneCall),
22    Transaction(TransactionStatement),
23    ExportDatabase(ExportDatabase),
24    ImportDatabase(ImportDatabase),
25    AttachDatabase(AttachDatabase),
26    UseDatabase(UseDatabase),
27    CreateMacro(CreateMacro),
28    InstallExtension(InstallExtension),
29    LoadExtension(LoadExtension),
30    Explain(Box<Statement>),
31    Profile(Box<Statement>),
32}
33
34// =============================================================================
35// Query
36// =============================================================================
37
38#[derive(Clone, Debug)]
39pub struct Query {
40    pub parts: Vec<QueryPart>,
41    /// UNION / UNION ALL chains: (is_all, query).
42    pub union_all: Vec<(bool, Query)>,
43}
44
45#[derive(Clone, Debug)]
46pub struct QueryPart {
47    pub reading_clauses: Vec<ReadingClause>,
48    pub updating_clauses: Vec<UpdatingClause>,
49    pub projection: Option<ProjectionBody>,
50    /// `true` = RETURN, `false` = WITH.
51    pub is_return: bool,
52}
53
54// =============================================================================
55// Reading Clauses
56// =============================================================================
57
58#[derive(Clone, Debug)]
59pub enum ReadingClause {
60    Match(MatchClause),
61    Unwind(UnwindClause),
62    InQueryCall(InQueryCall),
63    LoadFrom(LoadFrom),
64}
65
66#[derive(Clone, Debug)]
67pub struct MatchClause {
68    pub is_optional: bool,
69    pub patterns: Vec<Pattern>,
70    pub where_clause: Option<Spanned<Expression>>,
71}
72
73#[derive(Clone, Debug)]
74pub struct UnwindClause {
75    pub expression: Spanned<Expression>,
76    pub alias: Spanned<SmolStr>,
77}
78
79#[derive(Clone, Debug)]
80pub struct InQueryCall {
81    pub procedure: Spanned<SmolStr>,
82    pub args: Vec<Spanned<Expression>>,
83    pub yield_items: Vec<Spanned<SmolStr>>,
84}
85
86#[derive(Clone, Debug)]
87pub struct LoadFrom {
88    pub source: Spanned<Expression>,
89}
90
91// =============================================================================
92// Patterns
93// =============================================================================
94
95#[derive(Clone, Debug)]
96pub struct Pattern {
97    pub variable: Option<Spanned<SmolStr>>,
98    pub elements: Vec<PatternElement>,
99}
100
101#[derive(Clone, Debug)]
102pub enum PatternElement {
103    Node(NodePattern),
104    Relationship(RelationshipPattern),
105}
106
107#[derive(Clone, Debug)]
108pub struct NodePattern {
109    pub variable: Option<Spanned<SmolStr>>,
110    pub labels: Vec<Spanned<SmolStr>>,
111    pub properties: Option<Vec<(Spanned<SmolStr>, Spanned<Expression>)>>,
112    pub span: Span,
113}
114
115#[derive(Clone, Debug)]
116pub struct RelationshipPattern {
117    pub variable: Option<Spanned<SmolStr>>,
118    pub rel_types: Vec<Spanned<SmolStr>>,
119    pub direction: Direction,
120    /// Variable-length range: `[*min..max]`.
121    pub range: Option<(Option<u32>, Option<u32>)>,
122    pub properties: Option<Vec<(Spanned<SmolStr>, Spanned<Expression>)>>,
123    pub span: Span,
124}
125
126#[derive(Clone, Copy, Debug, PartialEq, Eq)]
127pub enum Direction {
128    Left,  // <-[]-
129    Right, // -[]->
130    Both,  // -[]-
131}
132
133// =============================================================================
134// Updating Clauses
135// =============================================================================
136
137#[derive(Clone, Debug)]
138pub enum UpdatingClause {
139    Create(Vec<Pattern>),
140    Merge(MergeClause),
141    Set(Vec<SetItem>),
142    Delete(DeleteClause),
143    Remove(Vec<RemoveItem>),
144}
145
146#[derive(Clone, Debug)]
147pub struct MergeClause {
148    pub pattern: Pattern,
149    pub on_match: Vec<SetItem>,
150    pub on_create: Vec<SetItem>,
151}
152
153#[derive(Clone, Debug)]
154pub struct DeleteClause {
155    pub detach: bool,
156    pub expressions: Vec<Spanned<Expression>>,
157}
158
159#[derive(Clone, Debug)]
160pub enum SetItem {
161    Property {
162        entity: Spanned<Expression>,
163        value: Spanned<Expression>,
164    },
165    AllProperties {
166        entity: Spanned<SmolStr>,
167        value: Spanned<Expression>,
168    },
169    Labels {
170        entity: Spanned<SmolStr>,
171        labels: Vec<Spanned<SmolStr>>,
172    },
173}
174
175#[derive(Clone, Debug)]
176pub enum RemoveItem {
177    Property(Spanned<Expression>),
178    Labels {
179        entity: Spanned<SmolStr>,
180        labels: Vec<Spanned<SmolStr>>,
181    },
182}
183
184// =============================================================================
185// Projection
186// =============================================================================
187
188#[derive(Clone, Debug)]
189pub struct ProjectionBody {
190    pub distinct: bool,
191    pub items: ProjectionItems,
192    pub order_by: Vec<(Spanned<Expression>, SortOrder)>,
193    pub skip: Option<Spanned<Expression>>,
194    pub limit: Option<Spanned<Expression>>,
195}
196
197#[derive(Clone, Debug)]
198pub enum ProjectionItems {
199    All,
200    Expressions(Vec<(Spanned<Expression>, Option<Spanned<SmolStr>>)>),
201}
202
203#[derive(Clone, Copy, Debug, PartialEq, Eq)]
204pub enum SortOrder {
205    Ascending,
206    Descending,
207}
208
209// =============================================================================
210// Expressions
211// =============================================================================
212
213#[derive(Clone, Debug)]
214pub enum Expression {
215    Literal(Literal),
216    Variable(SmolStr),
217    Parameter(SmolStr),
218    Property {
219        object: Box<Spanned<Expression>>,
220        key: Spanned<SmolStr>,
221    },
222    FunctionCall {
223        name: Vec<Spanned<SmolStr>>,
224        distinct: bool,
225        args: Vec<Spanned<Expression>>,
226    },
227    CountStar,
228    UnaryOp {
229        op: UnaryOp,
230        operand: Box<Spanned<Expression>>,
231    },
232    BinaryOp {
233        left: Box<Spanned<Expression>>,
234        op: BinaryOp,
235        right: Box<Spanned<Expression>>,
236    },
237    Comparison {
238        left: Box<Spanned<Expression>>,
239        ops: Vec<(ComparisonOp, Spanned<Expression>)>,
240    },
241    IsNull {
242        expr: Box<Spanned<Expression>>,
243        negated: bool,
244    },
245    ListLiteral(Vec<Spanned<Expression>>),
246    MapLiteral(Vec<(Spanned<SmolStr>, Spanned<Expression>)>),
247    Subscript {
248        expr: Box<Spanned<Expression>>,
249        index: Box<Spanned<Expression>>,
250    },
251    Slice {
252        expr: Box<Spanned<Expression>>,
253        from: Option<Box<Spanned<Expression>>>,
254        to: Option<Box<Spanned<Expression>>>,
255    },
256    Case {
257        operand: Option<Box<Spanned<Expression>>>,
258        whens: Vec<(Spanned<Expression>, Spanned<Expression>)>,
259        else_expr: Option<Box<Spanned<Expression>>>,
260    },
261    ExistsSubquery(Box<Query>),
262    CountSubquery(Box<Query>),
263    Quantifier {
264        kind: QuantifierKind,
265        variable: Spanned<SmolStr>,
266        list: Box<Spanned<Expression>>,
267        predicate: Box<Spanned<Expression>>,
268    },
269    StringOp {
270        left: Box<Spanned<Expression>>,
271        op: StringOp,
272        right: Box<Spanned<Expression>>,
273    },
274    InList {
275        expr: Box<Spanned<Expression>>,
276        list: Box<Spanned<Expression>>,
277        negated: bool,
278    },
279    /// Label predicate: `a:Label` (true if node has given label)
280    HasLabel {
281        expr: Box<Spanned<Expression>>,
282        labels: Vec<Spanned<SmolStr>>,
283    },
284    /// List comprehension: `[x IN list | expr]` or `[x IN list WHERE pred | expr]`
285    ListComprehension {
286        variable: Spanned<SmolStr>,
287        list: Box<Spanned<Expression>>,
288        filter: Option<Box<Spanned<Expression>>>,
289        projection: Option<Box<Spanned<Expression>>>,
290    },
291}
292
293#[derive(Clone, Debug)]
294pub enum Literal {
295    Integer(i64),
296    Float(f64),
297    String(SmolStr),
298    Bool(bool),
299    Null,
300}
301
302#[derive(Clone, Copy, Debug, PartialEq, Eq)]
303pub enum UnaryOp {
304    Not,
305    Minus,
306    BitwiseNot,
307}
308
309#[derive(Clone, Copy, Debug, PartialEq, Eq)]
310pub enum BinaryOp {
311    Add,
312    Sub,
313    Mul,
314    Div,
315    Mod,
316    Pow,
317    And,
318    Or,
319    Xor,
320    BitwiseAnd,
321    BitwiseOr,
322    ShiftLeft,
323    ShiftRight,
324    Concat,
325}
326
327#[derive(Clone, Copy, Debug, PartialEq, Eq)]
328pub enum ComparisonOp {
329    Eq,
330    Neq,
331    Lt,
332    Le,
333    Gt,
334    Ge,
335    RegexMatch,
336}
337
338#[derive(Clone, Copy, Debug, PartialEq, Eq)]
339pub enum StringOp {
340    StartsWith,
341    EndsWith,
342    Contains,
343}
344
345#[derive(Clone, Copy, Debug, PartialEq, Eq)]
346pub enum QuantifierKind {
347    All,
348    Any,
349    None,
350    Single,
351}
352
353// =============================================================================
354// DDL Statements
355// =============================================================================
356
357#[derive(Clone, Debug)]
358pub struct CreateNodeTable {
359    pub name: Spanned<SmolStr>,
360    pub if_not_exists: bool,
361    pub columns: Vec<ColumnDefinition>,
362    pub primary_key: Spanned<SmolStr>,
363}
364
365#[derive(Clone, Debug)]
366pub struct CreateRelTable {
367    pub name: Spanned<SmolStr>,
368    pub if_not_exists: bool,
369    pub from_table: Spanned<SmolStr>,
370    pub to_table: Spanned<SmolStr>,
371    pub columns: Vec<ColumnDefinition>,
372}
373
374#[derive(Clone, Debug)]
375pub struct ColumnDefinition {
376    pub name: Spanned<SmolStr>,
377    pub data_type: Spanned<SmolStr>,
378    pub default_value: Option<Spanned<Expression>>,
379}
380
381#[derive(Clone, Debug)]
382pub struct DropStatement {
383    pub object_type: DropObjectType,
384    pub name: Spanned<SmolStr>,
385    pub if_exists: bool,
386}
387
388#[derive(Clone, Copy, Debug, PartialEq, Eq)]
389pub enum DropObjectType {
390    Table,
391    Sequence,
392}
393
394#[derive(Clone, Debug)]
395pub struct AlterTable {
396    pub table_name: Spanned<SmolStr>,
397    pub action: AlterAction,
398}
399
400#[derive(Clone, Debug)]
401pub enum AlterAction {
402    AddColumn(ColumnDefinition),
403    DropColumn(Spanned<SmolStr>),
404    RenameColumn {
405        old_name: Spanned<SmolStr>,
406        new_name: Spanned<SmolStr>,
407    },
408    RenameTable(Spanned<SmolStr>),
409    Comment(SmolStr),
410}
411
412// =============================================================================
413// Copy / Import / Export
414// =============================================================================
415
416#[derive(Clone, Debug)]
417pub struct CopyFrom {
418    pub table_name: Spanned<SmolStr>,
419    pub source: Spanned<Expression>,
420    pub options: Vec<(Spanned<SmolStr>, Spanned<Expression>)>,
421}
422
423#[derive(Clone, Debug)]
424pub struct CopyTo {
425    pub source: CopyToSource,
426    pub destination: Spanned<Expression>,
427    pub options: Vec<(Spanned<SmolStr>, Spanned<Expression>)>,
428}
429
430#[derive(Clone, Debug)]
431pub enum CopyToSource {
432    Table(Spanned<SmolStr>),
433    Query(Box<Query>),
434}
435
436#[derive(Clone, Debug)]
437pub struct ExportDatabase {
438    pub path: Spanned<Expression>,
439    pub options: Vec<(Spanned<SmolStr>, Spanned<Expression>)>,
440}
441
442#[derive(Clone, Debug)]
443pub struct ImportDatabase {
444    pub path: Spanned<Expression>,
445}
446
447#[derive(Clone, Debug)]
448pub struct AttachDatabase {
449    pub path: Spanned<Expression>,
450    pub alias: Option<Spanned<SmolStr>>,
451    pub db_type: Option<Spanned<SmolStr>>,
452}
453
454#[derive(Clone, Debug)]
455pub struct UseDatabase {
456    pub name: Spanned<SmolStr>,
457}
458
459// =============================================================================
460// Procedures / Extensions / Macros
461// =============================================================================
462
463#[derive(Clone, Debug)]
464pub struct StandaloneCall {
465    pub procedure: Spanned<SmolStr>,
466    pub args: Vec<Spanned<Expression>>,
467}
468
469#[derive(Clone, Debug)]
470pub struct CreateMacro {
471    pub name: Spanned<SmolStr>,
472    pub params: Vec<Spanned<SmolStr>>,
473    pub body: Spanned<Expression>,
474}
475
476#[derive(Clone, Debug)]
477pub struct InstallExtension {
478    pub name: Spanned<SmolStr>,
479}
480
481#[derive(Clone, Debug)]
482pub struct LoadExtension {
483    pub path: Spanned<Expression>,
484}
485
486// =============================================================================
487// Transaction
488// =============================================================================
489
490#[derive(Clone, Debug)]
491pub enum TransactionStatement {
492    Begin(TransactionMode),
493    Commit,
494    Rollback,
495}
496
497#[derive(Clone, Copy, Debug, PartialEq, Eq)]
498pub enum TransactionMode {
499    ReadOnly,
500    ReadWrite,
501}