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