Merge pull request #383 from asarhaddon/ada2tco-do
[jackhill/mal.git] / rust / stepA_mal.rs
1 #![allow(non_snake_case)]
2
3 use std::rc::Rc;
4 //use std::collections::HashMap;
5 use fnv::FnvHashMap;
6 use itertools::Itertools;
7
8 #[macro_use]
9 extern crate lazy_static;
10 extern crate regex;
11 extern crate itertools;
12 extern crate fnv;
13
14 extern crate rustyline;
15 use rustyline::error::ReadlineError;
16 use rustyline::Editor;
17
18 #[macro_use]
19 mod types;
20 use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
21 use types::MalVal::{Nil,Bool,Str,Sym,List,Vector,Hash,Func,MalFunc};
22 use types::MalErr::{ErrString,ErrMalVal};
23 mod reader;
24 mod printer;
25 mod env;
26 use env::{Env,env_new,env_bind,env_find,env_get,env_set,env_sets};
27 #[macro_use]
28 mod core;
29
30 // read
31 fn read(str: &str) -> MalRet {
32 reader::read_str(str.to_string())
33 }
34
35 // eval
36 fn quasiquote(ast: &MalVal) -> MalVal {
37 match ast {
38 List(ref v,_) | Vector(ref v,_) if v.len() > 0 => {
39 let a0 = &v[0];
40 match a0 {
41 Sym(ref s) if s == "unquote" => v[1].clone(),
42 _ => {
43 match a0 {
44 List(ref v0,_) | Vector(ref v0,_) if v0.len() > 0 => {
45 match v0[0] {
46 Sym(ref s) if s == "splice-unquote" => {
47 list![Sym("concat".to_string()),
48 v0[1].clone(),
49 quasiquote(&list!(v[1..].to_vec()))]
50 },
51 _ => {
52 list![Sym("cons".to_string()),
53 quasiquote(a0),
54 quasiquote(&list!(v[1..].to_vec()))]
55 },
56 }
57 },
58 _ => {
59 list![Sym("cons".to_string()),
60 quasiquote(a0),
61 quasiquote(&list!(v[1..].to_vec()))]
62 }
63 }
64 }
65 }
66 },
67 _ => list![Sym("quote".to_string()), ast.clone()]
68 }
69 }
70
71 fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal,MalArgs)> {
72 match ast {
73 List(v,_) => {
74 match v[0] {
75 Sym(ref s) => {
76 match env_find(env, s) {
77 Some(e) => {
78 match env_get(&e, &v[0]) {
79 Ok(f @ MalFunc{is_macro: true, ..}) => {
80 Some((f, v[1..].to_vec()))
81 },
82 _ => None,
83 }
84 },
85 _ => None,
86 }
87 },
88 _ => None,
89 }
90 },
91 _ => None,
92 }
93 }
94
95 fn macroexpand(mut ast: MalVal, env: &Env) -> (bool, MalRet) {
96 let mut was_expanded = false;
97 while let Some((mf, args)) = is_macro_call(&ast, env) {
98 //println!("macroexpand 1: {:?}", ast);
99 ast = match mf.apply(args) {
100 Err(e) => return (false, Err(e)),
101 Ok(a) => a,
102 };
103 //println!("macroexpand 2: {:?}", ast);
104 was_expanded = true;
105 }
106 ((was_expanded, Ok(ast)))
107 }
108
109 fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
110 match ast {
111 Sym(_) => Ok(env_get(&env, &ast)?),
112 List(v,_) => {
113 let mut lst: MalArgs = vec![];
114 for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
115 Ok(list!(lst))
116 },
117 Vector(v,_) => {
118 let mut lst: MalArgs = vec![];
119 for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
120 Ok(vector!(lst))
121 },
122 Hash(hm,_) => {
123 let mut new_hm: FnvHashMap<String,MalVal> = FnvHashMap::default();
124 for (k,v) in hm.iter() {
125 new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
126 }
127 Ok(Hash(Rc::new(new_hm),Rc::new(Nil)))
128 },
129 _ => Ok(ast.clone()),
130 }
131 }
132
133 fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
134 let ret: MalRet;
135
136 'tco: loop {
137
138 ret = match ast.clone() {
139 List(l,_) => {
140 if l.len() == 0 { return Ok(ast); }
141 match macroexpand(ast.clone(), &env) {
142 (true, Ok(new_ast)) => {
143 ast = new_ast;
144 continue 'tco;
145 }
146 (_, Err(e)) => return Err(e),
147 _ => (),
148 }
149
150 if l.len() == 0 { return Ok(ast); }
151 let a0 = &l[0];
152 match a0 {
153 Sym(ref a0sym) if a0sym == "def!" => {
154 env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
155 },
156 Sym(ref a0sym) if a0sym == "let*" => {
157 env = env_new(Some(env.clone()));
158 let (a1, a2) = (l[1].clone(), l[2].clone());
159 match a1 {
160 List(ref binds,_) | Vector(ref binds,_) => {
161 for (b, e) in binds.iter().tuples() {
162 match b {
163 Sym(_) => {
164 let _ = env_set(&env, b.clone(),
165 eval(e.clone(), env.clone())?);
166 },
167 _ => {
168 return error("let* with non-Sym binding");
169 }
170 }
171 }
172 },
173 _ => {
174 return error("let* with non-List bindings");
175 }
176 };
177 ast = a2;
178 continue 'tco;
179 },
180 Sym(ref a0sym) if a0sym == "quote" => {
181 Ok(l[1].clone())
182 },
183 Sym(ref a0sym) if a0sym == "quasiquote" => {
184 ast = quasiquote(&l[1]);
185 continue 'tco;
186 },
187 Sym(ref a0sym) if a0sym == "defmacro!" => {
188 let (a1, a2) = (l[1].clone(), l[2].clone());
189 let r = eval(a2, env.clone())?;
190 match r {
191 MalFunc{eval, ast, env, params, ..} => {
192 Ok(env_set(&env, a1.clone(),
193 MalFunc{eval: eval, ast: ast.clone(), env: env.clone(),
194 params: params.clone(), is_macro: true,
195 meta: Rc::new(Nil)})?)
196 },
197 _ => error("set_macro on non-function"),
198 }
199 },
200 Sym(ref a0sym) if a0sym == "macroexpand" => {
201 match macroexpand(l[1].clone(), &env) {
202 (_, Ok(new_ast)) => Ok(new_ast),
203 (_, e) => return e,
204 }
205 },
206 Sym(ref a0sym) if a0sym == "try*" => {
207 match eval(l[1].clone(), env.clone()) {
208 Err(ref e) if l.len() >= 3 => {
209 let exc = match e {
210 ErrMalVal(mv) => mv.clone(),
211 ErrString(s) => Str(s.to_string()),
212 };
213 match l[2].clone() {
214 List(c,_) => {
215 let catch_env = env_bind(Some(env.clone()),
216 list!(vec![c[1].clone()]),
217 vec![exc])?;
218 eval(c[2].clone(), catch_env)
219 },
220 _ => error("invalid catch block"),
221 }
222 },
223 res => res,
224 }
225 },
226 Sym(ref a0sym) if a0sym == "do" => {
227 match eval_ast(&list!(l[1..l.len()-1].to_vec()), &env)? {
228 List(_,_) => {
229 ast = l.last().unwrap_or(&Nil).clone();
230 continue 'tco;
231 },
232 _ => error("invalid do form"),
233 }
234 },
235 Sym(ref a0sym) if a0sym == "if" => {
236 let cond = eval(l[1].clone(), env.clone())?;
237 match cond {
238 Bool(false) | Nil if l.len() >= 4 => {
239 ast = l[3].clone();
240 continue 'tco;
241 },
242 Bool(false) | Nil => Ok(Nil),
243 _ if l.len() >= 3 => {
244 ast = l[2].clone();
245 continue 'tco;
246 },
247 _ => Ok(Nil)
248 }
249 },
250 Sym(ref a0sym) if a0sym == "fn*" => {
251 let (a1, a2) = (l[1].clone(), l[2].clone());
252 Ok(MalFunc{eval: eval, ast: Rc::new(a2), env: env,
253 params: Rc::new(a1), is_macro: false,
254 meta: Rc::new(Nil)})
255 },
256 Sym(ref a0sym) if a0sym == "eval" => {
257 ast = eval(l[1].clone(), env.clone())?;
258 while let Some(ref e) = env.clone().outer {
259 env = e.clone();
260 }
261 continue 'tco;
262 },
263 _ => {
264 match eval_ast(&ast, &env)? {
265 List(ref el,_) => {
266 let ref f = el[0].clone();
267 let args = el[1..].to_vec();
268 match f {
269 Func(_,_) => f.apply(args),
270 MalFunc{ast: mast, env: menv, params, ..} => {
271 let a = &**mast;
272 let p = &**params;
273 env = env_bind(Some(menv.clone()), p.clone(), args)?;
274 ast = a.clone();
275 continue 'tco;
276 },
277 _ => error("attempt to call non-function"),
278 }
279 },
280 _ => {
281 error("expected a list")
282 }
283 }
284 }
285 }
286 },
287 _ => eval_ast(&ast, &env),
288 };
289
290 break;
291
292 } // end 'tco loop
293
294 ret
295 }
296
297 // print
298 fn print(ast: &MalVal) -> String {
299 ast.pr_str(true)
300 }
301
302 fn rep(str: &str, env: &Env) -> Result<String,MalErr> {
303 let ast = read(str)?;
304 let exp = eval(ast, env.clone())?;
305 Ok(print(&exp))
306 }
307
308 fn main() {
309 let mut args = std::env::args();
310 let arg1 = args.nth(1);
311
312 // `()` can be used when no completer is required
313 let mut rl = Editor::<()>::new();
314 if rl.load_history(".mal-history").is_err() {
315 println!("No previous history.");
316 }
317
318 // core.rs: defined using rust
319 let repl_env = env_new(None);
320 for (k, v) in core::ns() {
321 env_sets(&repl_env, k, v);
322 }
323 env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
324
325 // core.mal: defined using the language itself
326 let _ = rep("(def! *host-language* \"rust\")", &repl_env);
327 let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
328 let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", &repl_env);
329 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);
330 let _ = rep("(def! inc (fn* [x] (+ x 1)))", &repl_env);
331 let _ = rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))", &repl_env);
332 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);
333
334
335 // Invoked with arguments
336 if let Some(f) = arg1 {
337 match rep(&format!("(load-file \"{}\")",f), &repl_env) {
338 Ok(_) => std::process::exit(0),
339 Err(e) => {
340 println!("Error: {}", format_error(e));
341 std::process::exit(1);
342 }
343 }
344 }
345
346 // main repl loop
347 let _ = rep("(println (str \"Mal [\" *host-language* \"]\"))", &repl_env);
348 loop {
349 let readline = rl.readline("user> ");
350 match readline {
351 Ok(line) => {
352 rl.add_history_entry(&line);
353 rl.save_history(".mal-history").unwrap();
354 if line.len() > 0 {
355 match rep(&line, &repl_env) {
356 Ok(out) => println!("{}", out),
357 Err(e) => println!("Error: {}", format_error(e)),
358 }
359 }
360 },
361 Err(ReadlineError::Interrupted) => continue,
362 Err(ReadlineError::Eof) => break,
363 Err(err) => {
364 println!("Error: {:?}", err);
365 break
366 }
367 }
368 }
369 }
370
371 // vim: ts=2:sw=2:expandtab