2 //use std::collections::HashMap;
4 use itertools::Itertools;
7 extern crate lazy_static;
9 extern crate itertools;
12 extern crate rustyline;
13 use rustyline::error::ReadlineError;
14 use rustyline::Editor;
18 use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
19 use types::MalVal::{Nil,Bool,Str,Sym,List,Vector,Hash,Func,MalFunc};
23 use env::{Env,env_new,env_bind,env_get,env_set,env_sets};
28 fn read(str: &str) -> MalRet {
29 reader::read_str(str.to_string())
33 fn quasiquote(ast: &MalVal) -> MalVal {
35 List(ref v,_) | Vector(ref v,_) if v.len() > 0 => {
38 Sym(ref s) if s == "unquote" => v[1].clone(),
41 List(ref v0,_) | Vector(ref v0,_) if v0.len() > 0 => {
43 Sym(ref s) if s == "splice-unquote" => {
44 list![Sym("concat".to_string()),
46 quasiquote(&list!(v[1..].to_vec()))]
49 list![Sym("cons".to_string()),
51 quasiquote(&list!(v[1..].to_vec()))]
56 list![Sym("cons".to_string()),
58 quasiquote(&list!(v[1..].to_vec()))]
64 _ => list![Sym("quote".to_string()), ast.clone()]
68 fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
70 Sym(_) => Ok(env_get(&env, &ast)?),
72 let mut lst: MalArgs = vec![];
73 for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
77 let mut lst: MalArgs = vec![];
78 for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
82 let mut new_hm: FnvHashMap<String,MalVal> = FnvHashMap::default();
83 for (k,v) in hm.iter() {
84 new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
86 Ok(Hash(Rc::new(new_hm),Rc::new(Nil)))
92 fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
97 ret = match ast.clone() {
99 if l.len() == 0 { return Ok(ast); }
102 Sym(ref a0sym) if a0sym == "def!" => {
103 env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
105 Sym(ref a0sym) if a0sym == "let*" => {
106 env = env_new(Some(env.clone()));
107 let (a1, a2) = (l[1].clone(), l[2].clone());
109 List(ref binds,_) | Vector(ref binds,_) => {
110 for (b, e) in binds.iter().tuples() {
113 let _ = env_set(&env, b.clone(),
114 eval(e.clone(), env.clone())?);
117 return error("let* with non-Sym binding");
123 return error("let* with non-List bindings");
129 Sym(ref a0sym) if a0sym == "quote" => {
132 Sym(ref a0sym) if a0sym == "quasiquote" => {
133 ast = quasiquote(&l[1]);
136 Sym(ref a0sym) if a0sym == "do" => {
137 match eval_ast(&list!(l[1..l.len()-1].to_vec()), &env)? {
139 ast = l.last().unwrap_or(&Nil).clone();
142 _ => error("invalid do form"),
145 Sym(ref a0sym) if a0sym == "if" => {
146 let cond = eval(l[1].clone(), env.clone())?;
148 Bool(false) | Nil if l.len() >= 4 => {
152 Bool(false) | Nil => Ok(Nil),
153 _ if l.len() >= 3 => {
160 Sym(ref a0sym) if a0sym == "fn*" => {
161 let (a1, a2) = (l[1].clone(), l[2].clone());
162 Ok(MalFunc{eval: eval, ast: Rc::new(a2), env: env,
163 params: Rc::new(a1), is_macro: false,
166 Sym(ref a0sym) if a0sym == "eval" => {
167 ast = eval(l[1].clone(), env.clone())?;
168 while let Some(ref e) = env.clone().outer {
174 match eval_ast(&ast, &env)? {
176 let ref f = el[0].clone();
177 let args = el[1..].to_vec();
179 Func(_,_) => f.apply(args),
180 MalFunc{ast: mast, env: menv, params, ..} => {
183 env = env_bind(Some(menv.clone()), p.clone(), args)?;
187 _ => error("attempt to call non-function"),
191 error("expected a list")
197 _ => eval_ast(&ast, &env),
208 fn print(ast: &MalVal) -> String {
212 fn rep(str: &str, env: &Env) -> Result<String,MalErr> {
213 let ast = read(str)?;
214 let exp = eval(ast, env.clone())?;
219 let mut args = std::env::args();
220 let arg1 = args.nth(1);
222 // `()` can be used when no completer is required
223 let mut rl = Editor::<()>::new();
224 if rl.load_history(".mal-history").is_err() {
225 println!("No previous history.");
228 // core.rs: defined using rust
229 let repl_env = env_new(None);
230 for (k, v) in core::ns() {
231 env_sets(&repl_env, k, v);
233 env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
235 // core.mal: defined using the language itself
236 let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
237 let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", &repl_env);
239 // Invoked with arguments
240 if let Some(f) = arg1 {
241 match rep(&format!("(load-file \"{}\")",f), &repl_env) {
242 Ok(_) => std::process::exit(0),
244 println!("Error: {}", format_error(e));
245 std::process::exit(1);
252 let readline = rl.readline("user> ");
255 rl.add_history_entry(&line);
256 rl.save_history(".mal-history").unwrap();
258 match rep(&line, &repl_env) {
259 Ok(out) => println!("{}", out),
260 Err(e) => println!("Error: {}", format_error(e)),
264 Err(ReadlineError::Interrupted) => continue,
265 Err(ReadlineError::Eof) => break,
267 println!("Error: {:?}", err);
274 // vim: ts=2:sw=2:expandtab