diff options
| author | Joel Martin <github@martintribe.org> | 2014-10-25 14:02:42 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-06 21:58:57 -0600 |
| commit | 0a4d62f2f8362290dfc956ba5c948794a77a6e4d (patch) | |
| tree | edf2cd63393da7c4f27068e236d13a3a4e32d530 /rust/src | |
| parent | 8f5b0f1040de849da3fb5ade645308e7bbe7f025 (diff) | |
| download | mal-0a4d62f2f8362290dfc956ba5c948794a77a6e4d.tar.gz mal-0a4d62f2f8362290dfc956ba5c948794a77a6e4d.zip | |
rust: add step4_if_fn_do
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/core.rs | 147 | ||||
| -rw-r--r-- | rust/src/env.rs | 58 | ||||
| -rw-r--r-- | rust/src/printer.rs | 19 | ||||
| -rw-r--r-- | rust/src/step1_read_print.rs | 1 | ||||
| -rw-r--r-- | rust/src/step2_eval.rs | 1 | ||||
| -rw-r--r-- | rust/src/step3_env.rs | 3 | ||||
| -rw-r--r-- | rust/src/step4_if_fn_do.rs | 236 | ||||
| -rw-r--r-- | rust/src/types.rs | 81 |
8 files changed, 512 insertions, 34 deletions
diff --git a/rust/src/core.rs b/rust/src/core.rs new file mode 100644 index 0000000..9d70450 --- /dev/null +++ b/rust/src/core.rs @@ -0,0 +1,147 @@ +#![allow(dead_code)] + +use std::rc::Rc; +use std::collections::HashMap; + +use types::{MalVal,MalRet,Func,True,False,Int,Strn,List,Nil}; +use printer; + +// General functions + +fn equal_q(a:Vec<MalVal>) -> MalRet { + if a.len() != 2 { + return Err("Wrong arity to equal? call".to_string()); + } + match a[0] == a[1] { + true => Ok(Rc::new(True)), + false => Ok(Rc::new(False)), + } +} + +// String routines +fn pr_str(a:Vec<MalVal>) -> MalRet { + Ok(Rc::new(Strn(printer::pr_list(&a, true, "", "", " ")))) +} + +fn str(a:Vec<MalVal>) -> MalRet { + Ok(Rc::new(Strn(printer::pr_list(&a, false, "", "", "")))) +} + +fn prn(a:Vec<MalVal>) -> MalRet { + println!("{}", printer::pr_list(&a, true, "", "", " ")) + Ok(Rc::new(Nil)) +} + +fn println(a:Vec<MalVal>) -> MalRet { + println!("{}", printer::pr_list(&a, false, "", "", " ")) + Ok(Rc::new(Nil)) +} + +// 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(Rc::new(Int(f(a0,a1)))), + _ => Err("second arg must be an int".to_string()), + }, + _ => Err("first arg must be an int".to_string()), + } +} + +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(Rc::new(True)), + false => Ok(Rc::new(False)), + } + //Ok(Rc::new(Int(f(a0,a1)))), + }, + _ => Err("second arg must be an int".to_string()), + }, + _ => Err("first arg must be an int".to_string()), + } +} + +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) } + + +// Sequence functions +pub fn list(a:Vec<MalVal>) -> MalRet { + Ok(Rc::new(List(a))) +} + +pub fn list_q(a:Vec<MalVal>) -> MalRet { + if a.len() != 1 { + return Err("Wrong arity to list? call".to_string()); + } + match *a[0].clone() { + List(_) => Ok(Rc::new(True)), + _ => Ok(Rc::new(False)), + } +} + +pub fn count(a:Vec<MalVal>) -> MalRet { + if a.len() != 1 { + return Err("Wrong arity to count call".to_string()); + } + match *a[0].clone() { + List(ref lst) => { + Ok(Rc::new(Int(lst.len().to_int().unwrap()))) + }, + _ => Err("count called on non-sequence".to_string()), + } +} + +pub fn empty_q(a:Vec<MalVal>) -> MalRet { + if a.len() != 1 { + return Err("Wrong arity to empty? call".to_string()); + } + match *a[0].clone() { + List(ref lst) => { + match lst.len() { + 0 => Ok(Rc::new(True)), + _ => Ok(Rc::new(False)), + } + }, + _ => Err("empty? called on non-sequence".to_string()), + } +} + + + +pub fn ns() -> HashMap<String,MalVal> { + let mut ns: HashMap<String,MalVal> = HashMap::new();; + + ns.insert("=".to_string(), Rc::new(Func(equal_q))); + + ns.insert("pr-str".to_string(), Rc::new(Func(pr_str))); + ns.insert("str".to_string(), Rc::new(Func(str))); + ns.insert("prn".to_string(), Rc::new(Func(prn))); + ns.insert("println".to_string(), Rc::new(Func(println))); + + ns.insert("<".to_string(), Rc::new(Func(lt))); + ns.insert("<=".to_string(), Rc::new(Func(lte))); + ns.insert(">".to_string(), Rc::new(Func(gt))); + ns.insert(">=".to_string(), Rc::new(Func(gte))); + ns.insert("+".to_string(), Rc::new(Func(add))); + ns.insert("-".to_string(), Rc::new(Func(sub))); + ns.insert("*".to_string(), Rc::new(Func(mul))); + ns.insert("/".to_string(), Rc::new(Func(div))); + + ns.insert("list".to_string(), Rc::new(Func(list))); + ns.insert("list?".to_string(), Rc::new(Func(list_q))); + ns.insert("empty?".to_string(), Rc::new(Func(empty_q))); + ns.insert("count".to_string(), Rc::new(Func(count))); + + return ns; +} diff --git a/rust/src/env.rs b/rust/src/env.rs index 9c98f23..1471d20 100644 --- a/rust/src/env.rs +++ b/rust/src/env.rs @@ -1,9 +1,11 @@ use std::rc::Rc; use std::cell::RefCell; use std::collections::HashMap; +use std::fmt; -use types::{MalVal,MalRet,Nil}; +use types::{MalVal,MalRet,Nil,Sym,List}; +#[allow(dead_code)] struct EnvType { data: HashMap<String,MalVal>, outer: Option<Env>, @@ -11,10 +13,54 @@ struct EnvType { pub type Env = Rc<RefCell<EnvType>>; +#[allow(dead_code)] pub fn env_new(outer: Option<Env>) -> Env { Rc::new(RefCell::new(EnvType{data: HashMap::new(), outer: outer})) } +#[allow(dead_code)] +pub fn env_bind(env: &Env, + mbinds: MalVal, + mexprs: MalVal) -> Result<Env,String> { + let mut variadic = false; + match *mbinds { + List(ref binds) => { + match *mexprs { + List(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, strn.clone(), exprs[i].clone()); + } + } + _ => return Err("non-symbol bind".to_string()), + } + } + if variadic { + let (i, sym) = it.next().unwrap(); + match **sym { + Sym(ref s) => { + let rest = exprs.slice(i-1,exprs.len()).to_vec(); + env_set(env, s.clone(), Rc::new(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()), + } +} + +#[allow(dead_code)] pub fn env_find(env: Env, key: String) -> Option<Env> { if env.borrow().data.contains_key(&key) { Some(env) @@ -26,10 +72,12 @@ pub fn env_find(env: Env, key: String) -> Option<Env> { } } +#[allow(dead_code)] pub fn env_set(env: &Env, key: String, val: MalVal) { env.borrow_mut().data.insert(key, val.clone()); } +#[allow(dead_code)] pub fn env_get(env: Env, key: String) -> MalRet { match env_find(env, key.clone()) { Some(e) => { @@ -42,3 +90,11 @@ pub fn env_get(env: Env, key: String) -> MalRet { } } +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 index 59821ec..f46b66c 100644 --- a/rust/src/printer.rs +++ b/rust/src/printer.rs @@ -1,3 +1,5 @@ +use types::MalVal; + pub fn escape_str(s: &str) -> String { let mut escaped = String::new(); escaped.push('"'); @@ -24,3 +26,20 @@ pub fn unescape_str(s: &str) -> String { 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/step1_read_print.rs b/rust/src/step1_read_print.rs index bc4827d..25ca9b5 100644 --- a/rust/src/step1_read_print.rs +++ b/rust/src/step1_read_print.rs @@ -7,6 +7,7 @@ extern crate regex; use types::{MalVal,MalRet}; mod readline; mod types; +mod env; mod reader; mod printer; diff --git a/rust/src/step2_eval.rs b/rust/src/step2_eval.rs index 8d28a0f..11c1884 100644 --- a/rust/src/step2_eval.rs +++ b/rust/src/step2_eval.rs @@ -10,6 +10,7 @@ use std::collections::HashMap; use types::{MalVal,MalRet,Nil,Int,Sym,List,Func}; mod readline; mod types; +mod env; mod reader; mod printer; diff --git a/rust/src/step3_env.rs b/rust/src/step3_env.rs index c827cb5..e0957b9 100644 --- a/rust/src/step3_env.rs +++ b/rust/src/step3_env.rs @@ -42,9 +42,6 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { Ok(ast) } } - /* - Ok(ast) - */ } fn eval(ast: MalVal, env: Env) -> MalRet { diff --git a/rust/src/step4_if_fn_do.rs b/rust/src/step4_if_fn_do.rs new file mode 100644 index 0000000..c21ddce --- /dev/null +++ b/rust/src/step4_if_fn_do.rs @@ -0,0 +1,236 @@ +// support precompiled regexes in reader.rs +#![feature(phase)] +#[phase(plugin)] +extern crate regex_macros; +extern crate regex; + +use std::rc::Rc; + +use types::{MalVal,MalRet,MalFunc,MalFuncData, + Nil,False,Sym,List,Vector,Func}; +use env::{Env,env_new,env_bind,env_set,env_get}; +mod readline; +mod types; +mod reader; +mod printer; +mod env; +mod core; + +// read +fn read(str: String) -> MalRet { + reader::read_str(str) +} + +// eval +fn eval_ast(ast: MalVal, env: Env) -> MalRet { + let ast2 = ast.clone(); + match *ast2 { + //match *ast { + Sym(ref sym) => { + env_get(env.clone(), sym.clone()) + }, + List(ref a) => { + 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(Rc::new(List(ast_vec))) + }, + _ => { + Ok(ast) + } + } +} + +fn eval(ast: MalVal, env: Env) -> MalRet { + //println!("eval: {}, {}", ast, env.borrow()); + //println!("eval: {}", ast); + let ast2 = ast.clone(); + match *ast2 { + List(_) => (), // continue + _ => return eval_ast(ast2, env), + } + + // apply list + match *ast2 { + List(ref args) => { + if args.len() == 0 { + return Ok(ast); + } + let ref a0 = *args[0]; + match *a0 { + Sym(ref a0sym) => { + match a0sym.as_slice() { + "def!" => { + let a1 = (*args)[1].clone(); + let a2 = (*args)[2].clone(); + let res = eval(a2, env.clone()); + match res { + Ok(r) => { + match *a1 { + Sym(ref s) => { + env_set(&env.clone(), s.clone(), r.clone()); + return Ok(r); + }, + _ => { + return Err("def! of non-symbol".to_string()) + } + } + }, + Err(e) => return Err(e), + } + }, + "let*" => { + let let_env = env_new(Some(env.clone())); + let a1 = (*args)[1].clone(); + let a2 = (*args)[2].clone(); + match *a1 { + List(ref binds) | Vector(ref binds) => { + let mut it = binds.iter(); + while it.len() >= 2 { + let b = it.next().unwrap(); + let exp = it.next().unwrap(); + match **b { + Sym(ref bstr) => { + match eval(exp.clone(), let_env.clone()) { + Ok(r) => { + env_set(&let_env, bstr.clone(), r); + }, + Err(e) => { + return Err(e); + }, + } + }, + _ => { + return Err("let* with non-symbol binding".to_string()); + }, + } + } + }, + _ => return Err("let* with non-list bindings".to_string()), + } + return eval(a2, let_env.clone()); + }, + "do" => { + let el = Rc::new(List(args.slice(1,args.len()).to_vec())); + match eval_ast(el, env.clone()) { + Err(e) => return Err(e), + Ok(el) => { + match *el { + List(ref lst) => { + let ref last = lst[lst.len()-1]; + return Ok(last.clone()); + } + _ => (), + } + }, + } + }, + "if" => { + let a1 = (*args)[1].clone(); + let cond = eval(a1, env.clone()); + if cond.is_err() { return cond; } + match *cond.unwrap() { + False | Nil => { + if args.len() >= 4 { + let a3 = (*args)[3].clone(); + return eval(a3, env.clone()); + } else { + return Ok(Rc::new(Nil)); + } + }, + _ => { + let a2 = (*args)[2].clone(); + return eval(a2, env.clone()); + }, + } + }, + "fn*" => { + let a1 = (*args)[1].clone(); + let a2 = (*args)[2].clone(); + return Ok(Rc::new(MalFunc(MalFuncData{ + exp: a2, + env: env.clone(), + params: a1}))); + }, + _ => () + } + } + _ => (), + } + // function call + match eval_ast(ast, env.clone()) { + Err(e) => Err(e), + Ok(el) => { + match *el { + List(ref args) => { + // TODO: make this work + //match args.as_slice() { + // [&Func(f), rest..] => { + // (*f)(rest.to_vec()) + // }, + // _ => Err("attempt to call non-function".to_string()), + //} + let args2 = args.clone(); + match *args2[0] { + Func(f) => f(args.slice(1,args.len()).to_vec()), + MalFunc(ref mf) => { + let mfc = mf.clone(); + let alst = List(args.slice(1,args.len()).to_vec()); + let new_env = env_new(Some(mfc.env.clone())); + match env_bind(&new_env, mfc.params, + Rc::new(alst)) { + Ok(_) => return eval(mfc.exp, new_env), + Err(e) => return Err(e), + } + }, + _ => Err("attempt to call non-function".to_string()), + } + } + _ => Err("Invalid apply".to_string()), + } + } + } + } + _ => Err("Expected list".to_string()), + } +} + +// print +fn print(exp: MalVal) -> String { + exp.pr_str(true) +} + +fn rep(str: String, env: Env) -> Result<String,String> { + match read(str) { + Err(e) => Err(e), + Ok(ast) => { + //println!("read: {}", ast); + match eval(ast, env) { + Err(e) => Err(e), + Ok(exp) => Ok(print(exp)), + } + } + } +} + +fn main() { + let repl_env = env_new(None); + for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); } + + let _ = rep("(def! not (fn* (a) (if a false true)))".to_string(), + repl_env.clone()); + + loop { + let line = readline::mal_readline("user> "); + match line { None => break, _ => () } + match rep(line.unwrap(), repl_env.clone()) { + Ok(str) => println!("{}", str), + Err(str) => println!("Error: {}", str), + } + } +} diff --git a/rust/src/types.rs b/rust/src/types.rs index 75aecbc..8bd658f 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1,7 +1,8 @@ use std::rc::Rc; use std::collections; use std::fmt; -use super::printer::escape_str; +use super::printer::{escape_str,pr_list}; +use super::env::Env; #[deriving(Clone)] pub enum MalType { @@ -17,12 +18,20 @@ pub enum MalType { Func(fn(Vec<MalVal>) -> MalRet), //Func(fn(&[MalVal]) -> MalRet), //Func(|Vec<MalVal>|:'a -> MalRet), + MalFunc(MalFuncData), } pub type MalVal = Rc<MalType>; pub type MalRet = Result<MalVal,String>; +#[deriving(Clone)] +pub struct MalFuncData { + pub exp: MalVal, + pub env: Env, + pub params: MalVal, +} + impl MalType { pub fn pr_str(&self, print_readably: bool) -> String { @@ -40,49 +49,61 @@ impl MalType { } else { res.push_str(v.as_slice()) } - } + }, List(ref v) => { + res = pr_list(v, _r, "(", ")", " ") + }, + Vector(ref v) => { + res = pr_list(v, _r, "[", "]", " ") + }, + HashMap(ref v) => { let mut first = true; - res.push_str("("); - for item in v.iter() { + res.push_str("{"); + for (key, value) in v.iter() { if first { first = false; } else { res.push_str(" "); } - res.push_str(item.pr_str(_r).as_slice()); + res.push_str(key.as_slice()); + res.push_str(" "); + res.push_str(value.pr_str(_r).as_slice()); } - res.push_str(")") - } - // TODO: better function representation + res.push_str("}") + }, + // TODO: better native function representation //Func(ref v) => { Func(_) => { res.push_str(format!("#<function ...>").as_slice()) - } + }, + MalFunc(ref mf) => { + res.push_str(format!("(fn* {} {})", mf.params, mf.exp).as_slice()) + }, /* - Vector(ref v) => { - let mut first = true; - write!(f, "["); - for item in v.iter() { - if first { first = false; } else { write!(f, " ") } - item.fmt(f); - } - write!(f, "]"); - } - Hash_Map(ref v) => { - let mut first = true; - write!(f, "{}", "{"); - for (key, value) in v.iter() { - if first { first = false; } else { write!(f, " ") } - write!(f, "\"{}\"", *key); - write!(f, " "); - value.fmt(f); - } - write!(f, "{}", "}"); - } // Atom(ref v) => v.fmt(f), */ - _ => { res.push_str("#<unknown type>") } + //_ => { res.push_str("#<unknown type>") }, }; res } + +} + +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)) => a == b, + (&HashMap(ref a), &HashMap(ref b)) => a == b, + // TODO: fix this + (&Func(_), &Func(_)) => false, + (&MalFunc(_), &MalFunc(_)) => false, + _ => return false, + } + } } impl fmt::Show for MalType { |
