1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! Graph-pattern AST nodes.
use std::fmt;
use selene_core::DbString;
use crate::ast::{expr::ValueExpr, span::SourceSpan, util::Vec2OrMore};
/// Path traversal mode for `MATCH`.
#[derive(
Clone, Copy, Debug, Default, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize,
)]
pub enum PathMode {
/// Edges and nodes may repeat.
#[default]
Walk,
/// No repeated edges.
Trail,
/// No repeated nodes.
Acyclic,
/// No repeated nodes except the first and last node may be equal.
Simple,
}
/// Path selector.
#[derive(Clone, Copy, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum PathSelector {
/// ISO §16.6 G016 any path search: `ANY [N] [PATHS]`.
///
/// Retains up to N implementation-dependent path bindings per endpoint
/// partition (§22.4 GR13a). Bare `ANY` defaults to `paths = 1`.
Any {
/// Number of path bindings to retain per endpoint partition.
paths: u32,
},
/// `ALL`.
All,
/// `ANY SHORTEST`.
AnyShortest,
/// `ALL SHORTEST`.
AllShortest,
/// ISO §16.6 G019 counted shortest path search: `SHORTEST N [PATHS]`.
/// Retains up to N individually-shortest path bindings per endpoint
/// partition (§22.4 GR13b-i). `ANY SHORTEST` == `CountedShortest { paths: 1 }`.
CountedShortest {
/// Number of shortest path bindings to retain per endpoint partition.
paths: u32,
},
/// ISO §16.6 G020 counted shortest group search: `SHORTEST [N] GROUP[S]`.
/// Retains all bindings within the N smallest distinct-length groups per
/// endpoint partition (§22.4 GR13b-ii). `ALL SHORTEST` == `CountedShortestGroup { groups: 1 }`.
CountedShortestGroup {
/// Number of smallest distinct-length groups to retain per endpoint partition.
groups: u32,
},
}
impl fmt::Debug for PathSelector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Any { paths: 1 } => f.write_str("Any"),
Self::Any { paths } => f.debug_struct("Any").field("paths", paths).finish(),
Self::All => f.write_str("All"),
Self::AnyShortest => f.write_str("AnyShortest"),
Self::AllShortest => f.write_str("AllShortest"),
Self::CountedShortest { paths } => f
.debug_struct("CountedShortest")
.field("paths", paths)
.finish(),
Self::CountedShortestGroup { groups } => f
.debug_struct("CountedShortestGroup")
.field("groups", groups)
.finish(),
}
}
}
/// Match-mode modifier.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum MatchMode {
/// `DIFFERENT EDGES`.
DifferentEdges,
/// `REPEATABLE ELEMENTS`.
RepeatableElements,
}
/// Label expression.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub enum LabelExpr {
/// One label name.
Single(DbString),
/// Conjunction.
Conjunction(Vec2OrMore<LabelExpr>),
/// Disjunction.
Disjunction(Vec2OrMore<LabelExpr>),
/// Negation.
Negation(Box<LabelExpr>),
/// Wildcard label.
Wildcard,
}
/// Direction requested by an edge pattern.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum EdgeDirection {
/// `-[]->`.
Right,
/// `<-[]-`.
Left,
/// `-[]-`, matching either direction.
Undirected,
}
/// Edge-pattern quantifier.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum Quantifier {
/// ISO graph-pattern quantifier (`*`, `+`, `{n}`, `{m,n}`, etc.).
GraphPattern {
/// Minimum number of repetitions.
min: u32,
/// Maximum number of repetitions, or `None` for unbounded.
max: Option<u32>,
},
/// ISO questioned path primary (`?`), which preserves singleton exposure.
Questioned,
}
/// Node pattern.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct NodePattern {
/// Optional binding.
pub binding: Option<DbString>,
/// Optional label expression.
pub label_expr: Option<LabelExpr>,
/// Inline property predicates in source order.
pub properties: Vec<(DbString, ValueExpr)>,
/// Optional inline `WHERE`.
pub inline_where: Option<ValueExpr>,
/// Source span.
pub span: SourceSpan,
}
/// Edge pattern.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct EdgePattern {
/// Optional binding.
pub binding: Option<DbString>,
/// Direction.
pub direction: EdgeDirection,
/// Optional label expression.
pub label_expr: Option<LabelExpr>,
/// Inline property predicates in source order.
pub properties: Vec<(DbString, ValueExpr)>,
/// Optional variable-length quantifier.
pub quantifier: Option<Quantifier>,
/// Optional inline `WHERE`.
pub inline_where: Option<ValueExpr>,
/// Source span.
pub span: SourceSpan,
}
/// One graph-pattern element.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub enum PatternElement {
/// Node pattern.
Node(NodePattern),
/// Edge pattern.
Edge(EdgePattern),
}
/// One graph pattern.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct GraphPattern {
/// Optional path binding.
pub path_binding: Option<DbString>,
/// Alternating node/edge/node elements.
pub elements: Vec<PatternElement>,
/// Source span.
pub span: SourceSpan,
}
/// `MATCH` clause.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct MatchClause {
/// Whether this is `OPTIONAL MATCH`.
pub optional: bool,
/// Optional path selector.
pub selector: Option<PathSelector>,
/// Optional match mode.
pub match_mode: Option<MatchMode>,
/// Path mode; defaults to [`PathMode::Walk`].
pub path_mode: PathMode,
/// Whether the path mode was written explicitly in source.
pub path_mode_explicit: bool,
/// Whether an explicit `PATH` / `PATHS` keyword was written in source
/// (ISO/IEC 39075:2024 §16.6 `<path or paths>`, Feature G014).
///
/// Pure surface sugar per ISO §1.2.4: it has no semantic effect over the
/// no-keyword spelling. The flagger stamps `FeatureId::G014` iff this is
/// `true`; the runtime treats it as inert. `false` when absent.
pub path_or_paths: bool,
/// Graph patterns.
pub patterns: Vec<GraphPattern>,
/// Optional statement-level `WHERE`.
pub where_clause: Option<ValueExpr>,
/// Source span.
pub span: SourceSpan,
}