improv_engine/typed.rs
1//! Typed expression layer: the formula AST enriched with inferred value type
2//! and dimension (the set of categories a subexpression ranges over).
3//!
4//! Produced by the compiler's first phase (`compiler::infer`). See
5//! AGENT_STEERING.md / IMPROV.txt "Formula compiler".
6
7use improv_core_model::{BinaryOp, CategoryId, FuncId, UnaryOp, Value, ValueType};
8
9/// The dimensionality of a (sub)expression: the categories it is indexed by,
10/// kept sorted and deduplicated (canonical form).
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct Dim {
13 pub categories: Vec<CategoryId>,
14}
15
16impl Dim {
17 pub fn scalar() -> Self {
18 Dim {
19 categories: Vec::new(),
20 }
21 }
22
23 pub fn of(mut categories: Vec<CategoryId>) -> Self {
24 categories.sort();
25 categories.dedup();
26 Dim { categories }
27 }
28
29 pub fn is_scalar(&self) -> bool {
30 self.categories.is_empty()
31 }
32
33 /// True if `self`'s categories are a subset of `other`'s (broadcastable
34 /// into `other`).
35 pub fn is_subset_of(&self, other: &Dim) -> bool {
36 self.categories.iter().all(|c| other.categories.contains(c))
37 }
38
39 /// Union of two dimensions (the result dim when broadcasting).
40 pub fn union(&self, other: &Dim) -> Dim {
41 let mut cats = self.categories.clone();
42 cats.extend(other.categories.iter().copied());
43 Dim::of(cats)
44 }
45}
46
47/// A value type paired with its dimension.
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct TypeInfo {
50 pub value_type: ValueType,
51 pub dim: Dim,
52}
53
54/// The dimension-projection applied to a measure reference, resolved to the
55/// concrete category sets the compiler will keep / aggregate / drop.
56#[derive(Debug, Clone, PartialEq, Eq, Default)]
57pub struct ResolvedDimSpec {
58 pub by: Vec<CategoryId>,
59 pub over: Vec<CategoryId>,
60 pub except: Vec<CategoryId>,
61}
62
63#[derive(Debug, Clone, PartialEq)]
64pub enum TypedExprKind {
65 Literal(Value),
66 Ref(improv_core_model::MeasureId, ResolvedDimSpec),
67 UnaryOp(UnaryOp, Box<TypedExpr>),
68 BinaryOp(BinaryOp, Box<TypedExpr>, Box<TypedExpr>),
69 Call(FuncId, Vec<TypedExpr>),
70 /// A reduction over the `over` categories: `inputs` are the reduced
71 /// measures (same dimensions), `params` the other arguments (one value
72 /// per group), both in argument order.
73 Aggregate {
74 func: FuncId,
75 inputs: Vec<TypedExpr>,
76 params: Vec<TypedExpr>,
77 over: Vec<CategoryId>,
78 pred: Option<Box<TypedExpr>>,
79 window: Option<Window>,
80 /// An array function's landing categories (empty: not a list).
81 into: Vec<CategoryId>,
82 },
83 /// A per-item value of a category (`POSITION`, `@ITEM`): one cell per
84 /// item.
85 Items(CategoryId, Vec<(improv_core_model::ItemId, Value)>),
86 /// A fold (`SCAN`/`REDUCE`), already lowered: the whole formula.
87 Plan(Box<crate::plan::PlanNode>),
88 /// `COALESCE(a, …, default)` and `FILL(x, default)`: at each coordinate
89 /// of the domain, the first argument with a cell. The domain is `grid`
90 /// (every item combination, `FILL`) or else the cells of the arguments
91 /// that span the result (see `compiler::infer_coalesce`).
92 Coalesce {
93 args: Vec<TypedExpr>,
94 grid: Option<Vec<(CategoryId, Vec<improv_core_model::ItemId>)>>,
95 },
96 /// `RELABEL` / `SWAP`: `input` with each `(from, to, items)` category
97 /// renamed, its items mapped `(from item, to item)`; a cell whose item
98 /// has no mapping is dropped.
99 Remap {
100 input: Box<TypedExpr>,
101 maps: crate::plan::Remaps,
102 },
103 /// The value `offset` items along `cat` (`PREV` is −1), or `default`.
104 Shift {
105 input: Box<TypedExpr>,
106 cat: CategoryId,
107 offset: i64,
108 default: Option<Box<TypedExpr>>,
109 },
110}
111
112/// A window bound, relative to this item.
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum Bound {
115 First,
116 Last,
117 /// This item's position plus the offset.
118 Offset(i64),
119}
120
121/// `OVER T[from..to]`: a reduction per item over a window of items.
122pub type Window = (Bound, Bound);
123
124#[derive(Debug, Clone, PartialEq)]
125pub struct TypedExpr {
126 pub kind: TypedExprKind,
127 pub ty: TypeInfo,
128}