pub struct ExecutableModule<T> { /* private fields */ }
Expand description

Executable module together with its imports.

An ExecutableModule is a result of compiling a Block of statements. A module can import Values, such as commonly used functions. Importing is performed when building the module.

After the module is created, it can be associated with an environment via Self::with_env() and run. If the last statement of the block is an expression (that is, not terminated with a ;), it is the result of the execution; otherwise, the result is Value::void().

In some cases (e.g., when building a REPL) it is useful to get not only the outcome of the module execution, but the intermediate results as well. Use Self::with_mutable_env() for such cases.

ExecutableModules are generic with respect to the primitive value type, just like Value. The lifetime of a module depends on the lifetime of the code, but this dependency can be eliminated via [StripCode] implementation.

Examples

Basic usage

use arithmetic_parser::grammars::{F32Grammar, Parse, Untyped};
use arithmetic_eval::{env, fns, Environment, ExecutableModule, Value};

let module = Untyped::<F32Grammar>::parse_statements(
    "xs.fold(-INFINITY, max)",
)?;
let module = ExecutableModule::new("test", &module)?;

let mut env = Environment::new();
env.insert("INFINITY", Value::Prim(f32::INFINITY))
    .insert("xs", Value::void())
    .extend(env::Prelude::iter().chain(env::Comparisons::iter()));

// With the original imports, the returned value is `-INFINITY`.
assert_eq!(module.with_env(&env)?.run()?, Value::Prim(f32::NEG_INFINITY));

// Imports can be changed. Let's check that `xs` is indeed an import.
assert!(module.is_import("xs"));
// It's possible to iterate over imports, too.
let imports = module.import_names().collect::<HashSet<_>>();
assert!(imports.is_superset(&HashSet::from_iter(vec!["max", "fold"])));

// Change the `xs` import and run the module again.
let array = [1.0, -3.0, 2.0, 0.5].iter().copied()
    .map(Value::Prim)
    .collect();
env.insert("xs", Value::Tuple(array));
assert_eq!(module.with_env(&env)?.run()?, Value::Prim(2.0));

Reusing a module

The same module can be run with multiple imports:

let block = Untyped::<F32Grammar>::parse_statements("x + y")?;
let module = ExecutableModule::new("test", &block)?;

let mut env = Environment::new();
env.insert("x", Value::Prim(3.0)).insert("y", Value::Prim(5.0));
assert_eq!(module.with_env(&env)?.run()?, Value::Prim(8.0));

env.insert("x", Value::Prim(-1.0));
assert_eq!(module.with_env(&env)?.run()?, Value::Prim(4.0));

Behavior on errors

Self::with_mutable_env() modifies the environment even if an error occurs during execution:

let module = Untyped::<F32Grammar>::parse_statements("x = 5; assert_eq(x, 4);")?;
let module = ExecutableModule::new("test", &module)?;

let mut env = Environment::new();
env.extend(Assertions::iter());
assert!(module.with_mutable_env(&mut env)?.run().is_err());
assert_eq!(env["x"], Value::Prim(5.0));

Implementations§

source§

impl<T: Clone + Debug> ExecutableModule<T>

source

pub fn new<G, Id>(id: Id, block: &Block<'_, G>) -> Result<Self, Error>where Id: ModuleId, G: Grammar<Lit = T>,

Creates a new module.

source§

impl<T> ExecutableModule<T>

source

pub fn id(&self) -> &Arc<dyn ModuleId>

Gets the identifier of this module.

source

pub fn import_names(&self) -> impl Iterator<Item = &str> + '_

Returns a shared reference to imports of this module.

source

pub fn is_import(&self, name: &str) -> bool

Checks if the specified variable is an import.

source

pub fn with_env<'s>( &'s self, env: &'s Environment<T> ) -> Result<WithEnvironment<'s, T>, Error>

Combines this module with the specified Environment. The environment must contain all module imports; otherwise, an error will be raised.

Errors

Returns an error if the environment does not contain all variables imported by this module.

source

pub fn with_mutable_env<'s>( &'s self, env: &'s mut Environment<T> ) -> Result<WithEnvironment<'s, T>, Error>

Analogue of Self::with_env() that modifies the provided Environment when the module is run.

Errors

Returns an error if the environment does not contain all variables imported by this module.

Trait Implementations§

source§

impl<T: Clone> Clone for ExecutableModule<T>

source§

fn clone(&self) -> ExecutableModule<T>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<T: Debug> Debug for ExecutableModule<T>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<T> !RefUnwindSafe for ExecutableModule<T>

§

impl<T> Send for ExecutableModule<T>where T: Send + Sync,

§

impl<T> Sync for ExecutableModule<T>where T: Send + Sync,

§

impl<T> Unpin for ExecutableModule<T>where T: Unpin,

§

impl<T> !UnwindSafe for ExecutableModule<T>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for Twhere T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.