1 #![allow(non_snake_case)]
4 //use std::collections::HashMap;
6 use itertools::Itertools;
9 extern crate lazy_static;
11 extern crate itertools;
14 extern crate rustyline;
15 use rustyline::error::ReadlineError;
16 use rustyline::Editor;
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};
26 use env::{Env,env_new,env_bind,env_find,env_get,env_set,env_sets};
31 fn read(str: &str) -> MalRet {
32 reader::read_str(str.to_string())
36 fn quasiquote(ast: &MalVal) -> MalVal {
38 List(ref v,_) | Vector(ref v,_) if v.len() > 0 => {
41 Sym(ref s) if s == "unquote" => v[1].clone(),
44 List(ref v0,_) | Vector(ref v0,_) if v0.len() > 0 => {
46 Sym(ref s) if s == "splice-unquote" => {
47 list![Sym("concat".to_string()),
49 quasiquote(&list!(v[1..].to_vec()))]
52 list![Sym("cons".to_string()),
54 quasiquote(&list!(v[1..].to_vec()))]
59 list![Sym("cons".to_string()),
61 quasiquote(&list!(v[1..].to_vec()))]
67 _ => list![Sym("quote".to_string()), ast.clone()]
71 fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal,MalArgs)> {
76 match env_find(env, s) {
78 match env_get(&e, &v[0]) {
79 Ok(f @ MalFunc{is_macro: true, ..}) => {
80 Some((f, v[1..].to_vec()))
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)),
103 //println!("macroexpand 2: {:?}", ast);
106 ((was_expanded, Ok(ast)))
109 fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
111 Sym(_) => Ok(env_get(&env, &ast)?),
113 let mut lst: MalArgs = vec![];
114 for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
118 let mut lst: MalArgs = vec![];
119 for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
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())?);
127 Ok(Hash(Rc::new(new_hm),Rc::new(Nil)))
129 _ => Ok(ast.clone()),
133 fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
138 ret = match ast.clone() {
140 if l.len() == 0 { return Ok(ast); }
141 match macroexpand(ast.clone(), &env) {
142 (true, Ok(new_ast)) => {
146 (_, Err(e)) => return Err(e),
150 if l.len() == 0 { return Ok(ast); }
153 Sym(ref a0sym) if a0sym == "def!" => {
154 env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
156 Sym(ref a0sym) if a0sym == "let*" => {
157 env = env_new(Some(env.clone()));
158 let (a1, a2) = (l[1].clone(), l[2].clone());
160 List(ref binds,_) | Vector(ref binds,_) => {
161 for (b, e) in binds.iter().tuples() {
164 let _ = env_set(&env, b.clone(),
165 eval(e.clone(), env.clone())?);
168 return error("let* with non-Sym binding");
174 return error("let* with non-List bindings");
180 Sym(ref a0sym) if a0sym == "quote" => {
183 Sym(ref a0sym) if a0sym == "quasiquote" => {
184 ast = quasiquote(&l[1]);
187 Sym(ref a0sym) if a0sym == "defmacro!" => {
188 let (a1, a2) = (l[1].clone(), l[2].clone());
189 let r = eval(a2, env.clone())?;
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)})?)
197 _ => error("set_macro on non-function"),
200 Sym(ref a0sym) if a0sym == "macroexpand" => {
201 match macroexpand(l[1].clone(), &env) {
202 (_, Ok(new_ast)) => Ok(new_ast),
206 Sym(ref a0sym) if a0sym == "try*" => {
207 match eval(l[1].clone(), env.clone()) {
208 Err(ref e) if l.len() >= 3 => {
210 ErrMalVal(mv) => mv.clone(),
211 ErrString(s) => Str(s.to_string()),
215 let catch_env = env_bind(Some(env.clone()),
216 list!(vec![c[1].clone()]),
218 eval(c[2].clone(), catch_env)
220 _ => error("invalid catch block"),
226 Sym(ref a0sym) if a0sym == "do" => {
227 match eval_ast(&list!(l[1..l.len()-1].to_vec()), &env)? {
229 ast = l.last().unwrap_or(&Nil).clone();
232 _ => error("invalid do form"),
235 Sym(ref a0sym) if a0sym == "if" => {
236 let cond = eval(l[1].clone(), env.clone())?;
238 Bool(false) | Nil if l.len() >= 4 => {
242 Bool(false) | Nil => Ok(Nil),
243 _ if l.len() >= 3 => {
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,
256 Sym(ref a0sym) if a0sym == "eval" => {
257 ast = eval(l[1].clone(), env.clone())?;
258 while let Some(ref e) = env.clone().outer {
264 match eval_ast(&ast, &env)? {
266 let ref f = el[0].clone();
267 let args = el[1..].to_vec();
269 Func(_,_) => f.apply(args),
270 MalFunc{ast: mast, env: menv, params, ..} => {
273 env = env_bind(Some(menv.clone()), p.clone(), args)?;
277 _ => error("attempt to call non-function"),
281 error("expected a list")
287 _ => eval_ast(&ast, &env),
298 fn print(ast: &MalVal) -> String {
302 fn rep(str: &str, env: &Env) -> Result<String,MalErr> {
303 let ast = read(str)?;
304 let exp = eval(ast, env.clone())?;
309 let mut args = std::env::args();
310 let arg1 = args.nth(1);
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.");
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);
323 env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
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);
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),
340 println!("Error: {}", format_error(e));
341 std::process::exit(1);
347 let _ = rep("(println (str \"Mal [\" *host-language* \"]\"))", &repl_env);
349 let readline = rl.readline("user> ");
352 rl.add_history_entry(&line);
353 rl.save_history(".mal-history").unwrap();
355 match rep(&line, &repl_env) {
356 Ok(out) => println!("{}", out),
357 Err(e) => println!("Error: {}", format_error(e)),
361 Err(ReadlineError::Interrupted) => continue,
362 Err(ReadlineError::Eof) => break,
364 println!("Error: {:?}", err);
371 // vim: ts=2:sw=2:expandtab