diff options
| author | Joel Martin <github@martintribe.org> | 2014-10-25 16:28:32 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-06 21:58:58 -0600 |
| commit | 85bec8a08b76b5b4797ddd9976a138b22974e1c4 (patch) | |
| tree | 7316f0b7442fbe45a38c39fd32ff562efb95904d /rust/src | |
| parent | 2ee5367b95e98252e1446a1cc1a782c516f21e72 (diff) | |
| download | mal-85bec8a08b76b5b4797ddd9976a138b22974e1c4.tar.gz mal-85bec8a08b76b5b4797ddd9976a138b22974e1c4.zip | |
rust: add step6_file
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/core.rs | 24 | ||||
| -rw-r--r-- | rust/src/env.rs | 10 | ||||
| -rw-r--r-- | rust/src/step6_file.rs | 287 |
3 files changed, 320 insertions, 1 deletions
diff --git a/rust/src/core.rs b/rust/src/core.rs index 9d70450..1779b80 100644 --- a/rust/src/core.rs +++ b/rust/src/core.rs @@ -2,8 +2,10 @@ use std::rc::Rc; use std::collections::HashMap; +use std::io::File; use types::{MalVal,MalRet,Func,True,False,Int,Strn,List,Nil}; +use reader; use printer; // General functions @@ -37,6 +39,26 @@ fn println(a:Vec<MalVal>) -> MalRet { Ok(Rc::new(Nil)) } +fn read_string(a:Vec<MalVal>) -> MalRet { + match *a[0] { + Strn(ref a0) => reader::read_str(a0.to_string()), + _ => Err("read_string called with non-string".to_string()), + } +} + +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(Rc::new(Strn(s))), + Err(e) => Err(e.to_string()), + } + }, + _ => Err("slurp called with non-string".to_string()), + } +} + + // Numeric functions fn int_op(f: |i:int,j:int|-> int, a:Vec<MalVal>) -> MalRet { match *a[0] { @@ -128,6 +150,8 @@ pub fn ns() -> HashMap<String,MalVal> { 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("read-string".to_string(), Rc::new(Func(read_string))); + ns.insert("slurp".to_string(), Rc::new(Func(slurp))); ns.insert("<".to_string(), Rc::new(Func(lt))); ns.insert("<=".to_string(), Rc::new(Func(lte))); diff --git a/rust/src/env.rs b/rust/src/env.rs index 1471d20..3c98015 100644 --- a/rust/src/env.rs +++ b/rust/src/env.rs @@ -73,6 +73,14 @@ pub fn env_find(env: Env, key: String) -> Option<Env> { } #[allow(dead_code)] +pub fn env_root(env: &Env) -> Env { + match env.borrow().outer { + Some(ref ei) => env_root(ei), + None => env.clone(), + } +} + +#[allow(dead_code)] pub fn env_set(env: &Env, key: String, val: MalVal) { env.borrow_mut().data.insert(key, val.clone()); } @@ -86,7 +94,7 @@ pub fn env_get(env: Env, key: String) -> MalRet { None => Ok(Rc::new(Nil)), } }, - None => Ok(Rc::new(Nil)), + None => Err("'".to_string() + key + "' not found".to_string()), } } diff --git a/rust/src/step6_file.rs b/rust/src/step6_file.rs new file mode 100644 index 0000000..958704f --- /dev/null +++ b/rust/src/step6_file.rs @@ -0,0 +1,287 @@ +// support precompiled regexes in reader.rs +#![feature(phase)] +#[phase(plugin)] +extern crate regex_macros; +extern crate regex; + +use std::rc::Rc; +use std::os; + +use types::{MalVal,MalRet,MalFunc,MalFuncData, + Nil,False,Sym,Strn,List,Vector,Func}; +use env::{Env,env_new,env_bind,env_root,env_set,env_get}; +mod readline; +mod types; +mod reader; +mod printer; +mod env; +mod core; + +// read +fn read(str: String) -> MalRet { + reader::read_str(str) +} + +// eval +fn eval_ast(ast: MalVal, env: Env) -> MalRet { + let ast2 = ast.clone(); + match *ast2 { + //match *ast { + Sym(ref sym) => { + env_get(env.clone(), sym.clone()) + }, + List(ref a) => { + 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(mut ast: MalVal, mut env: Env) -> MalRet { + 'tco: loop { + + //println!("eval: {}, {}", ast, env.borrow()); + //println!("eval: {}", ast); + let ast2 = ast.clone(); + let ast3 = ast.clone(); + match *ast2 { + List(_) => (), // continue + _ => return eval_ast(ast2, env), + } + + // apply list + match *ast2 { + List(ref args) => { + if args.len() == 0 { + return Ok(ast3); + } + let ref a0 = *args[0]; + match *a0 { + Sym(ref a0sym) => { + match a0sym.as_slice() { + "def!" => { + let a1 = (*args)[1].clone(); + let a2 = (*args)[2].clone(); + let res = eval(a2, env.clone()); + match res { + Ok(r) => { + match *a1 { + Sym(ref s) => { + env_set(&env.clone(), s.clone(), r.clone()); + return Ok(r); + }, + _ => { + return Err("def! of non-symbol".to_string()) + } + } + }, + Err(e) => return Err(e), + } + }, + "let*" => { + let let_env = env_new(Some(env.clone())); + let a1 = (*args)[1].clone(); + let a2 = (*args)[2].clone(); + match *a1 { + List(ref binds) | Vector(ref binds) => { + let mut it = binds.iter(); + while it.len() >= 2 { + let b = it.next().unwrap(); + let exp = it.next().unwrap(); + match **b { + Sym(ref bstr) => { + match eval(exp.clone(), let_env.clone()) { + Ok(r) => { + env_set(&let_env, bstr.clone(), r); + }, + Err(e) => { + return Err(e); + }, + } + }, + _ => { + return Err("let* with non-symbol binding".to_string()); + }, + } + } + }, + _ => return Err("let* with non-list bindings".to_string()), + } + ast = a2; + env = let_env.clone(); + continue 'tco; + }, + "do" => { + let el = Rc::new(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(); + env = env.clone(); + continue 'tco; + }, + } + }, + "if" => { + let a1 = (*args)[1].clone(); + let cond = eval(a1, env.clone()); + if cond.is_err() { return cond; } + match *cond.unwrap() { + False | Nil => { + if args.len() >= 4 { + let a3 = (*args)[3].clone(); + ast = a3; + env = env.clone(); + continue 'tco; + } else { + return Ok(Rc::new(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(Rc::new(MalFunc(MalFuncData{ + exp: a2, + env: env.clone(), + params: a1}))); + }, + "eval" => { + let a1 = (*args)[1].clone(); + match eval(a1, env.clone()) { + Ok(exp) => { + ast = exp; + env = env_root(&env); + continue 'tco; + }, + Err(e) => return Err(e), + } + }, + _ => () + } + } + _ => (), + } + // function call + return match eval_ast(ast3, env.clone()) { + Err(e) => Err(e), + Ok(el) => { + match *el { + List(ref args) => { + // 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(_) => { + ast = mfc.exp; + env = new_env; + continue 'tco; + }, + Err(e) => Err(e), + } + }, + _ => Err("attempt to call non-function".to_string()), + } + } + _ => Err("Invalid apply".to_string()), + } + } + } + } + _ => return Err("Expected list".to_string()), + } + + } +} + +// print +fn print(exp: MalVal) -> String { + exp.pr_str(true) +} + +fn rep(str: String, env: Env) -> Result<String,String> { + match read(str) { + Err(e) => Err(e), + Ok(ast) => { + //println!("read: {}", ast); + match eval(ast, env) { + Err(e) => Err(e), + Ok(exp) => Ok(print(exp)), + } + } + } +} + +fn main() { + // core.rs: defined using rust + let repl_env = env_new(None); + for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); } + // see eval() for definition of "eval" + env_set(&repl_env, "*ARGV*".to_string(), Rc::new(List(vec![]))); + + // core.mal: defined using the language itself + let _ = rep("(def! not (fn* (a) (if a false true)))".to_string(), + repl_env.clone()); + let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))".to_string(), + repl_env.clone()); + + // Invoked with command line arguments + let args = os::args(); + if args.len() > 1 { + let mv_args = args.slice(2,args.len()).iter() + .map(|a| Rc::new(Strn(a.clone()))) + .collect::<Vec<MalVal>>(); + env_set(&repl_env, "*ARGV*".to_string(), Rc::new(List(mv_args))); + match rep("(load-file \"".to_string() + args[1] + "\")".to_string(), + repl_env.clone()) { + Ok(_) => { + os::set_exit_status(0); + return; + }, + Err(str) => { + println!("Error: {}", str); + os::set_exit_status(1); + return; + }, + } + } + + loop { + let line = readline::mal_readline("user> "); + match line { None => break, _ => () } + match rep(line.unwrap(), repl_env.clone()) { + Ok(str) => println!("{}", str), + Err(str) => println!("Error: {}", str), + } + } +} |
