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}