vortex_expr/
scope.rs

1use std::any::Any;
2use std::fmt::Display;
3use std::str::FromStr;
4use std::sync::Arc;
5
6use itertools::Itertools;
7use vortex_array::{Array, ArrayRef};
8use vortex_dtype::{DType, FieldPathSet};
9use vortex_error::{VortexError, VortexResult, vortex_bail, vortex_err};
10use vortex_utils::aliases::hash_map::HashMap;
11
12type ExprScope<T> = HashMap<Identifier, T>;
13
14#[derive(Clone, Debug, Eq, PartialEq, Hash)]
15pub enum Identifier {
16    Identity,
17    Other(Arc<str>),
18}
19
20impl FromStr for Identifier {
21    type Err = VortexError;
22
23    fn from_str(s: &str) -> Result<Self, Self::Err> {
24        if s.is_empty() {
25            vortex_bail!("Empty strings aren't allowed in identifiers")
26        } else {
27            Ok(Identifier::Other(s.into()))
28        }
29    }
30}
31
32impl PartialEq<str> for Identifier {
33    fn eq(&self, other: &str) -> bool {
34        match self {
35            Identifier::Identity => other.is_empty(),
36            Identifier::Other(str) => str.as_ref() == other,
37        }
38    }
39}
40
41impl From<&str> for Identifier {
42    fn from(value: &str) -> Self {
43        if value.is_empty() {
44            Identifier::Identity
45        } else {
46            Identifier::Other(Arc::from(value))
47        }
48    }
49}
50
51impl Identifier {
52    pub fn is_identity(&self) -> bool {
53        matches!(self, Self::Identity)
54    }
55}
56
57impl Display for Identifier {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        match self {
60            Identifier::Identity => write!(f, ""),
61            Identifier::Other(v) => write!(f, "{v}"),
62        }
63    }
64}
65
66/// Scope define the evaluation context/scope that an expression uses when being evaluated.
67/// There is a special `Identifier` (`Identity`) which is used to bind the initial array being evaluated
68///
69/// Other identifier can be bound with variables either before execution or while executing (see `Let`).
70/// Values can be extracted from the scope using the `Var` expression.
71///
72/// ```code
73/// <let x = lit(1) in var(Identifier::Identity) + var(x), { Identity -> Primitive[1,2,3]> ->
74/// <var(Identifier::Identity) + var(x), { Identity -> Primitive[1,2,3], x -> ConstantArray(1)> ->
75/// <Primitive[1,2,3] + var(x), { Identity -> Primitive[1,2,3], x -> ConstantArray(1)> ->
76/// <Primitive[1,2,3] + ConstantArray(1), { Identity -> Primitive[1,2,3], x -> ConstantArray(1)> ->
77/// <Primitive[2,3,4], { Identity -> Primitive[1,2,3], x -> ConstantArray(1)>
78/// ```
79///
80/// Other values can be bound before execution e.g.
81///  `<var("x") + var("y") + var("z"), x -> ..., y -> ..., z -> ...>`
82#[derive(Clone, Default)]
83pub struct Scope {
84    array_len: usize,
85    root_scope: Option<ArrayRef>,
86    /// A map from identifiers to arrays
87    arrays: ExprScope<ArrayRef>,
88    /// A map identifiers to opaque values used by expressions, but
89    /// cannot affect the result type/shape.
90    vars: ExprScope<Arc<dyn Any + Send + Sync>>,
91}
92
93pub type ScopeElement = (Identifier, ArrayRef);
94
95impl Scope {
96    pub fn new(arr: ArrayRef) -> Self {
97        Self {
98            array_len: arr.len(),
99            root_scope: Some(arr),
100            ..Default::default()
101        }
102    }
103
104    pub fn empty(len: usize) -> Self {
105        Self {
106            array_len: len,
107            ..Default::default()
108        }
109    }
110
111    /// Get a value out of the scope by its [`Identifier`]
112    pub fn array(&self, id: &Identifier) -> Option<&ArrayRef> {
113        if id.is_identity() {
114            return self.root_scope.as_ref();
115        }
116        self.arrays.get(id)
117    }
118
119    pub fn vars(&self, id: Identifier) -> VortexResult<&Arc<dyn Any + Send + Sync>> {
120        self.vars
121            .get(&id)
122            .ok_or_else(|| vortex_err!("cannot find {} in var scope", id))
123    }
124
125    pub fn is_empty(&self) -> bool {
126        self.array_len == 0
127    }
128
129    pub fn len(&self) -> usize {
130        self.array_len
131    }
132
133    pub fn copy_with_array(&self, ident: Identifier, value: ArrayRef) -> Self {
134        self.clone().with_array(ident, value)
135    }
136
137    /// Register an array with an identifier in the scope, overriding any existing value stored in it.
138    pub fn with_array(mut self, ident: Identifier, value: ArrayRef) -> Self {
139        assert_eq!(value.len(), self.len());
140
141        if ident.is_identity() {
142            self.root_scope = Some(value);
143        } else {
144            self.arrays.insert(ident, value);
145        }
146        self
147    }
148
149    /// Register an array with an identifier in the scope, overriding any existing value stored in it.
150    pub fn with_array_pair(self, (ident, value): ScopeElement) -> Self {
151        self.with_array(ident, value)
152    }
153
154    pub fn with_var(mut self, ident: Identifier, var: Arc<dyn Any + Send + Sync>) -> Self {
155        self.vars.insert(ident, var);
156        self
157    }
158
159    pub fn iter(&self) -> impl Iterator<Item = (&Identifier, &ArrayRef)> {
160        let values = self.arrays.iter();
161
162        self.root_scope
163            .iter()
164            .map(|s| (&Identifier::Identity, s))
165            .chain(values)
166    }
167}
168
169impl From<ArrayRef> for Scope {
170    fn from(value: ArrayRef) -> Self {
171        Self::new(value)
172    }
173}
174
175#[derive(Clone, Default, Debug)]
176pub struct ScopeDType {
177    root: Option<DType>,
178    types: ExprScope<DType>,
179}
180
181impl Display for ScopeDType {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        if let Some(root) = self.root.as_ref() {
184            write!(f, "$: {}", root)?;
185        }
186        if !self.types.is_empty() {
187            write!(f, ". ")?;
188            write!(
189                f,
190                "{}",
191                self.types
192                    .iter()
193                    .format_with(",", |x, f| f(&format_args!("{}: {}", x.0, x.1)))
194            )?;
195        }
196        Ok(())
197    }
198}
199
200pub type ScopeDTypeElement = (Identifier, DType);
201
202impl From<&Scope> for ScopeDType {
203    fn from(ctx: &Scope) -> Self {
204        Self {
205            root: ctx.root_scope.as_ref().map(|s| s.dtype().clone()),
206            types: HashMap::from_iter(
207                ctx.arrays
208                    .iter()
209                    .map(|(k, v)| (k.clone(), v.dtype().clone())),
210            ),
211        }
212    }
213}
214
215impl ScopeDType {
216    pub fn new(dtype: DType) -> Self {
217        Self {
218            root: Some(dtype),
219            ..Default::default()
220        }
221    }
222
223    pub fn dtype(&self, id: &Identifier) -> Option<&DType> {
224        if id.is_identity() {
225            return self.root.as_ref();
226        }
227        self.types.get(id)
228    }
229
230    pub fn copy_with_dtype(&self, ident: Identifier, dtype: DType) -> Self {
231        self.clone().with_dtype(ident, dtype)
232    }
233
234    pub fn with_dtype(mut self, ident: Identifier, dtype: DType) -> Self {
235        if ident.is_identity() {
236            self.root = Some(dtype);
237        } else {
238            self.types.insert(ident, dtype);
239        }
240        self
241    }
242
243    pub fn with_dtype_element(self, (ident, dtype): ScopeDTypeElement) -> Self {
244        self.with_dtype(ident, dtype)
245    }
246}
247
248#[derive(Default, Clone, Debug)]
249pub struct ScopeFieldPathSet {
250    root: Option<FieldPathSet>,
251    sets: ExprScope<FieldPathSet>,
252}
253
254pub type ScopeFieldPathSetElement = (Identifier, FieldPathSet);
255
256impl ScopeFieldPathSet {
257    pub fn new(path_set: FieldPathSet) -> Self {
258        Self {
259            root: Some(path_set),
260            ..Default::default()
261        }
262    }
263
264    pub fn set(&self, id: &Identifier) -> Option<&FieldPathSet> {
265        if id.is_identity() {
266            return self.root.as_ref();
267        }
268        self.sets.get(id)
269    }
270
271    pub fn copy_with_set(&self, ident: Identifier, set: FieldPathSet) -> Self {
272        self.clone().with_set(ident, set)
273    }
274
275    pub fn with_set(mut self, ident: Identifier, set: FieldPathSet) -> Self {
276        if ident.is_identity() {
277            self.root = Some(set);
278        } else {
279            self.sets.insert(ident, set);
280        }
281        self
282    }
283
284    pub fn with_set_element(self, (ident, set): ScopeFieldPathSetElement) -> Self {
285        self.with_set(ident, set)
286    }
287}