Skip to main content

lora_ast/
ast.rs

1use smallvec::SmallVec;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
4pub struct Span {
5    pub start: usize,
6    pub end: usize,
7}
8
9impl Span {
10    #[must_use]
11    pub const fn new(start: usize, end: usize) -> Self {
12        Self { start, end }
13    }
14}
15
16#[derive(Debug, Clone)]
17pub struct Document {
18    pub statement: Statement,
19    pub span: Span,
20}
21
22#[derive(Debug, Clone)]
23pub enum Statement {
24    Query(Query),
25    Schema(SchemaCommand),
26}
27
28#[derive(Debug, Clone)]
29pub enum SchemaCommand {
30    CreateIndex(CreateIndex),
31    DropIndex(DropIndex),
32    ShowIndexes(ShowIndexes),
33    CreateConstraint(CreateConstraint),
34    DropConstraint(DropConstraint),
35    ShowConstraints(ShowConstraints),
36}
37
38#[derive(Debug, Clone)]
39pub struct CreateConstraint {
40    pub name: ConstraintNameSpec,
41    pub if_not_exists: bool,
42    pub entity: IndexEntityKind,
43    /// Pattern variable in the `FOR` clause (`n`, `r`).
44    pub variable: String,
45    /// Label / rel-type. Always present — constraints don't accept the
46    /// wildcard form that LOOKUP indexes do.
47    pub label: String,
48    pub properties: Vec<String>,
49    pub kind: ConstraintKind,
50    pub span: Span,
51}
52
53#[derive(Debug, Clone)]
54pub struct DropConstraint {
55    pub name: ConstraintNameSpec,
56    pub if_exists: bool,
57    pub span: Span,
58}
59
60#[derive(Debug, Clone)]
61pub struct ShowConstraints {
62    pub pipeline: Option<ShowPipeline>,
63    pub span: Span,
64}
65
66/// `SHOW INDEXES YIELD … [WHERE …] [RETURN …]` tail. Modelled after the
67/// Neo4j syntax: YIELD is the anchor, optional WHERE filters the
68/// yielded rows, optional RETURN reprojects them. ORDER BY / SKIP /
69/// LIMIT can appear on either YIELD or RETURN — semantically applied
70/// to the rows at that stage.
71#[derive(Debug, Clone)]
72pub struct ShowPipeline {
73    pub yield_part: ShowYield,
74    pub where_: Option<Expr>,
75    pub return_part: Option<ShowReturn>,
76    pub span: Span,
77}
78
79#[derive(Debug, Clone)]
80pub struct ShowYield {
81    /// `YIELD *` — pass every catalog column through unchanged.
82    pub star: bool,
83    /// `YIELD a, b AS x` items (empty when `star` is true).
84    pub items: Vec<YieldItem>,
85    pub order: Vec<SortItem>,
86    pub skip: Option<Expr>,
87    pub limit: Option<Expr>,
88    pub span: Span,
89}
90
91#[derive(Debug, Clone)]
92pub struct ShowReturn {
93    pub items: Vec<ProjectionItem>,
94    pub order: Vec<SortItem>,
95    pub skip: Option<Expr>,
96    pub limit: Option<Expr>,
97    pub span: Span,
98}
99
100/// Type filter for `SHOW [TYPE] INDEXES`. `All` is the explicit
101/// unfiltered form; `Fulltext` and `Vector` parse but yield no rows
102/// because those index types aren't backed by anything in the catalog
103/// yet.
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub enum IndexKindFilter {
106    All,
107    Range,
108    Text,
109    Point,
110    Lookup,
111    Fulltext,
112    Vector,
113}
114
115#[derive(Debug, Clone)]
116pub enum ConstraintNameSpec {
117    Literal(String),
118    Parameter(String),
119}
120
121#[derive(Debug, Clone, PartialEq)]
122pub enum ConstraintKind {
123    /// `IS UNIQUE` — single or composite.
124    Unique,
125    /// `IS NOT NULL` — single property only.
126    Existence,
127    /// `IS NODE KEY` — single or composite, node only.
128    NodeKey,
129    /// `IS RELATIONSHIP KEY` — single or composite, relationship only.
130    RelationshipKey,
131    /// `IS :: Type` — single property only.
132    PropertyType(PropertyTypeExpr),
133}
134
135impl ConstraintKind {
136    /// Human-readable tag for SHOW CONSTRAINTS and diagnostics.
137    #[must_use]
138    pub fn type_tag(&self, entity: IndexEntityKind) -> &'static str {
139        match (self, entity) {
140            (ConstraintKind::Unique, IndexEntityKind::Node) => "NODE_PROPERTY_UNIQUENESS",
141            (ConstraintKind::Unique, IndexEntityKind::Relationship) => {
142                "RELATIONSHIP_PROPERTY_UNIQUENESS"
143            }
144            (ConstraintKind::Existence, IndexEntityKind::Node) => "NODE_PROPERTY_EXISTENCE",
145            (ConstraintKind::Existence, IndexEntityKind::Relationship) => {
146                "RELATIONSHIP_PROPERTY_EXISTENCE"
147            }
148            (ConstraintKind::NodeKey, _) => "NODE_KEY",
149            (ConstraintKind::RelationshipKey, _) => "RELATIONSHIP_KEY",
150            (ConstraintKind::PropertyType(_), IndexEntityKind::Node) => "NODE_PROPERTY_TYPE",
151            (ConstraintKind::PropertyType(_), IndexEntityKind::Relationship) => {
152                "RELATIONSHIP_PROPERTY_TYPE"
153            }
154        }
155    }
156}
157
158/// A closed dynamic union of property types: `T1 | T2 | ...`. A single
159/// type is represented as a one-element union.
160#[derive(Debug, Clone, PartialEq)]
161pub struct PropertyTypeExpr {
162    pub alternatives: Vec<PropertyTypeTerm>,
163}
164
165#[derive(Debug, Clone, PartialEq)]
166pub enum PropertyTypeTerm {
167    Scalar(ScalarType),
168    List {
169        inner: Box<PropertyTypeTerm>,
170        /// `LIST<X NOT NULL>` is the only fully-supported list shape in
171        /// Neo4j compatibility mode; we keep the flag for grammar fidelity
172        /// even though we reject `LIST<X>` (nullable elements) at the
173        /// catalog layer.
174        not_null: bool,
175    },
176    Vector {
177        coord: VectorCoordType,
178        dimension: u32,
179    },
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum ScalarType {
184    Boolean,
185    String,
186    Integer,
187    Float,
188    Date,
189    LocalTime,
190    ZonedTime,
191    LocalDateTime,
192    ZonedDateTime,
193    Duration,
194    Point,
195    Map,
196    Any,
197}
198
199impl ScalarType {
200    #[must_use]
201    pub const fn as_str(self) -> &'static str {
202        match self {
203            ScalarType::Boolean => "BOOLEAN",
204            ScalarType::String => "STRING",
205            ScalarType::Integer => "INTEGER",
206            ScalarType::Float => "FLOAT",
207            ScalarType::Date => "DATE",
208            ScalarType::LocalTime => "LOCAL TIME",
209            ScalarType::ZonedTime => "ZONED TIME",
210            ScalarType::LocalDateTime => "LOCAL DATETIME",
211            ScalarType::ZonedDateTime => "ZONED DATETIME",
212            ScalarType::Duration => "DURATION",
213            ScalarType::Point => "POINT",
214            ScalarType::Map => "MAP",
215            ScalarType::Any => "ANY",
216        }
217    }
218}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221pub enum VectorCoordType {
222    Int8,
223    Int16,
224    Int32,
225    Int64,
226    Float32,
227    Float64,
228}
229
230impl VectorCoordType {
231    #[must_use]
232    pub const fn as_str(self) -> &'static str {
233        match self {
234            VectorCoordType::Int8 => "INT8",
235            VectorCoordType::Int16 => "INT16",
236            VectorCoordType::Int32 => "INT32",
237            VectorCoordType::Int64 => "INT64",
238            VectorCoordType::Float32 => "FLOAT32",
239            VectorCoordType::Float64 => "FLOAT64",
240        }
241    }
242}
243
244#[derive(Debug, Clone)]
245pub struct DropIndex {
246    pub name: IndexNameSpec,
247    pub if_exists: bool,
248    pub span: Span,
249}
250
251#[derive(Debug, Clone)]
252pub struct CreateIndex {
253    pub kind: IndexKind,
254    pub name: Option<IndexNameSpec>,
255    pub if_not_exists: bool,
256    pub entity: IndexEntityKind,
257    /// Pattern variable in the FOR clause (`n` for `(n:Person)`, `r` for `()-[r:KNOWS]-()`).
258    pub variable: String,
259    /// `Some(label_or_type)` for property indexes; `None` for `LOOKUP` token indexes
260    /// where the label/type is the wildcard captured by `labels(n)` / `type(r)`.
261    /// For `FULLTEXT` (which accepts `:A|B|C`) this carries the first label; the
262    /// rest live in `additional_labels`.
263    pub label: Option<String>,
264    /// Extra labels beyond `label`. Only populated for `FULLTEXT` indexes
265    /// declared with the `(n:A|B|C)` pattern.
266    pub additional_labels: Vec<String>,
267    /// Property keys covered by the index. Empty for `LOOKUP` token indexes.
268    pub properties: Vec<String>,
269    pub options: Option<IndexOptions>,
270    pub span: Span,
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq)]
274pub enum IndexKind {
275    Range,
276    Text,
277    Point,
278    Lookup,
279    Vector,
280    Fulltext,
281}
282
283impl IndexKind {
284    #[must_use]
285    pub const fn as_str(self) -> &'static str {
286        match self {
287            IndexKind::Range => "RANGE",
288            IndexKind::Text => "TEXT",
289            IndexKind::Point => "POINT",
290            IndexKind::Lookup => "LOOKUP",
291            IndexKind::Vector => "VECTOR",
292            IndexKind::Fulltext => "FULLTEXT",
293        }
294    }
295}
296
297#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub enum IndexEntityKind {
299    Node,
300    Relationship,
301}
302
303impl IndexEntityKind {
304    #[must_use]
305    pub const fn as_str(self) -> &'static str {
306        match self {
307            IndexEntityKind::Node => "NODE",
308            IndexEntityKind::Relationship => "RELATIONSHIP",
309        }
310    }
311}
312
313#[derive(Debug, Clone)]
314pub enum IndexNameSpec {
315    Literal(String),
316    Parameter(String),
317}
318
319#[derive(Debug, Clone)]
320pub struct IndexOptions {
321    pub config: Vec<(String, Expr)>,
322    pub span: Span,
323}
324
325#[derive(Debug, Clone)]
326pub struct ShowIndexes {
327    /// Optional index-type filter (e.g. `SHOW RANGE INDEXES`). `None`
328    /// means no filter clause was written; `Some(IndexKindFilter::All)`
329    /// is the explicit `SHOW ALL INDEXES` form (semantically the same).
330    pub filter: Option<IndexKindFilter>,
331    pub pipeline: Option<ShowPipeline>,
332    pub span: Span,
333}
334
335#[derive(Debug, Clone)]
336pub enum Query {
337    Regular(RegularQuery),
338    StandaloneCall(StandaloneCall),
339}
340
341#[derive(Debug, Clone)]
342pub struct RegularQuery {
343    pub head: SingleQuery,
344    pub unions: Vec<UnionPart>,
345    pub span: Span,
346}
347
348#[derive(Debug, Clone)]
349pub struct UnionPart {
350    pub all: bool,
351    pub query: SingleQuery,
352    pub span: Span,
353}
354
355#[derive(Debug, Clone)]
356pub enum SingleQuery {
357    SinglePart(SinglePartQuery),
358    MultiPart(MultiPartQuery),
359}
360
361#[derive(Debug, Clone)]
362pub struct SinglePartQuery {
363    pub reading_clauses: Vec<ReadingClause>,
364    pub updating_clauses: Vec<UpdatingClause>,
365    pub return_clause: Option<Return>,
366    pub span: Span,
367}
368
369#[derive(Debug, Clone)]
370pub struct MultiPartQuery {
371    pub parts: Vec<QueryPart>,
372    pub tail: Box<SinglePartQuery>,
373    pub span: Span,
374}
375
376#[derive(Debug, Clone)]
377pub struct QueryPart {
378    pub reading_clauses: Vec<ReadingClause>,
379    pub updating_clauses: Vec<UpdatingClause>,
380    pub with_clause: With,
381    pub span: Span,
382}
383
384#[derive(Debug, Clone)]
385pub enum ReadingClause {
386    Match(Match),
387    Unwind(Unwind),
388    InQueryCall(InQueryCall),
389}
390
391#[derive(Debug, Clone)]
392pub enum UpdatingClause {
393    Create(Create),
394    Merge(Merge),
395    Delete(Delete),
396    Set(Set),
397    Remove(Remove),
398}
399
400#[derive(Debug, Clone)]
401pub struct Match {
402    pub optional: bool,
403    pub pattern: Pattern,
404    pub where_: Option<Expr>,
405    pub span: Span,
406}
407
408#[derive(Debug, Clone)]
409pub struct Unwind {
410    pub expr: Expr,
411    pub alias: Variable,
412    pub span: Span,
413}
414
415#[derive(Debug, Clone)]
416pub struct Create {
417    pub pattern: Pattern,
418    pub span: Span,
419}
420
421#[derive(Debug, Clone)]
422pub struct Merge {
423    pub pattern_part: PatternPart,
424    pub actions: Vec<MergeAction>,
425    pub span: Span,
426}
427
428#[derive(Debug, Clone)]
429pub struct MergeAction {
430    pub on_match: bool,
431    pub set: Set,
432    pub span: Span,
433}
434
435#[derive(Debug, Clone)]
436pub struct Delete {
437    pub detach: bool,
438    pub expressions: Vec<Expr>,
439    pub span: Span,
440}
441
442#[derive(Debug, Clone)]
443pub struct Set {
444    pub items: Vec<SetItem>,
445    pub span: Span,
446}
447
448#[derive(Debug, Clone)]
449pub enum SetItem {
450    SetProperty {
451        target: Expr,
452        value: Expr,
453        span: Span,
454    },
455    SetVariable {
456        variable: Variable,
457        value: Expr,
458        span: Span,
459    },
460    MutateVariable {
461        variable: Variable,
462        value: Expr,
463        span: Span,
464    },
465    SetLabels {
466        variable: Variable,
467        labels: Vec<String>,
468        span: Span,
469    },
470}
471
472#[derive(Debug, Clone)]
473pub struct Remove {
474    pub items: Vec<RemoveItem>,
475    pub span: Span,
476}
477
478#[derive(Debug, Clone)]
479pub enum RemoveItem {
480    Labels {
481        variable: Variable,
482        labels: Vec<String>,
483        span: Span,
484    },
485    Property {
486        expr: Expr,
487        span: Span,
488    },
489}
490
491#[derive(Debug, Clone)]
492pub struct InQueryCall {
493    pub procedure: ProcedureInvocation,
494    pub yield_items: Vec<YieldItem>,
495    pub where_: Option<Expr>,
496    pub span: Span,
497}
498
499#[derive(Debug, Clone)]
500pub struct StandaloneCall {
501    pub procedure: ProcedureInvocationKind,
502    pub yield_items: Vec<YieldItem>,
503    pub yield_all: bool,
504    pub span: Span,
505}
506
507#[derive(Debug, Clone)]
508pub enum ProcedureInvocationKind {
509    Explicit(ProcedureInvocation),
510    Implicit(ProcedureName),
511}
512
513#[derive(Debug, Clone)]
514pub struct ProcedureInvocation {
515    pub name: ProcedureName,
516    pub args: Vec<Expr>,
517    pub span: Span,
518}
519
520#[derive(Debug, Clone)]
521pub struct YieldItem {
522    pub field: Option<String>,
523    pub alias: Variable,
524    pub span: Span,
525}
526
527#[derive(Debug, Clone)]
528pub struct With {
529    pub body: ProjectionBody,
530    pub where_: Option<Expr>,
531    pub span: Span,
532}
533
534#[derive(Debug, Clone)]
535pub struct Return {
536    pub body: ProjectionBody,
537    pub span: Span,
538}
539
540#[derive(Debug, Clone)]
541pub struct ProjectionBody {
542    pub distinct: bool,
543    pub items: Vec<ProjectionItem>,
544    pub order: Vec<SortItem>,
545    pub skip: Option<Expr>,
546    pub limit: Option<Expr>,
547    pub span: Span,
548}
549
550#[derive(Debug, Clone)]
551pub enum ProjectionItem {
552    Expr {
553        expr: Expr,
554        alias: Option<Variable>,
555        span: Span,
556    },
557    Star {
558        span: Span,
559    },
560}
561
562#[derive(Debug, Clone)]
563pub struct SortItem {
564    pub expr: Expr,
565    pub direction: SortDirection,
566    pub span: Span,
567}
568
569#[derive(Debug, Clone, Copy, PartialEq, Eq)]
570pub enum SortDirection {
571    Asc,
572    Desc,
573}
574
575#[derive(Debug, Clone)]
576pub struct Pattern {
577    pub parts: Vec<PatternPart>,
578    pub span: Span,
579}
580
581#[derive(Debug, Clone)]
582pub struct PatternPart {
583    pub binding: Option<Variable>,
584    pub element: PatternElement,
585    pub span: Span,
586}
587
588#[derive(Debug, Clone)]
589pub enum PatternElement {
590    NodeChain {
591        head: NodePattern,
592        chain: Vec<PatternElementChain>,
593        span: Span,
594    },
595    Parenthesized(Box<PatternElement>, Span),
596    ShortestPath {
597        all: bool,
598        element: Box<PatternElement>,
599        span: Span,
600    },
601}
602
603#[derive(Debug, Clone)]
604pub struct PatternElementChain {
605    pub relationship: RelationshipPattern,
606    pub node: NodePattern,
607    pub span: Span,
608}
609
610#[derive(Debug, Clone)]
611pub struct NodePattern {
612    pub variable: Option<Variable>,
613    /// Each inner `Vec` is a disjunctive group (OR).
614    /// The outer `SmallVec` is conjunctive (AND across groups).
615    /// `:A:B` → `[[A], [B]]`;  `:A|B` → `[[A, B]]`.
616    pub labels: SmallVec<SmallVec<String, 2>, 2>,
617    pub properties: Option<Expr>,
618    pub span: Span,
619}
620
621#[derive(Debug, Clone)]
622pub struct RelationshipPattern {
623    pub direction: Direction,
624    pub detail: Option<RelationshipDetail>,
625    pub span: Span,
626}
627
628#[derive(Debug, Clone)]
629pub struct RelationshipDetail {
630    pub variable: Option<Variable>,
631    pub types: SmallVec<String, 2>,
632    pub range: Option<RangeLiteral>,
633    pub properties: Option<Expr>,
634    pub span: Span,
635}
636
637#[derive(Debug, Clone, Copy, PartialEq, Eq)]
638pub enum Direction {
639    Left,
640    Right,
641    Undirected,
642}
643
644#[derive(Debug, Clone)]
645pub struct RangeLiteral {
646    pub start: Option<u64>,
647    pub end: Option<u64>,
648    pub span: Span,
649}
650
651#[derive(Debug, Clone)]
652pub struct Variable {
653    pub name: String,
654    pub span: Span,
655}
656
657#[derive(Debug, Clone)]
658pub struct ProcedureName {
659    pub parts: Vec<String>,
660    pub span: Span,
661}
662
663#[derive(Debug, Clone)]
664pub enum Expr {
665    Variable(Variable),
666    Integer(i64, Span),
667    Float(f64, Span),
668    String(String, Span),
669    Bool(bool, Span),
670    Null(Span),
671    Parameter(String, Span),
672    List(Vec<Expr>, Span),
673    Map(Vec<(String, Expr)>, Span),
674    Property {
675        expr: Box<Expr>,
676        key: String,
677        span: Span,
678    },
679    Binary {
680        lhs: Box<Expr>,
681        op: BinaryOp,
682        rhs: Box<Expr>,
683        span: Span,
684    },
685    Unary {
686        op: UnaryOp,
687        expr: Box<Expr>,
688        span: Span,
689    },
690    FunctionCall {
691        name: Vec<String>,
692        distinct: bool,
693        args: Vec<Expr>,
694        span: Span,
695    },
696    Case {
697        input: Option<Box<Expr>>,
698        alternatives: Vec<(Expr, Expr)>,
699        else_expr: Option<Box<Expr>>,
700        span: Span,
701    },
702    ListPredicate {
703        kind: ListPredicateKind,
704        variable: Variable,
705        list: Box<Expr>,
706        predicate: Box<Expr>,
707        span: Span,
708    },
709    ListComprehension {
710        variable: Variable,
711        list: Box<Expr>,
712        filter: Option<Box<Expr>>,
713        map_expr: Option<Box<Expr>>,
714        span: Span,
715    },
716    Reduce {
717        accumulator: Variable,
718        init: Box<Expr>,
719        variable: Variable,
720        list: Box<Expr>,
721        expr: Box<Expr>,
722        span: Span,
723    },
724    MapProjection {
725        base: Box<Expr>,
726        selectors: Vec<MapProjectionSelector>,
727        span: Span,
728    },
729    Index {
730        expr: Box<Expr>,
731        index: Box<Expr>,
732        span: Span,
733    },
734    Slice {
735        expr: Box<Expr>,
736        from: Option<Box<Expr>>,
737        to: Option<Box<Expr>>,
738        span: Span,
739    },
740    ExistsSubquery {
741        pattern: Pattern,
742        where_: Option<Box<Expr>>,
743        span: Span,
744    },
745    PatternComprehension {
746        pattern: Box<PatternElement>,
747        where_: Option<Box<Expr>>,
748        map_expr: Box<Expr>,
749        span: Span,
750    },
751}
752
753#[derive(Debug, Clone)]
754pub enum MapProjectionSelector {
755    /// `.propertyName` — include a specific property
756    Property(String),
757    /// `.*` — include all properties
758    AllProperties,
759    /// `key: expr` — include a computed entry
760    Literal(String, Expr),
761}
762
763#[derive(Debug, Clone, Copy, PartialEq, Eq)]
764pub enum ListPredicateKind {
765    Any,
766    All,
767    None,
768    Single,
769}
770
771impl Expr {
772    #[must_use]
773    pub fn span(&self) -> Span {
774        match self {
775            Expr::Variable(v) => v.span,
776            Expr::Integer(_, s)
777            | Expr::Float(_, s)
778            | Expr::String(_, s)
779            | Expr::Bool(_, s)
780            | Expr::Null(s)
781            | Expr::Parameter(_, s)
782            | Expr::List(_, s)
783            | Expr::Map(_, s)
784            | Expr::Property { span: s, .. }
785            | Expr::Binary { span: s, .. }
786            | Expr::Unary { span: s, .. }
787            | Expr::FunctionCall { span: s, .. }
788            | Expr::Case { span: s, .. }
789            | Expr::ListPredicate { span: s, .. }
790            | Expr::ListComprehension { span: s, .. }
791            | Expr::Reduce { span: s, .. }
792            | Expr::MapProjection { span: s, .. }
793            | Expr::Index { span: s, .. }
794            | Expr::Slice { span: s, .. }
795            | Expr::ExistsSubquery { span: s, .. }
796            | Expr::PatternComprehension { span: s, .. } => *s,
797        }
798    }
799}
800
801#[derive(Debug, Clone, Copy, PartialEq, Eq)]
802pub enum BinaryOp {
803    Or,
804    Xor,
805    And,
806    Eq,
807    Ne,
808    Lt,
809    Gt,
810    Le,
811    Ge,
812    Add,
813    Sub,
814    Mul,
815    Div,
816    Mod,
817    Pow,
818    In,
819    StartsWith,
820    EndsWith,
821    Contains,
822    IsNull,
823    IsNotNull,
824    RegexMatch,
825}
826
827#[derive(Debug, Clone, Copy, PartialEq, Eq)]
828pub enum UnaryOp {
829    Not,
830    Pos,
831    Neg,
832}