aboutsummaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2015-03-02 21:33:10 -0600
committerJoel Martin <github@martintribe.org>2015-03-02 21:33:10 -0600
commit835fb7d8b06e2b44792a97ac89994658bf6d00af (patch)
tree578f67726ab9e3ce5fcbc50220e9761a66c5ddf1 /rust
parent6b72e6078a7d505ecf9d711eb4a16fc4dfac36b6 (diff)
parent8a98ef9a3f3a6b6d05d02dc305a0c886c907e0f3 (diff)
downloadmal-835fb7d8b06e2b44792a97ac89994658bf6d00af.tar.gz
mal-835fb7d8b06e2b44792a97ac89994658bf6d00af.zip
Merge branch 'master' into gh-pages
Conflicts: .gitignore
Diffstat (limited to 'rust')
-rw-r--r--rust/Cargo.toml39
-rw-r--r--rust/Makefile36
-rw-r--r--rust/src/core.rs561
-rw-r--r--rust/src/env.rs118
-rw-r--r--rust/src/printer.rs45
-rw-r--r--rust/src/reader.rs213
-rw-r--r--rust/src/readline.rs76
-rw-r--r--rust/src/step0_repl.rs25
-rw-r--r--rust/src/step1_read_print.rs52
-rw-r--r--rust/src/step2_eval.rs129
-rw-r--r--rust/src/step3_env.rs204
-rw-r--r--rust/src/step4_if_fn_do.rs235
-rw-r--r--rust/src/step5_tco.rs257
-rw-r--r--rust/src/step6_file.rs293
-rw-r--r--rust/src/step7_quote.rs352
-rw-r--r--rust/src/step8_macros.rs447
-rw-r--r--rust/src/step9_try.rs477
-rw-r--r--rust/src/stepA_mal.rs479
-rw-r--r--rust/src/types.rs405
19 files changed, 4443 insertions, 0 deletions
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
new file mode 100644
index 0000000..daf999d
--- /dev/null
+++ b/rust/Cargo.toml
@@ -0,0 +1,39 @@
+[package]
+
+name = "Mal"
+version = "0.0.1"
+authors = [ "Your name <you@example.com>" ]
+
+
+[dependencies.cadencemarseille-pcre]
+
+git = "https://github.com/kanaka/rust-pcre"
+
+
+#[profile.dev]
+#
+#debug = true
+
+
+[[bin]]
+name = "step0_repl"
+[[bin]]
+name = "step1_read_print"
+[[bin]]
+name = "step2_eval"
+[[bin]]
+name = "step3_env"
+[[bin]]
+name = "step4_if_fn_do"
+[[bin]]
+name = "step5_tco"
+[[bin]]
+name = "step6_file"
+[[bin]]
+name = "step7_quote"
+[[bin]]
+name = "step8_macros"
+[[bin]]
+name = "step9_try"
+[[bin]]
+name = "stepA_mal"
diff --git a/rust/Makefile b/rust/Makefile
new file mode 100644
index 0000000..da8a6c6
--- /dev/null
+++ b/rust/Makefile
@@ -0,0 +1,36 @@
+#####################
+
+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/stepA_mal.rs
+SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
+
+#####################
+
+SRCS = step1_read_print.rs step2_eval.rs step3_env.rs \
+ step4_if_fn_do.rs step5_tco.rs step6_file.rs step7_quote.rs \
+ step8_macros.rs step9_try.rs stepA_mal.rs
+BINS = $(SRCS:%.rs=target/%)
+
+#####################
+
+all: mal
+
+mal: ${SOURCES_BASE} $(word $(words ${SOURCES_LISP}),${SOURCES_LISP})
+ cargo build
+ cp $(word $(words ${BINS}),${BINS}) $@
+
+#$(BINS): target/%: src/%.rs
+# cargo build $*
+
+clean:
+ cargo clean
+ rm -f mal
+
+.PHONY: stats stats-lisp
+
+stats: $(SOURCES)
+ @wc $^
+stats-lisp: $(SOURCES_LISP)
+ @wc $^
diff --git a/rust/src/core.rs b/rust/src/core.rs
new file mode 100644
index 0000000..2bc3c39
--- /dev/null
+++ b/rust/src/core.rs
@@ -0,0 +1,561 @@
+#![allow(dead_code)]
+
+extern crate time;
+use std::collections::HashMap;
+use std::io::File;
+
+use types::{MalVal,MalRet,err_val,err_str,err_string,
+ Nil,Int,Strn,List,Vector,Hash_Map,Func,MalFunc,Atom,
+ _nil,_true,_false,_int,string,
+ list,vector,listm,vectorm,hash_mapm,func,funcm,malfuncd};
+use types;
+use readline;
+use reader;
+use printer;
+
+// General functions
+fn equal_q(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 2 {
+ return err_str("Wrong arity to equal? call");
+ }
+ match a[0] == a[1] {
+ true => Ok(_true()),
+ false => Ok(_false()),
+ }
+}
+
+// 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, "", "", " ")))
+}
+
+fn str(a:Vec<MalVal>) -> MalRet {
+ Ok(string(printer::pr_list(&a, false, "", "", "")))
+}
+
+fn prn(a:Vec<MalVal>) -> MalRet {
+ println!("{}", printer::pr_list(&a, true, "", "", " "))
+ Ok(_nil())
+}
+
+fn println(a:Vec<MalVal>) -> MalRet {
+ println!("{}", printer::pr_list(&a, false, "", "", " "))
+ 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_str("read_string called with non-string"),
+ }
+}
+
+fn slurp(a:Vec<MalVal>) -> MalRet {
+ match *a[0] {
+ Strn(ref a0) => {
+ match File::open(&Path::new(a0.as_slice())).read_to_string() {
+ Ok(s) => Ok(string(s)),
+ Err(e) => err_string(e.to_string()),
+ }
+ },
+ _ => err_str("slurp called with non-string"),
+ }
+}
+
+
+// Numeric functions
+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_str("second arg must be an int"),
+ },
+ _ => err_str("first arg must be an int"),
+ }
+}
+
+fn bool_op(f: |i:int,j:int|-> bool, a:Vec<MalVal>) -> MalRet {
+ match *a[0] {
+ Int(a0) => match *a[1] {
+ Int(a1) => {
+ match f(a0,a1) {
+ true => Ok(_true()),
+ false => Ok(_false()),
+ }
+ },
+ _ => err_str("second arg must be an int"),
+ },
+ _ => err_str("first arg must be an int"),
+ }
+}
+
+pub fn add(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i+j }, a) }
+pub fn sub(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i-j }, a) }
+pub fn mul(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i*j }, a) }
+pub fn div(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i/j }, a) }
+
+pub fn lt (a:Vec<MalVal>) -> MalRet { bool_op(|i,j| { i<j }, a) }
+pub fn lte(a:Vec<MalVal>) -> MalRet { bool_op(|i,j| { i<=j }, a) }
+pub fn gt (a:Vec<MalVal>) -> MalRet { bool_op(|i,j| { i>j }, a) }
+pub fn gte(a:Vec<MalVal>) -> MalRet { bool_op(|i,j| { i>=j }, a) }
+
+#[allow(unused_variable)]
+pub fn time_ms(a:Vec<MalVal>) -> MalRet {
+ //let x = time::now();
+ let now = time::get_time();
+ let now_ms = (now.sec * 1000).to_int().unwrap() + (now.nsec.to_int().unwrap() / 1000000);
+ Ok(_int(now_ms))
+}
+
+
+// Hash Map functions
+pub fn assoc(a:Vec<MalVal>) -> MalRet {
+ if a.len() < 3 {
+ return err_str("Wrong arity to assoc call");
+ }
+ match *a[0] {
+ Hash_Map(ref hm,_) => {
+ types::_assoc(hm, a.slice(1,a.len()).to_vec())
+ },
+ Nil => {
+ types::hash_mapv(a.slice(1,a.len()).to_vec())
+ }
+ _ => return err_str("assoc onto non-hash map"),
+ }
+}
+
+pub fn dissoc(a:Vec<MalVal>) -> MalRet {
+ if a.len() < 2 {
+ return err_str("Wrong arity to dissoc call");
+ }
+ match *a[0] {
+ Hash_Map(ref hm,_) => {
+ types::_dissoc(hm, a.slice(1,a.len()).to_vec())
+ },
+ Nil => {
+ Ok(_nil())
+ }
+ _ => return err_str("dissoc onto non-hash map"),
+ }
+}
+
+pub fn get(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 2 {
+ return err_str("Wrong arity to get call");
+ }
+ let a0 = a[0].clone();
+ let hm: &HashMap<String,MalVal> = match *a0 {
+ Hash_Map(ref hm,_) => hm,
+ Nil => return Ok(_nil()),
+ _ => return err_str("get on 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"),
+ }
+}
+
+pub fn contains_q(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 2 {
+ return err_str("Wrong arity to contains? call");
+ }
+ let a0 = a[0].clone();
+ let hm: &HashMap<String,MalVal> = match *a0 {
+ Hash_Map(ref hm,_) => hm,
+ Nil => return Ok(_false()),
+ _ => return err_str("contains? on non-hash map"),
+ };
+ match *a[1] {
+ Strn(ref key) => {
+ match hm.contains_key(key) {
+ true => Ok(_true()),
+ false => Ok(_false()),
+ }
+ },
+ _ => return err_str("contains? with non-string key"),
+ }
+}
+
+pub fn keys(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to keys call");
+ }
+ let a0 = a[0].clone();
+ let hm: &HashMap<String,MalVal> = match *a0 {
+ Hash_Map(ref hm,_) => hm,
+ Nil => return Ok(_nil()),
+ _ => return err_str("contains? on non-hash map"),
+ };
+ //if hm.len() == 0 { return Ok(_nil()); }
+ let mut keys = vec![];
+ for k in hm.keys() {
+ keys.push(string(k.to_string()));
+ }
+ Ok(list(keys))
+}
+
+pub fn vals(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to values call");
+ }
+ let a0 = a[0].clone();
+ let hm: &HashMap<String,MalVal> = match *a0 {
+ Hash_Map(ref hm,_) => hm,
+ Nil => return Ok(_nil()),
+ _ => return err_str("contains? on non-hash map"),
+ };
+ //if hm.len() == 0 { return Ok(_nil()); }
+ let mut vals = vec![];
+ for k in hm.values() {
+ vals.push(k.clone());
+ }
+ Ok(list(vals))
+}
+
+
+// Sequence functions
+pub fn cons(a:Vec<MalVal>) -> MalRet {
+ match *a[1] {
+ List(ref v,_) | Vector(ref v,_) => {
+ let mut new_v = v.clone();
+ new_v.insert(0, a[0].clone());
+ Ok(list(new_v))
+ },
+ _ => err_str("Second arg to cons not a sequence"),
+ }
+}
+
+pub fn concat(a:Vec<MalVal>) -> MalRet {
+ let mut new_v:Vec<MalVal> = vec![];
+ for lst in a.iter() {
+ match **lst {
+ List(ref l,_) | Vector(ref l,_) => {
+ new_v.push_all(l.as_slice());
+ },
+ _ => return err_str("concat called with non-sequence"),
+ }
+ }
+ Ok(list(new_v))
+}
+
+pub fn nth(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 2 {
+ 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_str("nth called with non-sequence"),
+ };
+ let idx = match *a1 {
+ Int(i) => {
+ match i.to_uint() {
+ Some(ui) => ui,
+ None => return Ok(_nil()),
+ }
+ },
+ _ => return err_str("nth called with non-integer index"),
+ };
+ if idx >= seq.len() {
+ return err_str("nth: index out of range")
+ } else {
+ Ok(seq[idx].clone())
+ }
+}
+
+pub fn first(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ 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_str("first called with non-sequence"),
+ };
+ if seq.len() == 0 {
+ Ok(_nil())
+ } else {
+ Ok(seq[0].clone())
+ }
+}
+
+pub fn rest(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ 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_str("rest called with non-sequence"),
+ };
+ if seq.len() == 0 {
+ Ok(list(vec![]))
+ } else {
+ Ok(list(seq.slice(1,seq.len()).to_vec()))
+ }
+}
+
+pub fn empty_q(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to empty? call");
+ }
+ match *a[0].clone() {
+ List(ref v,_) | Vector(ref v,_) => {
+ match v.len() {
+ 0 => Ok(_true()),
+ _ => Ok(_false()),
+ }
+ },
+ _ => err_str("empty? called on non-sequence"),
+ }
+}
+
+pub fn count(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to count call");
+ }
+ match *a[0].clone() {
+ List(ref v,_) | Vector(ref v,_) => {
+ Ok(_int(v.len().to_int().unwrap()))
+ },
+ Nil => Ok(_int(0)),
+ _ => 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),
+ }
+ }
+ },
+ _ => return err_str("map call with non-sequence"),
+ }
+ Ok(list(results))
+}
+
+pub fn conj(a:Vec<MalVal>) -> MalRet {
+ if a.len() < 2 {
+ return err_str("Wrong arity to conj call");
+ }
+ let mut new_v:Vec<MalVal> = vec![];
+ match *a[0].clone() {
+ List(ref l,_) => {
+ new_v.push_all(l.as_slice());
+ for mv in a.iter().skip(1) {
+ new_v.insert(0,mv.clone());
+ }
+ Ok(list(new_v))
+ },
+ Vector(ref l,_) => {
+ new_v.push_all(l.as_slice());
+ for mv in a.iter().skip(1) {
+ new_v.push(mv.clone());
+ }
+ Ok(vector(new_v))
+ },
+ _ => return err_str("conj called with non-sequence"),
+ }
+}
+
+
+// Metadata functions
+fn with_meta(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 2 {
+ return err_str("Wrong arity to with-meta call");
+ }
+ let mv = a[0].clone();
+ let meta = a[1].clone();
+ match *mv {
+ List(ref v,_) => Ok(listm(v.clone(),meta)),
+ Vector(ref v,_) => Ok(vectorm(v.clone(),meta)),
+ Hash_Map(ref hm,_) => Ok(hash_mapm(hm.clone(),meta)),
+ MalFunc(ref mfd,_) => Ok(malfuncd(mfd.clone(),meta)),
+ Func(f,_) => Ok(funcm(f,meta)),
+ _ => err_str("type does not support metadata"),
+ }
+}
+
+fn meta(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to meta call");
+ }
+ match *a[0].clone() {
+ List(_,ref meta) |
+ Vector(_,ref meta) |
+ Hash_Map(_,ref meta) |
+ MalFunc(_,ref meta) |
+ Func(_,ref meta) => Ok(meta.clone()),
+ _ => err_str("type does not support metadata"),
+ }
+}
+
+// Atom functions
+fn deref(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to deref call");
+ }
+ match *a[0].clone() {
+ Atom(ref val) => {
+ let val_cell = val.borrow();
+ Ok(val_cell.clone())
+ },
+ _ => err_str("deref called on non-atom"),
+ }
+}
+
+fn reset_bang(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 2 {
+ return err_str("Wrong arity to map call");
+ }
+ let a1 = a[1].clone();
+ match *a[0].clone() {
+ Atom(ref val) => {
+ let mut val_cell = val.borrow_mut();
+ let atm_mv = val_cell.deref_mut();
+ *atm_mv = a1.clone();
+ Ok(a1)
+ },
+ _ => err_str("reset! called on non-atom"),
+ }
+}
+
+fn swap_bang(a:Vec<MalVal>) -> MalRet {
+ if a.len() < 2 {
+ return err_str("Wrong arity to swap_q call");
+ }
+ let f = a[1].clone();
+ match *a[0].clone() {
+ Atom(ref val) => {
+ let mut val_cell = val.borrow_mut();
+ let atm_mv = val_cell.deref_mut();
+ let mut args = a.slice(2,a.len()).to_vec();
+ args.insert(0, atm_mv.clone());
+ match f.apply(args) {
+ Ok(new_mv) => {
+ *atm_mv = new_mv.clone();
+ Ok(new_mv)
+ }
+ Err(e) => Err(e),
+ }
+ },
+ _ => err_str("swap! called on non-atom"),
+ }
+}
+
+
+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));
+ ns.insert("symbol?".to_string(), func(types::symbol_q));
+ ns.insert("keyword".to_string(), func(types::_keyword));
+ ns.insert("keyword?".to_string(), func(types::keyword_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));
+
+ ns.insert("<".to_string(), func(lt));
+ ns.insert("<=".to_string(), func(lte));
+ ns.insert(">".to_string(), func(gt));
+ ns.insert(">=".to_string(), func(gte));
+ ns.insert("+".to_string(), func(add));
+ ns.insert("-".to_string(), func(sub));
+ ns.insert("*".to_string(), func(mul));
+ ns.insert("/".to_string(), func(div));
+ ns.insert("time-ms".to_string(), func(time_ms));
+
+ 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("assoc".to_string(), func(assoc));
+ ns.insert("dissoc".to_string(), func(dissoc));
+ ns.insert("get".to_string(), func(get));
+ ns.insert("contains?".to_string(), func(contains_q));
+ ns.insert("keys".to_string(), func(keys));
+ ns.insert("vals".to_string(), func(vals));
+
+ 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));
+ ns.insert("nth".to_string(), func(nth));
+ 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));
+ ns.insert("conj".to_string(), func(conj));
+
+ ns.insert("with-meta".to_string(), func(with_meta));
+ ns.insert("meta".to_string(), func(meta));
+ ns.insert("atom".to_string(), func(types::atom));
+ ns.insert("atom?".to_string(), func(types::atom_q));
+ ns.insert("deref".to_string(), func(deref));
+ ns.insert("reset!".to_string(), func(reset_bang));
+ ns.insert("swap!".to_string(), func(swap_bang));
+
+ return ns;
+}
diff --git a/rust/src/env.rs b/rust/src/env.rs
new file mode 100644
index 0000000..e9af154
--- /dev/null
+++ b/rust/src/env.rs
@@ -0,0 +1,118 @@
+#![allow(dead_code)]
+
+use std::rc::Rc;
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::fmt;
+
+use types::{MalVal,MalRet,Sym,List,Vector,_nil,list,err_string};
+
+struct EnvType {
+ data: HashMap<String,MalVal>,
+ outer: Option<Env>,
+}
+
+pub type Env = Rc<RefCell<EnvType>>;
+
+pub fn env_new(outer: Option<Env>) -> Env {
+ Rc::new(RefCell::new(EnvType{data: HashMap::new(), outer: outer}))
+}
+
+pub fn env_bind(env: &Env,
+ mbinds: MalVal,
+ mexprs: MalVal) -> Result<Env,String> {
+ let mut variadic = false;
+ match *mbinds {
+ List(ref binds,_) | Vector(ref binds,_) => {
+ match *mexprs {
+ List(ref exprs,_) | Vector(ref exprs,_) => {
+ let mut it = binds.iter().enumerate();
+ for (i, b) in it {
+ match **b {
+ Sym(ref strn) => {
+ if *strn == "&".to_string() {
+ variadic = true;
+ break;
+ } else {
+ env_set(env, b.clone(), exprs[i].clone());
+ }
+ }
+ _ => return Err("non-symbol bind".to_string()),
+ }
+ }
+ if variadic {
+ let (i, sym) = it.next().unwrap();
+ match **sym {
+ Sym(_) => {
+ let rest = exprs.slice(i-1,exprs.len()).to_vec();
+ env_set(env, sym.clone(), list(rest));
+ }
+ _ => return Err("& bind to non-symbol".to_string()),
+ }
+ }
+ Ok(env.clone())
+ },
+ _ => Err("exprs must be a list".to_string()),
+ }
+ },
+ _ => Err("binds must be a list".to_string()),
+ }
+}
+
+pub fn env_find(env: Env, key: MalVal) -> Option<Env> {
+ match *key {
+ Sym(ref k) => {
+ if env.borrow().data.contains_key(k) {
+ Some(env)
+ } else {
+ match env.borrow().outer {
+ Some(ref e) => env_find(e.clone(), key.clone()),
+ None => None,
+ }
+ }
+ },
+ _ => None
+ }
+}
+
+pub fn env_root(env: &Env) -> Env {
+ match env.borrow().outer {
+ Some(ref ei) => env_root(ei),
+ None => env.clone(),
+ }
+}
+
+pub fn env_set(env: &Env, key: MalVal, val: MalVal) {
+ match *key {
+ Sym(ref k) => {
+ env.borrow_mut().data.insert(k.to_string(), val.clone());
+ },
+ _ => {},
+ }
+}
+
+pub fn env_get(env: Env, key: MalVal) -> MalRet {
+ match *key {
+ Sym(ref k) => {
+ match env_find(env, key.clone()) {
+ Some(e) => {
+ match e.borrow().data.find_copy(k) {
+ Some(v) => Ok(v),
+ None => Ok(_nil()),
+ }
+ },
+ None => err_string("'".to_string() + k.to_string() + "' not found".to_string()),
+ }
+ }
+ _ => err_string("env_get called with non-symbol key".to_string()),
+ }
+}
+
+impl fmt::Show for EnvType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.outer {
+ Some(ref o) => write!(f, "[{}/outer:{}]", self.data, o.borrow()),
+ _ => write!(f, "{}", self.data)
+ }
+ }
+}
diff --git a/rust/src/printer.rs b/rust/src/printer.rs
new file mode 100644
index 0000000..f46b66c
--- /dev/null
+++ b/rust/src/printer.rs
@@ -0,0 +1,45 @@
+use types::MalVal;
+
+pub fn escape_str(s: &str) -> String {
+ let mut escaped = String::new();
+ escaped.push('"');
+ for c in s.as_slice().chars() {
+ let _ = match c {
+ '"' => escaped.push_str("\\\""),
+ '\\' => escaped.push_str("\\\\"),
+ '\x08' => escaped.push_str("\\b"),
+ '\x0c' => escaped.push_str("\\f"),
+ '\n' => escaped.push_str("\\n"),
+ '\r' => escaped.push_str("\\r"),
+ '\t' => escaped.push_str("\\t"),
+ _ => escaped.push(c),
+ };
+ };
+
+ escaped.push('"');
+
+ escaped
+}
+
+pub fn unescape_str(s: &str) -> String {
+ let re1 = regex!(r#"\\""#);
+ let re2 = regex!(r#"\n"#);
+ re2.replace_all(re1.replace_all(s.as_slice(), "\"").as_slice(), "\n")
+}
+
+pub fn pr_list(lst: &Vec<MalVal>, pr: bool,
+ start: &str , end: &str, join: &str) -> String {
+ let mut first = true;
+ let mut res = String::new();
+ res.push_str(start);
+ for mv in lst.iter() {
+ if first {
+ first = false;
+ } else {
+ res.push_str(join);
+ }
+ res.push_str(mv.pr_str(pr).as_slice());
+ }
+ res.push_str(end);
+ res
+}
diff --git a/rust/src/reader.rs b/rust/src/reader.rs
new file mode 100644
index 0000000..d7b2b4c
--- /dev/null
+++ b/rust/src/reader.rs
@@ -0,0 +1,213 @@
+//#![feature(phase)]
+//#[phase(plugin)]
+//extern crate regex_macros;
+//extern crate regex;
+
+extern crate pcre;
+
+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;
+
+#[deriving(Show, Clone)]
+struct Reader {
+ tokens : Vec<String>,
+ position : uint,
+}
+
+impl Reader {
+ fn next(&mut self) -> Option<String> {
+ if self.position < self.tokens.len() {
+ self.position += 1;
+ Some(self.tokens[self.position-1].to_string())
+ } else {
+ None
+ }
+ }
+ fn peek(&self) -> Option<String> {
+ if self.position < self.tokens.len() {
+ Some(self.tokens[self.position].to_string())
+ } else {
+ None
+ }
+ }
+}
+
+fn tokenize(str :String) -> Vec<String> {
+ let mut results = vec![];
+
+ let re = match Pcre::compile(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)"###) {
+ Err(_) => { fail!("failed to compile regex") },
+ Ok(re) => re
+ };
+
+ let mut it = re.matches(str.as_slice());
+ loop {
+ let opt_m = it.next();
+ if opt_m.is_none() { break; }
+ let m = opt_m.unwrap();
+ if m.group(1) == "" { break; }
+ if m.group(1).starts_with(";") { continue; }
+
+ results.push((*m.group(1)).to_string());
+ }
+ results
+}
+
+fn read_atom(rdr : &mut Reader) -> MalRet {
+ let otoken = rdr.next();
+ //println!("read_atom: {}", otoken);
+ 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) {
+ let num : Option<int> = from_str(token);
+ Ok(_int(num.unwrap()))
+ } else if regex!(r#"^".*"$"#).is_match(token) {
+ let new_str = token.slice(1,token.len()-1);
+ Ok(string(unescape_str(new_str)))
+ } else if regex!(r#"^:"#).is_match(token) {
+ Ok(string("\u029e".to_string() + token.slice(1,token.len())))
+ } else if token == "nil" {
+ Ok(_nil())
+ } else if token == "true" {
+ Ok(_true())
+ } else if token == "false" {
+ Ok(_false())
+ } else {
+ Ok(symbol(token))
+ }
+}
+
+fn read_seq(rdr : &mut Reader, start: &str, end: &str) -> Result<Vec<MalVal>,String> {
+ let otoken = rdr.next();
+ if otoken.is_none() {
+ return Err("read_atom underflow".to_string());
+ }
+ let stoken = otoken.unwrap();
+ let token = stoken.as_slice();
+ if token != start {
+ return Err("expected '".to_string() + start.to_string() + "'".to_string());
+ }
+
+ let mut ast_vec : Vec<MalVal> = vec![];
+ loop {
+ let otoken = rdr.peek();
+ if otoken.is_none() {
+ return Err("expected '".to_string() + end.to_string() + "', got EOF".to_string());
+ }
+ let stoken = otoken.unwrap();
+ let token = stoken.as_slice();
+ if token == end { break; }
+
+ match read_form(rdr) {
+ Ok(mv) => ast_vec.push(mv),
+ Err(ErrString(es)) => return Err(es),
+ Err(ErrMalVal(_)) => return Err("read_seq exception".to_string()),
+ }
+ }
+ rdr.next();
+
+ Ok(ast_vec)
+}
+
+fn read_list(rdr : &mut Reader) -> MalRet {
+ match read_seq(rdr, "(", ")") {
+ Ok(seq) => Ok(list(seq)),
+ Err(es) => err_string(es),
+ }
+}
+
+fn read_vector(rdr : &mut Reader) -> MalRet {
+ match read_seq(rdr, "[", "]") {
+ Ok(seq) => Ok(vector(seq)),
+ Err(es) => err_string(es),
+ }
+}
+
+fn read_hash_map(rdr : &mut Reader) -> MalRet {
+ match read_seq(rdr, "{", "}") {
+ Ok(seq) => hash_mapv(seq),
+ Err(es) => err_string(es),
+ }
+}
+
+fn read_form(rdr : &mut Reader) -> MalRet {
+ let otoken = rdr.peek();
+ //println!("read_form: {}", otoken);
+ let stoken = otoken.unwrap();
+ let token = stoken.as_slice();
+ match token {
+ "'" => {
+ let _ = rdr.next();
+ match read_form(rdr) {
+ Ok(f) => Ok(list(vec![symbol("quote"), f])),
+ Err(e) => Err(e),
+ }
+ },
+ "`" => {
+ let _ = rdr.next();
+ match read_form(rdr) {
+ Ok(f) => Ok(list(vec![symbol("quasiquote"), f])),
+ Err(e) => Err(e),
+ }
+ },
+ "~" => {
+ let _ = rdr.next();
+ match read_form(rdr) {
+ Ok(f) => Ok(list(vec![symbol("unquote"), f])),
+ Err(e) => Err(e),
+ }
+ },
+ "~@" => {
+ let _ = rdr.next();
+ match read_form(rdr) {
+ Ok(f) => Ok(list(vec![symbol("splice-unquote"), f])),
+ Err(e) => Err(e),
+ }
+ },
+ "^" => {
+ let _ = rdr.next();
+ match read_form(rdr) {
+ Ok(meta) => {
+ match read_form(rdr) {
+ Ok(f) => Ok(list(vec![symbol("with-meta"), f, meta])),
+ Err(e) => Err(e),
+ }
+ },
+ Err(e) => Err(e),
+ }
+ },
+ "@" => {
+ let _ = rdr.next();
+ match read_form(rdr) {
+ Ok(f) => Ok(list(vec![symbol("deref"), f])),
+ Err(e) => Err(e),
+ }
+ },
+
+ ")" => err_str("unexected ')'"),
+ "(" => read_list(rdr),
+
+ "]" => err_str("unexected ']'"),
+ "[" => read_vector(rdr),
+
+ "}" => err_str("unexected '}'"),
+ "{" => read_hash_map(rdr),
+
+ _ => read_atom(rdr)
+ }
+}
+
+pub fn read_str(str :String) -> MalRet {
+ let tokens = tokenize(str);
+ if tokens.len() == 0 {
+ // any malval as the error slot means empty line
+ return err_val(_nil())
+ }
+ //println!("tokens: {}", tokens);
+ let rdr = &mut Reader{tokens: tokens, position: 0};
+ read_form(rdr)
+}
diff --git a/rust/src/readline.rs b/rust/src/readline.rs
new file mode 100644
index 0000000..17d1ed9
--- /dev/null
+++ b/rust/src/readline.rs
@@ -0,0 +1,76 @@
+// Based on: https://github.com/shaleh/rust-readline (MIT)
+extern crate libc;
+
+use std::c_str;
+
+use std::io::{File, Append, Write};
+use std::io::BufferedReader;
+
+mod ext_readline {
+ extern crate libc;
+ use self::libc::c_char;
+ #[link(name = "readline")]
+ extern {
+ pub fn add_history(line: *const c_char);
+ pub fn readline(p: *const c_char) -> *const c_char;
+ }
+}
+
+pub fn add_history(line: &str) {
+ unsafe {
+ ext_readline::add_history(line.to_c_str().as_ptr());
+ }
+}
+
+pub fn readline(prompt: &str) -> Option<String> {
+ let cprmt = prompt.to_c_str();
+ unsafe {
+ let ret = ext_readline::readline(cprmt.as_ptr());
+ if ret.is_null() { // user pressed Ctrl-D
+ None
+ }
+ else {
+ c_str::CString::new(ret, true).as_str().map(|ret| ret.to_string())
+ }
+ }
+}
+
+// --------------------------------------------
+
+static mut history_loaded : bool = false;
+static HISTORY_FILE : &'static str = "/home/joelm/.mal-history";
+
+fn load_history() {
+ unsafe {
+ if history_loaded { return; }
+ history_loaded = true;
+ }
+
+ let path = Path::new(HISTORY_FILE);
+ let mut file = BufferedReader::new(File::open(&path));
+ for line in file.lines() {
+ let rt: &[_] = &['\r', '\n'];
+ let line2 = line.unwrap();
+ let line3 = line2.as_slice().trim_right_chars(rt);
+ add_history(line3);
+ }
+}
+
+fn append_to_history(line: &str) {
+ let path = Path::new("/home/joelm/.mal-history");
+ let mut file = File::open_mode(&path, Append, Write);
+ let _ = file.write_line(line);
+}
+
+pub fn mal_readline (prompt: &str) -> Option<String> {
+ load_history();
+ let line = readline(prompt);
+ match line {
+ None => None,
+ _ => {
+ add_history(line.clone().unwrap().as_slice());
+ append_to_history(line.clone().unwrap().as_slice());
+ line
+ }
+ }
+}
diff --git a/rust/src/step0_repl.rs b/rust/src/step0_repl.rs
new file mode 100644
index 0000000..ac9cf24
--- /dev/null
+++ b/rust/src/step0_repl.rs
@@ -0,0 +1,25 @@
+use readline::mal_readline;
+mod readline;
+
+// read
+fn read(str: String) -> String {
+ str
+}
+
+// eval
+fn eval(ast: String) -> String {
+ ast
+}
+
+// print
+fn print(exp: String) -> String {
+ exp
+}
+
+fn main() {
+ loop {
+ let line = mal_readline("user> ");
+ match line { None => break, _ => () }
+ println!("{}", print(eval(read(line.unwrap()))));
+ }
+}
diff --git a/rust/src/step1_read_print.rs b/rust/src/step1_read_print.rs
new file mode 100644
index 0000000..3ce11e6
--- /dev/null
+++ b/rust/src/step1_read_print.rs
@@ -0,0 +1,52 @@
+// support precompiled regexes in reader.rs
+#![feature(phase)]
+#[phase(plugin)]
+extern crate regex_macros;
+extern crate regex;
+
+use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal};
+mod readline;
+mod types;
+mod env;
+mod reader;
+mod printer;
+
+// read
+fn read(str: String) -> MalRet {
+ reader::read_str(str)
+}
+
+// eval
+fn eval(ast: MalVal) -> MalRet {
+ Ok(ast)
+}
+
+// print
+fn print(exp: MalVal) -> String {
+ exp.pr_str(true)
+}
+
+fn rep(str: String) -> Result<String,MalError> {
+ match read(str) {
+ Err(e) => Err(e),
+ Ok(ast) => {
+ //println!("read: {}", ast);
+ match eval(ast) {
+ Err(e) => Err(e),
+ Ok(exp) => Ok(print(exp)),
+ }
+ }
+ }
+}
+
+fn main() {
+ loop {
+ let line = readline::mal_readline("user> ");
+ match line { None => break, _ => () }
+ match rep(line.unwrap()) {
+ Ok(str) => println!("{}", str),
+ Err(ErrMalVal(_)) => (), // Blank line
+ Err(ErrString(s)) => println!("Error: {}", s),
+ }
+ }
+}
diff --git a/rust/src/step2_eval.rs b/rust/src/step2_eval.rs
new file mode 100644
index 0000000..2cf7897
--- /dev/null
+++ b/rust/src/step2_eval.rs
@@ -0,0 +1,129 @@
+// 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,
+ Int,Sym,List,Vector,Hash_Map,
+ _nil,_int,list,vector,hash_map,func};
+mod readline;
+mod types;
+mod reader;
+mod printer;
+mod env; // because types uses env
+
+// read
+fn read(str: String) -> MalRet {
+ reader::read_str(str)
+}
+
+// eval
+fn eval_ast(ast: MalVal, env: &HashMap<String,MalVal>) -> MalRet {
+ match *ast {
+ Sym(ref sym) => {
+ match env.find_copy(sym) {
+ Some(mv) => Ok(mv),
+ None => Ok(_nil()),
+ }
+ },
+ List(ref a,_) | Vector(ref a,_) => {
+ let mut ast_vec : Vec<MalVal> = vec![];
+ for mv in a.iter() {
+ match eval(mv.clone(), env) {
+ 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) {
+ Ok(mv) => { new_hm.insert(key.to_string(), mv); },
+ Err(e) => return Err(e),
+ }
+ }
+ Ok(hash_map(new_hm))
+ },
+ _ => {
+ Ok(ast.clone())
+ }
+ }
+}
+
+fn eval(ast: MalVal, env: &HashMap<String,MalVal>) -> MalRet {
+ let ast2 = ast.clone();
+ match *ast2 {
+ List(_,_) => (), // continue
+ _ => return eval_ast(ast2, env),
+ }
+
+ // apply list
+ 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_str("Invalid apply"),
+ }
+ }
+ }
+}
+
+// print
+fn print(exp: MalVal) -> String {
+ exp.pr_str(true)
+}
+
+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);
+ match eval(ast, env) {
+ Err(e) => Err(e),
+ Ok(exp) => Ok(print(exp)),
+ }
+ }
+ }
+}
+
+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_str("second arg must be an int"),
+ },
+ _ => err_str("first arg must be an int"),
+ }
+}
+fn add(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i+j }, a) }
+fn sub(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i-j }, a) }
+fn mul(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i*j }, a) }
+fn div(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i/j }, a) }
+
+fn main() {
+ let mut repl_env : HashMap<String,MalVal> = HashMap::new();
+ repl_env.insert("+".to_string(), func(add));
+ repl_env.insert("-".to_string(), func(sub));
+ repl_env.insert("*".to_string(), func(mul));
+ repl_env.insert("/".to_string(), func(div));
+
+ loop {
+ let line = readline::mal_readline("user> ");
+ match line { None => break, _ => () }
+ match rep(line.unwrap().as_slice(), &repl_env) {
+ Ok(str) => println!("{}", str),
+ Err(ErrMalVal(_)) => (), // Blank line
+ Err(ErrString(s)) => println!("Error: {}", s),
+ }
+ }
+}
diff --git a/rust/src/step3_env.rs b/rust/src/step3_env.rs
new file mode 100644
index 0000000..b2d49cd
--- /dev/null
+++ b/rust/src/step3_env.rs
@@ -0,0 +1,204 @@
+// 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,
+ Int,Sym,List,Vector,Hash_Map,
+ symbol,_int,list,vector,hash_map,func};
+use env::{Env,env_new,env_set,env_get};
+mod readline;
+mod types;
+mod reader;
+mod printer;
+mod env;
+
+// 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(_) => {
+ env_get(env.clone(), ast)
+ },
+ 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(_) => {
+ env_set(&env.clone(), a1.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(_) => {
+ match eval(exp.clone(), let_env.clone()) {
+ Ok(r) => {
+ env_set(&let_env, b.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) => {
+ 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 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_str("second arg must be an int"),
+ },
+ _ => err_str("first arg must be an int"),
+ }
+}
+fn add(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i+j }, a) }
+fn sub(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i-j }, a) }
+fn mul(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i*j }, a) }
+fn div(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i/j }, a) }
+
+fn main() {
+ let repl_env = env_new(None);
+ env_set(&repl_env, symbol("+"), func(add));
+ env_set(&repl_env, symbol("-"), func(sub));
+ env_set(&repl_env, symbol("*"), func(mul));
+ env_set(&repl_env, symbol("/"), func(div));
+
+ 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),
+ }
+ }
+}
diff --git a/rust/src/step4_if_fn_do.rs b/rust/src/step4_if_fn_do.rs
new file mode 100644
index 0000000..92abf92
--- /dev/null
+++ b/rust/src/step4_if_fn_do.rs
@@ -0,0 +1,235 @@
+// 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,
+ symbol,_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(_) => {
+ env_get(env.clone(), ast)
+ },
+ 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(_) => {
+ env_set(&env.clone(), a1.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(_) => {
+ match eval(exp.clone(), let_env.clone()) {
+ Ok(r) => {
+ env_set(&let_env, b.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, _nil()));
+ },
+ _ => { // 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, symbol(k.as_slice()), 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),
+ }
+ }
+}
diff --git a/rust/src/step5_tco.rs b/rust/src/step5_tco.rs
new file mode 100644
index 0000000..9223cbf
--- /dev/null
+++ b/rust/src/step5_tco.rs
@@ -0,0 +1,257 @@
+// 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,
+ symbol,_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(_) => {
+ env_get(env.clone(), ast)
+ },
+ 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(_) => {
+ env_set(&env.clone(), a1.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(_) => {
+ match eval(exp.clone(), let_env.clone()) {
+ Ok(r) => {
+ env_set(&let_env, b.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, _nil()));
+ },
+ _ => { // 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, symbol(k.as_slice()), 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),
+ }
+ }
+}
diff --git a/rust/src/step6_file.rs b/rust/src/step6_file.rs
new file mode 100644
index 0000000..1e87116
--- /dev/null
+++ b/rust/src/step6_file.rs
@@ -0,0 +1,293 @@
+// 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,
+ symbol,_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(_) => {
+ env_get(env.clone(), ast)
+ },
+ 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(_) => {
+ env_set(&env.clone(), a1.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(_) => {
+ match eval(exp.clone(), let_env.clone()) {
+ Ok(r) => {
+ env_set(&let_env, b.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, _nil()));
+ },
+ "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, symbol(k.as_slice()), v);
+ }
+ // see eval() for definition of "eval"
+ env_set(&repl_env, symbol("*ARGV*".as_slice()), 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, symbol("*ARGV*".as_slice()), 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),
+ }
+ }
+}
diff --git a/rust/src/step7_quote.rs b/rust/src/step7_quote.rs
new file mode 100644
index 0000000..b113920
--- /dev/null
+++ b/rust/src/step7_quote.rs
@@ -0,0 +1,352 @@
+// 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,
+ symbol,_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 is_pair(x: MalVal) -> bool {
+ match *x {
+ List(ref lst,_) | Vector(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,_) | Vector(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,_) | Vector(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 eval_ast(ast: MalVal, env: Env) -> MalRet {
+ let ast2 = ast.clone();
+ match *ast2 {
+ //match *ast {
+ Sym(_) => {
+ env_get(env.clone(), ast)
+ },
+ 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
+ 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(_) => {
+ env_set(&env.clone(), a1.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(_) => {
+ match eval(exp.clone(), let_env.clone()) {
+ Ok(r) => {
+ env_set(&let_env, b.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;
+ },
+ }
+ }
+ },
+ "fn*" => {
+ let a1 = (*args)[1].clone();
+ let a2 = (*args)[2].clone();
+ return Ok(malfunc(eval, a2, env.clone(), a1, _nil()));
+ },
+ "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, symbol(k.as_slice()), v);
+ }
+ // see eval() for definition of "eval"
+ env_set(&repl_env, symbol("*ARGV*".as_slice()), 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, symbol("*ARGV*".as_slice()), 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),
+ }
+ }
+}
diff --git a/rust/src/step8_macros.rs b/rust/src/step8_macros.rs
new file mode 100644
index 0000000..7450de8
--- /dev/null
+++ b/rust/src/step8_macros.rs
@@ -0,0 +1,447 @@
+// 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,
+ symbol,_nil,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,_) | Vector(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,_) | Vector(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,_) | Vector(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,_) => {
+ match *lst[0] {
+ Sym(_) => {
+ if env_find(env.clone(), lst[0].clone()).is_some() {
+ match env_get(env, lst[0].clone()) {
+ 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(_) => {
+ match env_get(env.clone(), a0.clone()) {
+ 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(_) => {
+ env_get(env.clone(), ast)
+ },
+ 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(_) => {
+ env_set(&env.clone(), a1.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(_) => {
+ match eval(exp.clone(), let_env.clone()) {
+ Ok(r) => {
+ env_set(&let_env, b.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(_) => {
+ let mut new_mfd = mfd.clone();
+ new_mfd.is_macro = true;
+ let mf = malfuncd(new_mfd,_nil());
+ env_set(&env.clone(), a1.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())
+ },
+ "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, _nil()));
+ },
+ "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, symbol(k.as_slice()), v);
+ }
+ // see eval() for definition of "eval"
+ env_set(&repl_env, symbol("*ARGV*".as_slice()), 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());
+ let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env.clone());
+ let _ = rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", 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, symbol("*ARGV*".as_slice()), 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
+ 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),
+ }
+ }
+}
diff --git a/rust/src/step9_try.rs b/rust/src/step9_try.rs
new file mode 100644
index 0000000..0f6bd88
--- /dev/null
+++ b/rust/src/step9_try.rs
@@ -0,0 +1,477 @@
+// 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,
+ symbol,_nil,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,_) | Vector(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,_) | Vector(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,_) | Vector(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,_) => {
+ match *lst[0] {
+ Sym(_) => {
+ if env_find(env.clone(), lst[0].clone()).is_some() {
+ match env_get(env, lst[0].clone()) {
+ 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(_) => {
+ match env_get(env.clone(), a0.clone()) {
+ 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(_) => {
+ env_get(env.clone(), ast)
+ },
+ 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(_) => {
+ env_set(&env.clone(), a1.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(_) => {
+ match eval(exp.clone(), let_env.clone()) {
+ Ok(r) => {
+ env_set(&let_env, b.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(_) => {
+ let mut new_mfd = mfd.clone();
+ new_mfd.is_macro = true;
+ let mf = malfuncd(new_mfd,_nil());
+ env_set(&env.clone(), a1.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();
+ match *c1 {
+ Sym(_) => {},
+ _ => 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, c1.clone(), 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, _nil()));
+ },
+ "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, symbol(k.as_slice()), v);
+ }
+ // see eval() for definition of "eval"
+ env_set(&repl_env, symbol("*ARGV*".as_slice()), 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());
+ let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env.clone());
+ let _ = rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", 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, symbol("*ARGV*".as_slice()), 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
+ 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),
+ }
+ }
+}
diff --git a/rust/src/stepA_mal.rs b/rust/src/stepA_mal.rs
new file mode 100644
index 0000000..8e30867
--- /dev/null
+++ b/rust/src/stepA_mal.rs
@@ -0,0 +1,479 @@
+// 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,
+ symbol,_nil,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,_) | Vector(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,_) | Vector(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,_) | Vector(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,_) => {
+ match *lst[0] {
+ Sym(_) => {
+ if env_find(env.clone(), lst[0].clone()).is_some() {
+ match env_get(env, lst[0].clone()) {
+ 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(_) => {
+ match env_get(env.clone(), a0.clone()) {
+ 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(_) => {
+ env_get(env.clone(), ast)
+ },
+ 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(_) => {
+ env_set(&env.clone(), a1.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(_) => {
+ match eval(exp.clone(), let_env.clone()) {
+ Ok(r) => {
+ env_set(&let_env, b.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(_) => {
+ let mut new_mfd = mfd.clone();
+ new_mfd.is_macro = true;
+ let mf = malfuncd(new_mfd,_nil());
+ env_set(&env.clone(), a1.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();
+ match *c1 {
+ Sym(_) => {},
+ _ => 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, c1.clone(), 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, _nil()));
+ },
+ "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, symbol(k.as_slice()), v);
+ }
+ // see eval() for definition of "eval"
+ env_set(&repl_env, symbol("*ARGV*".as_slice()), 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());
+ let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env.clone());
+ let _ = rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", 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, symbol("*ARGV*".as_slice()), 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),
+ }
+ }
+}
diff --git a/rust/src/types.rs b/rust/src/types.rs
new file mode 100644
index 0000000..141c3db
--- /dev/null
+++ b/rust/src/types.rs
@@ -0,0 +1,405 @@
+#![allow(dead_code)]
+
+use std::rc::Rc;
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::fmt;
+use super::printer::{escape_str,pr_list};
+use super::env::{Env,env_new,env_bind};
+
+#[deriving(Clone)]
+#[allow(non_camel_case_types)]
+pub enum MalType {
+ Nil,
+ True,
+ False,
+ Int(int),
+ Strn(String),
+ Sym(String),
+ List(Vec<MalVal>, MalVal),
+ Vector(Vec<MalVal>, MalVal),
+ Hash_Map(HashMap<String, MalVal>, MalVal),
+ Func(fn(Vec<MalVal>) -> MalRet, MalVal),
+ //Func(fn(&[MalVal]) -> MalRet),
+ //Func(|Vec<MalVal>|:'a -> MalRet),
+ MalFunc(MalFuncData, MalVal),
+ Atom(RefCell<MalVal>),
+}
+
+pub type MalVal = Rc<MalType>;
+
+#[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 {
+ pub eval: fn(MalVal, Env) -> MalRet,
+ pub exp: MalVal,
+ pub env: Env,
+ pub params: MalVal,
+ pub is_macro: bool,
+}
+
+impl MalType {
+ pub fn pr_str(&self, print_readably: bool) -> String {
+ let _r = print_readably;
+ let mut res = String::new();
+ match *self {
+ Nil => res.push_str("nil"),
+ True => res.push_str("true"),
+ False => res.push_str("false"),
+ Int(v) => res.push_str(v.to_string().as_slice()),
+ Sym(ref v) => res.push_str((*v).as_slice()),
+ Strn(ref v) => {
+ if v.as_slice().starts_with("\u029e") {
+ res.push_str(":");
+ res.push_str(v.as_slice().slice(2,v.len()))
+ } else if print_readably {
+ res.push_str(escape_str((*v).as_slice()).as_slice())
+ } else {
+ res.push_str(v.as_slice())
+ }
+ },
+ List(ref v,_) => {
+ res = pr_list(v, _r, "(", ")", " ")
+ },
+ Vector(ref v,_) => {
+ res = pr_list(v, _r, "[", "]", " ")
+ },
+ Hash_Map(ref v,_) => {
+ let mut first = true;
+ res.push_str("{");
+ for (key, value) in v.iter() {
+ if first { first = false; } else { res.push_str(" "); }
+ if key.as_slice().starts_with("\u029e") {
+ res.push_str(":");
+ res.push_str(key.as_slice().slice(2,key.len()))
+ } else if print_readably {
+ res.push_str(escape_str(key.as_slice()).as_slice())
+ } else {
+ res.push_str(key.as_slice())
+ }
+ res.push_str(" ");
+ res.push_str(value.pr_str(_r).as_slice());
+ }
+ res.push_str("}")
+ },
+ // TODO: better native function representation
+ Func(_,_) => {
+ res.push_str(format!("#<function ...>").as_slice())
+ },
+ MalFunc(ref mf,_) => {
+ res.push_str(format!("(fn* {} {})", mf.params, mf.exp).as_slice())
+ },
+ Atom(ref v) => {
+ res = format!("(atom {})", v.borrow());
+ },
+ };
+ res
+ }
+
+ pub fn apply(&self, args:Vec<MalVal>) -> MalRet {
+ match *self {
+ Func(f,_) => f(args),
+ MalFunc(ref mf,_) => {
+ let mfc = mf.clone();
+ let alst = list(args);
+ 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_string(e),
+ }
+ },
+ _ => err_str("attempt to call non-function"),
+ }
+
+ }
+}
+
+impl PartialEq for MalType {
+ fn eq(&self, other: &MalType) -> bool {
+ match (self, other) {
+ (&Nil, &Nil) |
+ (&True, &True) |
+ (&False, &False) => true,
+ (&Int(ref a), &Int(ref b)) => a == b,
+ (&Strn(ref a), &Strn(ref b)) => a == b,
+ (&Sym(ref a), &Sym(ref b)) => a == b,
+ (&List(ref a,_), &List(ref b,_)) |
+ (&Vector(ref a,_), &Vector(ref b,_)) |
+ (&List(ref a,_), &Vector(ref b,_)) |
+ (&Vector(ref a,_), &List(ref b,_)) => a == b,
+ (&Hash_Map(ref a,_), &Hash_Map(ref b,_)) => a == b,
+ // TODO: fix this
+ (&Func(_,_), &Func(_,_)) => false,
+ (&MalFunc(_,_), &MalFunc(_,_)) => false,
+ _ => return false,
+ }
+ }
+}
+
+impl fmt::Show for MalType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.pr_str(true))
+ }
+}
+
+
+// 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(a: Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to symbol call");
+ }
+ match *a[0].clone() {
+ Strn(ref s) => {
+ Ok(Rc::new(Sym(s.to_string())))
+ },
+ _ => return err_str("symbol called on non-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()),
+ }
+}
+
+// Keywords
+pub fn _keyword(a: Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to keyword call");
+ }
+ match *a[0].clone() {
+ Strn(ref s) => {
+ Ok(Rc::new(Strn("\u029e".to_string() + s.to_string())))
+ },
+ _ => return err_str("keyword called on non-string"),
+ }
+}
+pub fn keyword_q(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to keyword? call");
+ }
+ match *a[0].clone() {
+ Strn(ref s) => {
+ if s.as_slice().starts_with("\u029e") {
+ Ok(_true())
+ } else {
+ Ok(_false())
+ }
+ },
+ _ => 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,_nil())) }
+pub fn listm(seq: Vec<MalVal>, meta: MalVal) -> MalVal {
+ Rc::new(List(seq,meta))
+}
+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,_nil())) }
+pub fn vectorm(seq: Vec<MalVal>, meta: MalVal) -> MalVal {
+ Rc::new(Vector(seq,meta))
+}
+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,_nil()))
+}
+pub fn hash_mapm(hm: HashMap<String,MalVal>, meta: MalVal) -> MalVal {
+ Rc::new(Hash_Map(hm,meta))
+}
+pub fn _assoc(hm: &HashMap<String,MalVal>, a:Vec<MalVal>) -> MalRet {
+ if a.len() % 2 == 1 {
+ return err_str("odd number of hash-map keys/values");
+ }
+ let mut new_hm = hm.clone();
+ let mut it = a.iter();
+ loop {
+ let k = match it.next() {
+ Some(mv) => match *mv.clone() {
+ Strn(ref s) => s.to_string(),
+ _ => return err_str("key is not a string in hash-map call"),
+ },
+ None => break,
+ };
+ let v = it.next().unwrap();
+ new_hm.insert(k, v.clone());
+ }
+ Ok(Rc::new(Hash_Map(new_hm,_nil())))
+}
+pub fn _dissoc(hm: &HashMap<String,MalVal>, a:Vec<MalVal>) -> MalRet {
+ let mut new_hm = hm.clone();
+ let mut it = a.iter();
+ loop {
+ let k = match it.next() {
+ Some(mv) => match *mv.clone() {
+ Strn(ref s) => s.to_string(),
+ _ => return err_str("key is not a string in hash-map call"),
+ },
+ None => break,
+ };
+ new_hm.remove(&k);
+ }
+ Ok(Rc::new(Hash_Map(new_hm,_nil())))
+}
+pub fn hash_mapv(seq: Vec<MalVal>) -> MalRet {
+ let new_hm: HashMap<String,MalVal> = HashMap::new();
+ _assoc(&new_hm, seq)
+}
+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()),
+ }
+}
+
+// Functions
+pub fn func(f: fn(Vec<MalVal>) -> MalRet) -> MalVal {
+ Rc::new(Func(f, _nil()))
+}
+pub fn funcm(f: fn(Vec<MalVal>) -> MalRet, meta: MalVal) -> MalVal {
+ Rc::new(Func(f, meta))
+}
+pub fn malfunc(eval: fn(MalVal, Env) -> MalRet,
+ exp: MalVal,
+ env: Env,
+ params: MalVal,
+ meta: MalVal) -> MalVal {
+ Rc::new(MalFunc(MalFuncData{eval: eval,
+ exp: exp,
+ env: env,
+ params: params,
+ is_macro: false},meta))
+}
+pub fn malfuncd(mfd: MalFuncData, meta: MalVal) -> MalVal {
+ Rc::new(MalFunc(mfd,meta))
+}
+
+
+// Atoms
+pub fn atom_q(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to atom? call");
+ }
+ match *a[0].clone() {
+ Atom(_) => Ok(_true()),
+ _ => Ok(_false()),
+ }
+}
+pub fn atom(a:Vec<MalVal>) -> MalRet {
+ if a.len() != 1 {
+ return err_str("Wrong arity to atom call");
+ }
+ Ok(Rc::new(Atom(RefCell::new(a[0].clone()))))
+}
+
+
+// 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()),
+ }
+}