//! Types used in the interpreter. use crate::ast; use std::backtrace::Backtrace; use std::collections::BTreeMap; use std::collections::HashMap; #[derive(Debug)] pub enum Error { DuplicateEnvNames(ast::EnvName, Backtrace), DuplicateNames(ast::Name, Backtrace), NameNotFound(ast::Name, Env, Backtrace), IndexOutOfBounds(usize, Backtrace), FieldNotFound(ast::Label, Backtrace), EnvNotFound(ast::EnvName, Backtrace), LastStatementNotAnExpr(Backtrace), NotAFunction(ast::Expr, Backtrace), NotARecord(ast::Value, Backtrace), NotAReference(ast::Expr, ast::Value, Backtrace), NotABoolean(ast::Value, Backtrace), NotAnInt(ast::Value, Backtrace), OpError(ast::Value, ast::Op, ast::Value, Backtrace), MigrationError(ast::Value, String, Backtrace), ArgumentsMismatch(Backtrace), } impl PartialEq for Error { fn eq(&self, other: &Self) -> bool { match (self, other) { (Error::DuplicateEnvNames(a1, _), Error::DuplicateEnvNames(a2, _)) => a1 == a2, (Error::DuplicateNames(a1, _), Error::DuplicateNames(a2, _)) => a1 == a2, (Error::IndexOutOfBounds(a1, _), Error::IndexOutOfBounds(a2, _)) => a1 == a2, (Error::NameNotFound(a1, _, _), Error::NameNotFound(a2, _, _)) => a1 == a2, (Error::FieldNotFound(a1, _), Error::FieldNotFound(a2, _)) => a1 == a2, (Error::EnvNotFound(a1, _), Error::EnvNotFound(a2, _)) => a1 == a2, (Error::LastStatementNotAnExpr(_), Error::LastStatementNotAnExpr(_)) => true, (Error::NotAFunction(a1, _), Error::NotAFunction(a2, _)) => a1 == a2, (Error::NotARecord(a1, _), Error::NotARecord(a2, _)) => a1 == a2, (Error::NotAReference(a1, b1, _), Error::NotAReference(a2, b2, _)) => { a1 == a2 && b1 == b2 } (Error::NotABoolean(a1, _), Error::NotABoolean(a2, _)) => a1 == a2, (Error::NotAnInt(a1, _), Error::NotAnInt(a2, _)) => a1 == a2, (Error::MigrationError(a1, b1, _), Error::MigrationError(a2, b2, _)) => { a1 == a2 && b1 == b2 } (Error::ArgumentsMismatch(_), Error::ArgumentsMismatch(_)) => true, _ => false, } } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Error::DuplicateEnvNames(a, b) => { write!( f, "Closure generation failed. Duplicate env names: {a:?}\n{b}" ) } Error::DuplicateNames(a, b) => write!(f, "Duplicate names: {a:?}\n{b}"), Error::IndexOutOfBounds(a, b) => write!(f, "Index out of bounds: {a:?}\n{b}"), Error::NameNotFound(a, e, b) => write!( f, "Name not found: {a:?} in {:#?}\n{b}", e.env.keys().collect::>() ), Error::FieldNotFound(a, b) => write!(f, "Field not found: {a:?}\n{b}"), Error::EnvNotFound(a, b) => write!(f, "Env not found: {a:?}\n{b}"), Error::LastStatementNotAnExpr(a) => { write!(f, "Last statement is not an expression\n{a}") } Error::NotAFunction(a, b) => write!(f, "Not a function {a:?}\n{b}"), Error::NotARecord(a, b) => write!(f, "Not a record {a:?}\n{b}"), Error::NotAReference(a, b, c) => write!( f, "Not a reference. Are you missing a `mut`? {a:?} {b:?}\n{c}" ), Error::NotABoolean(a, b) => write!(f, "Not a boolean {a:?}\n{b}"), Error::NotAnInt(a, b) => write!(f, "Not an integer {a:?}\n{b}"), Error::ArgumentsMismatch(a) => write!(f, "Arguments mismatch\n{a}"), Error::OpError(a, b, c, d) => write!(f, "Op Error {a:?} {b:?} {c:?}\n{d}"), Error::MigrationError(a, b, c) => { write!(f, "Migration error for:\n{a:#?}\n{b:#?}\n{c}") } } } } impl From for Error { fn from(error: ast::Error) -> Error { match error { ast::Error::LabelNotFound(l) => Error::FieldNotFound(l, Backtrace::capture()), ast::Error::IndexOutOfBounds(i) => Error::IndexOutOfBounds(i, Backtrace::capture()), } } } #[derive(PartialEq, Debug, Clone)] struct Namer { counter: u64, } impl Namer { fn new() -> Namer { Namer { counter: 0 } } pub fn next(&mut self) -> u64 { self.counter += 1; self.counter } } pub struct State { pub name: String, env_namer: Namer, pub envs: Envs, pub variables: Variables, pub primitive_funcs: PrimitiveFuncs, } pub struct PrimitiveFuncs { pub map: HashMap, } pub type PrimFunc = fn(Vec) -> ast::Value; impl PrimitiveFuncs { pub fn new(prims: Vec<(&str, PrimFunc)>) -> Self { let mut map: HashMap = HashMap::new(); for (key, func) in prims.into_iter() { map.insert(key.into(), func); } PrimitiveFuncs { map } } pub fn get(&self, name: &ast::Name) -> Option<&PrimFunc> { self.map.get(name) } } pub struct Variables { pub vars: HashMap, namer: Namer, } impl Variables { #[allow(clippy::new_without_default)] pub fn new() -> Self { Variables { vars: HashMap::new(), namer: Namer::new(), } } pub fn insert(&mut self, value: ast::Value) -> ast::Ref { let name = ast::Ref(self.namer.next()); self.vars.insert(name.clone(), value); name } pub fn get<'a>(&'a self, reference: &ast::Ref) -> &'a ast::Value { self.vars.get(reference).unwrap() } pub fn set(&mut self, reference: ast::Ref, new_value: ast::Value) { self.vars.insert(reference, new_value); } } impl State { pub fn new(name: String, primitive_funcs: PrimitiveFuncs) -> State { State { name, env_namer: Namer::new(), envs: Envs(BTreeMap::new()), variables: Variables::new(), primitive_funcs, } } pub fn generate_env(&mut self, env: Env) -> Result { let env_name = ast::EnvName(format!("{}_{}", self.name, self.env_namer.next())); self.envs.insert(&env_name, env)?; Ok(env_name) } } #[derive(PartialEq, Debug, Clone)] pub struct Env { env: BTreeMap, pub name: ast::EnvName, } impl Env { pub fn new(name: ast::EnvName) -> Env { Env { env: BTreeMap::new(), name, } } pub fn get(&self, name: &ast::Name) -> Result<&ast::Value, Error> { match self.env.get(name) { None => Err(Error::NameNotFound( name.clone(), self.clone(), Backtrace::capture(), )), Some(v) => Ok(v), } } pub fn insert(&mut self, name: ast::Name, value: ast::Value) { self.env.insert(name.clone(), value); } pub fn insert_nodup(&mut self, name: &ast::Name, value: ast::Value) -> Result<(), Error> { if self.env.insert(name.clone(), value).is_some() { Err(Error::DuplicateNames(name.clone(), Backtrace::capture())) } else { Ok(()) } } } #[derive(PartialEq, Debug, Clone)] pub struct Envs(pub BTreeMap); impl Envs { pub fn get(&self, env_name: &ast::EnvName) -> Result<&Env, Error> { self.0 .get(env_name) .ok_or(Error::EnvNotFound(env_name.clone(), Backtrace::capture())) } pub fn get_mut(&mut self, env_name: &ast::EnvName) -> Result<&mut Env, Error> { self.0 .get_mut(env_name) .ok_or(Error::EnvNotFound(env_name.clone(), Backtrace::capture())) } pub fn insert(&mut self, name: &ast::EnvName, env: Env) -> Result<(), Error> { if self.0.insert(name.clone(), env).is_some() { Err(Error::DuplicateEnvNames(name.clone(), Backtrace::capture())) } else { Ok(()) } } }