rust: add step9_try. Refactor error handling.
authorJoel Martin <github@martintribe.org>
Tue, 28 Oct 2014 01:45:27 +0000 (20:45 -0500)
committerJoel Martin <github@martintribe.org>
Wed, 7 Jan 2015 03:58:59 +0000 (21:58 -0600)
Additional core functions.

15 files changed:
rust/Cargo.toml
rust/Makefile
rust/src/core.rs
rust/src/env.rs
rust/src/reader.rs
rust/src/step1_read_print.rs
rust/src/step2_eval.rs
rust/src/step3_env.rs
rust/src/step4_if_fn_do.rs
rust/src/step5_tco.rs
rust/src/step6_file.rs
rust/src/step7_quote.rs
rust/src/step8_macros.rs
rust/src/step9_try.rs [new file with mode: 0644]
rust/src/types.rs

index d0eee4f..bce22ed 100644 (file)
@@ -37,3 +37,5 @@ name = "step6_file"
 name = "step7_quote"
 [[bin]]
 name = "step8_macros"
+[[bin]]
+name = "step9_try"
index 0e0cb05..7ec86e3 100644 (file)
@@ -3,13 +3,14 @@
 SOURCES_BASE = src/types.rs src/readline.rs \
               src/reader.rs src/printer.rs \
               src/env.rs src/core.rs
-SOURCES_LISP = src/env.rs src/core.rs src/step7_quote.rs
+SOURCES_LISP = src/env.rs src/core.rs src/step9_try.rs
 SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
 
 #####################
 
 SRCS = step0_repl.rs step1_read_print.rs step2_eval.rs step3_env.rs \
-       step4_if_fn_do.rs step5_tco.rs step6_file.rs step7_quote.rs
+       step4_if_fn_do.rs step5_tco.rs step6_file.rs step7_quote.rs \
+       step8_macros.rs step9_try.rs
 BINS = $(SRCS:%.rs=target/%)
 
 #####################
index 98eb32a..61dc0ce 100644 (file)
@@ -4,16 +4,18 @@ extern crate time;
 use std::collections::HashMap;
 use std::io::File;
 
-use types::{MalVal,MalRet,Int,Strn,List,Vector,
+use types::{MalVal,MalRet,err_val,err_str,err_string,
+            Int,Strn,List,Vector,Hash_Map,
             _nil,_true,_false,_int,string,list,func};
+use types;
+use readline;
 use reader;
 use printer;
 
 // General functions
-
 fn equal_q(a:Vec<MalVal>) -> MalRet {
     if a.len() != 2 {
-        return Err("Wrong arity to equal? call".to_string());
+        return err_str("Wrong arity to equal? call");
     }
     match a[0] == a[1] {
         true => Ok(_true()),
@@ -21,6 +23,14 @@ fn equal_q(a:Vec<MalVal>) -> MalRet {
     }
 }
 
+// Errors/Exceptions
+fn throw(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 1 {
+        return err_str("Wrong arity to throw call");
+    }
+    err_val(a[0].clone())
+}
+
 // String routines
 fn pr_str(a:Vec<MalVal>) -> MalRet {
     Ok(string(printer::pr_list(&a, true, "", "", " ")))
@@ -40,10 +50,20 @@ fn println(a:Vec<MalVal>) -> MalRet {
     Ok(_nil())
 }
 
+fn readline(a:Vec<MalVal>) -> MalRet {
+    match *a[0] {
+        Strn(ref a0) => match readline::mal_readline(a0.as_slice()) {
+            Some(line) => Ok(string(line)),
+            None       => err_val(_nil()),
+        },
+        _ => err_str("read_string called with non-string"),
+    }
+}
+
 fn read_string(a:Vec<MalVal>) -> MalRet {
     match *a[0] {
         Strn(ref a0) => reader::read_str(a0.to_string()),
-        _ => Err("read_string called with non-string".to_string()),
+        _ => err_str("read_string called with non-string"),
     }
 }
 
@@ -52,10 +72,10 @@ fn slurp(a:Vec<MalVal>) -> MalRet {
         Strn(ref a0) => {
             match File::open(&Path::new(a0.as_slice())).read_to_string() {
                 Ok(s) => Ok(string(s)),
-                Err(e) => Err(e.to_string()),
+                Err(e) => err_string(e.to_string()),
             }
         },
-        _ => Err("slurp called with non-string".to_string()),
+        _ => err_str("slurp called with non-string"),
     }
 }
 
@@ -65,9 +85,9 @@ fn int_op(f: |i:int,j:int|-> int, a:Vec<MalVal>) -> MalRet {
     match *a[0] {
         Int(a0) => match *a[1] {
             Int(a1) => Ok(_int(f(a0,a1))),
-            _ => Err("second arg must be an int".to_string()),
+            _ => err_str("second arg must be an int"),
         },
-        _ => Err("first arg must be an int".to_string()),
+        _ => err_str("first arg must be an int"),
     }
 }
 
@@ -80,9 +100,9 @@ fn bool_op(f: |i:int,j:int|-> bool, a:Vec<MalVal>) -> MalRet {
                     false => Ok(_false()), 
                 }
             },
-            _ => Err("second arg must be an int".to_string()),
+            _ => err_str("second arg must be an int"),
         },
-        _ => Err("first arg must be an int".to_string()),
+        _ => err_str("first arg must be an int"),
     }
 }
 
@@ -105,19 +125,29 @@ pub fn time_ms(a:Vec<MalVal>) -> MalRet {
 }
 
 
