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