use std::rc::Rc; use std::cell::RefCell; //use std::collections::HashMap; use fnv::FnvHashMap; use itertools::Itertools; use types::MalErr::{ErrString,ErrMalVal}; use types::MalVal::{Nil,Bool,Int,Str,Sym,List,Vector,Hash,Func,MalFunc,Atom}; use env::{Env,env_bind}; #[derive(Debug, Clone)] pub enum MalVal { Nil, Bool(bool), Int(i64), //Float(f64), Str(String), Sym(String), List(Rc>, Rc), Vector(Rc>, Rc), Hash(Rc>, Rc), Func(fn(MalArgs) -> MalRet, Rc), MalFunc { eval: fn(ast: MalVal, env: Env) -> MalRet, ast: Rc, env: Env, params: Rc, is_macro: bool, meta: Rc, }, Atom(Rc>), } #[derive(Debug)] pub enum MalErr { ErrString(String), ErrMalVal(MalVal), } pub type MalArgs = Vec; pub type MalRet = Result; // type utility macros macro_rules! list { ($seq:expr) => {{ List(Rc::new($seq),Rc::new(Nil)) }}; [$($args:expr),*] => {{ let v: Vec = vec![$($args),*]; List(Rc::new(v),Rc::new(Nil)) }} } macro_rules! vector { ($seq:expr) => {{ Vector(Rc::new($seq),Rc::new(Nil)) }}; [$($args:expr),*] => {{ let v: Vec = vec![$($args),*]; Vector(Rc::new(v),Rc::new(Nil)) }} } // type utility functions pub fn error(s: &str) -> MalRet { Err(ErrString(s.to_string())) } pub fn format_error(e: MalErr) -> String { match e { ErrString(s) => s.clone(), ErrMalVal(mv) => mv.pr_str(true), } } pub fn atom(mv: &MalVal) -> MalVal { Atom(Rc::new(RefCell::new(mv.clone()))) } impl MalVal { pub fn keyword(&self) -> MalRet { match self { Str(s) if s.starts_with("\u{29e}") => Ok(Str(s.to_string())), Str(s) => Ok(Str(format!("\u{29e}{}", s))), _ => error("invalid type for keyword"), } } pub fn empty_q(&self) -> MalRet { match self { List(l,_) | Vector(l,_) => Ok(Bool(l.len() == 0)), Nil => Ok(Bool(true)), _ => error("invalid type for empty?"), } } pub fn count(&self) -> MalRet { match self { List(l,_) | Vector(l,_) => Ok(Int(l.len() as i64)), Nil => Ok(Int(0)), _ => error("invalid type for count"), } } pub fn apply(&self, args: MalArgs) -> MalRet { match *self { Func(f,_) => f(args), MalFunc{eval, ref ast, ref env, ref params, ..} => { let a = &**ast; let p = &**params; let fn_env = env_bind(Some(env.clone()), p.clone(), args)?; Ok(eval(a.clone(), fn_env)?) } _ => error("attempt to call non-function"), } } pub fn keyword_q(&self) -> bool { match self { Str(s) if s.starts_with("\u{29e}") => true, _ => false, } } pub fn deref(&self) -> MalRet { match self { Atom(a) => Ok(a.borrow().clone()), _ => error("attempt to deref a non-Atom"), } } pub fn reset_bang(&self, new: &MalVal) -> MalRet { match self { Atom(a) => { *a.borrow_mut() = new.clone(); Ok(new.clone()) }, _ => error("attempt to reset! a non-Atom"), } } pub fn swap_bang(&self, args: &MalArgs) -> MalRet { match self { Atom(a) => { let f = &args[0]; let mut fargs = args[1..].to_vec(); fargs.insert(0, a.borrow().clone()); *a.borrow_mut() = f.apply(fargs)?; Ok(a.borrow().clone()) }, _ => error("attempt to swap! a non-Atom"), } } pub fn get_meta(&self) -> MalRet { match self { List(_,meta) | Vector(_,meta) | Hash(_,meta) => Ok((&**meta).clone()), Func(_,meta) => Ok((&**meta).clone()), MalFunc{meta,..} => Ok((&**meta).clone()), _ => error("meta not supported by type"), } } pub fn with_meta(&mut self, new_meta: &MalVal) -> MalRet { match self { List(_, ref mut meta) | Vector(_, ref mut meta) | Hash(_, ref mut meta) | Func(_,ref mut meta) | MalFunc{ref mut meta, ..} => { *meta = Rc::new((&*new_meta).clone()); }, _ => return error("with-meta not supported by type"), }; Ok(self.clone()) } } impl PartialEq for MalVal { fn eq(&self, other: &MalVal) -> bool { match (self, other) { (Nil,Nil) => true, (Bool(ref a),Bool(ref b)) => a == b, (Int(ref a),Int(ref b)) => a == b, (Str(ref a),Str(ref b)) => a == b, (Sym(ref a),Sym(ref b)) => a == b, (List(ref a,_),List(ref b,_)) | (Vector(ref a,_),Vector(ref b,_)) | (List(ref a,_),Vector(ref b,_)) | (Vector(ref a,_),List(ref b,_)) => a == b, (Hash(ref a,_),Hash(ref b,_)) => a == b, (MalFunc{..}, MalFunc{..}) => false, _ => false, } } } pub fn func(f: fn(MalArgs) -> MalRet) -> MalVal { Func(f, Rc::new(Nil)) } pub fn _assoc(mut hm: FnvHashMap, kvs: MalArgs) -> MalRet { if kvs.len() % 2 != 0 { return error("odd number of elements") } for (k, v) in kvs.iter().tuples() { match k { Str(s) => { hm.insert(s.to_string(), v.clone()); }, _ => { return error("key is not string") }, } } Ok(Hash(Rc::new(hm),Rc::new(Nil))) } pub fn _dissoc(mut hm: FnvHashMap, ks: MalArgs) -> MalRet { for k in ks.iter() { match k { Str(ref s) => { hm.remove(s); }, _ => { return error("key is not string") }, } } Ok(Hash(Rc::new(hm),Rc::new(Nil))) } pub fn hash_map(kvs: MalArgs) -> MalRet { let hm: FnvHashMap = FnvHashMap::default(); _assoc(hm, kvs) } // vim: ts=2:sw=2:expandtab