-// Sequence functions
-pub fn _list(a:Vec<MalVal>) -> MalRet { Ok(list(a)) }
-
-pub fn list_q(a:Vec<MalVal>) -> MalRet {
-    if a.len() != 1 {
-        return Err("Wrong arity to list? call".to_string());
+// Hash Map functions
+pub fn get(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 2 {
+        return err_str("Wrong arity to get call");
     }
-    match *a[0].clone() {
-        List(_) => Ok(_true()),
-        _ => Ok(_false()),
+    let a0 = a[0].clone();
+    let hm: &HashMap<String,MalVal> = match *a0 {
+        Hash_Map(ref hm) => hm,
+        _ => return err_str("get of non-hash map"),
+    };
+    match *a[1] {
+        Strn(ref key) => {
+            match hm.find_copy(key) {
+                Some(v) => Ok(v),
+                None    => Ok(_nil()),
+            }
+        },
+        _ => return err_str("get with non-string key"),
     }
 }
 
+
+// Sequence functions
 pub fn cons(a:Vec<MalVal>) -> MalRet {
     match *a[1] {
         List(ref v) | Vector(ref v) => {
@@ -125,7 +155,7 @@ pub fn cons(a:Vec<MalVal>) -> MalRet {
             new_v.insert(0, a[0].clone());
             Ok(list(new_v))
         },
-        _ => Err("Second arg to cons not a sequence".to_string()),
+        _ => err_str("Second arg to cons not a sequence"),
     }
 }
 
@@ -136,7 +166,7 @@ pub fn concat(a:Vec<MalVal>) -> MalRet {
             List(ref l) | Vector(ref l) => {
                 new_v.push_all(l.as_slice());
             },
-            _ => return Err("concat called with non-sequence".to_string()),
+            _ => return err_str("concat called with non-sequence"),
         }
     }
     Ok(list(new_v))
@@ -144,13 +174,13 @@ pub fn concat(a:Vec<MalVal>) -> MalRet {
 
 pub fn nth(a:Vec<MalVal>) -> MalRet {
     if a.len() != 2 {
-        return Err("Wrong arity to nth call".to_string());
+        return err_str("Wrong arity to nth call");
     }
     let a0 = a[0].clone();
     let a1 = a[1].clone();
     let seq = match *a0 {
         List(ref v) | Vector(ref v) => v,
-        _ => return Err("nth called with non-sequence".to_string()),
+        _ => return err_str("nth called with non-sequence"),
     };
     let idx = match *a1 {
         Int(i) => {
@@ -159,7 +189,7 @@ pub fn nth(a:Vec<MalVal>) -> MalRet {
                 None => return Ok(_nil()),
             }
         },
-        _ => return Err("nth called with non-integer index".to_string()),
+        _ => return err_str("nth called with non-integer index"),
     };
     if idx >= seq.len() {
         Ok(_nil())
@@ -170,12 +200,12 @@ pub fn nth(a:Vec<MalVal>) -> MalRet {
 
 pub fn first(a:Vec<MalVal>) -> MalRet {
     if a.len() != 1 {
-        return Err("Wrong arity to first call".to_string());
+        return err_str("Wrong arity to first call");
     }
     let a0 = a[0].clone();
     let seq = match *a0 {
         List(ref v) | Vector(ref v) => v,
-        _ => return Err("first called with non-sequence".to_string()),
+        _ => return err_str("first called with non-sequence"),
     };
     if seq.len() == 0 {
         Ok(_nil())
@@ -186,12 +216,12 @@ pub fn first(a:Vec<MalVal>) -> MalRet {
 
 pub fn rest(a:Vec<MalVal>) -> MalRet {
     if a.len() != 1 {
-        return Err("Wrong arity to rest call".to_string());
+        return err_str("Wrong arity to rest call");
     }
     let a0 = a[0].clone();
     let seq = match *a0 {
         List(ref v) | Vector(ref v) => v,
-        _ => return Err("rest called with non-sequence".to_string()),
+        _ => return err_str("rest called with non-sequence"),
     };
     if seq.len() == 0 {
         Ok(list(vec![]))
@@ -200,31 +230,67 @@ pub fn rest(a:Vec<MalVal>) -> MalRet {
     }
 }
 
-pub fn count(a:Vec<MalVal>) -> MalRet {
+pub fn empty_q(a:Vec<MalVal>) -> MalRet {
     if a.len() != 1 {
-        return Err("Wrong arity to count call".to_string());
+        return err_str("Wrong arity to empty? call");
     }
     match *a[0].clone() {
         List(ref v) | Vector(ref v) => {
-            Ok(_int(v.len().to_int().unwrap()))
+            match v.len() {
+                0 => Ok(_true()),
+                _ => Ok(_false()),
+            }
         },
-        _ => Err("count called on non-sequence".to_string()),
+        _ => err_str("empty? called on non-sequence"),
     }
 }
 
-pub fn empty_q(a:Vec<MalVal>) -> MalRet {
+pub fn count(a:Vec<MalVal>) -> MalRet {
     if a.len() != 1 {
-        return Err("Wrong arity to empty? call".to_string());
+        return err_str("Wrong arity to count call");
     }
     match *a[0].clone() {
         List(ref v) | Vector(ref v) => {
-            match v.len() {
-                0 => Ok(_true()),
-                _ => Ok(_false()),
+            Ok(_int(v.len().to_int().unwrap()))
+        },
+        _ => err_str("count called on non-sequence"),
+    }
+}
+
+pub fn apply(a:Vec<MalVal>) -> MalRet {
+    if a.len() < 2 {
+        return err_str("apply call needs 2 or more arguments");
+    }
+    let ref f = a[0];
+    let mut args = a.slice(1,a.len()-1).to_vec();
+    match *a[a.len()-1] {
+        List(ref v) | Vector(ref v) => {
+            args.push_all(v.as_slice());
+            f.apply(args)
+        },
+        _ => err_str("apply call with non-sequence"),
+    }
+}
+
+pub fn map(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 2 {
+        return err_str("Wrong arity to map call");
+    }
+    let mut results:Vec<MalVal> = vec![];
+    let ref f = a[0].clone();
+    let seq = a[1].clone();
+    match *seq {
+        List(ref v) | Vector(ref v) => {
+            for mv in v.iter() {
+                match f.apply(vec![mv.clone()]) {
+                    Ok(res) => results.push(res),
+                    Err(e) => return Err(e),
+                }
             }
         },
-        _ => Err("empty? called on non-sequence".to_string()),
+        _ => return err_str("map call with non-sequence"),
     }
+    Ok(list(results))
 }
 
 
@@ -233,11 +299,17 @@ pub fn ns() -> HashMap<String,MalVal> {
     let mut ns: HashMap<String,MalVal> = HashMap::new();;
 
     ns.insert("=".to_string(), func(equal_q));
+    ns.insert("throw".to_string(), func(throw));
+    ns.insert("nil?".to_string(), func(types::nil_q));
+    ns.insert("true?".to_string(), func(types::true_q));
+    ns.insert("false?".to_string(), func(types::false_q));
+    ns.insert("symbol?".to_string(), func(types::symbol_q));
 
     ns.insert("pr-str".to_string(), func(pr_str));
     ns.insert("str".to_string(), func(str));
     ns.insert("prn".to_string(), func(prn));
     ns.insert("println".to_string(), func(println));
+    ns.insert("readline".to_string(), func(readline));
     ns.insert("read-string".to_string(), func(read_string));
     ns.insert("slurp".to_string(), func(slurp));
 
@@ -251,8 +323,15 @@ pub fn ns() -> HashMap<String,MalVal> {
     ns.insert("/".to_string(), func(div));
     ns.insert("time-ms".to_string(), func(time_ms));
 
-    ns.insert("list".to_string(), func(_list));
-    ns.insert("list?".to_string(), func(list_q));
+    ns.insert("list".to_string(), func(types::listv));
+    ns.insert("list?".to_string(), func(types::list_q));
+    ns.insert("vector".to_string(), func(types::vectorv));
+    ns.insert("vector?".to_string(), func(types::vector_q));
+    ns.insert("hash-map".to_string(), func(types::hash_mapv));
+    ns.insert("map?".to_string(), func(types::hash_map_q));
+    ns.insert("get".to_string(), func(get));
+
+    ns.insert("sequential?".to_string(), func(types::sequential_q));
     ns.insert("cons".to_string(), func(cons));
     ns.insert("concat".to_string(), func(concat));
     ns.insert("empty?".to_string(), func(empty_q));
@@ -260,6 +339,8 @@ pub fn ns() -> HashMap<String,MalVal> {
     ns.insert("first".to_string(), func(first));
     ns.insert("rest".to_string(), func(rest));
     ns.insert("count".to_string(), func(count));
+    ns.insert("apply".to_string(), func(apply));
+    ns.insert("map".to_string(), func(map));
 
     return ns;
 }
index 730010a..0d72e62 100644 (file)
@@ -5,7 +5,7 @@ use std::cell::RefCell;
 use std::collections::HashMap;
 use std::fmt;
 
-use types::{MalVal,MalRet,Sym,List,_nil,list};
+use types::{MalVal,MalRet,Sym,List,_nil,list,err_string};
 
 struct EnvType {
     data: HashMap<String,MalVal>,
@@ -89,7 +89,7 @@ pub fn env_get(env: Env, key: String) -> MalRet {
                 None => Ok(_nil()),
             }
         },
-        None    => Err("'".to_string() + key + "' not found".to_string()),
+        None    => err_string("'".to_string() + key + "' not found".to_string()),
     }
 }
 
index 6a52dd2..652a7d7 100644 (file)
@@ -5,8 +5,9 @@
 
 extern crate pcre;
 
-use types::{MalVal,MalRet,
-            _nil,_true,_false,_int,symbol,string,list,vector,hash_mapv};
+use types::{MalVal,MalRet,ErrString,ErrMalVal,
+            _nil,_true,_false,_int,symbol,string,list,vector,hash_mapv,
+            err_str,err_string,err_val};
 use self::pcre::Pcre;
 use super::printer::unescape_str;
 
@@ -58,7 +59,7 @@ fn tokenize(str :String) -> Vec<String> {
 fn read_atom(rdr : &mut Reader) -> MalRet {
     let otoken = rdr.next();
     //println!("read_atom: {}", otoken);
-    if otoken.is_none() { return Err("read_atom underflow".to_string()); }
+    if otoken.is_none() { return err_str("read_atom underflow"); }
     let stoken = otoken.unwrap();
     let token = stoken.as_slice();
     if regex!(r"^-?[0-9]+$").is_match(token) {
@@ -101,7 +102,8 @@ fn read_seq(rdr : &mut Reader, start: &str, end: &str) -> Result<Vec<MalVal>,Str
 
         match read_form(rdr) {
             Ok(mv) => ast_vec.push(mv),
-            Err(e) => return Err(e),
+            Err(ErrString(es)) => return Err(es),
+            Err(ErrMalVal(_)) => return Err("read_seq exception".to_string()),
         }
     }
     rdr.next();
@@ -112,21 +114,21 @@ fn read_seq(rdr : &mut Reader, start: &str, end: &str) -> Result<Vec<MalVal>,Str
 fn read_list(rdr : &mut Reader) -> MalRet {
     match read_seq(rdr, "(", ")") {
         Ok(seq) => Ok(list(seq)),
-        Err(e) => Err(e),
+        Err(es) => err_string(es),
     }
 }
 
 fn read_vector(rdr : &mut Reader) -> MalRet {
     match read_seq(rdr, "[", "]") {
         Ok(seq) => Ok(vector(seq)),
-        Err(e) => Err(e),
+        Err(es) => err_string(es),
     }
 }
 
 fn read_hash_map(rdr : &mut Reader) -> MalRet {
     match read_seq(rdr, "{", "}") {
         Ok(seq) => hash_mapv(seq),
-        Err(e) => Err(e),
+        Err(es) => err_string(es),
     }
 }
 
@@ -165,13 +167,13 @@ fn read_form(rdr : &mut Reader) -> MalRet {
             }
         },
 
-        ")" => Err("unexected ')'".to_string()),
+        ")" => err_str("unexected ')'"),
         "(" => read_list(rdr),
 
-        "]" => Err("unexected ']'".to_string()),
+        "]" => err_str("unexected ']'"),
         "[" => read_vector(rdr),
 
-        "}" => Err("unexected '}'".to_string()),
+        "}" => err_str("unexected '}'"),
         "{" => read_hash_map(rdr),
 
         _   => read_atom(rdr)
@@ -181,7 +183,8 @@ fn read_form(rdr : &mut Reader) -> MalRet {
 pub fn read_str(str :String) -> MalRet {
     let tokens = tokenize(str);
     if tokens.len() == 0 {
-        return Err("<empty line>".to_string());
+        // any malval as the error slot means empty line
+        return err_val(_nil())
     }
     //println!("tokens: {}", tokens);
     let rdr = &mut Reader{tokens: tokens, position: 0};
index d69ea6d..3ce11e6 100644 (file)
@@ -4,7 +4,7 @@
 extern crate regex_macros;
 extern crate regex;
 
-use types::{MalVal,MalRet};
+use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal};
 mod readline;
 mod types;
 mod env;
@@ -26,7 +26,7 @@ fn print(exp: MalVal) -> String {
     exp.pr_str(true)
 }
 
-fn rep(str: String) -> Result<String,String> {
+fn rep(str: String) -> Result<String,MalError> {
     match read(str) {
         Err(e) => Err(e),
         Ok(ast) => {
@@ -45,11 +45,8 @@ fn main() {
         match line { None => break, _ => () }
         match rep(line.unwrap()) {
             Ok(str)  => println!("{}", str),
-            Err(str) => {
-                if str.as_slice() != "<empty line>" {
-                    println!("Error: {}", str)
-                }
-            }
+            Err(ErrMalVal(_)) => (),  // Blank line
+            Err(ErrString(s)) => println!("Error: {}", s),
         }
     }
 }
index 4a0fcb5..29c53d9 100644 (file)
@@ -6,13 +6,14 @@ extern crate regex;
 
 use std::collections::HashMap;
 
-use types::{MalVal,MalRet,Int,Sym,List,Vector,Hash_Map,
+use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str,
+            Int,Sym,List,Vector,Hash_Map,
             _nil,_int,list,vector,hash_map,func};
 mod readline;
 mod types;
-mod env;
 mod reader;
 mod printer;
+mod env; // because types uses env
 
 // read
 fn read(str: String) -> MalRet {
@@ -71,7 +72,7 @@ fn eval(ast: MalVal, env: &HashMap<String,MalVal>) -> MalRet {
                     let ref f = args.clone()[0];
                     f.apply(args.slice(1,args.len()).to_vec())
                 }
-                _ => Err("Invalid apply".to_string()),
+                _ => err_str("Invalid apply"),
             }
         }
     }
@@ -82,8 +83,8 @@ fn print(exp: MalVal) -> String {
     exp.pr_str(true)
 }
 
-fn rep(str: String, env: &HashMap<String,MalVal>) -> Result<String,String> {
-    match read(str) {
+fn rep(str: &str, env: &HashMap<String,MalVal>) -> Result<String,MalError> {
+    match read(str.to_string()) {
         Err(e) => Err(e),
         Ok(ast) => {
             //println!("read: {}", ast);
@@ -99,9 +100,9 @@ fn int_op(f: |i:int,j:int|-> int, a:Vec<MalVal>) -> MalRet {
     match *a[0] {
         Int(a0) => match *a[1] {
             Int(a1) => Ok(_int(f(a0,a1))),
-            _ => Err("second arg must be an int".to_string()),
+            _ => err_str("second arg must be an int"),
         },
-        _ => Err("first arg must be an int".to_string()),
+        _ => err_str("first arg must be an int"),
     }
 }
 fn add(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i+j }, a) }
@@ -119,9 +120,10 @@ fn main() {
     loop {
         let line = readline::mal_readline("user> ");
         match line { None => break, _ => () }
-        match rep(line.unwrap(), &repl_env) {
+        match rep(line.unwrap().as_slice(), &repl_env) {
             Ok(str)  => println!("{}", str),
-            Err(str) => println!("Error: {}", str),
+            Err(ErrMalVal(_)) => (),  // Blank line
+            Err(ErrString(s)) => println!("Error: {}", s),
         }
     }
 }
index f9f79ea..ac7199c 100644 (file)
@@ -6,7 +6,8 @@ extern crate regex;
 
 use std::collections::HashMap;
 
-use types::{MalVal,MalRet,Int,Sym,List,Vector,Hash_Map,
+use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str,
+            Int,Sym,List,Vector,Hash_Map,
             _int,list,vector,hash_map,func};
 use env::{Env,env_new,env_set,env_get};
 mod readline;
@@ -67,84 +68,88 @@ fn eval(ast: MalVal, env: Env) -> MalRet {
 
     // apply list
     match *ast2 {
+        List(_) => (),  // continue
+        _ => return Ok(ast2),
+    }
+
+    let (args, a0sym) = match *ast2 {
         List(ref args) => {
             if args.len() == 0 { 
                 return Ok(ast);
             }
             let ref a0 = *args[0];
             match *a0 {
-                Sym(ref a0sym) => {
-                    match a0sym.as_slice() {
-                        "def!" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            let res = eval(a2, env.clone());
-                            match res {
-                                Ok(r) => {
-                                    match *a1 {
-                                        Sym(ref s) => {
-                                            env_set(&env.clone(), s.clone(), r.clone());
-                                            return Ok(r);
-                                        },
-                                        _ => {
-                                            return Err("def! of non-symbol".to_string())
-                                        }
-                                    }
-                                },
-                                Err(e) => return Err(e),
-                            }
-                        },
-                        "let*" => {
-                            let let_env = env_new(Some(env.clone()));
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            match *a1 {
-                                List(ref binds) | Vector(ref binds) => {
-                                    let mut it = binds.iter();
-                                    while it.len() >= 2 {
-                                        let b = it.next().unwrap();
-                                        let exp = it.next().unwrap();
-                                        match **b {
-                                            Sym(ref bstr) => {
-                                                match eval(exp.clone(), let_env.clone()) {
-                                                    Ok(r) => {
-                                                        env_set(&let_env, bstr.clone(), r);
-                                                    },
-                                                    Err(e) => {
-                                                        return Err(e);
-                                                    },
-                                                }
-                                            },
-                                            _ => {
-                                                return Err("let* with non-symbol binding".to_string());
-                                            },
-                                        }
-                                    }
-                                },
-                                _ => return Err("let* with non-list bindings".to_string()),
-                            }
-                            return eval(a2, let_env.clone());
+                Sym(ref a0sym) => (args, a0sym.as_slice()),
+                _ => (args, "__<fn*>__"),
+            }
+        },
+        _ => return err_str("Expected list"),
+    };
+
+    match a0sym {
+        "def!" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            let res = eval(a2, env.clone());
+            match res {
+                Ok(r) => {
+                    match *a1 {
+                        Sym(ref s) => {
+                            env_set(&env.clone(), s.clone(), r.clone());
+                            return Ok(r);
                         },
-                        _ => ()
+                        _ => {
+                            return err_str("def! of non-symbol")
+                        }
                     }
-                }
-                _ => (),
+                },
+                Err(e) => return Err(e),
             }
-            // function call
+        },
+        "let*" => {
+            let let_env = env_new(Some(env.clone()));
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            match *a1 {
+                List(ref binds) | Vector(ref binds) => {
+                    let mut it = binds.iter();
+                    while it.len() >= 2 {
+                        let b = it.next().unwrap();
+                        let exp = it.next().unwrap();
+                        match **b {
+                            Sym(ref bstr) => {
+                                match eval(exp.clone(), let_env.clone()) {
+                                    Ok(r) => {
+                                        env_set(&let_env, bstr.clone(), r);
+                                    },
+                                    Err(e) => {
+                                        return Err(e);
+                                    },
+                                }
+                            },
+                            _ => {
+                                return err_str("let* with non-symbol binding");
+                            },
+                        }
+                    }
+                },
+                _ => return err_str("let* with non-list bindings"),
+            }
+            return eval(a2, let_env.clone());
+        },
+        _ => { // function call
             return match eval_ast(ast, env) {
                 Err(e) => Err(e),
                 Ok(el) => {
-                    match *el {
-                        List(ref args) => {
-                            let ref f = args.clone()[0];
-                            f.apply(args.slice(1,args.len()).to_vec())
-                        }
-                        _ => Err("Invalid apply".to_string()),
-                    }
+                    let args = match *el {
+                        List(ref args) => args,
+                        _ => return err_str("Invalid apply"),
+                    };
+                    let ref f = args.clone()[0];
+                    f.apply(args.slice(1,args.len()).to_vec())
                 }
-            }
-        }
-        _ => Err("Expected list".to_string()),
+            };
+        },
     }
 }
 
@@ -153,8 +158,8 @@ fn print(exp: MalVal) -> String {
     exp.pr_str(true)
 }
 
-fn rep(str: String, env: Env) -> Result<String,String> {
-    match read(str) {
+fn rep(str: &str, env: Env) -> Result<String,MalError> {
+    match read(str.to_string()) {
         Err(e) => Err(e),
         Ok(ast) => {
             //println!("read: {}", ast);
@@ -170,9 +175,9 @@ fn int_op(f: |i:int,j:int|-> int, a:Vec<MalVal>) -> MalRet {
     match *a[0] {
         Int(a0) => match *a[1] {
             Int(a1) => Ok(_int(f(a0,a1))),
-            _ => Err("second arg must be an int".to_string()),
+            _ => err_str("second arg must be an int"),
         },
-        _ => Err("first arg must be an int".to_string()),
+        _ => err_str("first arg must be an int"),
     }
 }
 fn add(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i+j }, a) }
@@ -190,9 +195,10 @@ fn main() {
     loop {
         let line = readline::mal_readline("user> ");
         match line { None => break, _ => () }
-        match rep(line.unwrap(), repl_env.clone()) {
+        match rep(line.unwrap().as_slice(), repl_env.clone()) {
             Ok(str)  => println!("{}", str),
-            Err(str) => println!("Error: {}", str),
+            Err(ErrMalVal(_)) => (),  // Blank line
+            Err(ErrString(s)) => println!("Error: {}", s),
         }
     }
 }
dissimilarity index 65%
index 0f3603d..c5c9413 100644 (file)
-// support precompiled regexes in reader.rs
-#![feature(phase)]
-#[phase(plugin)]
-extern crate regex_macros;
-extern crate regex;
-
-use std::collections::HashMap;
-
-use types::{MalVal,MalRet,Nil,False,Sym,List,Vector,Hash_Map,
-            _nil,list,vector,hash_map,malfunc};
-use env::{Env,env_new,env_set,env_get};
-mod readline;
-mod types;
-mod reader;
-mod printer;
-mod env;
-mod core;
-
-// read
-fn read(str: String) -> MalRet {
-    reader::read_str(str)
-}
-
-// eval
-fn eval_ast(ast: MalVal, env: Env) -> MalRet {
-    let ast2 = ast.clone();
-    match *ast2 {
-    //match *ast {
-        Sym(ref sym) => {
-            env_get(env.clone(), sym.clone())
-        },
-        List(ref a) | Vector(ref a) => {
-            let mut ast_vec : Vec<MalVal> = vec![];
-            for mv in a.iter() {
-                let mv2 = mv.clone();
-                match eval(mv2, env.clone()) {
-                    Ok(mv) => { ast_vec.push(mv); },
-                    Err(e) => { return Err(e); },
-                }
-            }
-            Ok(match *ast { List(_) => list(ast_vec),
-                            _       => vector(ast_vec) })
-        },
-        Hash_Map(ref hm) => {
-            let mut new_hm: HashMap<String,MalVal> = HashMap::new();
-            for (key, value) in hm.iter() {
-                match eval(value.clone(), env.clone()) {
-                    Ok(mv) => { new_hm.insert(key.to_string(), mv); },
-                    Err(e) => return Err(e),
-                }
-            }
-            Ok(hash_map(new_hm))
-        },
-        _ => {
-            Ok(ast)
-        }
-    }
-}
-
-fn eval(ast: MalVal, env: Env) -> MalRet {
-    //println!("eval: {}, {}", ast, env.borrow());
-    //println!("eval: {}", ast);
-    let ast2 = ast.clone();
-    match *ast2 {
-        List(_) => (),  // continue
-        _ => return eval_ast(ast2, env),
-    }
-
-    // apply list
-    match *ast2 {
-        List(ref args) => {
-            if args.len() == 0 { 
-                return Ok(ast);
-            }
-            let ref a0 = *args[0];
-            match *a0 {
-                Sym(ref a0sym) => {
-                    match a0sym.as_slice() {
-                        "def!" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            let res = eval(a2, env.clone());
-                            match res {
-                                Ok(r) => {
-                                    match *a1 {
-                                        Sym(ref s) => {
-                                            env_set(&env.clone(), s.clone(), r.clone());
-                                            return Ok(r);
-                                        },
-                                        _ => {
-                                            return Err("def! of non-symbol".to_string())
-                                        }
-                                    }
-                                },
-                                Err(e) => return Err(e),
-                            }
-                        },
-                        "let*" => {
-                            let let_env = env_new(Some(env.clone()));
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            match *a1 {
-                                List(ref binds) | Vector(ref binds) => {
-                                    let mut it = binds.iter();
-                                    while it.len() >= 2 {
-                                        let b = it.next().unwrap();
-                                        let exp = it.next().unwrap();
-                                        match **b {
-                                            Sym(ref bstr) => {
-                                                match eval(exp.clone(), let_env.clone()) {
-                                                    Ok(r) => {
-                                                        env_set(&let_env, bstr.clone(), r);
-                                                    },
-                                                    Err(e) => {
-                                                        return Err(e);
-                                                    },
-                                                }
-                                            },
-                                            _ => {
-                                                return Err("let* with non-symbol binding".to_string());
-                                            },
-                                        }
-                                    }
-                                },
-                                _ => return Err("let* with non-list bindings".to_string()),
-                            }
-                            return eval(a2, let_env.clone());
-                        },
-                        "do" => {
-                            let el = list(args.slice(1,args.len()).to_vec());
-                            match eval_ast(el, env.clone()) {
-                                Err(e) => return Err(e),
-                                Ok(el) => {
-                                    match *el {
-                                        List(ref lst) => {
-                                            let ref last = lst[lst.len()-1];
-                                            return Ok(last.clone());
-                                        }
-                                        _ => (),
-                                    }
-                                },
-                            }
-                        },
-                        "if" => {
-                            let a1 = (*args)[1].clone();
-                            let cond = eval(a1, env.clone());
-                            if cond.is_err() { return cond; }
-                            match *cond.unwrap() {
-                                False | Nil => {
-                                    if args.len() >= 4 {
-                                        let a3 = (*args)[3].clone();
-                                        return eval(a3, env.clone());
-                                    } else {
-                                        return Ok(_nil());
-                                    }
-                                },
-                                _ => {
-                                    let a2 = (*args)[2].clone();
-                                    return eval(a2, env.clone());
-                                },
-                            }
-                        },
-                        "fn*" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            return Ok(malfunc(eval, a2, env.clone(), a1));
-                        },
-                        _ => ()
-                    }
-                }
-                _ => (),
-            }
-            // function call
-            return match eval_ast(ast, env.clone()) {
-                Err(e) => Err(e),
-                Ok(el) => {
-                    match *el {
-                        List(ref args) => {
-                            let ref f = args.clone()[0];
-                            f.apply(args.slice(1,args.len()).to_vec())
-                        }
-                        _ => Err("Invalid apply".to_string()),
-                    }
-                }
-            }
-        }
-        _ => Err("Expected list".to_string()),
-    }
-}
-
-// print
-fn print(exp: MalVal) -> String {
-    exp.pr_str(true)
-}
-
-fn rep(str: String, env: Env) -> Result<String,String> {
-    match read(str) {
-        Err(e) => Err(e),
-        Ok(ast) => {
-            //println!("read: {}", ast);
-            match eval(ast, env) {
-                Err(e)  => Err(e),
-                Ok(exp) => Ok(print(exp)),
-            }
-        }
-    }
-}
-
-fn main() {
-    let repl_env = env_new(None);
-    for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); }
-
-    let _ = rep("(def! not (fn* (a) (if a false true)))".to_string(),
-                repl_env.clone());
-
-    loop {
-        let line = readline::mal_readline("user> ");
-        match line { None => break, _ => () }
-        match rep(line.unwrap(), repl_env.clone()) {
-            Ok(str)  => println!("{}", str),
-            Err(str) => println!("Error: {}", str),
-        }
-    }
-}
+// support precompiled regexes in reader.rs
+#![feature(phase)]
+#[phase(plugin)]
+extern crate regex_macros;
+extern crate regex;
+
+use std::collections::HashMap;
+
+use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str,
+            Nil,False,Sym,List,Vector,Hash_Map,
+            _nil,list,vector,hash_map,malfunc};
+use env::{Env,env_new,env_set,env_get};
+mod readline;
+mod types;
+mod reader;
+mod printer;
+mod env;
+mod core;
+
+// read
+fn read(str: String) -> MalRet {
+    reader::read_str(str)
+}
+
+// eval
+fn eval_ast(ast: MalVal, env: Env) -> MalRet {
+    let ast2 = ast.clone();
+    match *ast2 {
+    //match *ast {
+        Sym(ref sym) => {
+            env_get(env.clone(), sym.clone())
+        },
+        List(ref a) | Vector(ref a) => {
+            let mut ast_vec : Vec<MalVal> = vec![];
+            for mv in a.iter() {
+                let mv2 = mv.clone();
+                match eval(mv2, env.clone()) {
+                    Ok(mv) => { ast_vec.push(mv); },
+                    Err(e) => { return Err(e); },
+                }
+            }
+            Ok(match *ast { List(_) => list(ast_vec),
+                            _       => vector(ast_vec) })
+        },
+        Hash_Map(ref hm) => {
+            let mut new_hm: HashMap<String,MalVal> = HashMap::new();
+            for (key, value) in hm.iter() {
+                match eval(value.clone(), env.clone()) {
+                    Ok(mv) => { new_hm.insert(key.to_string(), mv); },
+                    Err(e) => return Err(e),
+                }
+            }
+            Ok(hash_map(new_hm))
+        },
+        _ => {
+            Ok(ast)
+        }
+    }
+}
+
+fn eval(ast: MalVal, env: Env) -> MalRet {
+    //println!("eval: {}, {}", ast, env.borrow());
+    //println!("eval: {}", ast);
+    let ast2 = ast.clone();
+    match *ast2 {
+        List(_) => (),  // continue
+        _ => return eval_ast(ast2, env),
+    }
+
+    // apply list
+    match *ast2 {
+        List(_) => (),  // continue
+        _ => return Ok(ast2),
+    }
+
+    let (args, a0sym) = match *ast2 {
+        List(ref args) => {
+            if args.len() == 0 { 
+                return Ok(ast);
+            }
+            let ref a0 = *args[0];
+            match *a0 {
+                Sym(ref a0sym) => (args, a0sym.as_slice()),
+                _ => (args, "__<fn*>__"),
+            }
+        },
+        _ => return err_str("Expected list"),
+    };
+
+    match a0sym {
+        "def!" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            let res = eval(a2, env.clone());
+            match res {
+                Ok(r) => {
+                    match *a1 {
+                        Sym(ref s) => {
+                            env_set(&env.clone(), s.clone(), r.clone());
+                            return Ok(r);
+                        },
+                        _ => {
+                            return err_str("def! of non-symbol")
+                        }
+                    }
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        "let*" => {
+            let let_env = env_new(Some(env.clone()));
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            match *a1 {
+                List(ref binds) | Vector(ref binds) => {
+                    let mut it = binds.iter();
+                    while it.len() >= 2 {
+                        let b = it.next().unwrap();
+                        let exp = it.next().unwrap();
+                        match **b {
+                            Sym(ref bstr) => {
+                                match eval(exp.clone(), let_env.clone()) {
+                                    Ok(r) => {
+                                        env_set(&let_env, bstr.clone(), r);
+                                    },
+                                    Err(e) => {
+                                        return Err(e);
+                                    },
+                                }
+                            },
+                            _ => {
+                                return err_str("let* with non-symbol binding");
+                            },
+                        }
+                    }
+                },
+                _ => return err_str("let* with non-list bindings"),
+            }
+            return eval(a2, let_env.clone());
+        },
+        "do" => {
+            let el = list(args.slice(1,args.len()).to_vec());
+            return match eval_ast(el, env.clone()) {
+                Err(e) => return Err(e),
+                Ok(el) => {
+                    match *el {
+                        List(ref lst) => {
+                            let ref last = lst[lst.len()-1];
+                            return Ok(last.clone());
+                        }
+                        _ => return err_str("invalid do call"),
+                    }
+                },
+            };
+        },
+        "if" => {
+            let a1 = (*args)[1].clone();
+            let cond = eval(a1, env.clone());
+            match cond {
+                Err(e) => return Err(e),
+                Ok(c) => match *c {
+                    False | Nil => {
+                        if args.len() >= 4 {
+                            let a3 = (*args)[3].clone();
+                            return eval(a3, env.clone());
+                        } else {
+                            return Ok(_nil());
+                        }
+                    },
+                    _ => {
+                        let a2 = (*args)[2].clone();
+                        return eval(a2, env.clone());
+                    },
+                }
+            }
+        },
+        "fn*" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            return Ok(malfunc(eval, a2, env.clone(), a1));
+        },
+        _ => { // function call
+            return match eval_ast(ast, env.clone()) {
+                Err(e) => Err(e),
+                Ok(el) => {
+                    let args = match *el {
+                        List(ref args) => args,
+                        _ => return err_str("Invalid apply"),
+                    };
+                    let ref f = args.clone()[0];
+                    f.apply(args.slice(1,args.len()).to_vec())
+                }
+            };
+        },
+    }
+}
+
+// print
+fn print(exp: MalVal) -> String {
+    exp.pr_str(true)
+}
+
+fn rep(str: &str, env: Env) -> Result<String,MalError> {
+    match read(str.to_string()) {
+        Err(e) => Err(e),
+        Ok(ast) => {
+            //println!("read: {}", ast);
+            match eval(ast, env) {
+                Err(e)  => Err(e),
+                Ok(exp) => Ok(print(exp)),
+            }
+        }
+    }
+}
+
+fn main() {
+    // core.rs: defined using rust
+    let repl_env = env_new(None);
+    for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); }
+
+    // core.mal: defined using the language itself
+    let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone());
+
+    loop {
+        let line = readline::mal_readline("user> ");
+        match line { None => break, _ => () }
+        match rep(line.unwrap().as_slice(), repl_env.clone()) {
+            Ok(str)  => println!("{}", str),
+            Err(ErrMalVal(_)) => (),  // Blank line
+            Err(ErrString(s)) => println!("Error: {}", s),
+        }
+    }
+}
dissimilarity index 66%
index 9c826c7..c86e249 100644 (file)
-// support precompiled regexes in reader.rs
-#![feature(phase)]
-#[phase(plugin)]
-extern crate regex_macros;
-extern crate regex;
-
-use std::collections::HashMap;
-
-use types::{MalVal,MalRet,MalFunc,Nil,False,Sym,List,Vector,Hash_Map,Func,
-            _nil,list,vector,hash_map,malfunc};
-use env::{Env,env_new,env_bind,env_set,env_get};
-mod readline;
-mod types;
-mod reader;
-mod printer;
-mod env;
-mod core;
-
-// read
-fn read(str: String) -> MalRet {
-    reader::read_str(str)
-}
-
-// eval
-fn eval_ast(ast: MalVal, env: Env) -> MalRet {
-    let ast2 = ast.clone();
-    match *ast2 {
-    //match *ast {
-        Sym(ref sym) => {
-            env_get(env.clone(), sym.clone())
-        },
-        List(ref a) | Vector(ref a) => {
-            let mut ast_vec : Vec<MalVal> = vec![];
-            for mv in a.iter() {
-                let mv2 = mv.clone();
-                match eval(mv2, env.clone()) {
-                    Ok(mv) => { ast_vec.push(mv); },
-                    Err(e) => { return Err(e); },
-                }
-            }
-            Ok(match *ast { List(_) => list(ast_vec),
-                            _       => vector(ast_vec) })
-        },
-        Hash_Map(ref hm) => {
-            let mut new_hm: HashMap<String,MalVal> = HashMap::new();
-            for (key, value) in hm.iter() {
-                match eval(value.clone(), env.clone()) {
-                    Ok(mv) => { new_hm.insert(key.to_string(), mv); },
-                    Err(e) => return Err(e),
-                }
-            }
-            Ok(hash_map(new_hm))
-        },
-        _ => {
-            Ok(ast)
-        }
-    }
-}
-
-fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
-    'tco: loop {
-
-    //println!("eval: {}, {}", ast, env.borrow());
-    //println!("eval: {}", ast);
-    let ast2 = ast.clone();
-    match *ast2 {
-        List(_) => (),  // continue
-        _ => return eval_ast(ast2, env),
-    }
-
-    // apply list
-    let ast3 = ast2.clone();
-    match *ast2 {
-        List(ref args) => {
-            if args.len() == 0 { 
-                return Ok(ast3);
-            }
-            let ref a0 = *args[0];
-            match *a0 {
-                Sym(ref a0sym) => {
-                    match a0sym.as_slice() {
-                        "def!" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            let res = eval(a2, env.clone());
-                            match res {
-                                Ok(r) => {
-                                    match *a1 {
-                                        Sym(ref s) => {
-                                            env_set(&env.clone(), s.clone(), r.clone());
-                                            return Ok(r);
-                                        },
-                                        _ => {
-                                            return Err("def! of non-symbol".to_string())
-                                        }
-                                    }
-                                },
-                                Err(e) => return Err(e),
-                            }
-                        },
-                        "let*" => {
-                            let let_env = env_new(Some(env.clone()));
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            match *a1 {
-                                List(ref binds) | Vector(ref binds) => {
-                                    let mut it = binds.iter();
-                                    while it.len() >= 2 {
-                                        let b = it.next().unwrap();
-                                        let exp = it.next().unwrap();
-                                        match **b {
-                                            Sym(ref bstr) => {
-                                                match eval(exp.clone(), let_env.clone()) {
-                                                    Ok(r) => {
-                                                        env_set(&let_env, bstr.clone(), r);
-                                                    },
-                                                    Err(e) => {
-                                                        return Err(e);
-                                                    },
-                                                }
-                                            },
-                                            _ => {
-                                                return Err("let* with non-symbol binding".to_string());
-                                            },
-                                        }
-                                    }
-                                },
-                                _ => return Err("let* with non-list bindings".to_string()),
-                            }
-                            ast = a2;
-                            env = let_env.clone();
-                            continue 'tco;
-                        },
-                        "do" => {
-                            let el = list(args.slice(1,args.len()-1).to_vec());
-                            match eval_ast(el, env.clone()) {
-                                Err(e) => return Err(e),
-                                Ok(_) => {
-                                    let ref last = args[args.len()-1];
-                                    ast = last.clone();
-                                    continue 'tco;
-                                },
-                            }
-                        },
-                        "if" => {
-                            let a1 = (*args)[1].clone();
-                            let cond = eval(a1, env.clone());
-                            if cond.is_err() { return cond; }
-                            match *cond.unwrap() {
-                                False | Nil => {
-                                    if args.len() >= 4 {
-                                        let a3 = (*args)[3].clone();
-                                        ast = a3;
-                                        env = env.clone();
-                                        continue 'tco;
-                                    } else {
-                                        return Ok(_nil());
-                                    }
-                                },
-                                _ => {
-                                    let a2 = (*args)[2].clone();
-                                    ast = a2;
-                                    env = env.clone();
-                                    continue 'tco;
-                                },
-                            }
-                        },
-                        "fn*" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            return Ok(malfunc(eval, a2, env.clone(), a1));
-                        },
-                        _ => ()
-                    }
-                }
-                _ => (),
-            }
-            // function call
-            return match eval_ast(ast3, env.clone()) {
-                Err(e) => Err(e),
-                Ok(el) => {
-                    match *el {
-                        List(ref args) => {
-                            let args2 = args.clone();
-                            match *args2[0] {
-                                Func(f) => f(args.slice(1,args.len()).to_vec()),
-                                MalFunc(ref mf) => {
-                                    let mfc = mf.clone();
-                                    let alst = list(args.slice(1,args.len()).to_vec());
-                                    let new_env = env_new(Some(mfc.env.clone()));
-                                    match env_bind(&new_env, mfc.params, alst) {
-                                        Ok(_) => {
-                                            ast = mfc.exp;
-                                            env = new_env;
-                                            continue 'tco;
-                                        },
-                                        Err(e) => Err(e),
-                                    }
-                                },
-                                _ => Err("attempt to call non-function".to_string()),
-                            }
-                        }
-                        _ => Err("Invalid apply".to_string()),
-                    }
-                }
-            }
-        }
-        _ => return Err("Expected list".to_string()),
-    }
-
-    }
-}
-
-// print
-fn print(exp: MalVal) -> String {
-    exp.pr_str(true)
-}
-
-fn rep(str: String, env: Env) -> Result<String,String> {
-    match read(str) {
-        Err(e) => Err(e),
-        Ok(ast) => {
-            //println!("read: {}", ast);
-            match eval(ast, env) {
-                Err(e)  => Err(e),
-                Ok(exp) => Ok(print(exp)),
-            }
-        }
-    }
-}
-
-fn main() {
-    // core.rs: defined using rust
-    let repl_env = env_new(None);
-    for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); }
-
-    // core.mal: defined using the language itself
-    let _ = rep("(def! not (fn* (a) (if a false true)))".to_string(),
-                repl_env.clone());
-
-    loop {
-        let line = readline::mal_readline("user> ");
-        match line { None => break, _ => () }
-        match rep(line.unwrap(), repl_env.clone()) {
-            Ok(str)  => println!("{}", str),
-            Err(str) => println!("Error: {}", str),
-        }
-    }
-}
+// support precompiled regexes in reader.rs
+#![feature(phase)]
+#[phase(plugin)]
+extern crate regex_macros;
+extern crate regex;
+
+use std::collections::HashMap;
+
+use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str,
+            Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc,
+            _nil,list,vector,hash_map,malfunc};
+use env::{Env,env_new,env_bind,env_set,env_get};
+mod readline;
+mod types;
+mod reader;
+mod printer;
+mod env;
+mod core;
+
+// read
+fn read(str: String) -> MalRet {
+    reader::read_str(str)
+}
+
+// eval
+fn eval_ast(ast: MalVal, env: Env) -> MalRet {
+    let ast2 = ast.clone();
+    match *ast2 {
+    //match *ast {
+        Sym(ref sym) => {
+            env_get(env.clone(), sym.clone())
+        },
+        List(ref a) | Vector(ref a) => {
+            let mut ast_vec : Vec<MalVal> = vec![];
+            for mv in a.iter() {
+                let mv2 = mv.clone();
+                match eval(mv2, env.clone()) {
+                    Ok(mv) => { ast_vec.push(mv); },
+                    Err(e) => { return Err(e); },
+                }
+            }
+            Ok(match *ast { List(_) => list(ast_vec),
+                            _       => vector(ast_vec) })
+        },
+        Hash_Map(ref hm) => {
+            let mut new_hm: HashMap<String,MalVal> = HashMap::new();
+            for (key, value) in hm.iter() {
+                match eval(value.clone(), env.clone()) {
+                    Ok(mv) => { new_hm.insert(key.to_string(), mv); },
+                    Err(e) => return Err(e),
+                }
+            }
+            Ok(hash_map(new_hm))
+        },
+        _ => {
+            Ok(ast)
+        }
+    }
+}
+
+fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
+    'tco: loop {
+
+    //println!("eval: {}, {}", ast, env.borrow());
+    //println!("eval: {}", ast);
+    let ast2 = ast.clone();
+    let ast3 = ast.clone();
+    match *ast2 {
+        List(_) => (),  // continue
+        _ => return eval_ast(ast2, env),
+    }
+
+    // apply list
+    match *ast2 {
+        List(_) => (),  // continue
+        _ => return Ok(ast2),
+    }
+
+    let (args, a0sym) = match *ast2 {
+        List(ref args) => {
+            if args.len() == 0 { 
+                return Ok(ast3);
+            }
+            let ref a0 = *args[0];
+            match *a0 {
+                Sym(ref a0sym) => (args, a0sym.as_slice()),
+                _ => (args, "__<fn*>__"),
+            }
+        },
+        _ => return err_str("Expected list"),
+    };
+
+    match a0sym {
+        "def!" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            let res = eval(a2, env.clone());
+            match res {
+                Ok(r) => {
+                    match *a1 {
+                        Sym(ref s) => {
+                            env_set(&env.clone(), s.clone(), r.clone());
+                            return Ok(r);
+                        },
+                        _ => {
+                            return err_str("def! of non-symbol")
+                        }
+                    }
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        "let*" => {
+            let let_env = env_new(Some(env.clone()));
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            match *a1 {
+                List(ref binds) | Vector(ref binds) => {
+                    let mut it = binds.iter();
+                    while it.len() >= 2 {
+                        let b = it.next().unwrap();
+                        let exp = it.next().unwrap();
+                        match **b {
+                            Sym(ref bstr) => {
+                                match eval(exp.clone(), let_env.clone()) {
+                                    Ok(r) => {
+                                        env_set(&let_env, bstr.clone(), r);
+                                    },
+                                    Err(e) => {
+                                        return Err(e);
+                                    },
+                                }
+                            },
+                            _ => {
+                                return err_str("let* with non-symbol binding");
+                            },
+                        }
+                    }
+                },
+                _ => return err_str("let* with non-list bindings"),
+            }
+            ast = a2;
+            env = let_env.clone();
+            continue 'tco;
+        },
+        "do" => {
+            let el = list(args.slice(1,args.len()-1).to_vec());
+            match eval_ast(el, env.clone()) {
+                Err(e) => return Err(e),
+                Ok(_) => {
+                    let ref last = args[args.len()-1];
+                    ast = last.clone();
+                    continue 'tco;
+                },
+            }
+        },
+        "if" => {
+            let a1 = (*args)[1].clone();
+            let cond = eval(a1, env.clone());
+            match cond {
+                Err(e) => return Err(e),
+                Ok(c) => match *c {
+                    False | Nil => {
+                        if args.len() >= 4 {
+                            let a3 = (*args)[3].clone();
+                            ast = a3;
+                            env = env.clone();
+                            continue 'tco;
+                        } else {
+                            return Ok(_nil());
+                        }
+                    },
+                    _ => {
+                        let a2 = (*args)[2].clone();
+                        ast = a2;
+                        env = env.clone();
+                        continue 'tco;
+                    },
+                }
+            }
+        },
+        "fn*" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            return Ok(malfunc(eval, a2, env.clone(), a1));
+        },
+        _ => { // function call
+            return match eval_ast(ast3, env.clone()) {
+                Err(e) => Err(e),
+                Ok(el) => {
+                    let args = match *el {
+                        List(ref args) => args,
+                        _ => return err_str("Invalid apply"),
+                    };
+                    match *args.clone()[0] {
+                        Func(f) => f(args.slice(1,args.len()).to_vec()),
+                        MalFunc(ref mf) => {
+                            let mfc = mf.clone();
+                            let alst = list(args.slice(1,args.len()).to_vec());
+                            let new_env = env_new(Some(mfc.env.clone()));
+                            match env_bind(&new_env, mfc.params, alst) {
+                                Ok(_) => {
+                                    ast = mfc.exp;
+                                    env = new_env;
+                                    continue 'tco;
+                                },
+                                Err(e) => err_str(e.as_slice()),
+                            }
+                        },
+                        _ => err_str("attempt to call non-function"),
+                    }
+                }
+            }
+        },
+    }
+
+    }
+}
+
+// print
+fn print(exp: MalVal) -> String {
+    exp.pr_str(true)
+}
+
+fn rep(str: &str, env: Env) -> Result<String,MalError> {
+    match read(str.to_string()) {
+        Err(e) => Err(e),
+        Ok(ast) => {
+            //println!("read: {}", ast);
+            match eval(ast, env) {
+                Err(e)  => Err(e),
+                Ok(exp) => Ok(print(exp)),
+            }
+        }
+    }
+}
+
+fn main() {
+    // core.rs: defined using rust
+    let repl_env = env_new(None);
+    for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); }
+
+    // core.mal: defined using the language itself
+    let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone());
+
+    loop {
+        let line = readline::mal_readline("user> ");
+        match line { None => break, _ => () }
+        match rep(line.unwrap().as_slice(), repl_env.clone()) {
+            Ok(str)  => println!("{}", str),
+            Err(ErrMalVal(_)) => (),  // Blank line
+            Err(ErrString(s)) => println!("Error: {}", s),
+        }
+    }
+}
dissimilarity index 63%
index 237b7f0..86e0672 100644 (file)
-// support precompiled regexes in reader.rs
-#![feature(phase)]
-#[phase(plugin)]
-extern crate regex_macros;
-extern crate regex;
-
-use std::collections::HashMap;
-use std::os;
-
-use types::{MalVal,MalRet,MalFunc,Nil,False,Sym,List,Vector,Hash_Map,Func,
-            _nil,string,list,vector,hash_map,malfunc};
-use env::{Env,env_new,env_bind,env_root,env_set,env_get};
-mod readline;
-mod types;
-mod reader;
-mod printer;
-mod env;
-mod core;
-
-// read
-fn read(str: String) -> MalRet {
-    reader::read_str(str)
-}
-
-// eval
-fn eval_ast(ast: MalVal, env: Env) -> MalRet {
-    let ast2 = ast.clone();
-    match *ast2 {
-    //match *ast {
-        Sym(ref sym) => {
-            env_get(env.clone(), sym.clone())
-        },
-        List(ref a) | Vector(ref a) => {
-            let mut ast_vec : Vec<MalVal> = vec![];
-            for mv in a.iter() {
-                let mv2 = mv.clone();
-                match eval(mv2, env.clone()) {
-                    Ok(mv) => { ast_vec.push(mv); },
-                    Err(e) => { return Err(e); },
-                }
-            }
-            Ok(match *ast { List(_) => list(ast_vec),
-                            _       => vector(ast_vec) })
-        },
-        Hash_Map(ref hm) => {
-            let mut new_hm: HashMap<String,MalVal> = HashMap::new();
-            for (key, value) in hm.iter() {
-                match eval(value.clone(), env.clone()) {
-                    Ok(mv) => { new_hm.insert(key.to_string(), mv); },
-                    Err(e) => return Err(e),
-                }
-            }
-            Ok(hash_map(new_hm))
-        },
-        _ => {
-            Ok(ast)
-        }
-    }
-}
-
-fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
-    'tco: loop {
-
-    //println!("eval: {}, {}", ast, env.borrow());
-    //println!("eval: {}", ast);
-    let ast2 = ast.clone();
-    let ast3 = ast.clone();
-    match *ast2 {
-        List(_) => (),  // continue
-        _ => return eval_ast(ast2, env),
-    }
-
-    // apply list
-    match *ast2 {
-        List(ref args) => {
-            if args.len() == 0 { 
-                return Ok(ast3);
-            }
-            let ref a0 = *args[0];
-            match *a0 {
-                Sym(ref a0sym) => {
-                    match a0sym.as_slice() {
-                        "def!" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            let res = eval(a2, env.clone());
-                            match res {
-                                Ok(r) => {
-                                    match *a1 {
-                                        Sym(ref s) => {
-                                            env_set(&env.clone(), s.clone(), r.clone());
-                                            return Ok(r);
-                                        },
-                                        _ => {
-                                            return Err("def! of non-symbol".to_string())
-                                        }
-                                    }
-                                },
-                                Err(e) => return Err(e),
-                            }
-                        },
-                        "let*" => {
-                            let let_env = env_new(Some(env.clone()));
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            match *a1 {
-                                List(ref binds) | Vector(ref binds) => {
-                                    let mut it = binds.iter();
-                                    while it.len() >= 2 {
-                                        let b = it.next().unwrap();
-                                        let exp = it.next().unwrap();
-                                        match **b {
-                                            Sym(ref bstr) => {
-                                                match eval(exp.clone(), let_env.clone()) {
-                                                    Ok(r) => {
-                                                        env_set(&let_env, bstr.clone(), r);
-                                                    },
-                                                    Err(e) => {
-                                                        return Err(e);
-                                                    },
-                                                }
-                                            },
-                                            _ => {
-                                                return Err("let* with non-symbol binding".to_string());
-                                            },
-                                        }
-                                    }
-                                },
-                                _ => return Err("let* with non-list bindings".to_string()),
-                            }
-                            ast = a2;
-                            env = let_env.clone();
-                            continue 'tco;
-                        },
-                        "do" => {
-                            let el = list(args.slice(1,args.len()-1).to_vec());
-                            match eval_ast(el, env.clone()) {
-                                Err(e) => return Err(e),
-                                Ok(_) => {
-                                    let ref last = args[args.len()-1];
-                                    ast = last.clone();
-                                    continue 'tco;
-                                },
-                            }
-                        },
-                        "if" => {
-                            let a1 = (*args)[1].clone();
-                            let cond = eval(a1, env.clone());
-                            if cond.is_err() { return cond; }
-                            match *cond.unwrap() {
-                                False | Nil => {
-                                    if args.len() >= 4 {
-                                        let a3 = (*args)[3].clone();
-                                        ast = a3;
-                                        env = env.clone();
-                                        continue 'tco;
-                                    } else {
-                                        return Ok(_nil());
-                                    }
-                                },
-                                _ => {
-                                    let a2 = (*args)[2].clone();
-                                    ast = a2;
-                                    env = env.clone();
-                                    continue 'tco;
-                                },
-                            }
-                        },
-                        "fn*" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            return Ok(malfunc(eval, a2, env.clone(), a1));
-                        },
-                        "eval" => {
-                            let a1 = (*args)[1].clone();
-                            match eval(a1, env.clone()) {
-                                Ok(exp) => {
-                                    ast = exp;
-                                    env = env_root(&env);
-                                    continue 'tco;
-                                },
-                                Err(e) => return Err(e),
-                            }
-                        },
-                        _ => ()
-                    }
-                }
-                _ => (),
-            }
-            // function call
-            return match eval_ast(ast3, env.clone()) {
-                Err(e) => Err(e),
-                Ok(el) => {
-                    match *el {
-                        List(ref args) => {
-                            let args2 = args.clone();
-                            match *args2[0] {
-                                Func(f) => f(args.slice(1,args.len()).to_vec()),
-                                MalFunc(ref mf) => {
-                                    let mfc = mf.clone();
-                                    let alst = list(args.slice(1,args.len()).to_vec());
-                                    let new_env = env_new(Some(mfc.env.clone()));
-                                    match env_bind(&new_env, mfc.params, alst) {
-                                        Ok(_) => {
-                                            ast = mfc.exp;
-                                            env = new_env;
-                                            continue 'tco;
-                                        },
-                                        Err(e) => Err(e),
-                                    }
-                                },
-                                _ => Err("attempt to call non-function".to_string()),
-                            }
-                        }
-                        _ => Err("Invalid apply".to_string()),
-                    }
-                }
-            }
-        }
-        _ => return Err("Expected list".to_string()),
-    }
-
-    }
-}
-
-// print
-fn print(exp: MalVal) -> String {
-    exp.pr_str(true)
-}
-
-fn rep(str: String, env: Env) -> Result<String,String> {
-    match read(str) {
-        Err(e) => Err(e),
-        Ok(ast) => {
-            //println!("read: {}", ast);
-            match eval(ast, env) {
-                Err(e)  => Err(e),
-                Ok(exp) => Ok(print(exp)),
-            }
-        }
-    }
-}
-
-fn main() {
-    // core.rs: defined using rust
-    let repl_env = env_new(None);
-    for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); }
-    // see eval() for definition of "eval"
-    env_set(&repl_env, "*ARGV*".to_string(), list(vec![]));
-
-    // core.mal: defined using the language itself
-    let _ = rep("(def! not (fn* (a) (if a false true)))".to_string(),
-                repl_env.clone());
-    let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))".to_string(),
-                repl_env.clone());
-
-    // Invoked with command line arguments
-    let args = os::args();
-    if args.len() > 1 {
-        let mv_args = args.slice(2,args.len()).iter()
-            .map(|a| string(a.to_string()))
-            .collect::<Vec<MalVal>>();
-        env_set(&repl_env, "*ARGV*".to_string(), list(mv_args));
-        match rep("(load-file \"".to_string() + args[1] + "\")".to_string(),
-                  repl_env.clone()) {
-            Ok(_) => {
-                os::set_exit_status(0);
-                return;
-            },
-            Err(str) => {
-                println!("Error: {}", str);
-                os::set_exit_status(1);
-                return;
-            },
-        }
-    }
-
-    loop {
-        let line = readline::mal_readline("user> ");
-        match line { None => break, _ => () }
-        match rep(line.unwrap(), repl_env.clone()) {
-            Ok(str)  => println!("{}", str),
-            Err(str) => println!("Error: {}", str),
-        }
-    }
-}
+// support precompiled regexes in reader.rs
+#![feature(phase)]
+#[phase(plugin)]
+extern crate regex_macros;
+extern crate regex;
+
+use std::collections::HashMap;
+use std::os;
+
+use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str,
+            Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc,
+            _nil,string,list,vector,hash_map,malfunc};
+use env::{Env,env_new,env_bind,env_root,env_set,env_get};
+mod readline;
+mod types;
+mod reader;
+mod printer;
+mod env;
+mod core;
+
+// read
+fn read(str: String) -> MalRet {
+    reader::read_str(str)
+}
+
+// eval
+fn eval_ast(ast: MalVal, env: Env) -> MalRet {
+    let ast2 = ast.clone();
+    match *ast2 {
+    //match *ast {
+        Sym(ref sym) => {
+            env_get(env.clone(), sym.clone())
+        },
+        List(ref a) | Vector(ref a) => {
+            let mut ast_vec : Vec<MalVal> = vec![];
+            for mv in a.iter() {
+                let mv2 = mv.clone();
+                match eval(mv2, env.clone()) {
+                    Ok(mv) => { ast_vec.push(mv); },
+                    Err(e) => { return Err(e); },
+                }
+            }
+            Ok(match *ast { List(_) => list(ast_vec),
+                            _       => vector(ast_vec) })
+        },
+        Hash_Map(ref hm) => {
+            let mut new_hm: HashMap<String,MalVal> = HashMap::new();
+            for (key, value) in hm.iter() {
+                match eval(value.clone(), env.clone()) {
+                    Ok(mv) => { new_hm.insert(key.to_string(), mv); },
+                    Err(e) => return Err(e),
+                }
+            }
+            Ok(hash_map(new_hm))
+        },
+        _ => {
+            Ok(ast)
+        }
+    }
+}
+
+fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
+    'tco: loop {
+
+    //println!("eval: {}, {}", ast, env.borrow());
+    //println!("eval: {}", ast);
+    let ast2 = ast.clone();
+    let ast3 = ast.clone();
+    match *ast2 {
+        List(_) => (),  // continue
+        _ => return eval_ast(ast2, env),
+    }
+
+    // apply list
+    match *ast2 {
+        List(_) => (),  // continue
+        _ => return Ok(ast2),
+    }
+
+    let (args, a0sym) = match *ast2 {
+        List(ref args) => {
+            if args.len() == 0 { 
+                return Ok(ast3);
+            }
+            let ref a0 = *args[0];
+            match *a0 {
+                Sym(ref a0sym) => (args, a0sym.as_slice()),
+                _ => (args, "__<fn*>__"),
+            }
+        },
+        _ => return err_str("Expected list"),
+    };
+
+    match a0sym {
+        "def!" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            let res = eval(a2, env.clone());
+            match res {
+                Ok(r) => {
+                    match *a1 {
+                        Sym(ref s) => {
+                            env_set(&env.clone(), s.clone(), r.clone());
+                            return Ok(r);
+                        },
+                        _ => {
+                            return err_str("def! of non-symbol")
+                        }
+                    }
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        "let*" => {
+            let let_env = env_new(Some(env.clone()));
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            match *a1 {
+                List(ref binds) | Vector(ref binds) => {
+                    let mut it = binds.iter();
+                    while it.len() >= 2 {
+                        let b = it.next().unwrap();
+                        let exp = it.next().unwrap();
+                        match **b {
+                            Sym(ref bstr) => {
+                                match eval(exp.clone(), let_env.clone()) {
+                                    Ok(r) => {
+                                        env_set(&let_env, bstr.clone(), r);
+                                    },
+                                    Err(e) => {
+                                        return Err(e);
+                                    },
+                                }
+                            },
+                            _ => {
+                                return err_str("let* with non-symbol binding");
+                            },
+                        }
+                    }
+                },
+                _ => return err_str("let* with non-list bindings"),
+            }
+            ast = a2;
+            env = let_env.clone();
+            continue 'tco;
+        },
+        "do" => {
+            let el = list(args.slice(1,args.len()-1).to_vec());
+            match eval_ast(el, env.clone()) {
+                Err(e) => return Err(e),
+                Ok(_) => {
+                    let ref last = args[args.len()-1];
+                    ast = last.clone();
+                    continue 'tco;
+                },
+            }
+        },
+        "if" => {
+            let a1 = (*args)[1].clone();
+            let cond = eval(a1, env.clone());
+            match cond {
+                Err(e) => return Err(e),
+                Ok(c) => match *c {
+                    False | Nil => {
+                        if args.len() >= 4 {
+                            let a3 = (*args)[3].clone();
+                            ast = a3;
+                            env = env.clone();
+                            continue 'tco;
+                        } else {
+                            return Ok(_nil());
+                        }
+                    },
+                    _ => {
+                        let a2 = (*args)[2].clone();
+                        ast = a2;
+                        env = env.clone();
+                        continue 'tco;
+                    },
+                }
+            }
+        },
+        "fn*" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            return Ok(malfunc(eval, a2, env.clone(), a1));
+        },
+        "eval" => {
+            let a1 = (*args)[1].clone();
+            match eval(a1, env.clone()) {
+                Ok(exp) => {
+                    ast = exp;
+                    env = env_root(&env);
+                    continue 'tco;
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        _ => { // function call
+            return match eval_ast(ast3, env.clone()) {
+                Err(e) => Err(e),
+                Ok(el) => {
+                    let args = match *el {
+                        List(ref args) => args,
+                        _ => return err_str("Invalid apply"),
+                    };
+                    match *args.clone()[0] {
+                        Func(f) => f(args.slice(1,args.len()).to_vec()),
+                        MalFunc(ref mf) => {
+                            let mfc = mf.clone();
+                            let alst = list(args.slice(1,args.len()).to_vec());
+                            let new_env = env_new(Some(mfc.env.clone()));
+                            match env_bind(&new_env, mfc.params, alst) {
+                                Ok(_) => {
+                                    ast = mfc.exp;
+                                    env = new_env;
+                                    continue 'tco;
+                                },
+                                Err(e) => err_str(e.as_slice()),
+                            }
+                        },
+                        _ => err_str("attempt to call non-function"),
+                    }
+                }
+            }
+        },
+    }
+
+    }
+}
+
+// print
+fn print(exp: MalVal) -> String {
+    exp.pr_str(true)
+}
+
+fn rep(str: &str, env: Env) -> Result<String,MalError> {
+    match read(str.to_string()) {
+        Err(e) => Err(e),
+        Ok(ast) => {
+            //println!("read: {}", ast);
+            match eval(ast, env) {
+                Err(e)  => Err(e),
+                Ok(exp) => Ok(print(exp)),
+            }
+        }
+    }
+}
+
+fn main() {
+    // core.rs: defined using rust
+    let repl_env = env_new(None);
+    for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); }
+    // see eval() for definition of "eval"
+    env_set(&repl_env, "*ARGV*".to_string(), list(vec![]));
+
+    // core.mal: defined using the language itself
+    let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone());
+    let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env.clone());
+
+    // Invoked with command line arguments
+    let args = os::args();
+    if args.len() > 1 {
+        let mv_args = args.slice(2,args.len()).iter()
+            .map(|a| string(a.to_string()))
+            .collect::<Vec<MalVal>>();
+        env_set(&repl_env, "*ARGV*".to_string(), list(mv_args));
+        let lf = "(load-file \"".to_string() + args[1] + "\")".to_string();
+        match rep(lf.as_slice(), repl_env.clone()) {
+            Ok(_) => {
+                os::set_exit_status(0);
+                return;
+            },
+            Err(str) => {
+                println!("Error: {}", str);
+                os::set_exit_status(1);
+                return;
+            },
+        }
+    }
+
+    loop {
+        let line = readline::mal_readline("user> ");
+        match line { None => break, _ => () }
+        match rep(line.unwrap().as_slice(), repl_env.clone()) {
+            Ok(str)  => println!("{}", str),
+            Err(ErrMalVal(_)) => (),  // Blank line
+            Err(ErrString(s)) => println!("Error: {}", s),
+        }
+    }
+}
index 3427eea..ac0634d 100644 (file)
@@ -7,7 +7,8 @@ extern crate regex;
 use std::collections::HashMap;
 use std::os;
 
