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