diff options
| author | Joel Martin <github@martintribe.org> | 2015-03-02 21:33:10 -0600 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-03-02 21:33:10 -0600 |
| commit | 835fb7d8b06e2b44792a97ac89994658bf6d00af (patch) | |
| tree | 578f67726ab9e3ce5fcbc50220e9761a66c5ddf1 /rust | |
| parent | 6b72e6078a7d505ecf9d711eb4a16fc4dfac36b6 (diff) | |
| parent | 8a98ef9a3f3a6b6d05d02dc305a0c886c907e0f3 (diff) | |
| download | mal-835fb7d8b06e2b44792a97ac89994658bf6d00af.tar.gz mal-835fb7d8b06e2b44792a97ac89994658bf6d00af.zip | |
Merge branch 'master' into gh-pages
Conflicts:
.gitignore
Diffstat (limited to 'rust')
| -rw-r--r-- | rust/Cargo.toml | 39 | ||||
| -rw-r--r-- | rust/Makefile | 36 | ||||
| -rw-r--r-- | rust/src/core.rs | 561 | ||||
| -rw-r--r-- | rust/src/env.rs | 118 | ||||
| -rw-r--r-- | rust/src/printer.rs | 45 | ||||
| -rw-r--r-- | rust/src/reader.rs | 213 | ||||
| -rw-r--r-- | rust/src/readline.rs | 76 | ||||
| -rw-r--r-- | rust/src/step0_repl.rs | 25 | ||||
| -rw-r--r-- | rust/src/step1_read_print.rs | 52 | ||||
| -rw-r--r-- | rust/src/step2_eval.rs | 129 | ||||
| -rw-r--r-- | rust/src/step3_env.rs | 204 | ||||
| -rw-r--r-- | rust/src/step4_if_fn_do.rs | 235 | ||||
| -rw-r--r-- | rust/src/step5_tco.rs | 257 | ||||
| -rw-r--r-- | rust/src/step6_file.rs | 293 | ||||
| -rw-r--r-- | rust/src/step7_quote.rs | 352 | ||||
| -rw-r--r-- | rust/src/step8_macros.rs | 447 | ||||
| -rw-r--r-- | rust/src/step9_try.rs | 477 | ||||
| -rw-r--r-- | rust/src/stepA_mal.rs | 479 | ||||
| -rw-r--r-- | rust/src/types.rs | 405 |
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()), + } +} |
