//! 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, Backtrace), FieldNotFound(ast::Label, Backtrace), EnvNotFound(ast::EnvName, Backtrace), LastStatementNotAnExpr(Backtrace), NotAFunction(ast::Expr, Backtrace), NotARecord(ast::Value, Backtrace), NotAReference(ast::Value, Backtrace), NotABoolean(ast::Value, 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::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, _), Error::NotAReference(a2, _)) => a1 == a2, (Error::NotABoolean(a1, _), Error::NotABoolean(a2, _)) => a1 == a2, (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::NameNotFound(a, b) => write!(f, "Name not found: {a:?}\n{b}"), 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) => write!(f, "Not a reference {a:?}\n{b}"), Error::NotABoolean(a, b) => write!(f, "Not a boolean {a:?}\n{b}"), Error::ArgumentsMismatch(a) => write!(f, "Arguments mismatch\n{a}"), } } } impl From for Error { fn from(error: ast::Error) -> Error { match error { ast::Error::LabelNotFound(l) => Error::FieldNotFound(l, 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 { 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> { self.env .get(name) .ok_or(Error::NameNotFound(name.clone(), Backtrace::capture())) } 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(()) } } }