diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-03-05 00:22:43 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-03-05 00:40:57 -0800 |
| commit | bbeb1b87c2b848189c0f71c93cf4b70b32d45a4d (patch) | |
| tree | 257ac1a8a70f7f981be440c22763dd9ec7e1e562 | |
| parent | a551bd4cd4d802173057ed124b911abed4ec530b (diff) | |
| download | mal-bbeb1b87c2b848189c0f71c93cf4b70b32d45a4d.tar.gz mal-bbeb1b87c2b848189c0f71c93cf4b70b32d45a4d.zip | |
Update Rust code to the current Rust nightly
Lots of changes! Hopefully everything is still semantically equivalent (tests
still pass)
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | rust/Cargo.toml | 40 | ||||
| -rw-r--r-- | rust/Makefile | 4 | ||||
| -rw-r--r-- | rust/src/bin/step0_repl.rs (renamed from rust/src/step0_repl.rs) | 5 | ||||
| -rw-r--r-- | rust/src/bin/step1_read_print.rs (renamed from rust/src/step1_read_print.rs) | 15 | ||||
| -rw-r--r-- | rust/src/bin/step2_eval.rs (renamed from rust/src/step2_eval.rs) | 31 | ||||
| -rw-r--r-- | rust/src/bin/step3_env.rs (renamed from rust/src/step3_env.rs) | 35 | ||||
| -rw-r--r-- | rust/src/bin/step4_if_fn_do.rs (renamed from rust/src/step4_if_fn_do.rs) | 38 | ||||
| -rw-r--r-- | rust/src/bin/step5_tco.rs (renamed from rust/src/step5_tco.rs) | 43 | ||||
| -rw-r--r-- | rust/src/bin/step6_file.rs (renamed from rust/src/step6_file.rs) | 75 | ||||
| -rw-r--r-- | rust/src/bin/step7_quote.rs (renamed from rust/src/step7_quote.rs) | 80 | ||||
| -rw-r--r-- | rust/src/bin/step8_macros.rs (renamed from rust/src/step8_macros.rs) | 97 | ||||
| -rw-r--r-- | rust/src/bin/step9_try.rs (renamed from rust/src/step9_try.rs) | 87 | ||||
| -rw-r--r-- | rust/src/bin/stepA_mal.rs | 381 | ||||
| -rw-r--r-- | rust/src/core.rs | 285 | ||||
| -rw-r--r-- | rust/src/env.rs | 50 | ||||
| -rw-r--r-- | rust/src/lib.rs | 17 | ||||
| -rw-r--r-- | rust/src/printer.rs | 6 | ||||
| -rw-r--r-- | rust/src/reader.rs | 46 | ||||
| -rw-r--r-- | rust/src/readline.rs | 57 | ||||
| -rw-r--r-- | rust/src/stepA_mal.rs | 479 | ||||
| -rw-r--r-- | rust/src/types.rs | 96 |
22 files changed, 851 insertions, 1121 deletions
@@ -315,10 +315,7 @@ tool (cargo) to build. ``` cd rust -# Need patched pcre lib (should be temporary) -git clone https://github.com/kanaka/rust-pcre cadencemarseille-pcre -cargo build --release -./target/stepX_YYY +cargo run --release --bin stepX_YYY ``` ### Scala ### diff --git a/rust/Cargo.toml b/rust/Cargo.toml index daf999d..da6cdb7 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,39 +1,13 @@ [package] -name = "Mal" +name = "mal" version = "0.0.1" authors = [ "Your name <you@example.com>" ] +[dependencies] +time = "0.1" +regex = "0.1" +libc = "0.1" -[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" +[dependencies.pcre] +git = "https://github.com/alexcrichton/rust-pcre" diff --git a/rust/Makefile b/rust/Makefile index fa1edc9..c71289d 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -3,7 +3,7 @@ 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_LISP = src/env.rs src/core.rs src/bin/stepA_mal.rs SOURCES = $(SOURCES_BASE) $(SOURCES_LISP) ##################### @@ -28,7 +28,7 @@ clean: cargo clean rm -f mal -.PHONY: stats stats-lisp +.PHONY: stats stats-lisp mal stats: $(SOURCES) @wc $^ diff --git a/rust/src/step0_repl.rs b/rust/src/bin/step0_repl.rs index ac9cf24..030c551 100644 --- a/rust/src/step0_repl.rs +++ b/rust/src/bin/step0_repl.rs @@ -1,5 +1,6 @@ -use readline::mal_readline; -mod readline; +extern crate mal; + +use mal::readline::mal_readline; // read fn read(str: String) -> String { diff --git a/rust/src/step1_read_print.rs b/rust/src/bin/step1_read_print.rs index 3ce11e6..8be7e0d 100644 --- a/rust/src/step1_read_print.rs +++ b/rust/src/bin/step1_read_print.rs @@ -1,15 +1,8 @@ -// support precompiled regexes in reader.rs -#![feature(phase)] -#[phase(plugin)] -extern crate regex_macros; -extern crate regex; +extern crate mal; -use types::{MalVal,MalRet,MalError,ErrString,ErrMalVal}; -mod readline; -mod types; -mod env; -mod reader; -mod printer; +use mal::types::{MalVal, MalRet, MalError}; +use mal::types::MalError::{ErrString, ErrMalVal}; +use mal::{readline, reader}; // read fn read(str: String) -> MalRet { diff --git a/rust/src/step2_eval.rs b/rust/src/bin/step2_eval.rs index 2cf7897..3efa51e 100644 --- a/rust/src/step2_eval.rs +++ b/rust/src/bin/step2_eval.rs @@ -1,19 +1,12 @@ -// support precompiled regexes in reader.rs -#![feature(phase)] -#[phase(plugin)] -extern crate regex_macros; -extern crate regex; +extern crate mal; 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 +use mal::types::{MalVal, MalRet, MalError, err_str}; +use mal::types::{_nil, list, vector, hash_map, _int, func}; +use mal::types::MalType::{Sym, List, Vector, Hash_Map, Int}; +use mal::types::MalError::{ErrString, ErrMalVal}; +use mal::{readline, reader}; // read fn read(str: String) -> MalRet { @@ -24,8 +17,8 @@ fn read(str: String) -> MalRet { fn eval_ast(ast: MalVal, env: &HashMap<String,MalVal>) -> MalRet { match *ast { Sym(ref sym) => { - match env.find_copy(sym) { - Some(mv) => Ok(mv), + match env.get(sym) { + Some(mv) => Ok(mv.clone()), None => Ok(_nil()), } }, @@ -70,7 +63,7 @@ fn eval(ast: MalVal, env: &HashMap<String,MalVal>) -> MalRet { match *el { List(ref args,_) => { let ref f = args.clone()[0]; - f.apply(args.slice(1,args.len()).to_vec()) + f.apply(args[1..].to_vec()) } _ => err_str("Invalid apply"), } @@ -96,7 +89,9 @@ fn rep(str: &str, env: &HashMap<String,MalVal>) -> Result<String,MalError> { } } -fn int_op(f: |i:int,j:int|-> int, a:Vec<MalVal>) -> MalRet { +fn int_op<F>(f: F, a:Vec<MalVal>) -> MalRet + where F: FnOnce(isize, isize) -> isize +{ match *a[0] { Int(a0) => match *a[1] { Int(a1) => Ok(_int(f(a0,a1))), @@ -120,7 +115,7 @@ fn main() { loop { let line = readline::mal_readline("user> "); match line { None => break, _ => () } - match rep(line.unwrap().as_slice(), &repl_env) { + match rep(&line.unwrap(), &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/bin/step3_env.rs index b2d49cd..0e75b26 100644 --- a/rust/src/step3_env.rs +++ b/rust/src/bin/step3_env.rs @@ -1,20 +1,13 @@ -// support precompiled regexes in reader.rs -#![feature(phase)] -#[phase(plugin)] -extern crate regex_macros; -extern crate regex; +extern crate mal; 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; +use mal::types::{MalVal, MalRet, MalError, err_str}; +use mal::types::{symbol, _int, list, vector, hash_map, func}; +use mal::types::MalError::{ErrString, ErrMalVal}; +use mal::types::MalType::{Int, Sym, List, Vector, Hash_Map}; +use mal::env::{Env, env_new, env_set, env_get}; +use mal::{readline, reader}; // read fn read(str: String) -> MalRet { @@ -27,7 +20,7 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { match *ast2 { //match *ast { Sym(_) => { - env_get(env.clone(), ast) + env_get(&env, &ast) }, List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec<MalVal> = vec![]; @@ -74,12 +67,12 @@ fn eval(ast: MalVal, env: Env) -> MalRet { let (args, a0sym) = match *ast2 { List(ref args,_) => { - if args.len() == 0 { + if args.len() == 0 { return Ok(ast); } let ref a0 = *args[0]; match *a0 { - Sym(ref a0sym) => (args, a0sym.as_slice()), + Sym(ref a0sym) => (args, &a0sym[..]), _ => (args, "__<fn*>__"), } }, @@ -146,7 +139,7 @@ fn eval(ast: MalVal, env: Env) -> MalRet { _ => return err_str("Invalid apply"), }; let ref f = args.clone()[0]; - f.apply(args.slice(1,args.len()).to_vec()) + f.apply(args[1..].to_vec()) } }; }, @@ -171,7 +164,9 @@ fn rep(str: &str, env: Env) -> Result<String,MalError> { } } -fn int_op(f: |i:int,j:int|-> int, a:Vec<MalVal>) -> MalRet { +fn int_op<F>(f: F, a:Vec<MalVal>) -> MalRet + where F: FnOnce(isize, isize) -> isize +{ match *a[0] { Int(a0) => match *a[1] { Int(a1) => Ok(_int(f(a0,a1))), @@ -195,7 +190,7 @@ fn main() { loop { let line = readline::mal_readline("user> "); match line { None => break, _ => () } - match rep(line.unwrap().as_slice(), repl_env.clone()) { + match rep(&line.unwrap(), 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/bin/step4_if_fn_do.rs index 92abf92..afbf69a 100644 --- a/rust/src/step4_if_fn_do.rs +++ b/rust/src/bin/step4_if_fn_do.rs @@ -1,21 +1,13 @@ -// support precompiled regexes in reader.rs -#![feature(phase)] -#[phase(plugin)] -extern crate regex_macros; -extern crate regex; +extern crate mal; 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; +use mal::types::{MalVal, MalRet, MalError, err_str}; +use mal::types::{symbol, _nil, list, vector, hash_map, malfunc}; +use mal::types::MalType::{Nil, False, Sym, List, Vector, Hash_Map}; +use mal::types::MalError::{ErrString, ErrMalVal}; +use mal::{readline, reader, core}; +use mal::env::{env_set, env_get, env_new, Env}; // read fn read(str: String) -> MalRet { @@ -27,9 +19,7 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(_) => { - env_get(env.clone(), ast) - }, + Sym(_) => env_get(&env, &ast), List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec<MalVal> = vec![]; for mv in a.iter() { @@ -75,12 +65,12 @@ fn eval(ast: MalVal, env: Env) -> MalRet { let (args, a0sym) = match *ast2 { List(ref args,_) => { - if args.len() == 0 { + if args.len() == 0 { return Ok(ast); } let ref a0 = *args[0]; match *a0 { - Sym(ref a0sym) => (args, a0sym.as_slice()), + Sym(ref a0sym) => (args, &a0sym[..]), _ => (args, "__<fn*>__"), } }, @@ -139,7 +129,7 @@ fn eval(ast: MalVal, env: Env) -> MalRet { return eval(a2, let_env.clone()); }, "do" => { - let el = list(args.slice(1,args.len()).to_vec()); + let el = list(args[1..].to_vec()); return match eval_ast(el, env.clone()) { Err(e) => return Err(e), Ok(el) => { @@ -188,7 +178,7 @@ fn eval(ast: MalVal, env: Env) -> MalRet { _ => return err_str("Invalid apply"), }; let ref f = args.clone()[0]; - f.apply(args.slice(1,args.len()).to_vec()) + f.apply(args[1..].to_vec()) } }; }, @@ -217,7 +207,7 @@ 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); + env_set(&repl_env, symbol(&k), v); } // core.mal: defined using the language itself @@ -226,7 +216,7 @@ fn main() { loop { let line = readline::mal_readline("user> "); match line { None => break, _ => () } - match rep(line.unwrap().as_slice(), repl_env.clone()) { + match rep(&line.unwrap(), 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/bin/step5_tco.rs index 9223cbf..4117634 100644 --- a/rust/src/step5_tco.rs +++ b/rust/src/bin/step5_tco.rs @@ -1,21 +1,14 @@ -// support precompiled regexes in reader.rs -#![feature(phase)] -#[phase(plugin)] -extern crate regex_macros; -extern crate regex; +extern crate mal; 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; +use mal::types::{MalVal, MalRet, MalError, err_str}; +use mal::types::{symbol, _nil, list, vector, hash_map, malfunc}; +use mal::types::MalType::{Nil, False, Sym, List, Vector, Hash_Map, Func, MalFunc}; +use mal::types::MalError::{ErrString, ErrMalVal}; +use mal::{readline, reader, core}; +use mal::env::{env_set, env_get, env_new, env_bind, Env}; + // read fn read(str: String) -> MalRet { @@ -27,9 +20,7 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(_) => { - env_get(env.clone(), ast) - }, + Sym(_) => env_get(&env, &ast), List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec<MalVal> = vec![]; for mv in a.iter() { @@ -78,12 +69,12 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let (args, a0sym) = match *ast2 { List(ref args,_) => { - if args.len() == 0 { + if args.len() == 0 { return Ok(ast3); } let ref a0 = *args[0]; match *a0 { - Sym(ref a0sym) => (args, a0sym.as_slice()), + Sym(ref a0sym) => (args, &a0sym[..]), _ => (args, "__<fn*>__"), } }, @@ -144,7 +135,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { continue 'tco; }, "do" => { - let el = list(args.slice(1,args.len()-1).to_vec()); + let el = list(args[1..args.len()-1].to_vec()); match eval_ast(el, env.clone()) { Err(e) => return Err(e), Ok(_) => { @@ -193,10 +184,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { _ => return err_str("Invalid apply"), }; match *args.clone()[0] { - Func(f,_) => f(args.slice(1,args.len()).to_vec()), + Func(f,_) => f(args[1..].to_vec()), MalFunc(ref mf,_) => { let mfc = mf.clone(); - let alst = list(args.slice(1,args.len()).to_vec()); + let alst = list(args[1..].to_vec()); let new_env = env_new(Some(mfc.env.clone())); match env_bind(&new_env, mfc.params, alst) { Ok(_) => { @@ -204,7 +195,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { env = new_env; continue 'tco; }, - Err(e) => err_str(e.as_slice()), + Err(e) => err_str(&e), } }, _ => err_str("attempt to call non-function"), @@ -239,7 +230,7 @@ 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); + env_set(&repl_env, symbol(&k), v); } // core.mal: defined using the language itself @@ -248,7 +239,7 @@ fn main() { loop { let line = readline::mal_readline("user> "); match line { None => break, _ => () } - match rep(line.unwrap().as_slice(), repl_env.clone()) { + match rep(&line.unwrap(), 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/bin/step6_file.rs index 1e87116..8d9a26c 100644 --- a/rust/src/step6_file.rs +++ b/rust/src/bin/step6_file.rs @@ -1,22 +1,16 @@ -// support precompiled regexes in reader.rs -#![feature(phase)] -#[phase(plugin)] -extern crate regex_macros; -extern crate regex; +#![feature(exit_status)] + +extern crate mal; use std::collections::HashMap; -use std::os; +use std::env as stdenv; -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; +use mal::types::{MalVal, MalRet, MalError, err_str}; +use mal::types::{symbol, _nil, string, list, vector, hash_map, malfunc}; +use mal::types::MalType::{Nil, False, Sym, List, Vector, Hash_Map, Func, MalFunc}; +use mal::types::MalError::{ErrString, ErrMalVal}; +use mal::{readline, reader, core}; +use mal::env::{env_set, env_get, env_new, env_bind, env_root, Env}; // read fn read(str: String) -> MalRet { @@ -28,9 +22,7 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(_) => { - env_get(env.clone(), ast) - }, + Sym(_) => env_get(&env, &ast), List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec<MalVal> = vec![]; for mv in a.iter() { @@ -79,12 +71,12 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let (args, a0sym) = match *ast2 { List(ref args,_) => { - if args.len() == 0 { + if args.len() == 0 { return Ok(ast3); } let ref a0 = *args[0]; match *a0 { - Sym(ref a0sym) => (args, a0sym.as_slice()), + Sym(ref a0sym) => (args, &a0sym[..]), _ => (args, "__<fn*>__"), } }, @@ -145,7 +137,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { continue 'tco; }, "do" => { - let el = list(args.slice(1,args.len()-1).to_vec()); + let el = list(args[1..args.len()-1].to_vec()); match eval_ast(el, env.clone()) { Err(e) => return Err(e), Ok(_) => { @@ -205,10 +197,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { _ => return err_str("Invalid apply"), }; match *args.clone()[0] { - Func(f,_) => f(args.slice(1,args.len()).to_vec()), + Func(f,_) => f(args[1..].to_vec()), MalFunc(ref mf,_) => { let mfc = mf.clone(); - let alst = list(args.slice(1,args.len()).to_vec()); + let alst = list(args[1..].to_vec()); let new_env = env_new(Some(mfc.env.clone())); match env_bind(&new_env, mfc.params, alst) { Ok(_) => { @@ -216,7 +208,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { env = new_env; continue 'tco; }, - Err(e) => err_str(e.as_slice()), + Err(e) => err_str(&e), } }, _ => err_str("attempt to call non-function"), @@ -251,40 +243,37 @@ 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); + env_set(&repl_env, symbol(&k), v); } // see eval() for definition of "eval" - env_set(&repl_env, symbol("*ARGV*".as_slice()), list(vec![])); + env_set(&repl_env, symbol("*ARGV*"), 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(); + let args = stdenv::args(); if args.len() > 1 { - let mv_args = args.slice(2,args.len()).iter() - .map(|a| string(a.to_string())) + let mv_args = args.skip(2) + .map(|a| string(a)) .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; - }, + env_set(&repl_env, symbol("*ARGV*"), list(mv_args)); + let lf = format!("(load-file \"{}\")", + stdenv::args().skip(1).next().unwrap()); + return match rep(&lf, repl_env.clone()) { + Ok(_) => stdenv::set_exit_status(0), Err(str) => { - println!("Error: {}", str); - os::set_exit_status(1); - return; - }, - } + println!("Error: {:?}", str); + stdenv::set_exit_status(1); + } + }; } loop { let line = readline::mal_readline("user> "); match line { None => break, _ => () } - match rep(line.unwrap().as_slice(), repl_env.clone()) { + match rep(&line.unwrap(), 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/bin/step7_quote.rs index b113920..77d5d33 100644 --- a/rust/src/step7_quote.rs +++ b/rust/src/bin/step7_quote.rs @@ -1,22 +1,17 @@ -// support precompiled regexes in reader.rs -#![feature(phase)] -#[phase(plugin)] -extern crate regex_macros; -extern crate regex; +#![feature(exit_status)] + +extern crate mal; use std::collections::HashMap; -use std::os; +use std::env as stdenv; + +use mal::types::{MalVal, MalRet, MalError, err_str}; +use mal::types::{symbol, _nil, string, list, vector, hash_map, malfunc}; +use mal::types::MalType::{Nil, False, Sym, List, Vector, Hash_Map, Func, MalFunc}; +use mal::types::MalError::{ErrString, ErrMalVal}; +use mal::{readline, reader, core}; +use mal::env::{env_set, env_get, env_new, env_bind, env_root, Env}; -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 { @@ -57,7 +52,7 @@ fn quasiquote(ast: MalVal) -> MalVal { 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()))]) + quasiquote(list(args[1..].to_vec()))]) } }, _ => (), @@ -66,7 +61,7 @@ fn quasiquote(ast: MalVal) -> MalVal { _ => (), } } - let rest = list(args.slice(1,args.len()).to_vec()); + let rest = list(args[1..].to_vec()); return list(vec![symbol("cons"), quasiquote(a0.clone()), quasiquote(rest)]) @@ -79,9 +74,7 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(_) => { - env_get(env.clone(), ast) - }, + Sym(_) => env_get(&env, &ast), List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec<MalVal> = vec![]; for mv in a.iter() { @@ -130,12 +123,12 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let (args, a0sym) = match *ast2 { List(ref args,_) => { - if args.len() == 0 { + if args.len() == 0 { return Ok(ast3); } let ref a0 = *args[0]; match *a0 { - Sym(ref a0sym) => (args, a0sym.as_slice()), + Sym(ref a0sym) => (args, &a0sym[..]), _ => (args, "__<fn*>__"), } }, @@ -204,7 +197,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { continue 'tco; }, "do" => { - let el = list(args.slice(1,args.len()-1).to_vec()); + let el = list(args[1..args.len()-1].to_vec()); match eval_ast(el, env.clone()) { Err(e) => return Err(e), Ok(_) => { @@ -264,10 +257,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { _ => return err_str("Invalid apply"), }; match *args.clone()[0] { - Func(f,_) => f(args.slice(1,args.len()).to_vec()), + Func(f,_) => f(args[1..].to_vec()), MalFunc(ref mf,_) => { let mfc = mf.clone(); - let alst = list(args.slice(1,args.len()).to_vec()); + let alst = list(args[1..].to_vec()); let new_env = env_new(Some(mfc.env.clone())); match env_bind(&new_env, mfc.params, alst) { Ok(_) => { @@ -275,7 +268,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { env = new_env; continue 'tco; }, - Err(e) => err_str(e.as_slice()), + Err(e) => err_str(&e), } }, _ => err_str("attempt to call non-function"), @@ -310,40 +303,37 @@ 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); + env_set(&repl_env, symbol(&k), v); } // see eval() for definition of "eval" - env_set(&repl_env, symbol("*ARGV*".as_slice()), list(vec![])); + env_set(&repl_env, symbol("*ARGV*"), 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(); + let args = stdenv::args(); if args.len() > 1 { - let mv_args = args.slice(2,args.len()).iter() - .map(|a| string(a.to_string())) + let mv_args = args.skip(2) + .map(|a| string(a)) .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; - }, + env_set(&repl_env, symbol("*ARGV*"), list(mv_args)); + let lf = format!("(load-file \"{}\")", + stdenv::args().skip(1).next().unwrap()); + return match rep(&lf, repl_env.clone()) { + Ok(_) => stdenv::set_exit_status(0), Err(str) => { - println!("Error: {}", str); - os::set_exit_status(1); - return; - }, - } + println!("Error: {:?}", str); + stdenv::set_exit_status(1); + } + }; } loop { let line = readline::mal_readline("user> "); match line { None => break, _ => () } - match rep(line.unwrap().as_slice(), repl_env.clone()) { + match rep(&line.unwrap(), 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/bin/step8_macros.rs index 7450de8..a808926 100644 --- a/rust/src/step8_macros.rs +++ b/rust/src/bin/step8_macros.rs @@ -1,22 +1,16 @@ -// support precompiled regexes in reader.rs -#![feature(phase)] -#[phase(plugin)] -extern crate regex_macros; -extern crate regex; +#![feature(exit_status)] + +extern crate mal; use std::collections::HashMap; -use std::os; +use std::env as stdenv; -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; +use mal::types::{MalVal, MalRet, MalError, err_str}; +use mal::types::{symbol, _nil, string, list, vector, hash_map, malfunc, malfuncd}; +use mal::types::MalType::{Nil, False, Sym, List, Vector, Hash_Map, Func, MalFunc}; +use mal::types::MalError::{ErrString, ErrMalVal}; +use mal::{readline, reader, core}; +use mal::env::{env_set, env_get, env_new, env_bind, env_find, env_root, Env}; // read fn read(str: String) -> MalRet { @@ -57,7 +51,7 @@ fn quasiquote(ast: MalVal) -> MalVal { 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()))]) + quasiquote(list(args[1..].to_vec()))]) } }, _ => (), @@ -66,7 +60,7 @@ fn quasiquote(ast: MalVal) -> MalVal { _ => (), } } - let rest = list(args.slice(1,args.len()).to_vec()); + let rest = list(args[1..].to_vec()); return list(vec![symbol("cons"), quasiquote(a0.clone()), quasiquote(rest)]) @@ -80,8 +74,8 @@ fn is_macro_call(ast: MalVal, env: Env) -> bool { List(ref lst,_) => { match *lst[0] { Sym(_) => { - if env_find(env.clone(), lst[0].clone()).is_some() { - match env_get(env, lst[0].clone()) { + if env_find(&env, &lst[0]).is_some() { + match env_get(&env, &lst[0]) { Ok(f) => { match *f { MalFunc(ref mfd,_) => { @@ -112,21 +106,11 @@ fn macroexpand(mut ast: MalVal, env: Env) -> MalRet { }; 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), - } - }, + Sym(_) => try!(env_get(&env, &a0)), _ => break, }; match *mf { - MalFunc(_,_) => { - match mf.apply(args.slice(1,args.len()).to_vec()) { - Ok(r) => ast = r, - Err(e) => return Err(e), - } - }, + MalFunc(_,_) => ast = try!(mf.apply(args[1..].to_vec())), _ => break, } } @@ -137,9 +121,7 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { let ast2 = ast.clone(); match *ast2 { //match *ast { - Sym(_) => { - env_get(env.clone(), ast) - }, + Sym(_) => env_get(&env, &ast), List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec<MalVal> = vec![]; for mv in a.iter() { @@ -194,12 +176,12 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let (args, a0sym) = match *ast2 { List(ref args,_) => { - if args.len() == 0 { + if args.len() == 0 { return Ok(ast3); } let ref a0 = *args[0]; match *a0 { - Sym(ref a0sym) => (args, a0sym.as_slice()), + Sym(ref a0sym) => (args, &a0sym[..]), _ => (args, "__<fn*>__"), } }, @@ -296,7 +278,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { return macroexpand(a1, env.clone()) }, "do" => { - let el = list(args.slice(1,args.len()-1).to_vec()); + let el = list(args[1..args.len()-1].to_vec()); match eval_ast(el, env.clone()) { Err(e) => return Err(e), Ok(_) => { @@ -356,10 +338,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { _ => return err_str("Invalid apply"), }; match *args.clone()[0] { - Func(f,_) => f(args.slice(1,args.len()).to_vec()), + Func(f,_) => f(args[1..].to_vec()), MalFunc(ref mf,_) => { let mfc = mf.clone(); - let alst = list(args.slice(1,args.len()).to_vec()); + let alst = list(args[1..].to_vec()); let new_env = env_new(Some(mfc.env.clone())); match env_bind(&new_env, mfc.params, alst) { Ok(_) => { @@ -367,7 +349,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { env = new_env; continue 'tco; }, - Err(e) => err_str(e.as_slice()), + Err(e) => err_str(&e), } }, _ => err_str("attempt to call non-function"), @@ -402,10 +384,10 @@ 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); + env_set(&repl_env, symbol(&k), v); } // see eval() for definition of "eval" - env_set(&repl_env, symbol("*ARGV*".as_slice()), list(vec![])); + env_set(&repl_env, symbol("*ARGV*"), list(vec![])); // core.mal: defined using the language itself let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone()); @@ -414,31 +396,28 @@ fn main() { 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(); + let args = stdenv::args(); if args.len() > 1 { - let mv_args = args.slice(2,args.len()).iter() - .map(|a| string(a.to_string())) + let mv_args = args.skip(2) + .map(|a| string(a)) .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; - }, + env_set(&repl_env, symbol("*ARGV*"), list(mv_args)); + let lf = format!("(load-file \"{}\")", + stdenv::args().skip(1).next().unwrap()); + return match rep(&lf, repl_env.clone()) { + Ok(_) => stdenv::set_exit_status(0), Err(str) => { - println!("Error: {}", str); - os::set_exit_status(1); - return; - }, - } + println!("Error: {:?}", str); + stdenv::set_exit_status(1); + } + }; } // repl loop loop { let line = readline::mal_readline("user> "); match line { None => break, _ => () } - match rep(line.unwrap().as_slice(), repl_env.clone()) { + match rep(&line.unwrap(), 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/bin/step9_try.rs index 0f6bd88..f6c5dbf 100644 --- a/rust/src/step9_try.rs +++ b/rust/src/bin/step9_try.rs @@ -1,22 +1,16 @@ -// support precompiled regexes in reader.rs -#![feature(phase)] -#[phase(plugin)] -extern crate regex_macros; -extern crate regex; +#![feature(exit_status)] + +extern crate mal; use std::collections::HashMap; -use std::os; +use std::env as stdenv; -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; +use mal::types::{MalVal, MalRet, MalError, err_str}; +use mal::types::{symbol, _nil, string, list, vector, hash_map, malfunc, malfuncd}; +use mal::types::MalType::{Nil, False, Sym, List, Vector, Hash_Map, Func, MalFunc}; +use mal::types::MalError::{ErrString, ErrMalVal}; +use mal::{readline, reader, core}; +use mal::env::{env_set, env_get, env_new, env_bind, env_find, env_root, Env}; // read fn read(str: String) -> MalRet { @@ -26,7 +20,7 @@ fn read(str: String) -> MalRet { // eval fn is_pair(x: MalVal) -> bool { match *x { - List(ref lst,_) | Vector(ref lst,_) => lst.len() > 0, + List(ref lst, _) | Vector(ref lst, _) => lst.len() > 0, _ => false, } } @@ -57,7 +51,7 @@ fn quasiquote(ast: MalVal) -> MalVal { 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()))]) + quasiquote(list(args[1..].to_vec()))]) } }, _ => (), @@ -66,7 +60,7 @@ fn quasiquote(ast: MalVal) -> MalVal { _ => (), } } - let rest = list(args.slice(1,args.len()).to_vec()); + let rest = list(args[1..].to_vec()); return list(vec![symbol("cons"), quasiquote(a0.clone()), quasiquote(rest)]) @@ -80,8 +74,8 @@ fn is_macro_call(ast: MalVal, env: Env) -> bool { List(ref lst,_) => { match *lst[0] { Sym(_) => { - if env_find(env.clone(), lst[0].clone()).is_some() { - match env_get(env, lst[0].clone()) { + if env_find(&env, &lst[0]).is_some() { + match env_get(&env, &lst[0]) { Ok(f) => { match *f { MalFunc(ref mfd,_) => { @@ -113,7 +107,7 @@ fn macroexpand(mut ast: MalVal, env: Env) -> MalRet { let ref a0 = args[0]; let mf = match **a0 { Sym(_) => { - match env_get(env.clone(), a0.clone()) { + match env_get(&env, &a0) { Ok(mf) => mf, Err(e) => return Err(e), } @@ -122,7 +116,7 @@ fn macroexpand(mut ast: MalVal, env: Env) -> MalRet { }; match *mf { MalFunc(_,_) => { - match mf.apply(args.slice(1,args.len()).to_vec()) { + match mf.apply(args[1..].to_vec()) { Ok(r) => ast = r, Err(e) => return Err(e), } @@ -138,7 +132,7 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet { match *ast2 { //match *ast { Sym(_) => { - env_get(env.clone(), ast) + env_get(&env, &ast) }, List(ref a,_) | Vector(ref a,_) => { let mut ast_vec : Vec<MalVal> = vec![]; @@ -194,12 +188,12 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { let (args, a0sym) = match *ast2 { List(ref args,_) => { - if args.len() == 0 { + if args.len() == 0 { return Ok(ast3); } let ref a0 = *args[0]; match *a0 { - Sym(ref a0sym) => (args, a0sym.as_slice()), + Sym(ref a0sym) => (args, &a0sym[..]), _ => (args, "__<fn*>__"), } }, @@ -326,7 +320,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { }; } "do" => { - let el = list(args.slice(1,args.len()-1).to_vec()); + let el = list(args[1..args.len()-1].to_vec()); match eval_ast(el, env.clone()) { Err(e) => return Err(e), Ok(_) => { @@ -386,10 +380,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { _ => return err_str("Invalid apply"), }; match *args.clone()[0] { - Func(f,_) => f(args.slice(1,args.len()).to_vec()), + Func(f,_) => f(args[1..].to_vec()), MalFunc(ref mf,_) => { let mfc = mf.clone(); - let alst = list(args.slice(1,args.len()).to_vec()); + let alst = list(args[1..].to_vec()); let new_env = env_new(Some(mfc.env.clone())); match env_bind(&new_env, mfc.params, alst) { Ok(_) => { @@ -397,7 +391,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { env = new_env; continue 'tco; }, - Err(e) => err_str(e.as_slice()), + Err(e) => err_str(&e), } }, _ => err_str("attempt to call non-function"), @@ -432,10 +426,10 @@ 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); + env_set(&repl_env, symbol(&k), v); } // see eval() for definition of "eval" - env_set(&repl_env, symbol("*ARGV*".as_slice()), list(vec![])); + env_set(&repl_env, symbol("*ARGV*"), list(vec![])); // core.mal: defined using the language itself let _ = rep("(def! not (fn* (a) (if a false true)))", repl_env.clone()); @@ -444,31 +438,28 @@ fn main() { 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(); + let args = stdenv::args(); if args.len() > 1 { - let mv_args = args.slice(2,args.len()).iter() - .map(|a| string(a.to_string())) + let mv_args = args.skip(2) + .map(|a| string(a)) .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; - }, + env_set(&repl_env, symbol("*ARGV*"), list(mv_args)); + let lf = format!("(load-file \"{}\")", + stdenv::args().skip(1).next().unwrap()); + return match rep(&lf, repl_env.clone()) { + Ok(_) => stdenv::set_exit_status(0), Err(str) => { - println!("Error: {}", str); - os::set_exit_status(1); - return; - }, - } + println!("Error: {:?}", str); + stdenv::set_exit_status(1); + } + }; } // repl loop loop { let line = readline::mal_readline("user> "); match line { None => break, _ => () } - match rep(line.unwrap().as_slice(), repl_env.clone()) { + match rep(&line.unwrap(), repl_env.clone()) { Ok(str) => println!("{}", str), Err(ErrMalVal(_)) => (), // Blank line Err(ErrString(s)) => println!("Error: {}", s), diff --git a/rust/src/bin/stepA_mal.rs b/rust/src/bin/stepA_mal.rs new file mode 100644 index 0000000..e38b16f --- /dev/null +++ b/rust/src/bin/stepA_mal.rs @@ -0,0 +1,381 @@ +#![feature(exit_status)] + +extern crate mal; + +use std::collections::HashMap; +use std::env as stdenv; + +use mal::types::{MalVal, MalRet, MalError, err_str}; +use mal::types::{symbol, _nil, string, list, vector, hash_map, malfunc, malfuncd}; +use mal::types::MalType::{Nil, False, Sym, List, Vector, Hash_Map, Func, MalFunc}; +use mal::types::MalError::{ErrString, ErrMalVal}; +use mal::{readline, reader, core}; +use mal::env::{env_set, env_get, env_new, env_bind, env_find, env_root, Env}; + +// 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 == "unquote" => return args[1].clone(), + _ => (), + } + if is_pair(a0.clone()) { + match **a0 { + List(ref a0args,_) | Vector(ref a0args,_) => { + match *a0args[0] { + Sym(ref s) if *s == "split-unqoute" => { + return list(vec![symbol("concat"), + a0args[1].clone(), + quasiquote(list(args[1..].to_vec()))]) + }, + _ => (), + } + }, + _ => (), + } + } + let rest = list(args[1..].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 { + let lst = match *ast { + List(ref lst,_) => &lst[0], + _ => return false + }; + match **lst { + Sym(_) => {}, + _ => return false + } + if env_find(&env, lst).is_none() { + return false + } + let f = match env_get(&env, lst) { + Ok(f) => f, + _ => return false + }; + match *f { + MalFunc(ref mfd,_) => mfd.is_macro, + _ => 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(_) => try!(env_get(&env, &a0)), + _ => break, + }; + match *mf { + MalFunc(_,_) => ast = try!(mf.apply(args[1..].to_vec())), + _ => break, + } + } + Ok(ast) +} + +fn eval_ast(ast: MalVal, env: Env) -> MalRet { + match *ast { + Sym(_) => env_get(&env, &ast), + List(ref a,_) | Vector(ref a,_) => { + let mut ast_vec : Vec<MalVal> = vec![]; + for mv in a.iter() { + let mv2 = mv.clone(); + ast_vec.push(try!(eval(mv2, env.clone()))); + } + 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() { + new_hm.insert(key.to_string(), + try!(eval(value.clone(), env.clone()))); + } + Ok(hash_map(new_hm)) + } + _ => Ok(ast.clone()), + } +} + +fn eval(mut ast: MalVal, mut env: Env) -> MalRet { + 'tco: loop { + + //println!("eval: {}, {}", ast, env.borrow()); + //println!("eval: {}", ast); + match *ast { + List(_,_) => (), // continue + _ => return eval_ast(ast, env), + } + + // apply list + ast = try!(macroexpand(ast, env.clone())); + match *ast { + List(_,_) => (), // continue + _ => return Ok(ast), + } + + let tmp = ast; + let (args, a0sym) = match *tmp { + List(ref args,_) => { + if args.len() == 0 { + return Ok(tmp.clone()); + } + let ref a0 = *args[0]; + match *a0 { + Sym(ref a0sym) => (args, &a0sym[..]), + _ => (args, "__<fn*>__"), + } + }, + _ => return err_str("Expected list"), + }; + + match a0sym { + "def!" => { + let a1 = (*args)[1].clone(); + let a2 = (*args)[2].clone(); + let r = try!(eval(a2, env.clone())); + match *a1 { + Sym(_) => { + env_set(&env.clone(), a1, r.clone()); + return Ok(r); + }, + _ => return err_str("def! of non-symbol"), + } + }, + "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(_) => { + let r = try!(eval(exp.clone(), let_env.clone())); + env_set(&let_env, b.clone(), r); + }, + _ => 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(); + let r = try!(eval(a2, env.clone())); + 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"), + } + }, + "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[1..args.len()-1].to_vec()); + try!(eval_ast(el, env.clone())); + ast = args[args.len() - 1].clone(); + continue 'tco; + }, + "if" => { + let a1 = (*args)[1].clone(); + let c = try!(eval(a1, env.clone())); + match *c { + False | Nil => { + if args.len() >= 4 { + ast = args[3].clone(); + continue 'tco; + } else { + return Ok(_nil()); + } + }, + _ => { + ast = args[2].clone(); + continue 'tco; + }, + } + }, + "fn*" => { + let a1 = args[1].clone(); + let a2 = args[2].clone(); + return Ok(malfunc(eval, a2, env, a1, _nil())); + }, + "eval" => { + let a1 = (*args)[1].clone(); + ast = try!(eval(a1, env.clone())); + env = env_root(&env); + continue 'tco; + }, + _ => { // function call + let el = try!(eval_ast(tmp.clone(), env.clone())); + let args = match *el { + List(ref args,_) => args, + _ => return err_str("Invalid apply"), + }; + return match *args.clone()[0] { + Func(f,_) => f(args[1..].to_vec()), + MalFunc(ref mf,_) => { + let mfc = mf.clone(); + let alst = list(args[1..].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), + } + }, + _ => 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> { + let ast = try!(read(str.to_string())); + //println!("read: {}", ast); + let exp = try!(eval(ast, env)); + 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), v); + } + // see eval() for definition of "eval" + env_set(&repl_env, symbol("*ARGV*"), 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 = stdenv::args(); + if args.len() > 1 { + let mv_args = args.skip(2) + .map(|a| string(a)) + .collect::<Vec<MalVal>>(); + env_set(&repl_env, symbol("*ARGV*"), list(mv_args)); + let lf = format!("(load-file \"{}\")", + stdenv::args().skip(1).next().unwrap()); + return match rep(&lf, repl_env.clone()) { + Ok(_) => stdenv::set_exit_status(0), + Err(str) => { + println!("Error: {:?}", str); + stdenv::set_exit_status(1); + } + }; + } + + // 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(), repl_env.clone()) { + Ok(str) => println!("{}", str), + Err(ErrMalVal(_)) => (), // Blank line + Err(ErrString(s)) => println!("Error: {}", s), + } + } +} diff --git a/rust/src/core.rs b/rust/src/core.rs index 2bc3c39..f7257f3 100644 --- a/rust/src/core.rs +++ b/rust/src/core.rs @@ -1,31 +1,30 @@ #![allow(dead_code)] -extern crate time; use std::collections::HashMap; -use std::io::File; +use std::fs::File; +use std::io::prelude::*; +use std::num::ToPrimitive; +use time; 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::MalType::{Nil, Int, Strn, List, Vector, Hash_Map, Func, MalFunc, Atom}; use types; use readline; use reader; use printer; // General functions -fn equal_q(a:Vec<MalVal>) -> MalRet { +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()), - } + if a[0] == a[1] {Ok(_true())} else {Ok(_false())} } // Errors/Exceptions -fn throw(a:Vec<MalVal>) -> MalRet { +fn throw(a: Vec<MalVal>) -> MalRet { if a.len() != 1 { return err_str("Wrong arity to throw call"); } @@ -33,27 +32,27 @@ fn throw(a:Vec<MalVal>) -> MalRet { } // String routines -fn pr_str(a:Vec<MalVal>) -> MalRet { +fn pr_str(a: Vec<MalVal>) -> MalRet { Ok(string(printer::pr_list(&a, true, "", "", " "))) } -fn str(a:Vec<MalVal>) -> MalRet { +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, "", "", " ")) +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, "", "", " ")) +fn println(a: Vec<MalVal>) -> MalRet { + println!("{}", printer::pr_list(&a, false, "", "", " ")); Ok(_nil()) } -fn readline(a:Vec<MalVal>) -> MalRet { +fn readline(a: Vec<MalVal>) -> MalRet { match *a[0] { - Strn(ref a0) => match readline::mal_readline(a0.as_slice()) { + Strn(ref a0) => match readline::mal_readline(&a0) { Some(line) => Ok(string(line)), None => err_val(_nil()), }, @@ -61,18 +60,19 @@ fn readline(a:Vec<MalVal>) -> MalRet { } } -fn read_string(a:Vec<MalVal>) -> MalRet { +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 { +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)), + let mut s = String::new(); + match File::open(a0).and_then(|mut f| f.read_to_string(&mut s)) { + Ok(_) => Ok(string(s)), Err(e) => err_string(e.to_string()), } }, @@ -82,7 +82,9 @@ fn slurp(a:Vec<MalVal>) -> MalRet { // Numeric functions -fn int_op(f: |i:int,j:int|-> int, a:Vec<MalVal>) -> MalRet { +fn int_op<F>(f: F, a: Vec<MalVal>) -> MalRet + where F: FnOnce(isize, isize) -> isize +{ match *a[0] { Int(a0) => match *a[1] { Int(a1) => Ok(_int(f(a0,a1))), @@ -92,13 +94,15 @@ fn int_op(f: |i:int,j:int|-> int, a:Vec<MalVal>) -> MalRet { } } -fn bool_op(f: |i:int,j:int|-> bool, a:Vec<MalVal>) -> MalRet { +fn bool_op<F>(f: F, a: Vec<MalVal>) -> MalRet + where F: FnOnce(isize, isize) -> bool +{ match *a[0] { Int(a0) => match *a[1] { Int(a1) => { match f(a0,a1) { true => Ok(_true()), - false => Ok(_false()), + false => Ok(_false()), } }, _ => err_str("second arg must be an int"), @@ -107,83 +111,73 @@ fn bool_op(f: |i:int,j:int|-> bool, a:Vec<MalVal>) -> MalRet { } } -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 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) } +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 { +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); + let now_ms = (now.sec * 1000).to_isize().unwrap() + + (now.nsec.to_isize().unwrap() / 1000000); Ok(_int(now_ms)) } // Hash Map functions -pub fn assoc(a:Vec<MalVal>) -> MalRet { +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"), + Hash_Map(ref hm,_) => types::_assoc(hm, a[1..].to_vec()), + Nil => types::hash_mapv(a[1..].to_vec()), + _ => err_str("assoc onto non-hash map"), } } -pub fn dissoc(a:Vec<MalVal>) -> MalRet { +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"), + Hash_Map(ref hm,_) => types::_dissoc(hm, a[1..].to_vec()), + Nil => Ok(_nil()), + _ => err_str("dissoc onto non-hash map"), } } -pub fn get(a:Vec<MalVal>) -> MalRet { +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 { + let hm = match *a[0] { 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), + match hm.get(key) { + Some(v) => Ok(v.clone()), None => Ok(_nil()), } }, - _ => return err_str("get with non-string key"), + _ => err_str("get with non-string key"), } } -pub fn contains_q(a:Vec<MalVal>) -> MalRet { +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 { + let hm = match *a[0] { Hash_Map(ref hm,_) => hm, Nil => return Ok(_false()), _ => return err_str("contains? on non-hash map"), @@ -194,50 +188,37 @@ pub fn contains_q(a:Vec<MalVal>) -> MalRet { true => Ok(_true()), false => Ok(_false()), } - }, - _ => return err_str("contains? with non-string key"), + } + _ => err_str("contains? with non-string key"), } } -pub fn keys(a:Vec<MalVal>) -> MalRet { +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 { + let hm = match *a[0] { 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)) + Ok(list(hm.keys().map(|s| string(s.to_string())).collect())) } -pub fn vals(a:Vec<MalVal>) -> MalRet { +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 { + let hm = match *a[0] { 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)) + Ok(list(hm.values().map(|s| s.clone()).collect())) } - // Sequence functions -pub fn cons(a:Vec<MalVal>) -> MalRet { +pub fn cons(a: Vec<MalVal>) -> MalRet { match *a[1] { List(ref v,_) | Vector(ref v,_) => { let mut new_v = v.clone(); @@ -248,32 +229,28 @@ pub fn cons(a:Vec<MalVal>) -> MalRet { } } -pub fn concat(a:Vec<MalVal>) -> MalRet { - let mut new_v:Vec<MalVal> = vec![]; +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()); - }, + List(ref l,_) | Vector(ref l,_) => new_v.push_all(l), _ => return err_str("concat called with non-sequence"), } } Ok(list(new_v)) } -pub fn nth(a:Vec<MalVal>) -> MalRet { +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 { + let seq = match *a[0] { List(ref v,_) | Vector(ref v,_) => v, _ => return err_str("nth called with non-sequence"), }; - let idx = match *a1 { + let idx = match *a[1] { Int(i) => { - match i.to_uint() { + match i.to_usize() { Some(ui) => ui, None => return Ok(_nil()), } @@ -281,18 +258,17 @@ pub fn nth(a:Vec<MalVal>) -> MalRet { _ => return err_str("nth called with non-integer index"), }; if idx >= seq.len() { - return err_str("nth: index out of range") + err_str("nth: index out of range") } else { Ok(seq[idx].clone()) } } -pub fn first(a:Vec<MalVal>) -> MalRet { +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 { + let seq = match *a[0] { List(ref v,_) | Vector(ref v,_) => v, _ => return err_str("first called with non-sequence"), }; @@ -303,27 +279,26 @@ pub fn first(a:Vec<MalVal>) -> MalRet { } } -pub fn rest(a:Vec<MalVal>) -> MalRet { +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 { + let seq = match *a[0] { 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())) + Ok(list(seq[1..].to_vec())) } } -pub fn empty_q(a:Vec<MalVal>) -> MalRet { +pub fn empty_q(a: Vec<MalVal>) -> MalRet { if a.len() != 1 { return err_str("Wrong arity to empty? call"); } - match *a[0].clone() { + match *a[0] { List(ref v,_) | Vector(ref v,_) => { match v.len() { 0 => Ok(_true()), @@ -334,48 +309,42 @@ pub fn empty_q(a:Vec<MalVal>) -> MalRet { } } -pub fn count(a:Vec<MalVal>) -> MalRet { +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())) - }, + match *a[0] { + List(ref v,_) | Vector(ref v,_) => Ok(_int(v.len().to_isize().unwrap())), Nil => Ok(_int(0)), _ => err_str("count called on non-sequence"), } } -pub fn apply(a:Vec<MalVal>) -> MalRet { +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(); + let mut args = a[1..a.len()-1].to_vec(); match *a[a.len()-1] { - List(ref v,_) | Vector(ref v,_) => { - args.push_all(v.as_slice()); + List(ref v, _) | Vector(ref v, _) => { + args.push_all(v); f.apply(args) }, _ => err_str("apply call with non-sequence"), } } -pub fn map(a:Vec<MalVal>) -> MalRet { +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 { + match *a[1] { 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), - } + let res = try!(a[0].apply(vec![mv.clone()])); + results.push(res); } }, _ => return err_str("map call with non-sequence"), @@ -383,53 +352,52 @@ pub fn map(a:Vec<MalVal>) -> MalRet { Ok(list(results)) } -pub fn conj(a:Vec<MalVal>) -> MalRet { +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() { + let mut new_v: Vec<MalVal> = vec![]; + match *a[0] { List(ref l,_) => { - new_v.push_all(l.as_slice()); + new_v.push_all(l); for mv in a.iter().skip(1) { - new_v.insert(0,mv.clone()); + new_v.insert(0, mv.clone()); } Ok(list(new_v)) - }, + } Vector(ref l,_) => { - new_v.push_all(l.as_slice()); + new_v.push_all(l); for mv in a.iter().skip(1) { new_v.push(mv.clone()); } Ok(vector(new_v)) - }, - _ => return err_str("conj called with non-sequence"), + } + _ => err_str("conj called with non-sequence"), } } // Metadata functions -fn with_meta(a:Vec<MalVal>) -> MalRet { +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)), + match *a[0] { + 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 { +fn meta(a: Vec<MalVal>) -> MalRet { if a.len() != 1 { return err_str("Wrong arity to meta call"); } - match *a[0].clone() { + match *a[0] { List(_,ref meta) | Vector(_,ref meta) | Hash_Map(_,ref meta) | @@ -440,53 +408,42 @@ fn meta(a:Vec<MalVal>) -> MalRet { } // Atom functions -fn deref(a:Vec<MalVal>) -> MalRet { +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()) - }, + match *a[0] { + Atom(ref val) => Ok(val.borrow().clone()), _ => err_str("deref called on non-atom"), } } -fn reset_bang(a:Vec<MalVal>) -> MalRet { +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() { + match *a[0] { Atom(ref val) => { let mut val_cell = val.borrow_mut(); - let atm_mv = val_cell.deref_mut(); - *atm_mv = a1.clone(); - Ok(a1) + *val_cell = a[1].clone(); + Ok(a[1].clone()) }, _ => err_str("reset! called on non-atom"), } } -fn swap_bang(a:Vec<MalVal>) -> MalRet { +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() { + match *a[0] { 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), - } + let mut args = a[2..].to_vec(); + args.insert(0, val_cell.clone()); + *val_cell = try!(f.apply(args)); + Ok(val_cell.clone()) }, _ => err_str("swap! called on non-atom"), } @@ -494,7 +451,7 @@ fn swap_bang(a:Vec<MalVal>) -> MalRet { pub fn ns() -> HashMap<String,MalVal> { - let mut ns: HashMap<String,MalVal> = HashMap::new();; + let mut ns = HashMap::new();; ns.insert("=".to_string(), func(equal_q)); ns.insert("throw".to_string(), func(throw)); diff --git a/rust/src/env.rs b/rust/src/env.rs index e9af154..c27e499 100644 --- a/rust/src/env.rs +++ b/rust/src/env.rs @@ -1,11 +1,9 @@ -#![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}; +use types::{MalVal, MalRet, _nil, list, err_string}; +use types::MalType::{Sym, List, Vector}; struct EnvType { data: HashMap<String,MalVal>, @@ -27,10 +25,10 @@ pub fn env_bind(env: &Env, match *mexprs { List(ref exprs,_) | Vector(ref exprs,_) => { let mut it = binds.iter().enumerate(); - for (i, b) in it { + for (i, b) in it.by_ref() { match **b { Sym(ref strn) => { - if *strn == "&".to_string() { + if *strn == "&" { variadic = true; break; } else { @@ -44,7 +42,7 @@ pub fn env_bind(env: &Env, let (i, sym) = it.next().unwrap(); match **sym { Sym(_) => { - let rest = exprs.slice(i-1,exprs.len()).to_vec(); + let rest = exprs[i-1..].to_vec(); env_set(env, sym.clone(), list(rest)); } _ => return Err("& bind to non-symbol".to_string()), @@ -59,14 +57,15 @@ pub fn env_bind(env: &Env, } } -pub fn env_find(env: Env, key: MalVal) -> Option<Env> { - match *key { +pub fn env_find(env: &Env, key: &MalVal) -> Option<Env> { + match **key { Sym(ref k) => { - if env.borrow().data.contains_key(k) { - Some(env) + let map = env.borrow(); + if map.data.contains_key(k) { + Some(env.clone()) } else { - match env.borrow().outer { - Some(ref e) => env_find(e.clone(), key.clone()), + match map.outer { + Some(ref e) => env_find(e, key), None => None, } } @@ -84,35 +83,24 @@ pub fn env_root(env: &Env) -> Env { 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()); - }, + Sym(ref k) => { env.borrow_mut().data.insert(k.to_string(), val); } _ => {}, } } -pub fn env_get(env: Env, key: MalVal) -> MalRet { - match *key { +pub fn env_get(env: &Env, key: &MalVal) -> MalRet { + match **key { Sym(ref k) => { - match env_find(env, key.clone()) { + match env_find(env, key) { Some(e) => { - match e.borrow().data.find_copy(k) { - Some(v) => Ok(v), + match e.borrow().data.get(k) { + Some(v) => Ok(v.clone()), None => Ok(_nil()), } }, - None => err_string("'".to_string() + k.to_string() + "' not found".to_string()), + None => err_string(format!("'{}' not found", k)), } } _ => 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/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..04eda85 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,17 @@ +#![feature(io, fs, core, std_misc, collections)] + +extern crate libc; +extern crate pcre; +extern crate regex; +extern crate time; + +macro_rules! regex { + ($e:expr) => (::regex::Regex::new($e).unwrap()) +} + +pub mod core; +pub mod env; +pub mod printer; +pub mod reader; +pub mod readline; +pub mod types; diff --git a/rust/src/printer.rs b/rust/src/printer.rs index f46b66c..591822b 100644 --- a/rust/src/printer.rs +++ b/rust/src/printer.rs @@ -3,7 +3,7 @@ use types::MalVal; pub fn escape_str(s: &str) -> String { let mut escaped = String::new(); escaped.push('"'); - for c in s.as_slice().chars() { + for c in s.chars() { let _ = match c { '"' => escaped.push_str("\\\""), '\\' => escaped.push_str("\\\\"), @@ -24,7 +24,7 @@ pub fn escape_str(s: &str) -> String { 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") + re2.replace_all(&re1.replace_all(&s, "\""), "\n") } pub fn pr_list(lst: &Vec<MalVal>, pr: bool, @@ -38,7 +38,7 @@ pub fn pr_list(lst: &Vec<MalVal>, pr: bool, } else { res.push_str(join); } - res.push_str(mv.pr_str(pr).as_slice()); + res.push_str(&mv.pr_str(pr)); } res.push_str(end); res diff --git a/rust/src/reader.rs b/rust/src/reader.rs index d7b2b4c..4750c46 100644 --- a/rust/src/reader.rs +++ b/rust/src/reader.rs @@ -1,20 +1,14 @@ -//#![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 types::MalError::{ErrString, ErrMalVal}; +use types::{MalVal, MalRet, + _nil, _true, _false, _int, symbol, string, list, vector, hash_mapv, + err_str, err_string, err_val}; +use pcre::Pcre; use super::printer::unescape_str; -#[deriving(Show, Clone)] +#[derive(Debug, Clone)] struct Reader { - tokens : Vec<String>, - position : uint, + tokens: Vec<String>, + position: usize, } impl Reader { @@ -35,15 +29,15 @@ impl Reader { } } -fn tokenize(str :String) -> Vec<String> { +fn tokenize(str: String) -> Vec<String> { let mut results = vec![]; let re = match Pcre::compile(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)"###) { - Err(_) => { fail!("failed to compile regex") }, + Err(_) => { panic!("failed to compile regex") }, Ok(re) => re }; - let mut it = re.matches(str.as_slice()); + let mut it = re.matches(&str); loop { let opt_m = it.next(); if opt_m.is_none() { break; } @@ -61,15 +55,15 @@ fn read_atom(rdr : &mut Reader) -> MalRet { //println!("read_atom: {}", otoken); if otoken.is_none() { return err_str("read_atom underflow"); } let stoken = otoken.unwrap(); - let token = stoken.as_slice(); + let token = &stoken[..]; if regex!(r"^-?[0-9]+$").is_match(token) { - let num : Option<int> = from_str(token); + let num : Option<isize> = token.parse().ok(); Ok(_int(num.unwrap())) } else if regex!(r#"^".*"$"#).is_match(token) { - let new_str = token.slice(1,token.len()-1); + let new_str = &token[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()))) + Ok(string(format!("\u{29e}{}", &token[1..]))) } else if token == "nil" { Ok(_nil()) } else if token == "true" { @@ -87,19 +81,19 @@ fn read_seq(rdr : &mut Reader, start: &str, end: &str) -> Result<Vec<MalVal>,Str return Err("read_atom underflow".to_string()); } let stoken = otoken.unwrap(); - let token = stoken.as_slice(); + let token = &stoken[..]; if token != start { - return Err("expected '".to_string() + start.to_string() + "'".to_string()); + return Err(format!("expected '{}'", start)) } 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()); + return Err(format!("expected '{}', got EOF", end)); } let stoken = otoken.unwrap(); - let token = stoken.as_slice(); + let token = &stoken[..]; if token == end { break; } match read_form(rdr) { @@ -138,7 +132,7 @@ fn read_form(rdr : &mut Reader) -> MalRet { let otoken = rdr.peek(); //println!("read_form: {}", otoken); let stoken = otoken.unwrap(); - let token = stoken.as_slice(); + let token = &stoken[..]; match token { "'" => { let _ = rdr.next(); diff --git a/rust/src/readline.rs b/rust/src/readline.rs index 17d1ed9..37bf674 100644 --- a/rust/src/readline.rs +++ b/rust/src/readline.rs @@ -1,10 +1,11 @@ // Based on: https://github.com/shaleh/rust-readline (MIT) -extern crate libc; +use libc; -use std::c_str; - -use std::io::{File, Append, Write}; -use std::io::BufferedReader; +use std::ffi::{CStr, CString}; +use std::fs::{OpenOptions, File}; +use std::io::BufReader; +use std::io::prelude::*; +use std::str; mod ext_readline { extern crate libc; @@ -18,19 +19,21 @@ mod ext_readline { pub fn add_history(line: &str) { unsafe { - ext_readline::add_history(line.to_c_str().as_ptr()); + ext_readline::add_history(CString::new(line).unwrap().as_ptr()); } } pub fn readline(prompt: &str) -> Option<String> { - let cprmt = prompt.to_c_str(); + let cprmt = CString::new(prompt).unwrap(); unsafe { - let ret = ext_readline::readline(cprmt.as_ptr()); - if ret.is_null() { // user pressed Ctrl-D + let ptr = ext_readline::readline(cprmt.as_ptr()); + if ptr.is_null() { // user pressed Ctrl-D None - } - else { - c_str::CString::new(ret, true).as_str().map(|ret| ret.to_string()) + } else { + let ret = str::from_utf8(CStr::from_ptr(ptr).to_bytes()); + let ret = ret.ok().map(|s| s.to_string()); + libc::free(ptr as *mut _); + return ret; } } } @@ -38,7 +41,7 @@ pub fn readline(prompt: &str) -> Option<String> { // -------------------------------------------- static mut history_loaded : bool = false; -static HISTORY_FILE : &'static str = "/home/joelm/.mal-history"; +static HISTORY_FILE: &'static str = "/home/joelm/.mal-history"; fn load_history() { unsafe { @@ -46,31 +49,33 @@ fn load_history() { history_loaded = true; } - let path = Path::new(HISTORY_FILE); - let mut file = BufferedReader::new(File::open(&path)); + let file = match File::open(HISTORY_FILE) { + Ok(f) => f, + Err(..) => return + }; + let file = BufReader::new(file); for line in file.lines() { let rt: &[_] = &['\r', '\n']; let line2 = line.unwrap(); - let line3 = line2.as_slice().trim_right_chars(rt); + let line3 = line2.trim_right_matches(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); + let file = OpenOptions::new().append(true).write(true).create(true) + .open(HISTORY_FILE); + let mut file = match file { Ok(f) => f, Err(..) => return }; + let _ = file.write_all(line.as_bytes()); + let _ = file.write_all(b"\n"); } 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 - } + if let Some(ref s) = line { + add_history(s); + append_to_history(s); } + line } diff --git a/rust/src/stepA_mal.rs b/rust/src/stepA_mal.rs deleted file mode 100644 index 8e30867..0000000 --- a/rust/src/stepA_mal.rs +++ /dev/null @@ -1,479 +0,0 @@ -// 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 index 141c3db..0ad036c 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -7,28 +7,29 @@ use std::fmt; use super::printer::{escape_str,pr_list}; use super::env::{Env,env_new,env_bind}; -#[deriving(Clone)] +use self::MalType::*; +use self::MalError::*; + +#[derive(Clone)] #[allow(non_camel_case_types)] pub enum MalType { Nil, True, False, - Int(int), + Int(isize), 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)] +#[derive(Debug)] pub enum MalError { ErrString(String), ErrMalVal(MalVal), @@ -49,16 +50,7 @@ pub fn err_val(mv: MalVal) -> MalRet { Err(ErrMalVal(mv)) } -/* -pub enum MalRet { - Val(MalVal), - MalErr(MalVal), - StringErr(String), -} -*/ - - -#[deriving(Clone)] +#[derive(Clone)] pub struct MalFuncData { pub eval: fn(MalVal, Env) -> MalRet, pub exp: MalVal, @@ -70,59 +62,51 @@ pub struct MalFuncData { 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()), + Nil => "nil".to_string(), + True => "true".to_string(), + False => "false".to_string(), + Int(v) => v.to_string(), + Sym(ref v) => v.clone(), Strn(ref v) => { - if v.as_slice().starts_with("\u029e") { - res.push_str(":"); - res.push_str(v.as_slice().slice(2,v.len())) + if v.starts_with("\u{29e}") { + format!(":{}", &v[2..]) } else if print_readably { - res.push_str(escape_str((*v).as_slice()).as_slice()) + escape_str(v) } else { - res.push_str(v.as_slice()) + v.clone() } }, List(ref v,_) => { - res = pr_list(v, _r, "(", ")", " ") + pr_list(v, _r, "(", ")", " ") }, Vector(ref v,_) => { - res = pr_list(v, _r, "[", "]", " ") + pr_list(v, _r, "[", "]", " ") }, Hash_Map(ref v,_) => { - let mut first = true; + let mut res = String::new(); res.push_str("{"); - for (key, value) in v.iter() { - if first { first = false; } else { res.push_str(" "); } - if key.as_slice().starts_with("\u029e") { + for (i, (key, value)) in v.iter().enumerate() { + if i != 0 { res.push_str(" "); } + if key.starts_with("\u{29e}") { res.push_str(":"); - res.push_str(key.as_slice().slice(2,key.len())) + res.push_str(&key[2..]) } else if print_readably { - res.push_str(escape_str(key.as_slice()).as_slice()) + res.push_str(&escape_str(key)) } else { - res.push_str(key.as_slice()) + res.push_str(key) } res.push_str(" "); - res.push_str(value.pr_str(_r).as_slice()); + res.push_str(&value.pr_str(_r)); } - res.push_str("}") + res.push_str("}"); + res }, // 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 + Func(_, _) => format!("#<function ...>"), + MalFunc(ref mf,_) => format!("(fn* {:?} {:?})", mf.params, mf.exp), + Atom(ref v) => format!("(atom {:?})", &**v.borrow()), + } } pub fn apply(&self, args:Vec<MalVal>) -> MalRet { @@ -161,11 +145,11 @@ impl PartialEq for MalType { (&Func(_,_), &Func(_,_)) => false, (&MalFunc(_,_), &MalFunc(_,_)) => false, _ => return false, - } + } } } -impl fmt::Show for MalType { +impl fmt::Debug for MalType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.pr_str(true)) } @@ -206,7 +190,7 @@ pub fn false_q(a:Vec<MalVal>) -> MalRet { } } -pub fn _int(i: int) -> MalVal { Rc::new(Int(i)) } +pub fn _int(i: isize) -> MalVal { Rc::new(Int(i)) } // Symbols @@ -237,11 +221,9 @@ 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"), + match *a[0] { + Strn(ref s) => Ok(Rc::new(Strn(format!("\u{29e}{}", s)))), + _ => err_str("keyword called on non-string"), } } pub fn keyword_q(a:Vec<MalVal>) -> MalRet { @@ -250,7 +232,7 @@ pub fn keyword_q(a:Vec<MalVal>) -> MalRet { } match *a[0].clone() { Strn(ref s) => { - if s.as_slice().starts_with("\u029e") { + if s.starts_with("\u{29e}") { Ok(_true()) } else { Ok(_false()) |
