use crate::env::{car_cdr, Env}; use std::{collections::HashMap, rc::Rc}; // All Mal types should inherit from this #[derive(Clone, Debug)] pub enum MalType { List(MalArgs), Vector(MalArgs), Map(MalMap), Fun(fn(&[MalType]) -> MalRet, &'static str), // Used for base functions, implemented using the underlying language (rust) MalFun { eval: fn(ast: &MalType, env: Env) -> MalRet, params: Rc, ast: Rc, env: Env, }, // Used for functions defined within mal // Use Rc so I can now clone like there's no tomorrow Sym(String), Key(String), Str(String), Int(isize), Bool(bool), Nil, } impl MalType { pub fn if_number(&self) -> Result { match self { Self::Int(val) => Ok(*val), _ => Err(MalErr::unrecoverable( format!("{:?} is not a number", prt(self)).as_str(), )), } } pub fn if_list(&self) -> Result<&[MalType], MalErr> { match self { Self::List(list) => Ok(list), _ => Err(MalErr::unrecoverable( format!("{:?} is not a list", prt(self)).as_str(), )), } } pub fn if_symbol(&self) -> Result<&str, MalErr> { match self { Self::Sym(sym) => Ok(sym), _ => Err(MalErr::unrecoverable( format!("{:?} is not a symbol", prt(self)).as_str(), )), } } } use crate::types::MalType as M; fn mal_eq(a: &MalType, b: &[MalType]) -> MalRet { Ok(M::Bool(match a { M::Nil => b.iter().all(|el| matches!(el, M::Nil)), M::Bool(a) => b.iter().all(|el| matches!(el, M::Bool(b) if a == b)), M::Int(a) => b.iter().all(|el| matches!(el, M::Int(b) if a == b)), M::Key(a) => b.iter().all(|el| matches!(el, M::Key(b) if a == b)), M::Str(a) => b.iter().all(|el| matches!(el, M::Str(b) if a == b)), M::List(a) => b.iter().all(|el| { matches!(el, M::List(b) if a.len() == b.len() && a.iter().zip(b.iter()).all( |(a, b)| matches!(mal_eq(a, &[b.clone()]), Ok(M::Bool(true))))) }), M::Vector(a) => b.iter().all(|el| { matches!(el, M::Vector(b) if a.len() == b.len() && a.iter().zip(b.iter()).all( |(a, b)| matches!(mal_eq(a, &[b.clone()]), Ok(M::Bool(true))))) }), _ => { return Err(MalErr::unrecoverable( "Comparison not implemented for 'Map', 'Fun', 'MalFun' and 'Sym'", )) } })) } pub fn mal_comp(args: &[MalType]) -> MalRet { match args.len() { 0 => Ok(M::Bool(true)), _ => { let (car, cdr) = car_cdr(args)?; mal_eq(car, cdr) } } } pub fn mal_assert(args: &[MalType]) -> MalRet { args.iter().for_each(|i| match i { M::Nil | M::Bool(false) => panic!(), _ => (), }); Ok(M::Nil) } #[derive(PartialEq, Clone, Copy)] pub enum Severity { Recoverable, Unrecoverable, } pub struct MalErr { message: String, severity: Severity, } impl MalErr { pub fn new(message: String, severity: Severity) -> Self { Self { message, severity } } pub fn message(&self) -> String { self.message.to_string() } pub fn severity(&self) -> Severity { self.severity } pub fn is_recoverable(&self) -> bool { self.severity == Severity::Recoverable } pub fn recoverable(message: &str) -> Self { Self::new(message.to_owned(), Severity::Recoverable) } pub fn unrecoverable(message: &str) -> Self { Self::new(message.to_owned(), Severity::Unrecoverable) } } pub type MalArgs = Rc>; pub type MalMap = HashMap; pub type MalRet = Result; use crate::printer::prt; use MalType::{Key, Map, Str}; pub fn make_map(list: MalArgs) -> MalRet { if list.len() % 2 != 0 { return Err(MalErr::unrecoverable("Map length is odd: missing value")); } let mut map = MalMap::new(); for i in (0..list.len()).step_by(2) { match &list[i] { Key(k) | Str(k) => { let v = &list[i + 1]; map.insert(k.to_string(), v.clone()); } _ => { return Err(MalErr::unrecoverable( format!("Map key not valid: {}", prt(&list[i])).as_str(), )) } } } Ok(Map(map)) } pub fn escape_str(s: &str) -> String { format!( "\"{}\"", String::from(s) .replace('\\', "\\\\") .replace('\n', "\\n") .replace('\"', "\\\"") ) } pub fn unescape_str(s: &str) -> String { String::from(&s[1..s.len() - 1]) .replace("\\\\", "\\") .replace("\\n", "\n") .replace("\\\"", "\"") }