Skip to main content

uni_cypher/
locy_ast.rs

1use serde::{Deserialize, Serialize};
2
3use crate::ast::{Direction, Expr, Pattern, Query, ReturnClause, UnaryOp};
4
5/// A complete Locy program: optional module header, imports, and body statements.
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub struct LocyProgram {
8    pub module: Option<ModuleDecl>,
9    pub uses: Vec<UseDecl>,
10    pub statements: Vec<LocyStatement>,
11}
12
13/// A dotted name like `acme.compliance.rules`.
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub struct QualifiedName {
16    pub parts: Vec<String>,
17}
18
19impl std::fmt::Display for QualifiedName {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        write!(f, "{}", self.parts.join("."))
22    }
23}
24
25/// `MODULE acme.compliance`
26#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
27pub struct ModuleDecl {
28    pub name: QualifiedName,
29}
30
31/// `USE acme.common` or `USE acme.common { control, reachable }`
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub struct UseDecl {
34    pub name: QualifiedName,
35    /// `None` = glob import (all rules), `Some(vec)` = selective imports.
36    pub imports: Option<Vec<String>>,
37}
38
39/// A top-level statement in a Locy program.
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
41pub enum LocyStatement {
42    /// A standard Cypher query (passthrough).
43    Cypher(Query),
44    /// `CREATE RULE ... AS ...`
45    Rule(RuleDefinition),
46    /// `QUERY ruleName WHERE expr RETURN ...`
47    GoalQuery(GoalQuery),
48    /// `DERIVE ruleName WHERE ...`
49    DeriveCommand(DeriveCommand),
50    /// `ASSUME { mutations } THEN body`
51    AssumeBlock(AssumeBlock),
52    /// `ABDUCE [NOT] ruleName WHERE expr RETURN ...`
53    AbduceQuery(AbduceQuery),
54    /// `EXPLAIN RULE ruleName WHERE expr RETURN ...`
55    ExplainRule(ExplainRule),
56}
57
58// ═══════════════════════════════════════════════════════════════════════════
59// RULE DEFINITION
60// ═══════════════════════════════════════════════════════════════════════════
61
62/// `CREATE RULE name [PRIORITY n] AS MATCH pattern [WHERE conds] [ALONG ...] [FOLD ...] [WHERE having] [BEST BY ...] YIELD/DERIVE ...`
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
64pub struct RuleDefinition {
65    pub name: QualifiedName,
66    pub priority: Option<i64>,
67    pub match_pattern: Pattern,
68    pub where_conditions: Vec<RuleCondition>,
69    pub along: Vec<AlongBinding>,
70    pub fold: Vec<FoldBinding>,
71    /// Post-FOLD filter conditions (HAVING semantics). These filter on
72    /// aggregate results after FOLD computation.
73    pub having: Vec<Expr>,
74    pub best_by: Option<BestByClause>,
75    pub output: RuleOutput,
76}
77
78/// A condition in a rule WHERE clause.
79#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
80pub enum RuleCondition {
81    /// `x IS rule`, `x IS rule TO y`, `(x,y) IS rule`
82    IsReference(IsReference),
83    /// A standard Cypher expression used as a boolean condition.
84    Expression(Expr),
85}
86
87/// An IS rule reference in various forms.
88#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
89pub struct IsReference {
90    pub subjects: Vec<String>,
91    pub rule_name: QualifiedName,
92    pub target: Option<String>,
93    pub negated: bool,
94}
95
96// ═══════════════════════════════════════════════════════════════════════════
97// ALONG (path-carried values)
98// ═══════════════════════════════════════════════════════════════════════════
99
100/// `name = along_expression`
101#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
102pub struct AlongBinding {
103    pub name: String,
104    pub expr: LocyExpr,
105}
106
107/// Locy expression: extends Cypher expressions with `prev.field`.
108#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
109pub enum LocyExpr {
110    /// `prev.fieldName` — reference to previous hop's value.
111    PrevRef(String),
112    /// A standard Cypher expression.
113    Cypher(Expr),
114    /// Binary operation between Locy expressions.
115    BinaryOp {
116        left: Box<LocyExpr>,
117        op: LocyBinaryOp,
118        right: Box<LocyExpr>,
119    },
120    /// Unary operation (NOT, negation).
121    UnaryOp(UnaryOp, Box<LocyExpr>),
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
125pub enum LocyBinaryOp {
126    Add,
127    Sub,
128    Mul,
129    Div,
130    Mod,
131    Pow,
132    And,
133    Or,
134    Xor,
135    // Comparisons are handled via Cypher expression re-parse
136}
137
138// ═══════════════════════════════════════════════════════════════════════════
139// FOLD (aggregation)
140// ═══════════════════════════════════════════════════════════════════════════
141
142/// `name = fold_expression`
143#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
144pub struct FoldBinding {
145    pub name: String,
146    pub aggregate: Expr,
147}
148
149// ═══════════════════════════════════════════════════════════════════════════
150// BEST BY (optimized selection)
151// ═══════════════════════════════════════════════════════════════════════════
152
153/// Wrapper for the BEST BY clause items.
154#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
155pub struct BestByClause {
156    pub items: Vec<BestByItem>,
157}
158
159/// `expr [ASC|DESC]`
160#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
161pub struct BestByItem {
162    pub expr: Expr,
163    pub ascending: bool,
164}
165
166// ═══════════════════════════════════════════════════════════════════════════
167// YIELD (rule output schema)
168// ═══════════════════════════════════════════════════════════════════════════
169
170/// Either YIELD items or DERIVE clause as a rule's output.
171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
172pub enum RuleOutput {
173    Yield(YieldClause),
174    Derive(DeriveClause),
175}
176
177/// Wrapper for the YIELD clause items.
178#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179pub struct YieldClause {
180    pub items: Vec<LocyYieldItem>,
181}
182
183/// A single YIELD item, possibly marked as KEY or PROB.
184#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
185pub struct LocyYieldItem {
186    pub is_key: bool,
187    pub is_prob: bool,
188    pub expr: Expr,
189    pub alias: Option<String>,
190}
191
192// ═══════════════════════════════════════════════════════════════════════════
193// DERIVE (graph derivation in rule heads)
194// ═══════════════════════════════════════════════════════════════════════════
195
196/// `DERIVE pattern, pattern, ...` or `DERIVE MERGE a, b`
197#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
198pub enum DeriveClause {
199    Patterns(Vec<DerivePattern>),
200    Merge(String, String),
201}
202
203#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
204pub struct DerivePattern {
205    pub direction: Direction,
206    pub source: DeriveNodeSpec,
207    pub edge: DeriveEdgeSpec,
208    pub target: DeriveNodeSpec,
209}
210
211#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
212pub struct DeriveNodeSpec {
213    pub is_new: bool,
214    pub variable: String,
215    pub labels: Vec<String>,
216    pub properties: Option<Expr>,
217}
218
219#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
220pub struct DeriveEdgeSpec {
221    pub edge_type: String,
222    pub properties: Option<Expr>,
223}
224
225// ═══════════════════════════════════════════════════════════════════════════
226// GOAL-DIRECTED QUERY
227// ═══════════════════════════════════════════════════════════════════════════
228
229/// `QUERY ruleName [WHERE expr] [RETURN ...]`
230#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
231pub struct GoalQuery {
232    pub rule_name: QualifiedName,
233    pub where_expr: Option<Expr>,
234    pub return_clause: Option<ReturnClause>,
235}
236
237// ═══════════════════════════════════════════════════════════════════════════
238// DERIVE COMMAND (top-level)
239// ═══════════════════════════════════════════════════════════════════════════
240
241/// `DERIVE ruleName [WHERE expr]`
242#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
243pub struct DeriveCommand {
244    pub rule_name: QualifiedName,
245    pub where_expr: Option<Expr>,
246}
247
248// ═══════════════════════════════════════════════════════════════════════════
249// ASSUME BLOCK
250// ═══════════════════════════════════════════════════════════════════════════
251
252/// `ASSUME { mutations } THEN body`
253#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
254pub struct AssumeBlock {
255    pub mutations: Vec<crate::ast::Clause>,
256    pub body: Vec<LocyStatement>,
257}
258
259// ═══════════════════════════════════════════════════════════════════════════
260// ABDUCE QUERY
261// ═══════════════════════════════════════════════════════════════════════════
262
263/// `ABDUCE [NOT] ruleName [WHERE expr] [RETURN ...]`
264#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
265pub struct AbduceQuery {
266    pub negated: bool,
267    pub rule_name: QualifiedName,
268    pub where_expr: Option<Expr>,
269    pub return_clause: Option<ReturnClause>,
270}
271
272// ═══════════════════════════════════════════════════════════════════════════
273// EXPLAIN RULE
274// ═══════════════════════════════════════════════════════════════════════════
275
276/// `EXPLAIN RULE ruleName [WHERE expr] [RETURN ...]`
277#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
278pub struct ExplainRule {
279    pub rule_name: QualifiedName,
280    pub where_expr: Option<Expr>,
281    pub return_clause: Option<ReturnClause>,
282}