arithmetic_eval/executable/
mod.rs

1//! Executables output by a `Compiler` and related types.
2
3use core::{fmt, ops};
4
5use crate::{
6    alloc::{Box, String},
7    arith::{OrdArithmetic, StdArithmetic},
8    compiler::{Compiler, ImportSpans},
9    error::{Backtrace, ErrorWithBacktrace},
10    Environment, Error, ErrorKind, Value, VariableMap,
11};
12use arithmetic_parser::{grammars::Grammar, Block, StripCode};
13
14mod command;
15mod module_id;
16mod registers;
17
18pub use self::module_id::{IndexedId, ModuleId, WildcardId};
19pub(crate) use self::{
20    command::{Atom, Command, CompiledExpr, FieldName, SpannedAtom},
21    registers::{Executable, ExecutableFn, Registers},
22};
23
24/// Executable module together with its imports.
25///
26/// An `ExecutableModule` is a result of compiling a `Block` of statements. A module can *import*
27/// [`Value`]s, such as [commonly used functions](crate::fns). Importing is performed
28/// when building the module.
29///
30/// After the module is created, it can be [`run`](Self::run). If the last statement of the block
31/// is an expression (that is, not terminated with a `;`), it is the result of the execution;
32/// otherwise, the result is [`Value::void()`]. It is possible to run a module multiple times
33/// and to change imports by using [`Self::set_import()`].
34///
35/// In some cases (e.g., when building a REPL) it is useful to get not only the outcome
36/// of the module execution, but the intermediate results as well. Use [`Self::run_in_env()`]
37/// for such cases.
38///
39/// `ExecutableModule`s are generic with respect to the primitive value type, just like [`Value`].
40/// The lifetime of a module depends on the lifetime of the code, but this dependency
41/// can be eliminated via [`StripCode`] implementation.
42///
43/// # Examples
44///
45/// ## Basic usage
46///
47/// ```
48/// use arithmetic_parser::grammars::{F32Grammar, Parse, Untyped};
49/// use arithmetic_eval::{fns, Comparisons, ExecutableModule, Prelude, Value};
50/// # use core::iter::FromIterator;
51/// # use hashbrown::HashSet;
52///
53/// # fn main() -> anyhow::Result<()> {
54/// let module = Untyped::<F32Grammar>::parse_statements("xs.fold(-INFINITY, max)")?;
55/// let mut module = ExecutableModule::builder("test", &module)?
56///     .with_imports_from(&Prelude)
57///     .with_imports_from(&Comparisons)
58///     .with_import("INFINITY", Value::Prim(f32::INFINITY))
59///     // Set remaining imports to a fixed value.
60///     .set_imports(|_| Value::void());
61///
62/// // With the original imports, the returned value is `-INFINITY`.
63/// assert_eq!(module.run()?, Value::Prim(f32::NEG_INFINITY));
64///
65/// // Imports can be changed. Let's check that `xs` is indeed an import.
66/// assert!(module.imports().contains("xs"));
67/// // ...or even
68/// assert!(module.imports()["fold"].is_function());
69/// // It's possible to iterate over imports, too.
70/// let imports = module.imports().iter()
71///     .map(|(name, _)| name)
72///     .collect::<HashSet<_>>();
73/// assert!(imports.is_superset(&HashSet::from_iter(vec!["max", "fold"])));
74/// # drop(imports); // necessary to please the borrow checker
75///
76/// // Change the `xs` import and run the module again.
77/// let array = [1.0, -3.0, 2.0, 0.5].iter().copied()
78///     .map(Value::Prim)
79///     .collect();
80/// module.set_import("xs", Value::Tuple(array));
81/// assert_eq!(module.run()?, Value::Prim(2.0));
82/// # Ok(())
83/// # }
84/// ```
85///
86/// ## Reusing a module
87///
88/// The same module can be run with multiple imports:
89///
90/// ```
91/// # use arithmetic_parser::grammars::{F32Grammar, Parse, Untyped};
92/// # use arithmetic_eval::{Environment, ExecutableModule, Value};
93/// # use core::iter::FromIterator;
94/// # fn main() -> anyhow::Result<()> {
95/// let block = Untyped::<F32Grammar>::parse_statements("x + y")?;
96/// let mut module = ExecutableModule::builder("test", &block)?
97///     .with_import("x", Value::Prim(3.0))
98///     .with_import("y", Value::Prim(5.0))
99///     .build();
100/// assert_eq!(module.run()?, Value::Prim(8.0));
101///
102/// let mut env = Environment::from_iter(module.imports());
103/// env.insert("x", Value::Prim(-1.0));
104/// assert_eq!(module.run_in_env(&mut env)?, Value::Prim(4.0));
105/// # Ok(())
106/// # }
107/// ```
108///
109/// ## Behavior on errors
110///
111/// `run_in_env` modifies the environment even if an error occurs during execution:
112///
113/// ```
114/// # use arithmetic_parser::grammars::{F32Grammar, Parse, Untyped};
115/// # use arithmetic_eval::{Assertions, Environment, ExecutableModule, Value};
116/// # use core::iter::FromIterator;
117/// # fn main() -> anyhow::Result<()> {
118/// let module = Untyped::<F32Grammar>::parse_statements("x = 5; assert_eq(x, 4);")?;
119/// let module = ExecutableModule::builder("test", &module)?
120///     .with_imports_from(&Assertions)
121///     .build();
122///
123/// let mut env = Environment::from_iter(module.imports());
124/// assert!(module.run_in_env(&mut env).is_err());
125/// assert_eq!(env["x"], Value::Prim(5.0));
126/// # Ok(())
127/// # }
128/// ```
129#[derive(Debug)]
130pub struct ExecutableModule<'a, T> {
131    inner: Executable<'a, T>,
132    imports: ModuleImports<'a, T>,
133}
134
135impl<T: Clone> Clone for ExecutableModule<'_, T> {
136    fn clone(&self) -> Self {
137        Self {
138            inner: self.inner.clone(),
139            imports: self.imports.clone(),
140        }
141    }
142}
143
144impl<T: 'static + Clone> StripCode for ExecutableModule<'_, T> {
145    type Stripped = ExecutableModule<'static, T>;
146
147    fn strip_code(self) -> Self::Stripped {
148        ExecutableModule {
149            inner: self.inner.strip_code(),
150            imports: self.imports.strip_code(),
151        }
152    }
153}
154
155impl<'a, T> ExecutableModule<'a, T> {
156    pub(crate) fn from_parts(inner: Executable<'a, T>, imports: Registers<'a, T>) -> Self {
157        Self {
158            inner,
159            imports: ModuleImports { inner: imports },
160        }
161    }
162
163    /// Gets the identifier of this module.
164    pub fn id(&self) -> &dyn ModuleId {
165        self.inner.id()
166    }
167
168    /// Sets the value of an imported variable.
169    ///
170    /// # Panics
171    ///
172    /// Panics if the variable with the specified name is not an import. Check
173    /// that the import exists beforehand via [`imports().contains()`] if this is
174    /// unknown at compile time.
175    ///
176    /// [`imports().contains()`]: ModuleImports::contains()
177    pub fn set_import(&mut self, name: &str, value: Value<'a, T>) -> &mut Self {
178        self.imports.inner.set_var(name, value);
179        self
180    }
181
182    /// Returns shared reference to imports of this module.
183    pub fn imports(&self) -> &ModuleImports<'a, T> {
184        &self.imports
185    }
186
187    /// Combines this module with the specified `arithmetic`.
188    pub fn with_arithmetic<'s>(
189        &'s self,
190        arithmetic: &'s dyn OrdArithmetic<T>,
191    ) -> WithArithmetic<'s, 'a, T> {
192        WithArithmetic {
193            module: self,
194            arithmetic,
195        }
196    }
197}
198
199impl<'a, T: Clone + fmt::Debug> ExecutableModule<'a, T> {
200    /// Starts building a new module.
201    pub fn builder<G, Id>(
202        id: Id,
203        block: &Block<'a, G>,
204    ) -> Result<ExecutableModuleBuilder<'a, T>, Error<'a>>
205    where
206        Id: ModuleId,
207        G: Grammar<'a, Lit = T>,
208    {
209        let (module, import_spans) = Compiler::compile_module(id, block)?;
210        Ok(ExecutableModuleBuilder::new(module, import_spans))
211    }
212
213    fn run_with_registers(
214        &self,
215        registers: &mut Registers<'a, T>,
216        arithmetic: &dyn OrdArithmetic<T>,
217    ) -> Result<Value<'a, T>, ErrorWithBacktrace<'a>> {
218        let mut backtrace = Backtrace::default();
219        registers
220            .execute(&self.inner, arithmetic, Some(&mut backtrace))
221            .map_err(|err| ErrorWithBacktrace::new(err, backtrace))
222    }
223}
224
225impl<'a, T: Clone + fmt::Debug> ExecutableModule<'a, T>
226where
227    StdArithmetic: OrdArithmetic<T>,
228{
229    /// Runs the module with the current values of imports. This is a read-only operation;
230    /// neither the imports, nor other module state are modified by it.
231    pub fn run(&self) -> Result<Value<'a, T>, ErrorWithBacktrace<'a>> {
232        self.with_arithmetic(&StdArithmetic).run()
233    }
234
235    /// Runs the module with the specified [`Environment`]. The environment may contain some of
236    /// module imports; they will be used to override imports defined in the module.
237    ///
238    /// On execution, the environment is modified to reflect assignments in the topmost scope
239    /// of the module. The modification takes place regardless of whether or not the execution
240    /// succeeds. That is, if an error occurs, all preceding assignments in the topmost scope
241    /// still take place. See [the relevant example](#behavior-on-errors).
242    pub fn run_in_env(
243        &self,
244        env: &mut Environment<'a, T>,
245    ) -> Result<Value<'a, T>, ErrorWithBacktrace<'a>> {
246        self.with_arithmetic(&StdArithmetic).run_in_env(env)
247    }
248}
249
250/// Container for an [`ExecutableModule`] together with an [`OrdArithmetic`].
251#[derive(Debug)]
252pub struct WithArithmetic<'r, 'a, T> {
253    module: &'r ExecutableModule<'a, T>,
254    arithmetic: &'r dyn OrdArithmetic<T>,
255}
256
257impl<T> Clone for WithArithmetic<'_, '_, T> {
258    fn clone(&self) -> Self {
259        Self {
260            module: self.module,
261            arithmetic: self.arithmetic,
262        }
263    }
264}
265
266impl<T> Copy for WithArithmetic<'_, '_, T> {}
267
268impl<'a, T> WithArithmetic<'_, 'a, T>
269where
270    T: Clone + fmt::Debug,
271{
272    /// Runs the module with the previously provided [`OrdArithmetic`] and the current values
273    /// of imports.
274    ///
275    /// See [`ExecutableModule::run()`] for more details.
276    pub fn run(self) -> Result<Value<'a, T>, ErrorWithBacktrace<'a>> {
277        let mut registers = self.module.imports.inner.clone();
278        self.module
279            .run_with_registers(&mut registers, self.arithmetic)
280    }
281
282    /// Runs the module with the specified [`Environment`]. The environment may contain some of
283    /// module imports; they will be used to override imports defined in the module.
284    ///
285    /// See [`ExecutableModule::run_in_env()`] for more details.
286    pub fn run_in_env(
287        self,
288        env: &mut Environment<'a, T>,
289    ) -> Result<Value<'a, T>, ErrorWithBacktrace<'a>> {
290        let mut registers = self.module.imports.inner.clone();
291        registers.update_from_env(env);
292
293        let result = self
294            .module
295            .run_with_registers(&mut registers, self.arithmetic);
296        registers.update_env(env);
297        result
298    }
299}
300
301/// Imports of an [`ExecutableModule`].
302///
303/// Note that imports implement [`Index`](ops::Index) trait, which allows to eloquently
304/// get imports by name.
305#[derive(Debug)]
306pub struct ModuleImports<'a, T> {
307    inner: Registers<'a, T>,
308}
309
310impl<T: Clone> Clone for ModuleImports<'_, T> {
311    fn clone(&self) -> Self {
312        Self {
313            inner: self.inner.clone(),
314        }
315    }
316}
317
318impl<T: 'static + Clone> StripCode for ModuleImports<'_, T> {
319    type Stripped = ModuleImports<'static, T>;
320
321    fn strip_code(self) -> Self::Stripped {
322        ModuleImports {
323            inner: self.inner.strip_code(),
324        }
325    }
326}
327
328impl<'a, T> ModuleImports<'a, T> {
329    /// Checks if the imports contain a variable with the specified name.
330    pub fn contains(&self, name: &str) -> bool {
331        self.inner.variables_map().contains_key(name)
332    }
333
334    /// Gets the current value of the import with the specified name, or `None` if the import
335    /// is not defined.
336    pub fn get(&self, name: &str) -> Option<&Value<'a, T>> {
337        self.inner.get_var(name)
338    }
339
340    /// Iterates over imported variables.
341    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value<'a, T>)> + '_ {
342        self.inner.variables()
343    }
344}
345
346impl<'a, T> ops::Index<&str> for ModuleImports<'a, T> {
347    type Output = Value<'a, T>;
348
349    fn index(&self, index: &str) -> &Self::Output {
350        self.inner
351            .get_var(index)
352            .unwrap_or_else(|| panic!("Import `{}` is not defined", index))
353    }
354}
355
356impl<'a, T: Clone + 'a> IntoIterator for ModuleImports<'a, T> {
357    type Item = (String, Value<'a, T>);
358    type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
359
360    fn into_iter(self) -> Self::IntoIter {
361        Box::new(self.inner.into_variables())
362    }
363}
364
365impl<'a, 'r, T> IntoIterator for &'r ModuleImports<'a, T> {
366    type Item = (&'r str, &'r Value<'a, T>);
367    type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'r>;
368
369    fn into_iter(self) -> Self::IntoIter {
370        Box::new(self.iter())
371    }
372}
373
374/// Builder for an `ExecutableModule`.
375///
376/// The builder can be created via [`ExecutableModule::builder()`]. See [`ExecutableModule`] docs
377/// for the examples of usage.
378#[derive(Debug)]
379pub struct ExecutableModuleBuilder<'a, T> {
380    module: ExecutableModule<'a, T>,
381    undefined_imports: ImportSpans<'a>,
382}
383
384impl<'a, T> ExecutableModuleBuilder<'a, T> {
385    fn new(module: ExecutableModule<'a, T>, undefined_imports: ImportSpans<'a>) -> Self {
386        Self {
387            module,
388            undefined_imports,
389        }
390    }
391
392    /// Checks if all necessary imports are defined for this module.
393    pub fn has_undefined_imports(&self) -> bool {
394        self.undefined_imports.is_empty()
395    }
396
397    /// Iterates over the names of undefined imports.
398    pub fn undefined_imports(&self) -> impl Iterator<Item = &str> + '_ {
399        self.undefined_imports.keys().map(String::as_str)
400    }
401
402    /// Adds a single import. If the specified variable is not an import, does nothing.
403    pub fn with_import(mut self, name: &str, value: Value<'a, T>) -> Self {
404        if self.module.imports.contains(name) {
405            self.module.set_import(name, value);
406        }
407        self.undefined_imports.remove(name);
408        self
409    }
410
411    /// Sets undefined imports from the specified source. Imports defined previously and present
412    /// in the source are **not** overridden.
413    pub fn with_imports_from<V>(mut self, source: &V) -> Self
414    where
415        V: VariableMap<'a, T> + ?Sized,
416    {
417        let module = &mut self.module;
418        self.undefined_imports.retain(|var_name, _| {
419            source.get_variable(var_name).map_or(true, |value| {
420                module.set_import(var_name, value);
421                false
422            })
423        });
424        self
425    }
426
427    /// Tries to build this module.
428    ///
429    /// # Errors
430    ///
431    /// Fails if this module has at least one undefined import. In this case, the returned error
432    /// highlights one of such imports.
433    pub fn try_build(self) -> Result<ExecutableModule<'a, T>, Error<'a>> {
434        if let Some((var_name, span)) = self.undefined_imports.iter().next() {
435            let err = ErrorKind::Undefined(var_name.clone());
436            Err(Error::new(self.module.id(), span, err))
437        } else {
438            Ok(self.module)
439        }
440    }
441
442    /// A version of [`Self::try_build()`] that panics if there are undefined imports.
443    ///
444    /// # Panics
445    ///
446    /// - Panics if there are undefined imports.
447    pub fn build(self) -> ExecutableModule<'a, T> {
448        self.try_build().unwrap()
449    }
450
451    /// Sets the undefined imports using the provided closure and returns the resulting module.
452    /// The closure is called with the name of each undefined import and should return
453    /// the corresponding [`Value`].
454    pub fn set_imports<F>(mut self, mut setter: F) -> ExecutableModule<'a, T>
455    where
456        F: FnMut(&str) -> Value<'a, T>,
457    {
458        for var_name in self.undefined_imports.keys() {
459            self.module.set_import(var_name, setter(var_name));
460        }
461        self.module
462    }
463}
464
465#[cfg(test)]
466mod tests {
467    use super::*;
468    use crate::{compiler::Compiler, WildcardId};
469
470    use arithmetic_parser::grammars::{F32Grammar, Parse, Untyped};
471
472    #[test]
473    fn cloning_module() {
474        let block = "y = x + 2 * (x + 1) + 1; y";
475        let block = Untyped::<F32Grammar>::parse_statements(block).unwrap();
476        let (mut module, _) = Compiler::compile_module(WildcardId, &block).unwrap();
477
478        let mut module_copy = module.clone();
479        module_copy.set_import("x", Value::Prim(10.0));
480        let value = module_copy.run().unwrap();
481        assert_eq!(value, Value::Prim(33.0));
482
483        module.set_import("x", Value::Prim(5.0));
484        let value = module.run().unwrap();
485        assert_eq!(value, Value::Prim(18.0));
486    }
487}