Commit | Line | Data |
---|---|---|
b2ba4453 | 1 | #![allow(non_snake_case)] |
bbeb1b87 AC |
2 | |
3 | extern crate mal; | |
4 | ||
5 | use std::collections::HashMap; | |
6 | use std::env as stdenv; | |
b2ba4453 | 7 | use std::process as process; |
bbeb1b87 AC |
8 | |
9 | use mal::types::{MalVal, MalRet, MalError, err_str}; | |
10 | use mal::types::{symbol, _nil, string, list, vector, hash_map, malfunc, malfuncd}; | |
bbeb1b87 | 11 | use mal::types::MalError::{ErrString, ErrMalVal}; |
fb439f3c | 12 | use mal::types::MalType::{Nil, False, Sym, List, Vector, Hash_Map, Func, MalFunc}; |
bbeb1b87 AC |
13 | use mal::{readline, reader, core}; |
14 | use mal::env::{env_set, env_get, env_new, env_bind, env_find, env_root, Env}; | |
15 | ||
fb439f3c | 16 | |
bbeb1b87 AC |
17 | // read |
18 | fn read(str: String) -> MalRet { | |
19 | reader::read_str(str) | |
20 | } | |
21 | ||
22 | // eval | |
23 | fn is_pair(x: MalVal) -> bool { | |
24 | match *x { | |
25 | List(ref lst,_) | Vector(ref lst,_) => lst.len() > 0, | |
26 | _ => false, | |
27 | } | |
28 | } | |
29 | ||
30 | fn quasiquote(ast: MalVal) -> MalVal { | |
31 | if !is_pair(ast.clone()) { | |
32 | return list(vec![symbol("quote"), ast]) | |
33 | } | |
34 | ||
35 | match *ast.clone() { | |
36 | List(ref args,_) | Vector(ref args,_) => { | |
37 | let ref a0 = args[0]; | |
38 | match **a0 { | |
39 | Sym(ref s) if *s == "unquote" => return args[1].clone(), | |
40 | _ => (), | |
41 | } | |
42 | if is_pair(a0.clone()) { | |
43 | match **a0 { | |
44 | List(ref a0args,_) | Vector(ref a0args,_) => { | |
45 | match *a0args[0] { | |
e1b2062f | 46 | Sym(ref s) if *s == "splice-unquote" => { |
bbeb1b87 AC |
47 | return list(vec![symbol("concat"), |
48 | a0args[1].clone(), | |
49 | quasiquote(list(args[1..].to_vec()))]) | |
50 | }, | |
51 | _ => (), | |
52 | } | |
53 | }, | |
54 | _ => (), | |
55 | } | |
56 | } | |
57 | let rest = list(args[1..].to_vec()); | |
58 | return list(vec![symbol("cons"), | |
59 | quasiquote(a0.clone()), | |
60 | quasiquote(rest)]) | |
61 | }, | |
62 | _ => _nil(), // should never reach | |
63 | } | |
64 | } | |
65 | ||
66 | fn is_macro_call(ast: MalVal, env: Env) -> bool { | |
67 | let lst = match *ast { | |
68 | List(ref lst,_) => &lst[0], | |
69 | _ => return false | |
70 | }; | |
71 | match **lst { | |
72 | Sym(_) => {}, | |
73 | _ => return false | |
74 | } | |
75 | if env_find(&env, lst).is_none() { | |
76 | return false | |
77 | } | |
78 | let f = match env_get(&env, lst) { | |
79 | Ok(f) => f, | |
80 | _ => return false | |
81 | }; | |
82 | match *f { | |
83 | MalFunc(ref mfd,_) => mfd.is_macro, | |
84 | _ => false, | |
85 | } | |
86 | } | |
87 | ||
88 | fn macroexpand(mut ast: MalVal, env: Env) -> MalRet { | |
89 | while is_macro_call(ast.clone(), env.clone()) { | |
90 | let ast2 = ast.clone(); | |
91 | let args = match *ast2 { | |
92 | List(ref args,_) => args, | |
93 | _ => break, | |
94 | }; | |
95 | let ref a0 = args[0]; | |
96 | let mf = match **a0 { | |
97 | Sym(_) => try!(env_get(&env, &a0)), | |
98 | _ => break, | |
99 | }; | |
100 | match *mf { | |
101 | MalFunc(_,_) => ast = try!(mf.apply(args[1..].to_vec())), | |
102 | _ => break, | |
103 | } | |
104 | } | |
105 | Ok(ast) | |
106 | } | |
107 | ||
108 | fn eval_ast(ast: MalVal, env: Env) -> MalRet { | |
109 | match *ast { | |
110 | Sym(_) => env_get(&env, &ast), | |
111 | List(ref a,_) | Vector(ref a,_) => { | |
112 | let mut ast_vec : Vec<MalVal> = vec![]; | |
113 | for mv in a.iter() { | |
114 | let mv2 = mv.clone(); | |
115 | ast_vec.push(try!(eval(mv2, env.clone()))); | |
116 | } | |
117 | Ok(match *ast { List(_,_) => list(ast_vec), | |
118 | _ => vector(ast_vec) }) | |
119 | } | |
120 | Hash_Map(ref hm,_) => { | |
121 | let mut new_hm: HashMap<String,MalVal> = HashMap::new(); | |
122 | for (key, value) in hm.iter() { | |
123 | new_hm.insert(key.to_string(), | |
124 | try!(eval(value.clone(), env.clone()))); | |
125 | } | |
126 | Ok(hash_map(new_hm)) | |
127 | } | |
128 | _ => Ok(ast.clone()), | |
129 | } | |
130 | } | |
131 | ||
132 | fn eval(mut ast: MalVal, mut env: Env) -> MalRet { | |
133 | 'tco: loop { | |
134 | ||
135 | //println!("eval: {}, {}", ast, env.borrow()); | |
136 | //println!("eval: {}", ast); | |
137 | match *ast { | |
138 | List(_,_) => (), // continue | |
139 | _ => return eval_ast(ast, env), | |
140 | } | |
141 | ||
142 | // apply list | |
143 | ast = try!(macroexpand(ast, env.clone())); | |
144 | match *ast { | |
145 | List(_,_) => (), // continue | |
146 | _ => return Ok(ast), | |
147 | } | |
148 | ||
149 | let tmp = ast; | |
150 | let (args, a0sym) = match *tmp { | |
151 | List(ref args,_) => { | |
152 | if args.len() == 0 { | |
153 | return Ok(tmp.clone()); | |
154 | } | |
155 | let ref a0 = *args[0]; | |
156 | match *a0 { | |
157 | Sym(ref a0sym) => (args, &a0sym[..]), | |
158 | _ => (args, "__<fn*>__"), | |
159 | } | |
160 | }, | |
161 | _ => return err_str("Expected list"), | |
162 | }; | |
163 | ||
164 | match a0sym { | |
165 | "def!" => { | |
166 | let a1 = (*args)[1].clone(); | |
167 | let a2 = (*args)[2].clone(); | |
168 | let r = try!(eval(a2, env.clone())); | |
169 | match *a1 { | |
170 | Sym(_) => { | |
171 | env_set(&env.clone(), a1, r.clone()); | |
172 | return Ok(r); | |
173 | }, | |
174 | _ => return err_str("def! of non-symbol"), | |
175 | } | |
176 | }, | |
177 | "let*" => { | |
178 | let let_env = env_new(Some(env.clone())); | |
179 | let a1 = (*args)[1].clone(); | |
180 | let a2 = (*args)[2].clone(); | |
181 | match *a1 { | |
182 | List(ref binds,_) | Vector(ref binds,_) => { | |
183 | let mut it = binds.iter(); | |
184 | while it.len() >= 2 { | |
185 | let b = it.next().unwrap(); | |
186 | let exp = it.next().unwrap(); | |
187 | match **b { | |
188 | Sym(_) => { | |
189 | let r = try!(eval(exp.clone(), let_env.clone())); | |
190 | env_set(&let_env, b.clone(), r); | |
191 | }, | |
192 | _ => return err_str("let* with non-symbol binding"), | |
193 | } | |
194 | } | |
195 | }, | |
196 | _ => return err_str("let* with non-list bindings"), | |
197 | } | |
198 | ast = a2; | |
199 | env = let_env.clone(); | |
200 | continue 'tco; | |
201 | }, | |
202 | "quote" => return Ok((*args)[1].clone()), | |
203 | "quasiquote" => { | |
204 | let a1 = (*args)[1].clone(); | |
205 | ast = quasiquote(a1); | |
206 | continue 'tco; | |
207 | }, | |
208 | "defmacro!" => { | |
209 | let a1 = (*args)[1].clone(); | |
210 | let a2 = (*args)[2].clone(); | |
211 | let r = try!(eval(a2, env.clone())); | |
212 | match *r { | |
213 | MalFunc(ref mfd,_) => { | |
214 | match *a1 { | |
215 | Sym(_) => { | |
216 | let mut new_mfd = mfd.clone(); | |
217 | new_mfd.is_macro = true; | |
218 | let mf = malfuncd(new_mfd,_nil()); | |
219 | env_set(&env.clone(), a1.clone(), mf.clone()); | |
220 | return Ok(mf); | |
221 | }, | |
222 | _ => return err_str("def! of non-symbol"), | |
223 | } | |
224 | }, | |
225 | _ => return err_str("def! of non-symbol"), | |
226 | } | |
227 | }, | |
228 | "macroexpand" => { | |
229 | let a1 = (*args)[1].clone(); | |
230 | return macroexpand(a1, env.clone()) | |
231 | }, | |
232 | "try*" => { | |
233 | let a1 = (*args)[1].clone(); | |
234 | match eval(a1, env.clone()) { | |
235 | Ok(res) => return Ok(res), | |
236 | Err(err) => { | |
237 | if args.len() < 3 { return Err(err); } | |
238 | let a2 = (*args)[2].clone(); | |
239 | let cat = match *a2 { | |
240 | List(ref cat,_) => cat, | |
241 | _ => return err_str("invalid catch* clause"), | |
242 | }; | |
243 | if cat.len() != 3 { | |
244 | return err_str("wrong arity to catch* clause"); | |
245 | } | |
246 | let c1 = (*cat)[1].clone(); | |
247 | match *c1 { | |
248 | Sym(_) => {}, | |
249 | _ => return err_str("invalid catch* binding"), | |
250 | }; | |
251 | let exc = match err { | |
252 | ErrMalVal(mv) => mv, | |
253 | ErrString(s) => string(s), | |
254 | }; | |
255 | let bind_env = env_new(Some(env.clone())); | |
256 | env_set(&bind_env, c1.clone(), exc); | |
257 | let c2 = (*cat)[2].clone(); | |
258 | return eval(c2, bind_env); | |
259 | }, | |
260 | }; | |
261 | } | |
262 | "do" => { | |
263 | let el = list(args[1..args.len()-1].to_vec()); | |
264 | try!(eval_ast(el, env.clone())); | |
265 | ast = args[args.len() - 1].clone(); | |
266 | continue 'tco; | |
267 | }, | |
268 | "if" => { | |
269 | let a1 = (*args)[1].clone(); | |
270 | let c = try!(eval(a1, env.clone())); | |
271 | match *c { | |
272 | False | Nil => { | |
273 | if args.len() >= 4 { | |
274 | ast = args[3].clone(); | |
275 | continue 'tco; | |
276 | } else { | |
277 | return Ok(_nil()); | |
278 | } | |
279 | }, | |
280 | _ => { | |
281 | ast = args[2].clone(); | |
282 | continue 'tco; | |
283 | }, | |
284 | } | |
285 | }, | |
286 | "fn*" => { | |
287 | let a1 = args[1].clone(); | |
288 | let a2 = args[2].clone(); | |
289 | return Ok(malfunc(eval, a2, env, a1, _nil())); | |
290 | }, | |
291 | "eval" => { | |
292 | let a1 = (*args)[1].clone(); | |
293 | ast = try!(eval(a1, env.clone())); | |
294 | env = env_root(&env); | |
295 | continue 'tco; | |
296 | }, | |
297 | _ => { // function call | |
298 | let el = try!(eval_ast(tmp.clone(), env.clone())); | |
299 | let args = match *el { | |
300 | List(ref args,_) => args, | |
301 | _ => return err_str("Invalid apply"), | |
302 | }; | |
303 | return match *args.clone()[0] { | |
304 | Func(f,_) => f(args[1..].to_vec()), | |
305 | MalFunc(ref mf,_) => { | |
306 | let mfc = mf.clone(); | |
307 | let alst = list(args[1..].to_vec()); | |
308 | let new_env = env_new(Some(mfc.env.clone())); | |
309 | match env_bind(&new_env, mfc.params, alst) { | |
310 | Ok(_) => { | |
311 | ast = mfc.exp; | |
312 | env = new_env; | |
313 | continue 'tco; | |
314 | }, | |
315 | Err(e) => err_str(&e), | |
316 | } | |
317 | }, | |
318 | _ => err_str("attempt to call non-function"), | |
319 | } | |
320 | }, | |
321 | } | |
322 | ||
323 | } | |
324 | } | |
325 | ||
326 | ||
327 | fn print(exp: MalVal) -> String { | |
328 | exp.pr_str(true) | |
329 | } | |
330 | ||
331 | fn rep(str: &str, env: Env) -> Result<String,MalError> { | |
332 | let ast = try!(read(str.to_string())); | |
333 | //println!("read: {}", ast); | |
334 | let exp = try!(eval(ast, env)); | |
335 | Ok(print(exp)) | |
336 | } | |
337 | ||
338 | fn main() { | |
339 | // core.rs: defined using rust | |
340 | let repl_env = env_new(None); | |
341 | for (k, v) in core::ns().into_iter() { | |
342 | env_set(&repl_env, symbol(&k), v); | |
343 | } | |
344 | // see eval() for definition of "eval" | |
345 | env_set(&repl_env, symbol("*ARGV*"), list(vec![])); | |
346 | ||
347 | // core.mal: defined using the language itself | |
348 | let _ = rep("(def! *host-language* \"rust\")", repl_env.clone()); | |
349 | let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone()); | |
350 | let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env.clone()); | |
351 | let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env.clone()); | |
29ba1fb6 DM |
352 | let _ = rep("(def! *gensym-counter* (atom 0))", repl_env.clone()); |
353 | let _ = rep("(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))", repl_env.clone()); | |
354 | let _ = rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env.clone()); | |
bbeb1b87 AC |
355 | |
356 | // Invoked with command line arguments | |
357 | let args = stdenv::args(); | |
358 | if args.len() > 1 { | |
359 | let mv_args = args.skip(2) | |
360 | .map(|a| string(a)) | |
361 | .collect::<Vec<MalVal>>(); | |
362 | env_set(&repl_env, symbol("*ARGV*"), list(mv_args)); | |
363 | let lf = format!("(load-file \"{}\")", | |
364 | stdenv::args().skip(1).next().unwrap()); | |
365 | return match rep(&lf, repl_env.clone()) { | |
b2ba4453 | 366 | Ok(_) => process::exit(0), |
bbeb1b87 AC |
367 | Err(str) => { |
368 | println!("Error: {:?}", str); | |
b2ba4453 | 369 | process::exit(1); |
bbeb1b87 AC |
370 | } |
371 | }; | |
372 | } | |
373 | ||
374 | // repl loop | |
375 | let _ = rep("(println (str \"Mal [\" *host-language* \"]\"))", repl_env.clone()); | |
376 | loop { | |
377 | let line = readline::mal_readline("user> "); | |
378 | match line { None => break, _ => () } | |
379 | match rep(&line.unwrap(), repl_env.clone()) { | |
380 | Ok(str) => println!("{}", str), | |
381 | Err(ErrMalVal(_)) => (), // Blank line | |
382 | Err(ErrString(s)) => println!("Error: {}", s), | |
383 | } | |
384 | } | |
385 | } |