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 ...] [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    pub best_by: Option<BestByClause>,
72    pub output: RuleOutput,
73}
74
75/// A condition in a rule WHERE clause.
76#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
77pub enum RuleCondition {
78    /// `x IS rule`, `x IS rule TO y`, `(x,y) IS rule`
79    IsReference(IsReference),
80    /// A standard Cypher expression used as a boolean condition.
81    Expression(Expr),
82}
83
84/// An IS rule reference in various forms.
85#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
86pub struct IsReference {
87    pub subjects: Vec<String>,
88    pub rule_name: QualifiedName,
89    pub target: Option<String>,
90    pub negated: bool,
91}
92
93// ═══════════════════════════════════════════════════════════════════════════
94// ALONG (path-carried values)
95// ═══════════════════════════════════════════════════════════════════════════
96
97/// `name = along_expression`
98#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
99pub struct AlongBinding {
100    pub name: String,
101    pub expr: LocyExpr,
102}
103
104/// Locy expression: extends Cypher expressions with `prev.field`.
105#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
106pub enum LocyExpr {
107    /// `prev.fieldName` — reference to previous hop's value.
108    PrevRef(String),
109    /// A standard Cypher expression.
110    Cypher(Expr),
111    /// Binary operation between Locy expressions.
112    BinaryOp {
113        left: Box<LocyExpr>,
114        op: LocyBinaryOp,
115        right: Box<LocyExpr>,
116    },
117    /// Unary operation (NOT, negation).
118    UnaryOp(UnaryOp, Box<LocyExpr>),
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
122pub enum LocyBinaryOp {
123    Add,
124    Sub,
125    Mul,
126    Div,
127    Mod,
128    Pow,
129    And,
130    Or,
131    Xor,
132    // Comparisons are handled via Cypher expression re-parse
133}
134
135// ═══════════════════════════════════════════════════════════════════════════
136// FOLD (aggregation)
137// ═══════════════════════════════════════════════════════════════════════════
138
139/// `name = fold_expression`
140#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
141pub struct FoldBinding {
142    pub name: String,
143    pub aggregate: Expr,
144}
145
146// ═══════════════════════════════════════════════════════════════════════════
147// BEST BY (optimized selection)
148// ═══════════════════════════════════════════════════════════════════════════
149
150/// Wrapper for the BEST BY clause items.
151#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
152pub struct BestByClause {
153    pub items: Vec<BestByItem>,
154}
155
156/// `expr [ASC|DESC]`
157#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
158pub struct BestByItem {
159    pub expr: Expr,
160    pub ascending: bool,
161}
162
163// ═══════════════════════════════════════════════════════════════════════════
164// YIELD (rule output schema)
165// ═══════════════════════════════════════════════════════════════════════════
166
167/// Either YIELD items or DERIVE clause as a rule's output.
168#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
169pub enum RuleOutput {
170    Yield(YieldClause),
171    Derive(DeriveClause),
172}
173
174/// Wrapper for the YIELD clause items.
175#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
176pub struct YieldClause {
177    pub items: Vec<LocyYieldItem>,
178}
179
180/// A single YIELD item, possibly marked as KEY.
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182pub struct LocyYieldItem {
183    pub is_key: bool,
184    pub expr: Expr,
185    pub alias: Option<String>,
186}
187
188// ═══════════════════════════════════════════════════════════════════════════
189// DERIVE (graph derivation in rule heads)
190// ═══════════════════════════════════════════════════════════════════════════
191
192/// `DERIVE pattern, pattern, ...` or `DERIVE MERGE a, b`
193#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194pub enum DeriveClause {
195    Patterns(Vec<DerivePattern>),
196    Merge(String, String),
197}
198
199#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
200pub struct DerivePattern {
201    pub direction: Direction,
202    pub source: DeriveNodeSpec,
203    pub edge: DeriveEdgeSpec,
204    pub target: DeriveNodeSpec,
205}
206
207#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
208pub struct DeriveNodeSpec {
209    pub is_new: bool,
210    pub variable: String,
211    pub labels: Vec<String>,
212    pub properties: Option<Expr>,
213}
214
215#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
216pub struct DeriveEdgeSpec {
217    pub edge_type: String,
218    pub properties: Option<Expr>,
219}
220
221// ═══════════════════════════════════════════════════════════════════════════
222// GOAL-DIRECTED QUERY
223// ═══════════════════════════════════════════════════════════════════════════
224
225/// `QUERY ruleName WHERE expr [RETURN ...]`
226#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
227pub struct GoalQuery {
228    pub rule_name: QualifiedName,
229    pub where_expr: Expr,
230    pub return_clause: Option<ReturnClause>,
231}
232
233// ═══════════════════════════════════════════════════════════════════════════
234// DERIVE COMMAND (top-level)
235// ═══════════════════════════════════════════════════════════════════════════
236
237/// `DERIVE ruleName [WHERE expr]`
238#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
239pub struct DeriveCommand {
240    pub rule_name: QualifiedName,
241    pub where_expr: Option<Expr>,
242}
243
244// ═══════════════════════════════════════════════════════════════════════════
245// ASSUME BLOCK
246// ═══════════════════════════════════════════════════════════════════════════
247
248/// `ASSUME { mutations } THEN body`
249#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
250pub struct AssumeBlock {
251    pub mutations: Vec<crate::ast::Clause>,
252    pub body: Vec<LocyStatement>,
253}
254
255// ═══════════════════════════════════════════════════════════════════════════
256// ABDUCE QUERY
257// ═══════════════════════════════════════════════════════════════════════════
258
259/// `ABDUCE [NOT] ruleName WHERE expr [RETURN ...]`
260#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
261pub struct AbduceQuery {
262    pub negated: bool,
263    pub rule_name: QualifiedName,
264    pub where_expr: Expr,
265    pub return_clause: Option<ReturnClause>,
266}
267
268// ═══════════════════════════════════════════════════════════════════════════
269// EXPLAIN RULE
270// ═══════════════════════════════════════════════════════════════════════════
271
272/// `EXPLAIN RULE ruleName WHERE expr [RETURN ...]`
273#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
274pub struct ExplainRule {
275    pub rule_name: QualifiedName,
276    pub where_expr: Expr,
277    pub return_clause: Option<ReturnClause>,
278}