Commit | Line | Data |
---|---|---|
8f5b0f10 JM |
1 | use std::rc::Rc; |
2 | use std::cell::RefCell; | |
3 | use std::collections::HashMap; | |
4 | ||
bbeb1b87 AC |
5 | use types::{MalVal, MalRet, _nil, list, err_string}; |
6 | use types::MalType::{Sym, List, Vector}; | |
8f5b0f10 JM |
7 | |
8 | struct EnvType { | |
9 | data: HashMap<String,MalVal>, | |
10 | outer: Option<Env>, | |
11 | } | |
12 | ||
13 | pub type Env = Rc<RefCell<EnvType>>; | |
14 | ||
15 | pub fn env_new(outer: Option<Env>) -> Env { | |
16 | Rc::new(RefCell::new(EnvType{data: HashMap::new(), outer: outer})) | |
17 | } | |
18 | ||
0a4d62f2 JM |
19 | pub fn env_bind(env: &Env, |
20 | mbinds: MalVal, | |
21 | mexprs: MalVal) -> Result<Env,String> { | |
22 | let mut variadic = false; | |
23 | match *mbinds { | |
bd306723 | 24 | List(ref binds,_) | Vector(ref binds,_) => { |
0a4d62f2 | 25 | match *mexprs { |
bd306723 | 26 | List(ref exprs,_) | Vector(ref exprs,_) => { |
0a4d62f2 | 27 | let mut it = binds.iter().enumerate(); |
bbeb1b87 | 28 | for (i, b) in it.by_ref() { |
0a4d62f2 JM |
29 | match **b { |
30 | Sym(ref strn) => { | |
bbeb1b87 | 31 | if *strn == "&" { |
0a4d62f2 JM |
32 | variadic = true; |
33 | break; | |
34 | } else { | |
b8ee29b2 | 35 | env_set(env, b.clone(), exprs[i].clone()); |
0a4d62f2 JM |
36 | } |
37 | } | |
38 | _ => return Err("non-symbol bind".to_string()), | |
39 | } | |
40 | } | |
41 | if variadic { | |
42 | let (i, sym) = it.next().unwrap(); | |
43 | match **sym { | |
b8ee29b2 | 44 | Sym(_) => { |
bbeb1b87 | 45 | let rest = exprs[i-1..].to_vec(); |
b8ee29b2 | 46 | env_set(env, sym.clone(), list(rest)); |
0a4d62f2 JM |
47 | } |
48 | _ => return Err("& bind to non-symbol".to_string()), | |
49 | } | |
50 | } | |
51 | Ok(env.clone()) | |
52 | }, | |
53 | _ => Err("exprs must be a list".to_string()), | |
54 | } | |
55 | }, | |
56 | _ => Err("binds must be a list".to_string()), | |
57 | } | |
58 | } | |
59 | ||
bbeb1b87 AC |
60 | pub fn env_find(env: &Env, key: &MalVal) -> Option<Env> { |
61 | match **key { | |
b8ee29b2 | 62 | Sym(ref k) => { |
bbeb1b87 AC |
63 | let map = env.borrow(); |
64 | if map.data.contains_key(k) { | |
65 | Some(env.clone()) | |
b8ee29b2 | 66 | } else { |
bbeb1b87 AC |
67 | match map.outer { |
68 | Some(ref e) => env_find(e, key), | |
b8ee29b2 JM |
69 | None => None, |
70 | } | |
71 | } | |
72 | }, | |
73 | _ => None | |
8f5b0f10 JM |
74 | } |
75 | } | |
76 | ||
85bec8a0 JM |
77 | pub fn env_root(env: &Env) -> Env { |
78 | match env.borrow().outer { | |
79 | Some(ref ei) => env_root(ei), | |
80 | None => env.clone(), | |
81 | } | |
82 | } | |
83 | ||
b8ee29b2 JM |
84 | pub fn env_set(env: &Env, key: MalVal, val: MalVal) { |
85 | match *key { | |
bbeb1b87 | 86 | Sym(ref k) => { env.borrow_mut().data.insert(k.to_string(), val); } |
b8ee29b2 JM |
87 | _ => {}, |
88 | } | |
8f5b0f10 JM |
89 | } |
90 | ||
bbeb1b87 AC |
91 | pub fn env_get(env: &Env, key: &MalVal) -> MalRet { |
92 | match **key { | |
b8ee29b2 | 93 | Sym(ref k) => { |
bbeb1b87 | 94 | match env_find(env, key) { |
b8ee29b2 | 95 | Some(e) => { |
bbeb1b87 AC |
96 | match e.borrow().data.get(k) { |
97 | Some(v) => Ok(v.clone()), | |
b8ee29b2 JM |
98 | None => Ok(_nil()), |
99 | } | |
100 | }, | |
bbeb1b87 | 101 | None => err_string(format!("'{}' not found", k)), |
8f5b0f10 | 102 | } |
b8ee29b2 JM |
103 | } |
104 | _ => err_string("env_get called with non-symbol key".to_string()), | |
8f5b0f10 JM |
105 | } |
106 | } |