Skip to main content

uni_locy/compiler/
errors.rs

1use thiserror::Error;
2
3#[derive(Debug, Error, Clone, PartialEq)]
4pub enum LocyCompileError {
5    #[error("cyclic negation among rules: {}", rules.join(", "))]
6    CyclicNegation { rules: Vec<String> },
7
8    #[error("undefined rule: {name}")]
9    UndefinedRule { name: String },
10
11    #[error("prev reference in non-recursive rule '{rule}', field '{field}'")]
12    PrevInBaseCase { rule: String, field: String },
13
14    #[error("non-monotonic aggregate '{aggregate}' in recursive rule '{rule}'")]
15    NonMonotonicInRecursion { rule: String, aggregate: String },
16
17    #[error("BEST BY with monotonic fold '{fold}' in rule '{rule}'")]
18    BestByWithMonotonicFold { rule: String, fold: String },
19
20    #[error("post-FOLD WHERE in rule '{rule}' requires a FOLD clause")]
21    HavingWithoutFold { rule: String },
22
23    #[error("wardedness violation: variable '{variable}' in rule '{rule}' not bound by MATCH")]
24    WardednessViolation { rule: String, variable: String },
25
26    #[error("YIELD schema mismatch in rule '{rule}': {detail}")]
27    YieldSchemaMismatch { rule: String, detail: String },
28
29    #[error("mixed priority in rule '{rule}': some clauses have PRIORITY, others don't")]
30    MixedPriority { rule: String },
31
32    #[error("module not found: {name}")]
33    ModuleNotFound { name: String },
34
35    #[error("import not found: rule '{rule}' in module '{module}'")]
36    ImportNotFound { module: String, rule: String },
37
38    #[error(
39        "IS arity mismatch in rule '{rule}': reference to '{target}' provides {actual} bindings, but '{target}' yields {expected} columns"
40    )]
41    IsArityMismatch {
42        rule: String,
43        target: String,
44        expected: usize,
45        actual: usize,
46    },
47
48    #[error(
49        "prev.{field} in rule '{rule}' references unknown column; available columns from IS references: {available}"
50    )]
51    PrevFieldNotInSchema {
52        rule: String,
53        field: String,
54        available: String,
55    },
56
57    #[error("rule '{rule}' has {count} PROB columns; at most 1 is allowed")]
58    MultipleProbColumns { rule: String, count: usize },
59
60    // ─── Phase B (neural predicates preview) ─────────────────────────────
61    #[error(
62        "CREATE MODEL '{model_name}' parsed but neural_predicates_preview is disabled; \
63         set LocyConfig::neural_predicates_preview = true to enable"
64    )]
65    NeuralPreviewDisabled { model_name: String },
66
67    #[error("model name collision: '{name}' is already declared")]
68    ModelNameCollision { name: String },
69
70    #[error(
71        "model '{name}' arity mismatch in rule '{rule}': expected {expected} input(s), got {actual}"
72    )]
73    ModelArityMismatch {
74        name: String,
75        rule: String,
76        expected: usize,
77        actual: usize,
78    },
79
80    // ─── Phase C C2: CALIBRATE statement ────────────────────────────────
81    #[error(
82        "CALIBRATE references unknown model '{name}'; declare it with \
83         CREATE MODEL first"
84    )]
85    CalibrateUnknownModel { name: String },
86
87    #[error(
88        "CALIBRATE on model '{name}': calibration only applies to PROB \
89         outputs, but '{name}' is declared as {declared}"
90    )]
91    CalibrateOnNonProbModel { name: String, declared: String },
92
93    #[error(
94        "CALIBRATE on model '{model_name}': HOLDOUT must be in the open \
95         interval (0, 1); got {holdout}"
96    )]
97    CalibrateInvalidHoldout { model_name: String, holdout: f64 },
98
99    #[error(
100        "CALIBRATE '{model_name}' parsed but neural_predicates_preview is \
101         disabled; set LocyConfig::neural_predicates_preview = true to enable"
102    )]
103    CalibratePreviewDisabled { model_name: String },
104
105    // ─── Phase C C3: VALIDATE statement ─────────────────────────────────
106    #[error(
107        "VALIDATE references unknown rule '{name}'; declare it with \
108         CREATE RULE first"
109    )]
110    ValidateUnknownRule { name: String },
111
112    #[error(
113        "VALIDATE rule '{name}' has no PROB column; calibration metrics \
114         only apply to probability outputs"
115    )]
116    ValidateRuleHasNoProbColumn { name: String },
117
118    #[error("VALIDATE rule '{name}' must request at least one metric")]
119    ValidateNoMetrics { name: String },
120
121    /// Phase B follow-up: a WHERE clause invokes a neural model.
122    /// The lift machinery would require splitting the rule's
123    /// `body_logical` into pre-filter and post-filter halves so the
124    /// classifier can run between them — a planner refactor we've
125    /// scoped out of the current slice. Surface a clear error at
126    /// compile time directing the user to move the invocation into
127    /// a YIELD item, e.g. as a witness column they can filter on
128    /// downstream.
129    #[error(
130        "rule '{rule}' invokes neural model '{model}' in a WHERE clause, \
131         which is not yet supported. Lift the call into YIELD (e.g. \
132         `YIELD KEY x, {model}(x) AS p`) and apply the filter on the \
133         materialized rule output instead."
134    )]
135    WhereModelInvocationNotYetSupported { rule: String, model: String },
136
137    /// A neural-model invocation's feature expression is not a plain
138    /// variable or a single `node.property` access. Today's runtime
139    /// reads features either from match-bound variables (e.g.
140    /// `scorer(s)`) or from materialized property columns (e.g.
141    /// `scorer(s.tier)`); arithmetic (`scorer(s.tier + 1)`) and nested
142    /// calls (`scorer(normalize(s.revenue))`) are deferred to a
143    /// follow-up slice.
144    #[error(
145        "rule '{rule}': neural model '{model}' feature expression \
146         {expr} is unsupported — only plain variables and direct \
147         property access (`var.prop`) are accepted today"
148    )]
149    UnsupportedFeatureExpression {
150        rule: String,
151        model: String,
152        expr: String,
153    },
154}