Commit | Line | Data |
---|---|---|
bbeb1b87 AC |
1 | |
2 | extern crate mal; | |
4ee7c0f2 | 3 | |
5939404b | 4 | use std::collections::HashMap; |
bbeb1b87 | 5 | use std::env as stdenv; |
b2ba4453 | 6 | use std::process as process; |
bbeb1b87 AC |
7 | |
8 | use mal::types::{MalVal, MalRet, MalError, err_str}; | |
9 | use mal::types::{symbol, _nil, string, list, vector, hash_map, malfunc}; | |
bbeb1b87 | 10 | use mal::types::MalError::{ErrString, ErrMalVal}; |
fb439f3c | 11 | use mal::types::MalType::{Nil, False, Sym, List, Vector, Hash_Map, Func, MalFunc}; |
bbeb1b87 AC |
12 | use mal::{readline, reader, core}; |
13 | use mal::env::{env_set, env_get, env_new, env_bind, env_root, Env}; | |
4ee7c0f2 | 14 | |
4ee7c0f2 JM |
15 | |
16 | // read | |
17 | fn read(str: String) -> MalRet { | |
18 | reader::read_str(str) | |
19 | } | |
20 | ||
21 | // eval | |
22 | fn is_pair(x: MalVal) -> bool { | |
23 | match *x { | |
b554fd4e | 24 | List(ref lst,_) | Vector(ref lst,_) => lst.len() > 0, |
4ee7c0f2 JM |
25 | _ => false, |
26 | } | |
27 | } | |
28 | ||
29 | fn quasiquote(ast: MalVal) -> MalVal { | |
30 | if !is_pair(ast.clone()) { | |
31 | return list(vec![symbol("quote"), ast]) | |
32 | } | |
33 | ||
34 | match *ast.clone() { | |
b554fd4e | 35 | List(ref args,_) | Vector(ref args,_) => { |
4ee7c0f2 JM |
36 | let ref a0 = args[0]; |
37 | match **a0 { | |
fb439f3c | 38 | Sym(ref s) if *s == "unquote" => return args[1].clone(), |
4ee7c0f2 JM |
39 | _ => (), |
40 | } | |
41 | if is_pair(a0.clone()) { | |
42 | match **a0 { | |
b554fd4e | 43 | List(ref a0args,_) | Vector(ref a0args,_) => { |
fb439f3c JM |
44 | match *a0args[0] { |
45 | Sym(ref s) if *s == "splice-unquote" => { | |
46 | return list(vec![symbol("concat"), | |
47 | a0args[1].clone(), | |
48 | quasiquote(list(args[1..].to_vec()))]) | |
4ee7c0f2 JM |
49 | }, |
50 | _ => (), | |
51 | } | |
52 | }, | |
53 | _ => (), | |
54 | } | |
55 | } | |
bbeb1b87 | 56 | let rest = list(args[1..].to_vec()); |
4ee7c0f2 JM |
57 | return list(vec![symbol("cons"), |
58 | quasiquote(a0.clone()), | |
59 | quasiquote(rest)]) | |
60 | }, | |
61 | _ => _nil(), // should never reach | |
62 | } | |
63 | } | |
64 | ||
65 | fn eval_ast(ast: MalVal, env: Env) -> MalRet { | |
fb439f3c | 66 | match *ast { |
bbeb1b87 | 67 | Sym(_) => env_get(&env, &ast), |
bd306723 | 68 | List(ref a,_) | Vector(ref a,_) => { |
4ee7c0f2 JM |
69 | let mut ast_vec : Vec<MalVal> = vec![]; |
70 | for mv in a.iter() { | |
71 | let mv2 = mv.clone(); | |
fb439f3c | 72 | ast_vec.push(try!(eval(mv2, env.clone()))); |
4ee7c0f2 | 73 | } |
bd306723 | 74 | Ok(match *ast { List(_,_) => list(ast_vec), |
77b2da6c | 75 | _ => vector(ast_vec) }) |
fb439f3c | 76 | } |
bd306723 | 77 | Hash_Map(ref hm,_) => { |
5939404b JM |
78 | let mut new_hm: HashMap<String,MalVal> = HashMap::new(); |
79 | for (key, value) in hm.iter() { | |
fb439f3c JM |
80 | new_hm.insert(key.to_string(), |
81 | try!(eval(value.clone(), env.clone()))); | |
5939404b JM |
82 | } |
83 | Ok(hash_map(new_hm)) | |
4ee7c0f2 | 84 | } |
fb439f3c | 85 | _ => Ok(ast.clone()), |
4ee7c0f2 JM |
86 | } |
87 | } | |
88 | ||
89 | fn eval(mut ast: MalVal, mut env: Env) -> MalRet { | |
90 | 'tco: loop { | |
91 | ||
92 | //println!("eval: {}, {}", ast, env.borrow()); | |
93 | //println!("eval: {}", ast); | |
fb439f3c | 94 | match *ast { |
bd306723 | 95 | List(_,_) => (), // continue |
fb439f3c | 96 | _ => return eval_ast(ast, env), |
4ee7c0f2 JM |
97 | } |
98 | ||
99 | // apply list | |
fb439f3c | 100 | match *ast { |
bd306723 | 101 | List(_,_) => (), // continue |
fb439f3c | 102 | _ => return Ok(ast), |
3744d566 | 103 | } |
3744d566 | 104 | |
fb439f3c JM |
105 | let tmp = ast; |
106 | let (args, a0sym) = match *tmp { | |
bd306723 | 107 | List(ref args,_) => { |
bbeb1b87 | 108 | if args.len() == 0 { |
fb439f3c | 109 | return Ok(tmp.clone()); |
4ee7c0f2 JM |
110 | } |
111 | let ref a0 = *args[0]; | |
112 | match *a0 { | |
bbeb1b87 | 113 | Sym(ref a0sym) => (args, &a0sym[..]), |
3744d566 JM |
114 | _ => (args, "__<fn*>__"), |
115 | } | |
116 | }, | |
117 | _ => return err_str("Expected list"), | |
118 | }; | |
119 | ||
120 | match a0sym { | |
121 | "def!" => { | |
122 | let a1 = (*args)[1].clone(); | |
123 | let a2 = (*args)[2].clone(); | |
fb439f3c JM |
124 | let r = try!(eval(a2, env.clone())); |
125 | match *a1 { | |
126 | Sym(_) => { | |
127 | env_set(&env.clone(), a1, r.clone()); | |
128 | return Ok(r); | |
3744d566 | 129 | }, |
fb439f3c | 130 | _ => return err_str("def! of non-symbol"), |
3744d566 JM |
131 | } |
132 | }, | |
133 | "let*" => { | |
134 | let let_env = env_new(Some(env.clone())); | |
135 | let a1 = (*args)[1].clone(); | |
136 | let a2 = (*args)[2].clone(); | |
137 | match *a1 { | |
bd306723 | 138 | List(ref binds,_) | Vector(ref binds,_) => { |
3744d566 JM |
139 | let mut it = binds.iter(); |
140 | while it.len() >= 2 { | |
141 | let b = it.next().unwrap(); | |
142 | let exp = it.next().unwrap(); | |
143 | match **b { | |
b8ee29b2 | 144 | Sym(_) => { |
fb439f3c JM |
145 | let r = try!(eval(exp.clone(), let_env.clone())); |
146 | env_set(&let_env, b.clone(), r); | |
3744d566 | 147 | }, |
fb439f3c | 148 | _ => return err_str("let* with non-symbol binding"), |
3744d566 JM |
149 | } |
150 | } | |
151 | }, | |
152 | _ => return err_str("let* with non-list bindings"), | |
153 | } | |
154 | ast = a2; | |
155 | env = let_env.clone(); | |
156 | continue 'tco; | |
157 | }, | |
fb439f3c | 158 | "quote" => return Ok((*args)[1].clone()), |
3744d566 JM |
159 | "quasiquote" => { |
160 | let a1 = (*args)[1].clone(); | |
161 | ast = quasiquote(a1); | |
162 | continue 'tco; | |
163 | }, | |
164 | "do" => { | |
bbeb1b87 | 165 | let el = list(args[1..args.len()-1].to_vec()); |
fb439f3c JM |
166 | try!(eval_ast(el, env.clone())); |
167 | ast = args[args.len() - 1].clone(); | |
168 | continue 'tco; | |
3744d566 JM |
169 | }, |
170 | "if" => { | |
171 | let a1 = (*args)[1].clone(); | |
fb439f3c JM |
172 | let c = try!(eval(a1, env.clone())); |
173 | match *c { | |
174 | False | Nil => { | |
175 | if args.len() >= 4 { | |
176 | ast = args[3].clone(); | |
3744d566 | 177 | continue 'tco; |
fb439f3c JM |
178 | } else { |
179 | return Ok(_nil()); | |
180 | } | |
181 | }, | |
182 | _ => { | |
183 | ast = args[2].clone(); | |
184 | continue 'tco; | |
185 | }, | |
4ee7c0f2 | 186 | } |
3744d566 JM |
187 | }, |
188 | "fn*" => { | |
fb439f3c JM |
189 | let a1 = args[1].clone(); |
190 | let a2 = args[2].clone(); | |
191 | return Ok(malfunc(eval, a2, env, a1, _nil())); | |
3744d566 JM |
192 | }, |
193 | "eval" => { | |
194 | let a1 = (*args)[1].clone(); | |
fb439f3c JM |
195 | ast = try!(eval(a1, env.clone())); |
196 | env = env_root(&env); | |
197 | continue 'tco; | |
3744d566 JM |
198 | }, |
199 | _ => { // function call | |
fb439f3c JM |
200 | let el = try!(eval_ast(tmp.clone(), env.clone())); |
201 | let args = match *el { | |
202 | List(ref args,_) => args, | |
203 | _ => return err_str("Invalid apply"), | |
204 | }; | |
205 | return match *args.clone()[0] { | |
206 | Func(f,_) => f(args[1..].to_vec()), | |
207 | MalFunc(ref mf,_) => { | |
208 | let mfc = mf.clone(); | |
209 | let alst = list(args[1..].to_vec()); | |
210 | let new_env = env_new(Some(mfc.env.clone())); | |
211 | match env_bind(&new_env, mfc.params, alst) { | |
212 | Ok(_) => { | |
213 | ast = mfc.exp; | |
214 | env = new_env; | |
215 | continue 'tco; | |
3744d566 | 216 | }, |
fb439f3c | 217 | Err(e) => err_str(&e), |
4ee7c0f2 | 218 | } |
fb439f3c JM |
219 | }, |
220 | _ => err_str("attempt to call non-function"), | |
4ee7c0f2 | 221 | } |
3744d566 | 222 | }, |
4ee7c0f2 JM |
223 | } |
224 | ||
225 | } | |
226 | } | |
227 | ||
228 | ||
229 | fn print(exp: MalVal) -> String { | |
230 | exp.pr_str(true) | |
231 | } | |
232 | ||
3744d566 | 233 | fn rep(str: &str, env: Env) -> Result<String,MalError> { |
fb439f3c JM |
234 | let ast = try!(read(str.to_string())); |
235 | //println!("read: {}", ast); | |
236 | let exp = try!(eval(ast, env)); | |
237 | Ok(print(exp)) | |
4ee7c0f2 JM |
238 | } |
239 | ||
240 | fn main() { | |
241 | // core.rs: defined using rust | |
242 | let repl_env = env_new(None); | |
b8ee29b2 | 243 | for (k, v) in core::ns().into_iter() { |
bbeb1b87 | 244 | env_set(&repl_env, symbol(&k), v); |
b8ee29b2 | 245 | } |
4ee7c0f2 | 246 | // see eval() for definition of "eval" |
bbeb1b87 | 247 | env_set(&repl_env, symbol("*ARGV*"), list(vec![])); |
4ee7c0f2 JM |
248 | |
249 | // core.mal: defined using the language itself | |
3744d566 JM |
250 | let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone()); |
251 | let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env.clone()); | |
4ee7c0f2 JM |
252 | |
253 | // Invoked with command line arguments | |
bbeb1b87 | 254 | let args = stdenv::args(); |
4ee7c0f2 | 255 | if args.len() > 1 { |
bbeb1b87 AC |
256 | let mv_args = args.skip(2) |
257 | .map(|a| string(a)) | |
4ee7c0f2 | 258 | .collect::<Vec<MalVal>>(); |
bbeb1b87 AC |
259 | env_set(&repl_env, symbol("*ARGV*"), list(mv_args)); |
260 | let lf = format!("(load-file \"{}\")", | |
261 | stdenv::args().skip(1).next().unwrap()); | |
262 | return match rep(&lf, repl_env.clone()) { | |
b2ba4453 | 263 | Ok(_) => process::exit(0), |
4ee7c0f2 | 264 | Err(str) => { |
bbeb1b87 | 265 | println!("Error: {:?}", str); |
b2ba4453 | 266 | process::exit(1); |
bbeb1b87 AC |
267 | } |
268 | }; | |
4ee7c0f2 JM |
269 | } |
270 | ||
fb439f3c | 271 | // repl loop |
4ee7c0f2 JM |
272 | loop { |
273 | let line = readline::mal_readline("user> "); | |
274 | match line { None => break, _ => () } | |
bbeb1b87 | 275 | match rep(&line.unwrap(), repl_env.clone()) { |
4ee7c0f2 | 276 | Ok(str) => println!("{}", str), |
3744d566 JM |
277 | Err(ErrMalVal(_)) => (), // Blank line |
278 | Err(ErrString(s)) => println!("Error: {}", s), | |
4ee7c0f2 JM |
279 | } |
280 | } | |
281 | } |