use core::{fmt, ops};
use crate::{
alloc::{Box, String},
arith::{OrdArithmetic, StdArithmetic},
compiler::{Compiler, ImportSpans},
error::{Backtrace, ErrorWithBacktrace},
Environment, Error, ErrorKind, Value, VariableMap,
};
use arithmetic_parser::{grammars::Grammar, Block, StripCode};
mod command;
mod module_id;
mod registers;
pub use self::module_id::{IndexedId, ModuleId, WildcardId};
pub(crate) use self::{
command::{Atom, Command, CompiledExpr, FieldName, SpannedAtom},
registers::{Executable, ExecutableFn, Registers},
};
#[derive(Debug)]
pub struct ExecutableModule<'a, T> {
inner: Executable<'a, T>,
imports: ModuleImports<'a, T>,
}
impl<T: Clone> Clone for ExecutableModule<'_, T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
imports: self.imports.clone(),
}
}
}
impl<T: 'static + Clone> StripCode for ExecutableModule<'_, T> {
type Stripped = ExecutableModule<'static, T>;
fn strip_code(self) -> Self::Stripped {
ExecutableModule {
inner: self.inner.strip_code(),
imports: self.imports.strip_code(),
}
}
}
impl<'a, T> ExecutableModule<'a, T> {
pub(crate) fn from_parts(inner: Executable<'a, T>, imports: Registers<'a, T>) -> Self {
Self {
inner,
imports: ModuleImports { inner: imports },
}
}
pub fn id(&self) -> &dyn ModuleId {
self.inner.id()
}
pub fn set_import(&mut self, name: &str, value: Value<'a, T>) -> &mut Self {
self.imports.inner.set_var(name, value);
self
}
pub fn imports(&self) -> &ModuleImports<'a, T> {
&self.imports
}
pub fn with_arithmetic<'s>(
&'s self,
arithmetic: &'s dyn OrdArithmetic<T>,
) -> WithArithmetic<'s, 'a, T> {
WithArithmetic {
module: self,
arithmetic,
}
}
}
impl<'a, T: Clone + fmt::Debug> ExecutableModule<'a, T> {
pub fn builder<G, Id>(
id: Id,
block: &Block<'a, G>,
) -> Result<ExecutableModuleBuilder<'a, T>, Error<'a>>
where
Id: ModuleId,
G: Grammar<'a, Lit = T>,
{
let (module, import_spans) = Compiler::compile_module(id, block)?;
Ok(ExecutableModuleBuilder::new(module, import_spans))
}
fn run_with_registers(
&self,
registers: &mut Registers<'a, T>,
arithmetic: &dyn OrdArithmetic<T>,
) -> Result<Value<'a, T>, ErrorWithBacktrace<'a>> {
let mut backtrace = Backtrace::default();
registers
.execute(&self.inner, arithmetic, Some(&mut backtrace))
.map_err(|err| ErrorWithBacktrace::new(err, backtrace))
}
}
impl<'a, T: Clone + fmt::Debug> ExecutableModule<'a, T>
where
StdArithmetic: OrdArithmetic<T>,
{
pub fn run(&self) -> Result<Value<'a, T>, ErrorWithBacktrace<'a>> {
self.with_arithmetic(&StdArithmetic).run()
}
pub fn run_in_env(
&self,
env: &mut Environment<'a, T>,
) -> Result<Value<'a, T>, ErrorWithBacktrace<'a>> {
self.with_arithmetic(&StdArithmetic).run_in_env(env)
}
}
#[derive(Debug)]
pub struct WithArithmetic<'r, 'a, T> {
module: &'r ExecutableModule<'a, T>,
arithmetic: &'r dyn OrdArithmetic<T>,
}
impl<T> Clone for WithArithmetic<'_, '_, T> {
fn clone(&self) -> Self {
Self {
module: self.module,
arithmetic: self.arithmetic,
}
}
}
impl<T> Copy for WithArithmetic<'_, '_, T> {}
impl<'a, T> WithArithmetic<'_, 'a, T>
where
T: Clone + fmt::Debug,
{
pub fn run(self) -> Result<Value<'a, T>, ErrorWithBacktrace<'a>> {
let mut registers = self.module.imports.inner.clone();
self.module
.run_with_registers(&mut registers, self.arithmetic)
}
pub fn run_in_env(
self,
env: &mut Environment<'a, T>,
) -> Result<Value<'a, T>, ErrorWithBacktrace<'a>> {
let mut registers = self.module.imports.inner.clone();
registers.update_from_env(env);
let result = self
.module
.run_with_registers(&mut registers, self.arithmetic);
registers.update_env(env);
result
}
}
#[derive(Debug)]
pub struct ModuleImports<'a, T> {
inner: Registers<'a, T>,
}
impl<T: Clone> Clone for ModuleImports<'_, T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<T: 'static + Clone> StripCode for ModuleImports<'_, T> {
type Stripped = ModuleImports<'static, T>;
fn strip_code(self) -> Self::Stripped {
ModuleImports {
inner: self.inner.strip_code(),
}
}
}
impl<'a, T> ModuleImports<'a, T> {
pub fn contains(&self, name: &str) -> bool {
self.inner.variables_map().contains_key(name)
}
pub fn get(&self, name: &str) -> Option<&Value<'a, T>> {
self.inner.get_var(name)
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &Value<'a, T>)> + '_ {
self.inner.variables()
}
}
impl<'a, T> ops::Index<&str> for ModuleImports<'a, T> {
type Output = Value<'a, T>;
fn index(&self, index: &str) -> &Self::Output {
self.inner
.get_var(index)
.unwrap_or_else(|| panic!("Import `{}` is not defined", index))
}
}
impl<'a, T: Clone + 'a> IntoIterator for ModuleImports<'a, T> {
type Item = (String, Value<'a, T>);
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
fn into_iter(self) -> Self::IntoIter {
Box::new(self.inner.into_variables())
}
}
impl<'a, 'r, T> IntoIterator for &'r ModuleImports<'a, T> {
type Item = (&'r str, &'r Value<'a, T>);
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'r>;
fn into_iter(self) -> Self::IntoIter {
Box::new(self.iter())
}
}
#[derive(Debug)]
pub struct ExecutableModuleBuilder<'a, T> {
module: ExecutableModule<'a, T>,
undefined_imports: ImportSpans<'a>,
}
impl<'a, T> ExecutableModuleBuilder<'a, T> {
fn new(module: ExecutableModule<'a, T>, undefined_imports: ImportSpans<'a>) -> Self {
Self {
module,
undefined_imports,
}
}
pub fn has_undefined_imports(&self) -> bool {
self.undefined_imports.is_empty()
}
pub fn undefined_imports(&self) -> impl Iterator<Item = &str> + '_ {
self.undefined_imports.keys().map(String::as_str)
}
pub fn with_import(mut self, name: &str, value: Value<'a, T>) -> Self {
if self.module.imports.contains(name) {
self.module.set_import(name, value);
}
self.undefined_imports.remove(name);
self
}
pub fn with_imports_from<V>(mut self, source: &V) -> Self
where
V: VariableMap<'a, T> + ?Sized,
{
let module = &mut self.module;
self.undefined_imports.retain(|var_name, _| {
source.get_variable(var_name).map_or(true, |value| {
module.set_import(var_name, value);
false
})
});
self
}
pub fn try_build(self) -> Result<ExecutableModule<'a, T>, Error<'a>> {
if let Some((var_name, span)) = self.undefined_imports.iter().next() {
let err = ErrorKind::Undefined(var_name.clone());
Err(Error::new(self.module.id(), span, err))
} else {
Ok(self.module)
}
}
pub fn build(self) -> ExecutableModule<'a, T> {
self.try_build().unwrap()
}
pub fn set_imports<F>(mut self, mut setter: F) -> ExecutableModule<'a, T>
where
F: FnMut(&str) -> Value<'a, T>,
{
for var_name in self.undefined_imports.keys() {
self.module.set_import(var_name, setter(var_name));
}
self.module
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{compiler::Compiler, WildcardId};
use arithmetic_parser::grammars::{F32Grammar, Parse, Untyped};
#[test]
fn cloning_module() {
let block = "y = x + 2 * (x + 1) + 1; y";
let block = Untyped::<F32Grammar>::parse_statements(block).unwrap();
let (mut module, _) = Compiler::compile_module(WildcardId, &block).unwrap();
let mut module_copy = module.clone();
module_copy.set_import("x", Value::Prim(10.0));
let value = module_copy.run().unwrap();
assert_eq!(value, Value::Prim(33.0));
module.set_import("x", Value::Prim(5.0));
let value = module.run().unwrap();
assert_eq!(value, Value::Prim(18.0));
}
}