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 or PROB.
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182pub struct LocyYieldItem {
183    pub is_key: bool,
184    pub is_prob: bool,
185    pub expr: Expr,
186    pub alias: Option<String>,
187}
188
189// ═══════════════════════════════════════════════════════════════════════════
190// DERIVE (graph derivation in rule heads)
191// ═══════════════════════════════════════════════════════════════════════════
192
193/// `DERIVE pattern, pattern, ...` or `DERIVE MERGE a, b`
194#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
195pub enum DeriveClause {
196    Patterns(Vec<DerivePattern>),
197    Merge(String, String),
198}
199
200#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
201pub struct DerivePattern {
202    pub direction: Direction,
203    pub source: DeriveNodeSpec,
204    pub edge: DeriveEdgeSpec,
205    pub target: DeriveNodeSpec,
206}
207
208#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
209pub struct DeriveNodeSpec {
210    pub is_new: bool,
211    pub variable: String,
212    pub labels: Vec<String>,
213    pub properties: Option<Expr>,
214}
215
216#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
217pub struct DeriveEdgeSpec {
218    pub edge_type: String,
219    pub properties: Option<Expr>,
220}
221
222// ═══════════════════════════════════════════════════════════════════════════
223// GOAL-DIRECTED QUERY
224// ═══════════════════════════════════════════════════════════════════════════
225
226/// `QUERY ruleName [WHERE expr] [RETURN ...]`
227#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
228pub struct GoalQuery {
229    pub rule_name: QualifiedName,
230    pub where_expr: Option<Expr>,
231    pub return_clause: Option<ReturnClause>,
232}
233
234// ═══════════════════════════════════════════════════════════════════════════
235// DERIVE COMMAND (top-level)
236// ═══════════════════════════════════════════════════════════════════════════
237
238/// `DERIVE ruleName [WHERE expr]`
239#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240pub struct DeriveCommand {
241    pub rule_name: QualifiedName,
242    pub where_expr: Option<Expr>,
243}
244
245// ═══════════════════════════════════════════════════════════════════════════
246// ASSUME BLOCK
247// ═══════════════════════════════════════════════════════════════════════════
248
249/// `ASSUME { mutations } THEN body`
250#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
251pub struct AssumeBlock {
252    pub mutations: Vec<crate::ast::Clause>,
253    pub body: Vec<LocyStatement>,
254}
255
256// ═══════════════════════════════════════════════════════════════════════════
257// ABDUCE QUERY
258// ═══════════════════════════════════════════════════════════════════════════
259
260/// `ABDUCE [NOT] ruleName [WHERE expr] [RETURN ...]`
261#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
262pub struct AbduceQuery {
263    pub negated: bool,
264    pub rule_name: QualifiedName,
265    pub where_expr: Option<Expr>,
266    pub return_clause: Option<ReturnClause>,
267}
268
269// ═══════════════════════════════════════════════════════════════════════════
270// EXPLAIN RULE
271// ═══════════════════════════════════════════════════════════════════════════
272
273/// `EXPLAIN RULE ruleName [WHERE expr] [RETURN ...]`
274#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
275pub struct ExplainRule {
276    pub rule_name: QualifiedName,
277    pub where_expr: Option<Expr>,
278    pub return_clause: Option<ReturnClause>,
279}