diff options
| author | Joel Martin <github@martintribe.org> | 2014-10-27 18:13:45 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-06 21:58:59 -0600 |
| commit | a77e2b31de9d1c1f5767e6bff56062f8b3c71211 (patch) | |
| tree | 1dcf0de2c407510aee4fac2ac5a8dfef7b63c963 /rust/src | |
| parent | 4ee7c0f2970accc0f49eeac7fb2a0359b159c8ae (diff) | |
| download | mal-a77e2b31de9d1c1f5767e6bff56062f8b3c71211.tar.gz mal-a77e2b31de9d1c1f5767e6bff56062f8b3c71211.zip | |
rust: add step8_macros
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/core.rs | 61 | ||||
| -rw-r--r-- | rust/src/step2_eval.rs | 16 | ||||
| -rw-r--r-- | rust/src/step3_env.rs | 16 | ||||
| -rw-r--r-- | rust/src/step4_if_fn_do.rs | 33 | ||||
| -rw-r--r-- | rust/src/step5_tco.rs | 11 | ||||
| -rw-r--r-- | rust/src/step6_file.rs | 9 | ||||
| -rw-r--r-- | rust/src/step7_quote.rs | 9 | ||||
| -rw-r--r-- | rust/src/step8_macros.rs | 438 | ||||
| -rw-r--r-- | rust/src/types.rs | 37 |
9 files changed, 546 insertions, 84 deletions
diff --git a/rust/src/core.rs b/rust/src/core.rs index 94042d6..3e1889c 100644 --- a/rust/src/core.rs +++ b/rust/src/core.rs @@ -142,6 +142,64 @@ pub fn concat(a:Vec<MalVal>) -> MalRet { Ok(list(new_v)) } +pub fn nth(a:Vec<MalVal>) -> MalRet { + if a.len() != 2 { + return Err("Wrong arity to nth call".to_string()); + } + let a0 = a[0].clone(); + let a1 = a[1].clone(); + let seq = match *a0 { + List(ref v) => v, + _ => return Err("nth called with non-sequence".to_string()), + }; + let idx = match *a1 { + Int(i) => { + match i.to_uint() { + Some(ui) => ui, + None => return Ok(_nil()), + } + }, + _ => return Err("nth called with non-integer index".to_string()), + }; + if idx >= seq.len() { + Ok(_nil()) + } else { + Ok(seq[idx].clone()) + } +} + +pub fn first(a:Vec<MalVal>) -> MalRet { + if a.len() != 1 { + return Err("Wrong arity to first call".to_string()); + } + let a0 = a[0].clone(); + let seq = match *a0 { + List(ref v) => v, + _ => return Err("first called with non-sequence".to_string()), + }; + if seq.len() == 0 { + Ok(_nil()) + } else { + Ok(seq[0].clone()) + } +} + +pub fn rest(a:Vec<MalVal>) -> MalRet { + if a.len() != 1 { + return Err("Wrong arity to rest call".to_string()); + } + let a0 = a[0].clone(); + let seq = match *a0 { + List(ref v) => v, + _ => return Err("rest called with non-sequence".to_string()), + }; + if seq.len() == 0 { + Ok(list(vec![])) + } else { + Ok(list(seq.slice(1,seq.len()).to_vec())) + } +} + pub fn count(a:Vec<MalVal>) -> MalRet { if a.len() != 1 { return Err("Wrong arity to count call".to_string()); @@ -198,6 +256,9 @@ pub fn ns() -> HashMap<String,MalVal> { ns.insert("cons".to_string(), func(cons)); ns.insert("concat".to_string(), func(concat)); ns.insert("empty?".to_string(), func(empty_q)); + ns.insert("nth".to_string(), func(nth)); + ns.insert("first".to_string(), func(first)); + ns.insert("rest".to_string(), func(rest)); ns.insert("count".to_string(), func(count)); return ns; diff --git a/rust/src/step2_eval.rs b/rust/src/step2_eval.rs index 3b27762..f8046b9 100644 --- a/rust/src/step2_eval.rs +++ b/rust/src/step2_eval.rs @@ -6,7 +6,7 @@ extern crate regex; use std::collections::HashMap; -use types::{MalVal,MalRet,Int,Sym,List,Func, +use types::{MalVal,MalRet,Int,Sym,List, _nil,_int,list,func}; mod readline; mod types; @@ -58,18 +58,8 @@ fn eval(ast: MalVal, env: &HashMap<String,MalVal>) -> MalRet { 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()), - _ => Err("attempt to call non-function".to_string()), - } + let ref f = args.clone()[0]; + f.apply(args.slice(1,args.len()).to_vec()) } _ => Err("Invalid apply".to_string()), } diff --git a/rust/src/step3_env.rs b/rust/src/step3_env.rs index 98d918b..4646a97 100644 --- a/rust/src/step3_env.rs +++ b/rust/src/step3_env.rs @@ -4,7 +4,7 @@ extern crate regex_macros; extern crate regex; -use types::{MalVal,MalRet,Int,Sym,List,Vector,Func, +use types::{MalVal,MalRet,Int,Sym,List,Vector, _int,list,func}; use env::{Env,env_new,env_set,env_get}; mod readline; @@ -123,18 +123,8 @@ fn eval(ast: MalVal, env: Env) -> MalRet { 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()), - _ => Err("attempt to call non-function".to_string()), - } + let ref f = args.clone()[0]; + f.apply(args.slice(1,args.len()).to_vec()) } _ => Err("Invalid apply".to_string()), } diff --git a/rust/src/step4_if_fn_do.rs b/rust/src/step4_if_fn_do.rs index acf18bf..175d1be 100644 --- a/rust/src/step4_if_fn_do.rs +++ b/rust/src/step4_if_fn_do.rs @@ -4,10 +4,10 @@ extern crate regex_macros; extern crate regex; -use types::{MalVal,MalRet,MalFunc, - Nil,False,Sym,List,Vector,Func, +use types::{MalVal,MalRet, + Nil,False,Sym,List,Vector, _nil,list,malfunc}; -use env::{Env,env_new,env_bind,env_set,env_get}; +use env::{Env,env_new,env_set,env_get}; mod readline; mod types; mod reader; @@ -115,7 +115,7 @@ fn eval(ast: MalVal, env: Env) -> MalRet { return eval(a2, let_env.clone()); }, "do" => { - let el = list(args.slice(1,args.len()-1).to_vec()); + let el = list(args.slice(1,args.len()).to_vec()); match eval_ast(el, env.clone()) { Err(e) => return Err(e), Ok(el) => { @@ -151,7 +151,7 @@ fn eval(ast: MalVal, env: Env) -> MalRet { "fn*" => { let a1 = (*args)[1].clone(); let a2 = (*args)[2].clone(); - return Ok(malfunc(a2, env.clone(), a1)); + return Ok(malfunc(eval, a2, env.clone(), a1)); }, _ => () } @@ -164,27 +164,8 @@ fn eval(ast: MalVal, env: Env) -> MalRet { 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, alst) { - Ok(_) => eval(mfc.exp, new_env), - Err(e) => Err(e), - } - }, - _ => Err("attempt to call non-function".to_string()), - } + let ref f = args.clone()[0]; + f.apply(args.slice(1,args.len()).to_vec()) } _ => Err("Invalid apply".to_string()), } diff --git a/rust/src/step5_tco.rs b/rust/src/step5_tco.rs index 3051fa7..345ec52 100644 --- a/rust/src/step5_tco.rs +++ b/rust/src/step5_tco.rs @@ -51,13 +51,13 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { //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 + let ast3 = ast2.clone(); match *ast2 { List(ref args) => { if args.len() == 0 { @@ -156,7 +156,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { "fn*" => { let a1 = (*args)[1].clone(); let a2 = (*args)[2].clone(); - return Ok(malfunc(a2, env.clone(), a1)); + return Ok(malfunc(eval, a2, env.clone(), a1)); }, _ => () } @@ -169,13 +169,6 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { 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()), diff --git a/rust/src/step6_file.rs b/rust/src/step6_file.rs index 69757fe..1b3427c 100644 --- a/rust/src/step6_file.rs +++ b/rust/src/step6_file.rs @@ -158,7 +158,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { "fn*" => { let a1 = (*args)[1].clone(); let a2 = (*args)[2].clone(); - return Ok(malfunc(a2, env.clone(), a1)); + return Ok(malfunc(eval, a2, env.clone(), a1)); }, "eval" => { let a1 = (*args)[1].clone(); @@ -182,13 +182,6 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { 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()), diff --git a/rust/src/step7_quote.rs b/rust/src/step7_quote.rs index ae284d4..c33d75b 100644 --- a/rust/src/step7_quote.rs +++ b/rust/src/step7_quote.rs @@ -217,7 +217,7 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { "fn*" => { let a1 = (*args)[1].clone(); let a2 = (*args)[2].clone(); - return Ok(malfunc(a2, env.clone(), a1)); + return Ok(malfunc(eval, a2, env.clone(), a1)); }, "eval" => { let a1 = (*args)[1].clone(); @@ -241,13 +241,6 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { 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()), diff --git a/rust/src/step8_macros.rs b/rust/src/step8_macros.rs new file mode 100644 index 0000000..c8cc9b3 --- /dev/null +++ b/rust/src/step8_macros.rs @@ -0,0 +1,438 @@ +// support precompiled regexes in reader.rs +#![feature(phase)] +#[phase(plugin)] +extern crate regex_macros; +extern crate regex; + +use std::os; + +use types::{MalVal,MalRet,MalFunc, + Nil,False,Sym,List,Vector,Func, + _nil,symbol,string,list,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) => 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) => { + 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) => { + 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) => { + let ref a0 = *lst[0]; + match *a0 { + Sym(ref a0sym) => { + if env_find(env.clone(), a0sym.to_string()).is_some() { + match env_get(env, a0sym.to_string()) { + 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()) { + match *ast.clone() { + List(ref args) => { + let ref a0 = args[0]; + match **a0 { + Sym(ref s) => { + match env_get(env.clone(), s.to_string()) { + Ok(mf) => { + match *mf { + MalFunc(_) => { + match mf.apply(args.slice(1,args.len()).to_vec()) { + Ok(r) => ast = r, + Err(e) => return Err(e), + } + }, + _ => break, + } + }, + Err(e) => return Err(e), + } + }, + _ => break, + } + }, + _ => break, + } + } + Ok(ast) +} + +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(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 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(); + + 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; + }, + "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(ref s) => { + let mut new_mfd = mfd.clone(); + new_mfd.is_macro = true; + let mf = malfuncd(new_mfd); + env_set(&env.clone(), s.clone(), mf.clone()); + return Ok(mf); + }, + _ => return Err("def! of non-symbol".to_string()), + } + }, + _ => return Err("def! of non-symbol".to_string()), + } + }, + Err(e) => return Err(e), + } + }, + "macroexpand" => { + let a1 = (*args)[1].clone(); + return macroexpand(a1, env.clone()) + }, + "do" => { + let el = list(args.slice(1,args.len()-1).to_vec()); + match eval_ast(el, env.clone()) { + Err(e) => return Err(e), + Ok(_) => { + let ref last = args[args.len()-1]; + ast = last.clone(); + continue 'tco; + }, + } + }, + "if" => { + let a1 = (*args)[1].clone(); + let cond = eval(a1, env.clone()); + 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(_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)); + }, + "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 + /* + if is_macro_call(ast3.clone(), env.clone()) { + println!("macro call"); + } + */ + return match eval_ast(ast3, env.clone()) { + Err(e) => Err(e), + Ok(el) => { + match *el { + List(ref args) => { + 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, 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(), 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| string(a.to_string())) + .collect::<Vec<MalVal>>(); + env_set(&repl_env, "*ARGV*".to_string(), 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), + } + } +} diff --git a/rust/src/types.rs b/rust/src/types.rs index 85262c7..b8e1de0 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use std::collections; use std::fmt; use super::printer::{escape_str,pr_list}; -use super::env::Env; +use super::env::{Env,env_new,env_bind}; #[deriving(Clone)] pub enum MalType { @@ -29,12 +29,13 @@ pub type MalRet = Result<MalVal,String>; #[deriving(Clone)] pub struct MalFuncData { - pub exp: MalVal, - pub env: Env, - pub params: MalVal, + pub eval: fn(MalVal, Env) -> MalRet, + pub exp: MalVal, + pub env: Env, + pub params: MalVal, + pub is_macro: bool, } - impl MalType { pub fn pr_str(&self, print_readably: bool) -> String { let _r = print_readably; @@ -86,6 +87,22 @@ impl MalType { res } + pub fn apply(&self, args:Vec<MalVal>) -> MalRet { + match *self { + Func(f) => f(args), + MalFunc(ref mf) => { + let mfc = mf.clone(); + let alst = list(args); + let new_env = env_new(Some(mfc.env.clone())); + match env_bind(&new_env, mfc.params, alst) { + Ok(_) => (mfc.eval)(mfc.exp, new_env), + Err(e) => Err(e), + } + }, + _ => Err("attempt to call non-function".to_string()), + } + + } } impl PartialEq for MalType { @@ -114,6 +131,7 @@ impl fmt::Show for MalType { } } + // Convenience constructor functions pub fn _nil() -> MalVal { Rc::new(Nil) } pub fn _true() -> MalVal { Rc::new(True) } @@ -129,6 +147,11 @@ pub fn list(lst: Vec<MalVal>) -> MalVal { Rc::new(List(lst)) } pub fn func(f: fn(Vec<MalVal>) -> MalRet ) -> MalVal { Rc::new(Func(f)) } -pub fn malfunc(exp: MalVal, env: Env, params: MalVal) -> MalVal { - Rc::new(MalFunc(MalFuncData{ exp: exp, env: env, params: params})) +pub fn malfunc(eval: fn(MalVal, Env) -> MalRet, + exp: MalVal, env: Env, params: MalVal) -> MalVal { + Rc::new(MalFunc(MalFuncData{eval: eval, exp: exp, env: env, + params: params, is_macro: false})) +} +pub fn malfuncd(mfd: MalFuncData) -> MalVal { + Rc::new(MalFunc(mfd)) } |