-use types::{MalVal,MalRet,MalFunc,Nil,False,Sym,List,Vector,Hash_Map,Func,
+use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str,
+            Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc,
             _nil,symbol,string,list,vector,hash_map,malfunc};
 use env::{Env,env_new,env_bind,env_root,env_set,env_get};
 mod readline;
@@ -115,7 +116,6 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
     //println!("eval: {}, {}", ast, env.borrow());
     //println!("eval: {}", ast);
     let ast2 = ast.clone();
-    let ast3 = ast.clone();
     match *ast2 {
         List(_) => (),  // continue
         _ => return eval_ast(ast2, env),
@@ -123,160 +123,166 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
 
     // apply list
     match *ast2 {
+        List(_) => (),  // continue
+        _ => return Ok(ast2),
+    }
+    let ast3 = ast2.clone();
+
+    let (args, a0sym) = match *ast2 {
         List(ref args) => {
             if args.len() == 0 { 
                 return Ok(ast3);
             }
             let ref a0 = *args[0];
             match *a0 {
-                Sym(ref a0sym) => {
-                    match a0sym.as_slice() {
-                        "def!" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            let res = eval(a2, env.clone());
-                            match res {
-                                Ok(r) => {
-                                    match *a1 {
-                                        Sym(ref s) => {
-                                            env_set(&env.clone(), s.clone(), r.clone());
-                                            return Ok(r);
-                                        },
-                                        _ => {
-                                            return Err("def! of non-symbol".to_string())
-                                        }
-                                    }
-                                },
-                                Err(e) => return Err(e),
-                            }
-                        },
-                        "let*" => {
-                            let let_env = env_new(Some(env.clone()));
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            match *a1 {
-                                List(ref binds) | Vector(ref binds) => {
-                                    let mut it = binds.iter();
-                                    while it.len() >= 2 {
-                                        let b = it.next().unwrap();
-                                        let exp = it.next().unwrap();
-                                        match **b {
-                                            Sym(ref bstr) => {
-                                                match eval(exp.clone(), let_env.clone()) {
-                                                    Ok(r) => {
-                                                        env_set(&let_env, bstr.clone(), r);
-                                                    },
-                                                    Err(e) => {
-                                                        return Err(e);
-                                                    },
-                                                }
-                                            },
-                                            _ => {
-                                                return Err("let* with non-symbol binding".to_string());
-                                            },
-                                        }
-                                    }
-                                },
-                                _ => return Err("let* with non-list bindings".to_string()),
-                            }
-                            ast = a2;
-                            env = let_env.clone();
-                            continue 'tco;
-                        },
-                        "quote" => {
-                            return Ok((*args)[1].clone());
-                        },
-                        "quasiquote" => {
-                            let a1 = (*args)[1].clone();
-                            ast = quasiquote(a1);
-                            continue 'tco;
-                        },
-                        "do" => {
-                            let el = list(args.slice(1,args.len()-1).to_vec());
-                            match eval_ast(el, env.clone()) {
-                                Err(e) => return Err(e),
-                                Ok(_) => {
-                                    let ref last = args[args.len()-1];
-                                    ast = last.clone();
-                                    continue 'tco;
-                                },
-                            }
-                        },
-                        "if" => {
-                            let a1 = (*args)[1].clone();
-                            let cond = eval(a1, env.clone());
-                            if cond.is_err() { return cond; }
-                            match *cond.unwrap() {
-                                False | Nil => {
-                                    if args.len() >= 4 {
-                                        let a3 = (*args)[3].clone();
-                                        ast = a3;
-                                        env = env.clone();
-                                        continue 'tco;
-                                    } else {
-                                        return Ok(_nil());
-                                    }
-                                },
-                                _ => {
-                                    let a2 = (*args)[2].clone();
-                                    ast = a2;
-                                    env = env.clone();
-                                    continue 'tco;
-                                },
-                            }
-                        },
-                        "fn*" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            return Ok(malfunc(eval, a2, env.clone(), a1));
-                        },
-                        "eval" => {
-                            let a1 = (*args)[1].clone();
-                            match eval(a1, env.clone()) {
-                                Ok(exp) => {
-                                    ast = exp;
-                                    env = env_root(&env);
-                                    continue 'tco;
-                                },
-                                Err(e) => return Err(e),
-                            }
+                Sym(ref a0sym) => (args, a0sym.as_slice()),
+                _ => (args, "__<fn*>__"),
+            }
+        },
+        _ => return err_str("Expected list"),
+    };
+
+    match a0sym {
+        "def!" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            let res = eval(a2, env.clone());
+            match res {
+                Ok(r) => {
+                    match *a1 {
+                        Sym(ref s) => {
+                            env_set(&env.clone(), s.clone(), r.clone());
+                            return Ok(r);
                         },
-                        _ => ()
+                        _ => {
+                            return err_str("def! of non-symbol")
+                        }
                     }
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        "let*" => {
+            let let_env = env_new(Some(env.clone()));
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            match *a1 {
+                List(ref binds) | Vector(ref binds) => {
+                    let mut it = binds.iter();
+                    while it.len() >= 2 {
+                        let b = it.next().unwrap();
+                        let exp = it.next().unwrap();
+                        match **b {
+                            Sym(ref bstr) => {
+                                match eval(exp.clone(), let_env.clone()) {
+                                    Ok(r) => {
+                                        env_set(&let_env, bstr.clone(), r);
+                                    },
+                                    Err(e) => {
+                                        return Err(e);
+                                    },
+                                }
+                            },
+                            _ => {
+                                return err_str("let* with non-symbol binding");
+                            },
+                        }
+                    }
+                },
+                _ => return err_str("let* with non-list bindings"),
+            }
+            ast = a2;
+            env = let_env.clone();
+            continue 'tco;
+        },
+        "quote" => {
+            return Ok((*args)[1].clone());
+        },
+        "quasiquote" => {
+            let a1 = (*args)[1].clone();
+            ast = quasiquote(a1);
+            continue 'tco;
+        },
+        "do" => {
+            let el = list(args.slice(1,args.len()-1).to_vec());
+            match eval_ast(el, env.clone()) {
+                Err(e) => return Err(e),
+                Ok(_) => {
+                    let ref last = args[args.len()-1];
+                    ast = last.clone();
+                    continue 'tco;
+                },
+            }
+        },
+        "if" => {
+            let a1 = (*args)[1].clone();
+            let cond = eval(a1, env.clone());
+            match cond {
+                Err(e) => return Err(e),
+                Ok(c) => match *c {
+                    False | Nil => {
+                        if args.len() >= 4 {
+                            let a3 = (*args)[3].clone();
+                            ast = a3;
+                            env = env.clone();
+                            continue 'tco;
+                        } else {
+                            return Ok(_nil());
+                        }
+                    },
+                    _ => {
+                        let a2 = (*args)[2].clone();
+                        ast = a2;
+                        env = env.clone();
+                        continue 'tco;
+                    },
                 }
-                _ => (),
             }
-            // function call
+        },
+        "fn*" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            return Ok(malfunc(eval, a2, env.clone(), a1));
+        },
+        "eval" => {
+            let a1 = (*args)[1].clone();
+            match eval(a1, env.clone()) {
+                Ok(exp) => {
+                    ast = exp;
+                    env = env_root(&env);
+                    continue 'tco;
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        _ => { // function call
             return match eval_ast(ast3, env.clone()) {
                 Err(e) => Err(e),
                 Ok(el) => {
-                    match *el {
-                        List(ref args) => {
-                            let args2 = args.clone();
-                            match *args2[0] {
-                                Func(f) => f(args.slice(1,args.len()).to_vec()),
-                                MalFunc(ref mf) => {
-                                    let mfc = mf.clone();
-                                    let alst = list(args.slice(1,args.len()).to_vec());
-                                    let new_env = env_new(Some(mfc.env.clone()));
-                                    match env_bind(&new_env, mfc.params, alst) {
-                                        Ok(_) => {
-                                            ast = mfc.exp;
-                                            env = new_env;
-                                            continue 'tco;
-                                        },
-                                        Err(e) => Err(e),
-                                    }
+                    let args = match *el {
+                        List(ref args) => args,
+                        _ => return err_str("Invalid apply"),
+                    };
+                    match *args.clone()[0] {
+                        Func(f) => f(args.slice(1,args.len()).to_vec()),
+                        MalFunc(ref mf) => {
+                            let mfc = mf.clone();
+                            let alst = list(args.slice(1,args.len()).to_vec());
+                            let new_env = env_new(Some(mfc.env.clone()));
+                            match env_bind(&new_env, mfc.params, alst) {
+                                Ok(_) => {
+                                    ast = mfc.exp;
+                                    env = new_env;
+                                    continue 'tco;
                                 },
-                                _ => Err("attempt to call non-function".to_string()),
+                                Err(e) => err_str(e.as_slice()),
                             }
-                        }
-                        _ => Err("Invalid apply".to_string()),
+                        },
+                        _ => err_str("attempt to call non-function"),
                     }
                 }
             }
-        }
-        _ => return Err("Expected list".to_string()),
+        },
     }
 
     }
@@ -287,8 +293,8 @@ fn print(exp: MalVal) -> String {
     exp.pr_str(true)
 }
 
-fn rep(str: String, env: Env) -> Result<String,String> {
-    match read(str) {
+fn rep(str: &str, env: Env) -> Result<String,MalError> {
+    match read(str.to_string()) {
         Err(e) => Err(e),
         Ok(ast) => {
             //println!("read: {}", ast);
@@ -308,10 +314,8 @@ fn main() {
     env_set(&repl_env, "*ARGV*".to_string(), list(vec![]));
 
     // core.mal: defined using the language itself
-    let _ = rep("(def! not (fn* (a) (if a false true)))".to_string(),
-                repl_env.clone());
-    let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))".to_string(),
-                repl_env.clone());
+    let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone());
+    let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env.clone());
 
     // Invoked with command line arguments
     let args = os::args();
@@ -320,8 +324,8 @@ fn main() {
             .map(|a| string(a.to_string()))
             .collect::<Vec<MalVal>>();
         env_set(&repl_env, "*ARGV*".to_string(), list(mv_args));
-        match rep("(load-file \"".to_string() + args[1] + "\")".to_string(),
-                  repl_env.clone()) {
+        let lf = "(load-file \"".to_string() + args[1] + "\")".to_string();
+        match rep(lf.as_slice(), repl_env.clone()) {
             Ok(_) => {
                 os::set_exit_status(0);
                 return;
@@ -337,9 +341,10 @@ fn main() {
     loop {
         let line = readline::mal_readline("user> ");
         match line { None => break, _ => () }
-        match rep(line.unwrap(), repl_env.clone()) {
+        match rep(line.unwrap().as_slice(), repl_env.clone()) {
             Ok(str)  => println!("{}", str),
-            Err(str) => println!("Error: {}", str),
+            Err(ErrMalVal(_)) => (),  // Blank line
+            Err(ErrString(s)) => println!("Error: {}", s),
         }
     }
 }
index 1c84c58..a79df33 100644 (file)
@@ -7,7 +7,8 @@ extern crate regex;
 use std::collections::HashMap;
 use std::os;
 
-use types::{MalVal,MalRet,MalFunc,Nil,False,Sym,List,Vector,Hash_Map,Func,
+use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str,
+            Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc,
             _nil,symbol,string,list,vector,hash_map,malfunc,malfuncd};
 use env::{Env,env_new,env_bind,env_root,env_find,env_set,env_get};
 mod readline;
@@ -105,27 +106,26 @@ fn is_macro_call(ast: MalVal, env: Env) -> bool {
 
 fn macroexpand(mut ast: MalVal, env: Env) -> MalRet {
     while is_macro_call(ast.clone(), env.clone()) {
-        match *ast.clone() {
-            List(ref args) => {
-                let ref a0 = args[0];
-                match **a0 {
-                    Sym(ref s) => {
-                        match env_get(env.clone(), s.to_string()) {
-                            Ok(mf) => {
-                                match *mf {
-                                    MalFunc(_) => {
-                                        match mf.apply(args.slice(1,args.len()).to_vec()) {
-                                            Ok(r) => ast = r,
-                                            Err(e) => return Err(e),
-                                        }
-                                    },
-                                    _ => break,
-                                }
-                            },
-                            Err(e) => return Err(e),
-                        }
-                    },
-                    _ => break,
+        let ast2 = ast.clone();
+        let args = match *ast2 {
+            List(ref args) => args,
+            _ => break,
+        };
+        let ref a0 = args[0];
+        let mf = match **a0 {
+            Sym(ref s) => {
+                match env_get(env.clone(), s.to_string()) {
+                    Ok(mf) => mf,
+                    Err(e) => return Err(e),
+                }
+            },
+            _ => break,
+        };
+        match *mf {
+            MalFunc(_) => {
+                match mf.apply(args.slice(1,args.len()).to_vec()) {
+                    Ok(r) => ast = r,
+                    Err(e) => return Err(e),
                 }
             },
             _ => break,
@@ -193,194 +193,189 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
     }
     let ast3 = ast2.clone();
 
-    match *ast2 {
+    let (args, a0sym) = match *ast2 {
         List(ref args) => {
             if args.len() == 0 { 
                 return Ok(ast3);
             }
             let ref a0 = *args[0];
             match *a0 {
-                Sym(ref a0sym) => {
-                    match a0sym.as_slice() {
-                        "def!" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            let res = eval(a2, env.clone());
-                            match res {
-                                Ok(r) => {
-                                    match *a1 {
-                                        Sym(ref s) => {
-                                            env_set(&env.clone(), s.clone(), r.clone());
-                                            return Ok(r);
-                                        },
-                                        _ => {
-                                            return Err("def! of non-symbol".to_string())
-                                        }
-                                    }
-                                },
-                                Err(e) => return Err(e),
-                            }
+                Sym(ref a0sym) => (args, a0sym.as_slice()),
+                _ => (args, "__<fn*>__"),
+            }
+        },
+        _ => return err_str("Expected list"),
+    };
+
+    match a0sym {
+        "def!" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            let res = eval(a2, env.clone());
+            match res {
+                Ok(r) => {
+                    match *a1 {
+                        Sym(ref s) => {
+                            env_set(&env.clone(), s.clone(), r.clone());
+                            return Ok(r);
                         },
-                        "let*" => {
-                            let let_env = env_new(Some(env.clone()));
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
+                        _ => {
+                            return err_str("def! of non-symbol")
+                        }
+                    }
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        "let*" => {
+            let let_env = env_new(Some(env.clone()));
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            match *a1 {
+                List(ref binds) | Vector(ref binds) => {
+                    let mut it = binds.iter();
+                    while it.len() >= 2 {
+                        let b = it.next().unwrap();
+                        let exp = it.next().unwrap();
+                        match **b {
+                            Sym(ref bstr) => {
+                                match eval(exp.clone(), let_env.clone()) {
+                                    Ok(r) => {
+                                        env_set(&let_env, bstr.clone(), r);
+                                    },
+                                    Err(e) => {
+                                        return Err(e);
+                                    },
+                                }
+                            },
+                            _ => {
+                                return err_str("let* with non-symbol binding");
+                            },
+                        }
+                    }
+                },
+                _ => return err_str("let* with non-list bindings"),
+            }
+            ast = a2;
+            env = let_env.clone();
+            continue 'tco;
+        },
+        "quote" => {
+            return Ok((*args)[1].clone());
+        },
+        "quasiquote" => {
+            let a1 = (*args)[1].clone();
+            ast = quasiquote(a1);
+            continue 'tco;
+        },
+        "defmacro!" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            match eval(a2, env.clone()) {
+                Ok(r) => {
+                    match *r {
+                        MalFunc(ref mfd) => {
                             match *a1 {
-                                List(ref binds) | Vector(ref binds) => {
-                                    let mut it = binds.iter();
-                                    while it.len() >= 2 {
-                                        let b = it.next().unwrap();
-                                        let exp = it.next().unwrap();
-                                        match **b {
-                                            Sym(ref bstr) => {
-                                                match eval(exp.clone(), let_env.clone()) {
-                                                    Ok(r) => {
-                                                        env_set(&let_env, bstr.clone(), r);
-                                                    },
-                                                    Err(e) => {
-                                                        return Err(e);
-                                                    },
-                                                }
-                                            },
-                                            _ => {
-                                                return Err("let* with non-symbol binding".to_string());
-                                            },
-                                        }
-                                    }
+                                Sym(ref s) => {
+                                    let mut new_mfd = mfd.clone();
+                                    new_mfd.is_macro = true;
+                                    let mf = malfuncd(new_mfd);
+                                    env_set(&env.clone(), s.clone(), mf.clone());
+                                    return Ok(mf);
                                 },
-                                _ => return Err("let* with non-list bindings".to_string()),
+                                _ => return err_str("def! of non-symbol"),
                             }
-                            ast = a2;
-                            env = let_env.clone();
-                            continue 'tco;
-                        },
-                        "quote" => {
-                            return Ok((*args)[1].clone());
                         },
-                        "quasiquote" => {
-                            let a1 = (*args)[1].clone();
-                            ast = quasiquote(a1);
-                            continue 'tco;
-                        },
-                        "defmacro!" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            match eval(a2, env.clone()) {
-                                Ok(r) => {
-                                    match *r {
-                                        MalFunc(ref mfd) => {
-                                            match *a1 {
-                                                Sym(ref s) => {
-                                                    let mut new_mfd = mfd.clone();
-                                                    new_mfd.is_macro = true;
-                                                    let mf = malfuncd(new_mfd);
-                                                    env_set(&env.clone(), s.clone(), mf.clone());
-                                                    return Ok(mf);
-                                                },
-                                                _ => return Err("def! of non-symbol".to_string()),
-                                            }
-                                        },
-                                        _ => return Err("def! of non-symbol".to_string()),
-                                    }
-                                },
-                                Err(e) => return Err(e),
-                            }
-                        },
-                        "macroexpand" => {
-                            let a1 = (*args)[1].clone();
-                            return macroexpand(a1, env.clone())
-                        },
-                        "do" => {
-                            let el = list(args.slice(1,args.len()-1).to_vec());
-                            match eval_ast(el, env.clone()) {
-                                Err(e) => return Err(e),
-                                Ok(_) => {
-                                    let ref last = args[args.len()-1];
-                                    ast = last.clone();
-                                    continue 'tco;
-                                },
-                            }
-                        },
-                        "if" => {
-                            let a1 = (*args)[1].clone();
-                            let cond = eval(a1, env.clone());
-                            if cond.is_err() { return cond; }
-                            match *cond.unwrap() {
-                                False | Nil => {
-                                    if args.len() >= 4 {
-                                        let a3 = (*args)[3].clone();
-                                        ast = a3;
-                                        env = env.clone();
-                                        continue 'tco;
-                                    } else {
-                                        return Ok(_nil());
-                                    }
-                                },
-                                _ => {
-                                    let a2 = (*args)[2].clone();
-                                    ast = a2;
-                                    env = env.clone();
-                                    continue 'tco;
-                                },
-                            }
-                        },
-                        "fn*" => {
-                            let a1 = (*args)[1].clone();
-                            let a2 = (*args)[2].clone();
-                            return Ok(malfunc(eval, a2, env.clone(), a1));
-                        },
-                        "eval" => {
-                            let a1 = (*args)[1].clone();
-                            match eval(a1, env.clone()) {
-                                Ok(exp) => {
-                                    ast = exp;
-                                    env = env_root(&env);
-                                    continue 'tco;
-                                },
-                                Err(e) => return Err(e),
-                            }
-                        },
-                        _ => ()
+                        _ => return err_str("def! of non-symbol"),
                     }
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        "macroexpand" => {
+            let a1 = (*args)[1].clone();
+            return macroexpand(a1, env.clone())
+        },
+        "do" => {
+            let el = list(args.slice(1,args.len()-1).to_vec());
+            match eval_ast(el, env.clone()) {
+                Err(e) => return Err(e),
+                Ok(_) => {
+                    let ref last = args[args.len()-1];
+                    ast = last.clone();
+                    continue 'tco;
+                },
+            }
+        },
+        "if" => {
+            let a1 = (*args)[1].clone();
+            let cond = eval(a1, env.clone());
+            match cond {
+                Err(e) => return Err(e),
+                Ok(c) => match *c {
+                    False | Nil => {
+                        if args.len() >= 4 {
+                            let a3 = (*args)[3].clone();
+                            ast = a3;
+                            env = env.clone();
+                            continue 'tco;
+                        } else {
+                            return Ok(_nil());
+                        }
+                    },
+                    _ => {
+                        let a2 = (*args)[2].clone();
+                        ast = a2;
+                        env = env.clone();
+                        continue 'tco;
+                    },
                 }
-                _ => (),
             }
-            // function call
-            /*
-            if is_macro_call(ast3.clone(), env.clone()) {
-                println!("macro call");
+        },
+        "fn*" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            return Ok(malfunc(eval, a2, env.clone(), a1));
+        },
+        "eval" => {
+            let a1 = (*args)[1].clone();
+            match eval(a1, env.clone()) {
+                Ok(exp) => {
+                    ast = exp;
+                    env = env_root(&env);
+                    continue 'tco;
+                },
+                Err(e) => return Err(e),
             }
-            */
+        },
+        _ => { // function call
             return match eval_ast(ast3, env.clone()) {
                 Err(e) => Err(e),
                 Ok(el) => {
-                    match *el {
-                        List(ref args) => {
-                            let args2 = args.clone();
-                            match *args2[0] {
-                                Func(f) => f(args.slice(1,args.len()).to_vec()),
-                                MalFunc(ref mf) => {
-                                    let mfc = mf.clone();
-                                    let alst = list(args.slice(1,args.len()).to_vec());
-                                    let new_env = env_new(Some(mfc.env.clone()));
-                                    match env_bind(&new_env, mfc.params, alst) {
-                                        Ok(_) => {
-                                            ast = mfc.exp;
-                                            env = new_env;
-                                            continue 'tco;
-                                        },
-                                        Err(e) => Err(e),
-                                    }
+                    let args = match *el {
+                        List(ref args) => args,
+                        _ => return err_str("Invalid apply"),
+                    };
+                    match *args.clone()[0] {
+                        Func(f) => f(args.slice(1,args.len()).to_vec()),
+                        MalFunc(ref mf) => {
+                            let mfc = mf.clone();
+                            let alst = list(args.slice(1,args.len()).to_vec());
+                            let new_env = env_new(Some(mfc.env.clone()));
+                            match env_bind(&new_env, mfc.params, alst) {
+                                Ok(_) => {
+                                    ast = mfc.exp;
+                                    env = new_env;
+                                    continue 'tco;
                                 },
-                                _ => Err("attempt to call non-function".to_string()),
+                                Err(e) => err_str(e.as_slice()),
                             }
-                        }
-                        _ => Err("Invalid apply".to_string()),
+                        },
+                        _ => err_str("attempt to call non-function"),
                     }
                 }
             }
-        }
-        _ => return Err("Expected list".to_string()),
+        },
     }
 
     }
@@ -391,8 +386,8 @@ fn print(exp: MalVal) -> String {
     exp.pr_str(true)
 }
 
-fn rep(str: String, env: Env) -> Result<String,String> {
-    match read(str) {
+fn rep(str: &str, env: Env) -> Result<String,MalError> {
+    match read(str.to_string()) {
         Err(e) => Err(e),
         Ok(ast) => {
             //println!("read: {}", ast);
@@ -412,10 +407,8 @@ fn main() {
     env_set(&repl_env, "*ARGV*".to_string(), list(vec![]));
 
     // core.mal: defined using the language itself
-    let _ = rep("(def! not (fn* (a) (if a false true)))".to_string(),
-                repl_env.clone());
-    let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))".to_string(),
-                repl_env.clone());
+    let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone());
+    let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env.clone());
 
     // Invoked with command line arguments
     let args = os::args();
@@ -424,8 +417,8 @@ fn main() {
             .map(|a| string(a.to_string()))
             .collect::<Vec<MalVal>>();
         env_set(&repl_env, "*ARGV*".to_string(), list(mv_args));
-        match rep("(load-file \"".to_string() + args[1] + "\")".to_string(),
-                  repl_env.clone()) {
+        let lf = "(load-file \"".to_string() + args[1] + "\")".to_string();
+        match rep(lf.as_slice(), repl_env.clone()) {
             Ok(_) => {
                 os::set_exit_status(0);
                 return;
@@ -441,9 +434,10 @@ fn main() {
     loop {
         let line = readline::mal_readline("user> ");
         match line { None => break, _ => () }
-        match rep(line.unwrap(), repl_env.clone()) {
+        match rep(line.unwrap().as_slice(), repl_env.clone()) {
             Ok(str)  => println!("{}", str),
-            Err(str) => println!("Error: {}", str),
+            Err(ErrMalVal(_)) => (),  // Blank line
+            Err(ErrString(s)) => println!("Error: {}", s),
         }
     }
 }
diff --git a/rust/src/step9_try.rs b/rust/src/step9_try.rs
new file mode 100644 (file)
index 0000000..f6dde04
--- /dev/null
@@ -0,0 +1,476 @@
+// support precompiled regexes in reader.rs
+#![feature(phase)]
+#[phase(plugin)]
+extern crate regex_macros;
+extern crate regex;
+
+use std::collections::HashMap;
+use std::os;
+
+use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal,err_str,
+            Nil,False,Sym,List,Vector,Hash_Map,Func,MalFunc,
+            _nil,symbol,string,list,vector,hash_map,malfunc,malfuncd};
+use env::{Env,env_new,env_bind,env_root,env_find,env_set,env_get};
+mod readline;
+mod types;
+mod reader;
+mod printer;
+mod env;
+mod core;
+
+// read
+fn read(str: String) -> MalRet {
+    reader::read_str(str)
+}
+
+// eval
+fn is_pair(x: MalVal) -> bool {
+    match *x {
+        List(ref lst) => lst.len() > 0,
+        _ => false,
+    }
+}
+
+fn quasiquote(ast: MalVal) -> MalVal {
+    if !is_pair(ast.clone()) {
+        return list(vec![symbol("quote"), ast])
+    }
+
+    match *ast.clone() {
+        List(ref args) => {
+            let ref a0 = args[0];
+            match **a0 {
+                Sym(ref s) => {
+                    if s.to_string() == "unquote".to_string() {
+                        let ref a1 = args[1];
+                        return a1.clone();
+                    }
+                },
+                _ => (),
+            }
+            if is_pair(a0.clone()) {
+                match **a0 {
+                    List(ref a0args) => {
+                        let a00 = a0args[0].clone();
+                        match *a00 {
+                            Sym(ref s) => {
+                                if s.to_string() == "splice-unquote".to_string() {
+                                    return list(vec![symbol("concat"),
+                                                     a0args[1].clone(),
+                                                     quasiquote(list(args.slice(1,args.len()).to_vec()))])
+                                }
+                            },
+                            _ => (),
+                        }
+                    },
+                    _ => (),
+                }
+            }
+            let rest = list(args.slice(1,args.len()).to_vec());
+            return list(vec![symbol("cons"),
+                             quasiquote(a0.clone()),
+                             quasiquote(rest)])
+        },
+        _ => _nil(), // should never reach
+    }
+}
+
+fn is_macro_call(ast: MalVal, env: Env) -> bool {
+    match *ast {
+        List(ref lst) => {
+            let ref a0 = *lst[0];
+            match *a0 {
+                Sym(ref a0sym) => {
+                    if env_find(env.clone(), a0sym.to_string()).is_some() {
+                        match env_get(env, a0sym.to_string()) {
+                            Ok(f) => {
+                                match *f {
+                                    MalFunc(ref mfd) => {
+                                        mfd.is_macro
+                                    },
+                                    _ => false,
+                                }
+                            },
+                            _ => false,
+                        }
+                    } else {
+                        false
+                    }
+                },
+                _ => false,
+            }
+        },
+        _ => false,
+    }
+}
+
+fn macroexpand(mut ast: MalVal, env: Env) -> MalRet {
+    while is_macro_call(ast.clone(), env.clone()) {
+        let ast2 = ast.clone();
+        let args = match *ast2 {
+            List(ref args) => args,
+            _ => break,
+        };
+        let ref a0 = args[0];
+        let mf = match **a0 {
+            Sym(ref s) => {
+                match env_get(env.clone(), s.to_string()) {
+                    Ok(mf) => mf,
+                    Err(e) => return Err(e),
+                }
+            },
+            _ => break,
+        };
+        match *mf {
+            MalFunc(_) => {
+                match mf.apply(args.slice(1,args.len()).to_vec()) {
+                    Ok(r) => ast = r,
+                    Err(e) => return Err(e),
+                }
+            },
+            _ => break,
+        }
+    }
+    Ok(ast)
+}
+
+fn eval_ast(ast: MalVal, env: Env) -> MalRet {
+    let ast2 = ast.clone();
+    match *ast2 {
+    //match *ast {
+        Sym(ref sym) => {
+            env_get(env.clone(), sym.clone())
+        },
+        List(ref a) | Vector(ref a) => {
+            let mut ast_vec : Vec<MalVal> = vec![];
+            for mv in a.iter() {
+                let mv2 = mv.clone();
+                match eval(mv2, env.clone()) {
+                    Ok(mv) => { ast_vec.push(mv); },
+                    Err(e) => { return Err(e); },
+                }
+            }
+            Ok(match *ast { List(_) => list(ast_vec),
+                            _       => vector(ast_vec) })
+        },
+        Hash_Map(ref hm) => {
+            let mut new_hm: HashMap<String,MalVal> = HashMap::new();
+            for (key, value) in hm.iter() {
+                match eval(value.clone(), env.clone()) {
+                    Ok(mv) => { new_hm.insert(key.to_string(), mv); },
+                    Err(e) => return Err(e),
+                }
+            }
+            Ok(hash_map(new_hm))
+        },
+        _ => {
+            Ok(ast)
+        }
+    }
+}
+
+fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
+    'tco: loop {
+
+    //println!("eval: {}, {}", ast, env.borrow());
+    //println!("eval: {}", ast);
+    let mut ast2 = ast.clone();
+    match *ast2 {
+        List(_) => (),  // continue
+        _ => return eval_ast(ast2, env),
+    }
+
+    // apply list
+    match macroexpand(ast2, env.clone()) {
+        Ok(a) => {
+            ast2 = a;
+        },
+        Err(e) => return Err(e),
+    }
+    match *ast2 {
+        List(_) => (),  // continue
+        _ => return Ok(ast2),
+    }
+    let ast3 = ast2.clone();
+
+    let (args, a0sym) = match *ast2 {
+        List(ref args) => {
+            if args.len() == 0 { 
+                return Ok(ast3);
+            }
+            let ref a0 = *args[0];
+            match *a0 {
+                Sym(ref a0sym) => (args, a0sym.as_slice()),
+                _ => (args, "__<fn*>__"),
+            }
+        },
+        _ => return err_str("Expected list"),
+    };
+
+    match a0sym {
+        "def!" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            let res = eval(a2, env.clone());
+            match res {
+                Ok(r) => {
+                    match *a1 {
+                        Sym(ref s) => {
+                            env_set(&env.clone(), s.clone(), r.clone());
+                            return Ok(r);
+                        },
+                        _ => {
+                            return err_str("def! of non-symbol")
+                        }
+                    }
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        "let*" => {
+            let let_env = env_new(Some(env.clone()));
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            match *a1 {
+                List(ref binds) | Vector(ref binds) => {
+                    let mut it = binds.iter();
+                    while it.len() >= 2 {
+                        let b = it.next().unwrap();
+                        let exp = it.next().unwrap();
+                        match **b {
+                            Sym(ref bstr) => {
+                                match eval(exp.clone(), let_env.clone()) {
+                                    Ok(r) => {
+                                        env_set(&let_env, bstr.clone(), r);
+                                    },
+                                    Err(e) => {
+                                        return Err(e);
+                                    },
+                                }
+                            },
+                            _ => {
+                                return err_str("let* with non-symbol binding");
+                            },
+                        }
+                    }
+                },
+                _ => return err_str("let* with non-list bindings"),
+            }
+            ast = a2;
+            env = let_env.clone();
+            continue 'tco;
+        },
+        "quote" => {
+            return Ok((*args)[1].clone());
+        },
+        "quasiquote" => {
+            let a1 = (*args)[1].clone();
+            ast = quasiquote(a1);
+            continue 'tco;
+        },
+        "defmacro!" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            match eval(a2, env.clone()) {
+                Ok(r) => {
+                    match *r {
+                        MalFunc(ref mfd) => {
+                            match *a1 {
+                                Sym(ref s) => {
+                                    let mut new_mfd = mfd.clone();
+                                    new_mfd.is_macro = true;
+                                    let mf = malfuncd(new_mfd);
+                                    env_set(&env.clone(), s.clone(), mf.clone());
+                                    return Ok(mf);
+                                },
+                                _ => return err_str("def! of non-symbol"),
+                            }
+                        },
+                        _ => return err_str("def! of non-symbol"),
+                    }
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        "macroexpand" => {
+            let a1 = (*args)[1].clone();
+            return macroexpand(a1, env.clone())
+        },
+        "try*" => {
+            let a1 = (*args)[1].clone();
+            match eval(a1, env.clone()) {
+                Ok(res) => return Ok(res),
+                Err(err) => {
+                    if args.len() < 3 { return Err(err); }
+                    let a2 = (*args)[2].clone();
+                    let cat = match *a2 {
+                        List(ref cat) => cat,
+                        _ => return err_str("invalid catch* clause"),
+                    };
+                    if cat.len() != 3 {
+                        return err_str("wrong arity to catch* clause");
+                    }
+                    let c1 = (*cat)[1].clone();
+                    let bstr = match *c1 {
+                        Sym(ref s) => s,
+                        _ => return err_str("invalid catch* binding"),
+                    };
+                    let exc = match err {
+                        ErrMalVal(mv) => mv,
+                        ErrString(s) => string(s),
+                    };
+                    let bind_env = env_new(Some(env.clone()));
+                    env_set(&bind_env, bstr.to_string(), exc);
+                    let c2 = (*cat)[2].clone();
+                    return eval(c2, bind_env);
+                },
+            };
+        }
+        "do" => {
+            let el = list(args.slice(1,args.len()-1).to_vec());
+            match eval_ast(el, env.clone()) {
+                Err(e) => return Err(e),
+                Ok(_) => {
+                    let ref last = args[args.len()-1];
+                    ast = last.clone();
+                    continue 'tco;
+                },
+            }
+        },
+        "if" => {
+            let a1 = (*args)[1].clone();
+            let cond = eval(a1, env.clone());
+            match cond {
+                Err(e) => return Err(e),
+                Ok(c) => match *c {
+                    False | Nil => {
+                        if args.len() >= 4 {
+                            let a3 = (*args)[3].clone();
+                            ast = a3;
+                            env = env.clone();
+                            continue 'tco;
+                        } else {
+                            return Ok(_nil());
+                        }
+                    },
+                    _ => {
+                        let a2 = (*args)[2].clone();
+                        ast = a2;
+                        env = env.clone();
+                        continue 'tco;
+                    },
+                }
+            }
+        },
+        "fn*" => {
+            let a1 = (*args)[1].clone();
+            let a2 = (*args)[2].clone();
+            return Ok(malfunc(eval, a2, env.clone(), a1));
+        },
+        "eval" => {
+            let a1 = (*args)[1].clone();
+            match eval(a1, env.clone()) {
+                Ok(exp) => {
+                    ast = exp;
+                    env = env_root(&env);
+                    continue 'tco;
+                },
+                Err(e) => return Err(e),
+            }
+        },
+        _ => { // function call
+            return match eval_ast(ast3, env.clone()) {
+                Err(e) => Err(e),
+                Ok(el) => {
+                    let args = match *el {
+                        List(ref args) => args,
+                        _ => return err_str("Invalid apply"),
+                    };
+                    match *args.clone()[0] {
+                        Func(f) => f(args.slice(1,args.len()).to_vec()),
+                        MalFunc(ref mf) => {
+                            let mfc = mf.clone();
+                            let alst = list(args.slice(1,args.len()).to_vec());
+                            let new_env = env_new(Some(mfc.env.clone()));
+                            match env_bind(&new_env, mfc.params, alst) {
+                                Ok(_) => {
+                                    ast = mfc.exp;
+                                    env = new_env;
+                                    continue 'tco;
+                                },
+                                Err(e) => err_str(e.as_slice()),
+                            }
+                        },
+                        _ => err_str("attempt to call non-function"),
+                    }
+                }
+            }
+        },
+    }
+
+    }
+}
+
+// print
+fn print(exp: MalVal) -> String {
+    exp.pr_str(true)
+}
+
+fn rep(str: &str, env: Env) -> Result<String,MalError> {
+    match read(str.to_string()) {
+        Err(e) => Err(e),
+        Ok(ast) => {
+            //println!("read: {}", ast);
+            match eval(ast, env) {
+                Err(e)  => Err(e),
+                Ok(exp) => Ok(print(exp)),
+            }
+        }
+    }
+}
+
+fn main() {
+    // core.rs: defined using rust
+    let repl_env = env_new(None);
+    for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); }
+    // see eval() for definition of "eval"
+    env_set(&repl_env, "*ARGV*".to_string(), list(vec![]));
+
+    // core.mal: defined using the language itself
+    let _ = rep("(def! *host-language* \"rust\")", repl_env.clone());
+    let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone());
+    let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env.clone());
+
+    // Invoked with command line arguments
+    let args = os::args();
+    if args.len() > 1 {
+        let mv_args = args.slice(2,args.len()).iter()
+            .map(|a| string(a.to_string()))
+            .collect::<Vec<MalVal>>();
+        env_set(&repl_env, "*ARGV*".to_string(), list(mv_args));
+        let lf = "(load-file \"".to_string() + args[1] + "\")".to_string();
+        match rep(lf.as_slice(), repl_env.clone()) {
+            Ok(_) => {
+                os::set_exit_status(0);
+                return;
+            },
+            Err(str) => {
+                println!("Error: {}", str);
+                os::set_exit_status(1);
+                return;
+            },
+        }
+    }
+
+    // repl loop
+    let _  = rep("(println (str \"Mal [\" *host-language* \"]\"))", repl_env.clone());
+    loop {
+        let line = readline::mal_readline("user> ");
+        match line { None => break, _ => () }
+        match rep(line.unwrap().as_slice(), repl_env.clone()) {
+            Ok(str)  => println!("{}", str),
+            Err(ErrMalVal(_)) => (),  // Blank line
+            Err(ErrString(s)) => println!("Error: {}", s),
+        }
+    }
+}
index e540ff1..97915cb 100644 (file)
@@ -26,7 +26,35 @@ pub enum MalType {
 
 pub type MalVal = Rc<MalType>;
 
-pub type MalRet = Result<MalVal,String>;
+#[deriving(Show)]
+pub enum MalError {
+    ErrString(String),
+    ErrMalVal(MalVal),
+}
+
+pub type MalRet = Result<MalVal,MalError>;
+
+
+pub fn err_string(s: String) -> MalRet {
+    Err(ErrString(s))
+}
+
+pub fn err_str(s: &str) -> MalRet {
+    Err(ErrString(s.to_string()))
+}
+
+pub fn err_val(mv: MalVal) -> MalRet {
+    Err(ErrMalVal(mv))
+}
+
+/*
+pub enum MalRet {
+    Val(MalVal),
+    MalErr(MalVal),
+    StringErr(String),
+}
+*/
+
 
 #[deriving(Clone)]
 pub struct MalFuncData {
@@ -98,10 +126,10 @@ impl MalType {
                 let new_env = env_new(Some(mfc.env.clone()));
                 match env_bind(&new_env, mfc.params, alst) {
                     Ok(_) => (mfc.eval)(mfc.exp, new_env),
-                    Err(e) => Err(e),
+                    Err(e) => err_string(e),
                 }
             },
-            _ => Err("attempt to call non-function".to_string()),
+            _ => err_str("attempt to call non-function"),
         }
 
     }
@@ -136,23 +164,90 @@ impl fmt::Show for MalType {
 }
 
 
-// Convenience constructor functions
+// Scalars
 pub fn _nil() -> MalVal { Rc::new(Nil) }
+pub fn nil_q(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 1 {
+        return err_str("Wrong arity to nil? call");
+    }
+    match *a[0].clone() {
+        Nil => Ok(_true()),
+        _   => Ok(_false()),
+    }
+}
+
 pub fn _true() -> MalVal { Rc::new(True) }
+pub fn true_q(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 1 {
+        return err_str("Wrong arity to true? call");
+    }
+    match *a[0].clone() {
+        True => Ok(_true()),
+        _    => Ok(_false()),
+    }
+}
+
 pub fn _false() -> MalVal { Rc::new(False) }
+pub fn false_q(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 1 {
+        return err_str("Wrong arity to false? call");
+    }
+    match *a[0].clone() {
+        False => Ok(_true()),
+        _     => Ok(_false()),
+    }
+}
+
 pub fn _int(i: int) -> MalVal { Rc::new(Int(i)) }
 
+
+// Symbols
 pub fn symbol(strn: &str) -> MalVal { Rc::new(Sym(strn.to_string())) }
+pub fn symbol_q(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 1 {
+        return err_str("Wrong arity to symbol? call");
+    }
+    match *a[0].clone() {
+        Sym(_) => Ok(_true()),
+        _      => Ok(_false()),
+    }
+}
+
+// Strings
 pub fn strn(strn: &str) -> MalVal { Rc::new(Strn(strn.to_string())) }
 pub fn string(strn: String) -> MalVal { Rc::new(Strn(strn)) }
 
+// Lists
 pub fn list(seq: Vec<MalVal>) -> MalVal { Rc::new(List(seq)) }
+pub fn listv(seq:Vec<MalVal>) -> MalRet { Ok(list(seq)) }
+pub fn list_q(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 1 {
+        return err_str("Wrong arity to list? call");
+    }
+    match *a[0].clone() {
+        List(_) => Ok(_true()),
+        _ => Ok(_false()),
+    }
+}
+
+// Vectors
 pub fn vector(seq: Vec<MalVal>) -> MalVal { Rc::new(Vector(seq)) }
-pub fn hash_map(hm: HashMap<String,MalVal>) -> MalVal { Rc::new(Hash_Map(hm)) }
+pub fn vectorv(seq: Vec<MalVal>) -> MalRet { Ok(vector(seq)) }
+pub fn vector_q(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 1 {
+        return err_str("Wrong arity to vector? call");
+    }
+    match *a[0].clone() {
+        Vector(_) => Ok(_true()),
+        _         => Ok(_false()),
+    }
+}
 
+// Hash Maps
+pub fn hash_map(hm: HashMap<String,MalVal>) -> MalVal { Rc::new(Hash_Map(hm)) }
 pub fn hash_mapv(seq: Vec<MalVal>) -> MalRet {
     if seq.len() % 2 == 1 {
-        return Err("odd number of elements to hash-map".to_string());
+        return err_str("odd number of elements to hash-map");
     }
     let mut new_hm: HashMap<String,MalVal> = HashMap::new();
     let mut it = seq.iter();
@@ -160,7 +255,7 @@ pub fn hash_mapv(seq: Vec<MalVal>) -> MalRet {
         let k = match it.next() {
             Some(mv) => match *mv.clone() {
                 Strn(ref s) => s.to_string(),
-                _ => return Err("key is not a string in hash-map call".to_string()),
+                _ => return err_str("key is not a string in hash-map call"),
             },
             None => break,
         };
@@ -169,6 +264,15 @@ pub fn hash_mapv(seq: Vec<MalVal>) -> MalRet {
     }
     Ok(Rc::new(Hash_Map(new_hm)))
 }
+pub fn hash_map_q(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 1 {
+        return err_str("Wrong arity to map? call");
+    }
+    match *a[0].clone() {
+        Hash_Map(_) => Ok(_true()),
+        _           => Ok(_false()),
+    }
+}
 
 pub fn func(f: fn(Vec<MalVal>) -> MalRet ) -> MalVal {
     Rc::new(Func(f))
@@ -181,3 +285,15 @@ pub fn malfunc(eval: fn(MalVal, Env) -> MalRet,
 pub fn malfuncd(mfd: MalFuncData) -> MalVal {
     Rc::new(MalFunc(mfd))
 }
+
+
+// General functions
+pub fn sequential_q(a:Vec<MalVal>) -> MalRet {
+    if a.len() != 1 {
+        return err_str("Wrong arity to sequential? call");
+    }
+    match *a[0].clone() {
+        List(_) | Vector(_) => Ok(_true()),
+        _                   => Ok(_false()),
+    }
+}