Add gensym and clean `or` macro to stepA of 19 implementations (part 3)
[jackhill/mal.git] / rust / src / bin / step7_quote.rs
CommitLineData
bbeb1b87
AC
1
2extern crate mal;
4ee7c0f2 3
5939404b 4use std::collections::HashMap;
bbeb1b87 5use std::env as stdenv;
b2ba4453 6use std::process as process;
bbeb1b87
AC
7
8use mal::types::{MalVal, MalRet, MalError, err_str};
9use mal::types::{symbol, _nil, string, list, vector, hash_map, malfunc};
bbeb1b87 10use mal::types::MalError::{ErrString, ErrMalVal};
fb439f3c 11use mal::types::MalType::{Nil, False, Sym, List, Vector, Hash_Map, Func, MalFunc};
bbeb1b87
AC
12use mal::{readline, reader, core};
13use mal::env::{env_set, env_get, env_new, env_bind, env_root, Env};
4ee7c0f2 14
4ee7c0f2
JM
15
16// read
17fn read(str: String) -> MalRet {
18 reader::read_str(str)
19}
20
21// eval
22fn 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
29fn 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
65fn 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
89fn 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// print
229fn print(exp: MalVal) -> String {
230 exp.pr_str(true)
231}
232
3744d566 233fn 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
240fn 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}