Add gensym and clean `or` macro to stepA of 19 implementations (part 3)
[jackhill/mal.git] / rust / src / env.rs
CommitLineData
8f5b0f10
JM
1use std::rc::Rc;
2use std::cell::RefCell;
3use std::collections::HashMap;
4
bbeb1b87
AC
5use types::{MalVal, MalRet, _nil, list, err_string};
6use types::MalType::{Sym, List, Vector};
8f5b0f10
JM
7
8struct EnvType {
9 data: HashMap<String,MalVal>,
10 outer: Option<Env>,
11}
12
13pub type Env = Rc<RefCell<EnvType>>;
14
15pub fn env_new(outer: Option<Env>) -> Env {
16 Rc::new(RefCell::new(EnvType{data: HashMap::new(), outer: outer}))
17}
18
0a4d62f2
JM
19pub 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
60pub 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
77pub 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
84pub 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
91pub 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}