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