diff --git a/src/eval.rs b/src/eval.rs index ca71fed..9d4594b 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -30,10 +30,107 @@ pub fn eval( vars: Rc>, funcs: Rc>, sym_loose: bool -) -> Result { - eval_inner(ast, vars, funcs, sym_loose, true) +) -> Result { + let mut car = ast.borrow().clone().car; + let mut cdr = ast.borrow().clone().cdr; + let ret = new_ast(Ctr::None, Ctr::None); + let mut iter = ret.clone(); + + // doing an initial variable check here allows us + // to find functions passed in as variables + if let Ctr::Symbol(ref tok) = car { + if let Some(val) = vars.borrow().get(tok) { + car = (**val).clone(); + } + } + + // another check to detect if we may have a function call + if let Ctr::Symbol(ref tok) = car { + if let Ctr::Seg(ast) = cdr { + if let Some(func) = funcs.borrow().get(tok) { + return func_call(func.clone(), ast.clone(), vars.clone(), funcs.clone()) + } else { + return Err(format!("Couldnt find function: {}.", tok)) + } + } else { + return Err(format!("Arguments to function not a list!")) + } + } + + let mut none = false; + while !none { + match car { + // if LIST: call eval inner on it with first_item=true + Ctr::Seg(ref inner) => { + match eval(inner.clone(), vars.clone(), funcs.clone(), sym_loose) { + Ok(res) => (*iter).borrow_mut().car = res, + Err(e) => return Err(format!("Evaluation error: {}", e)) + } + }, + + // if SYMBOL: unwrap naively + Ctr::Symbol(ref tok) => { + if let Some(val) = vars.borrow().get(&tok.clone()) { + (*iter).borrow_mut().car = (**val).clone(); + } else if sym_loose { + (*iter).borrow_mut().car = Ctr::Symbol(tok.to_string()); + } else { + return Err(format!("Undefined variable: {}", tok)) + } + }, + + // if OTHER: clone and set + _ => { + (*iter).borrow_mut().car = car.clone(); + } + } + + match cdr { + // if SYMBOL: unwrap naively, then end + Ctr::Symbol(ref tok) => { + if let Some(val) = vars.borrow().get(&tok.clone()) { + (*iter).borrow_mut().car = (**val).clone(); + } else if sym_loose { + (*iter).borrow_mut().car = Ctr::Symbol(tok.to_string()); + } else { + return Err(format!("Undefined variable: {}", tok)) + } + + none = true; + }, + + // if LIST: + // - iter.cdr = new_ast(None, None) + // - iter = iter.cdr + // - car = cdr.car + // - cdr = cdr.cdr + // - LOOP + Ctr::Seg(next) => { + let n = new_ast(Ctr::None, Ctr::None); + iter.borrow_mut().cdr = Ctr::Seg(n.clone()); + iter = n; + car = next.borrow().clone().car; + cdr = next.borrow().clone().cdr; + } + + // if OTHER: clone and set, and then end + _ => { + (*iter).borrow_mut().cdr = cdr.clone(); + none = true; + } + } + + if let Ctr::None = car { + if let Ctr::None = cdr { + none = true; + } + } + } + + return Ok(Ctr::Seg(ret)) } +/* fn eval_inner( ast: Ast, vars: Rc>, @@ -54,6 +151,8 @@ fn eval_inner( let res_car; let res_cdr; + // Handle Function Case + match process_ctr( ast.borrow().clone().car, vars.clone(), @@ -113,8 +212,10 @@ fn process_ctr( match ctr.clone() { Ctr::Symbol(token) => { + print!("[+] Detected Symbol: {}\n", token); let mut tok = token; if let Some(s) = vars.borrow().get(&tok) { + print!("[+] Found symbol value\n"); // is function, or variable alias? let mut pass = false; @@ -141,12 +242,15 @@ fn process_ctr( if first_item { if let Some(f) = funcs.borrow().get(&tok) { if let Ctr::Seg(args) = rest { + print!("[+] Calling function: {}\n", tok); match func_call(f.clone(), args, vars.clone(), funcs.clone()) { - Ok(a) => return Ok(a.clone()), + Ok(a) => { + print!("[+] Function Call Result: {:#?}\n", a); + return Ok(a.clone()) + }, Err(s) => return Err(s) } } else { - println!("AC::: {:?}\n", rest); return Err("evaluation: args to function not a list".to_string()) } } else { @@ -160,7 +264,7 @@ fn process_ctr( }, Ctr::Seg(tree) => { // if list is_car then we need to set first_item to true - match eval_inner(tree, vars, funcs, sym_loose, is_car && first_item) { + match eval_inner(tree, vars, funcs, sym_loose, is_car) { Ok(ast) => { Ok(Ctr::Seg(ast)) }, @@ -172,3 +276,4 @@ fn process_ctr( _ => Ok(ctr.clone()) } } +*/ diff --git a/src/func.rs b/src/func.rs index 331b013..94d522a 100644 --- a/src/func.rs +++ b/src/func.rs @@ -81,7 +81,13 @@ pub fn func_call( let mut n_args: Ast = args.clone(); if !called_func.eval_lazy { match eval(args, vars.clone(), funcs.clone(), called_func.loose_syms) { - Ok(rc_seg) => n_args = rc_seg.clone(), + Ok(rc_seg) => { + if let Ctr::Seg(ast) = rc_seg { + n_args = ast + } else { + return Err("Panicking: eval returned not a list for function args.".to_string()) + } + }, Err(s) => return Err( format!( "error evaluating args to {}: {}", @@ -166,15 +172,32 @@ pub fn func_call( ); } - let result = eval(f.ast.clone(), vars.clone(), funcs, called_func.loose_syms); + let mut result = Ctr::None; + let mut iterate = f.ast.clone(); + loop { + if let Ctr::Seg(ast) = iterate.borrow().clone().car { + match eval(ast, vars.clone(), funcs.clone(), called_func.loose_syms) { + Ok(ctr) => { + if let Ctr::Seg(ast) = ctr { + result = ast.borrow().clone().car; + } + }, + Err(e) => return Err(e) + } + } + + match iterate.clone().borrow().clone().cdr { + Ctr::Seg(next) => iterate = next.clone(), + Ctr::None => break, + _ => panic!("function body not in standard form!") + } + } + for n in 0..f.arg_syms.len() { vars.borrow_mut().remove(&f.arg_syms[n].clone()); } - match result { - Ok(r) => Ok(r.borrow().clone().car), - Err(e) => Err(e) - } + return Ok(result) } } } diff --git a/tests/test_eval.rs b/tests/test_eval.rs new file mode 100644 index 0000000..93033ec --- /dev/null +++ b/tests/test_eval.rs @@ -0,0 +1,228 @@ +mod func_tests { + use std::rc::Rc; + use std::cell::RefCell; + use relish::ast::{Ctr, Function, Operation, ExternalOperation}; + use relish::ast::{new_ast, lex, eval, VTable, FTable, ast_to_string}; + use relish::ast::{func_declare, Args}; + + // TODO: write generalized testing routine on top of list of inputs + + #[test] + fn eval_singlet() { + let test_doc = "(1)".to_string(); + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + + match lex(test_doc.clone()) { + Err(e) => { + println!("Lexing error: {}\n", e); + assert!(false) + }, + + Ok(initial_ast) => { + match eval(initial_ast.clone(), vt.clone(), ft.clone(), false) { + Err(e) => { + println!("Evaluation error: {}\n", e); + assert!(false) + }, + + Ok(reduced) => { + if let Ctr::Seg(reduced_ast) = reduced { + assert_eq!(ast_to_string(reduced_ast), test_doc) + } + } + } + } + } + } + + #[test] + fn eval_embedded_lists_no_funcs() { + let test_doc = "(1 (1 2 3 4 5) 5)".to_string(); + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + + match lex(test_doc.clone()) { + Err(e) => { + println!("Lexing error: {}\n", e); + assert!(false) + }, + + Ok(initial_ast) => { + match eval(initial_ast.clone(), vt.clone(), ft.clone(), false) { + Err(e) => { + println!("Evaluation error: {}\n", e); + assert!(false) + }, + + Ok(reduced) => { + if let Ctr::Seg(reduced_ast) = reduced { + assert_eq!(ast_to_string(reduced_ast), test_doc) + } + } + } + } + } + } + + #[test] + fn eval_function_call() { + let test_doc = "('one' (echo 'unwrap_me'))".to_string(); + let output = "('one' 'unwrap_me')"; + let test_external_func: Function = Function{ + name: String::from("echo"), + loose_syms: false, + eval_lazy: false, + args: Args::Lazy(1), + function: Operation::External( + ExternalOperation{ + arg_syms: vec!["input".to_string()], + ast: new_ast(Ctr::Seg(new_ast(Ctr::Symbol("input".to_string()), Ctr::None)), Ctr::None) + } + ) + }; + + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + if let Some(s) = func_declare(ft.clone(), + Rc::new(RefCell::new(test_external_func))) { + print!("Error declaring external func: {}", s); + assert!(false); + } + + match lex(test_doc) { + Err(e) => { + println!("Lexing error: {}\n", e); + assert!(false) + }, + + Ok(initial_ast) => { + match eval(initial_ast.clone(), vt.clone(), ft.clone(), false) { + Err(e) => { + println!("Evaluation error: {}\n", e); + assert!(false) + }, + + Ok(reduced) => { + if let Ctr::Seg(reduced_ast) = reduced { + let out_doc = ast_to_string(reduced_ast); + if out_doc != output { + print!("Erroneous output: {}\n", out_doc); + assert!(false) + } + } + } + } + } + } + } + + /* + #[test] + fn eval_embedded_func_calls() { + let test_doc = "1".to_string(); + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + + match lex(test_doc) { + Err(e) => { + println!("Lexing error: {}\n", e); + assert!(false) + }, + + Ok(initial_ast) => { + match eval(initial_ast.clone(), vt.clone(), ft.clone(), false) { + Err(e) => { + println!("Evaluation error: {}\n", e); + assert!(false) + }, + + Ok(reduced_ast) => { + // write tests here + } + } + } + } + } + + #[test] + fn eval_bad_vars() { + let test_doc = "1".to_string(); + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + + match lex(test_doc) { + Err(e) => { + println!("Lexing error: {}\n", e); + assert!(false) + }, + + Ok(initial_ast) => { + match eval(initial_ast.clone(), vt.clone(), ft.clone(), false) { + Err(e) => { + println!("Evaluation error: {}\n", e); + assert!(false) + }, + + Ok(reduced_ast) => { + // write tests here + } + } + } + } + } + + #[test] + fn eval_bad_func() { + let test_doc = "1".to_string(); + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + + match lex(test_doc) { + Err(e) => { + println!("Lexing error: {}\n", e); + assert!(false) + }, + + Ok(initial_ast) => { + match eval(initial_ast.clone(), vt.clone(), ft.clone(), false) { + Err(e) => { + println!("Evaluation error: {}\n", e); + assert!(false) + }, + + Ok(reduced_ast) => { + // write tests here + } + } + } + } + } + + #[test] + fn eval_verify_all_elems_cloned() { + let test_doc = "1".to_string(); + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + + match lex(test_doc) { + Err(e) => { + println!("Lexing error: {}\n", e); + assert!(false) + }, + + Ok(initial_ast) => { + match eval(initial_ast.clone(), vt.clone(), ft.clone(), false) { + Err(e) => { + println!("Evaluation error: {}\n", e); + assert!(false) + }, + + Ok(reduced_ast) => { + // write tests here + } + } + } + } + }*/ +} diff --git a/tests/test_func.rs b/tests/test_func.rs index 736d1dd..611ebac 100644 --- a/tests/test_func.rs +++ b/tests/test_func.rs @@ -352,6 +352,36 @@ mod func_tests { } } */ + + #[test] + fn eval_lazy_func_call() { + + } + + #[test] + fn sym_loose_func_call() { + + } + + #[test] + fn too_many_args() { + + } + + #[test] + fn not_enough_args() { + + } + + #[test] + fn bad_eval_arg() { + + } + + #[test] + fn bad_eval_fn_body() { + + } } /* TESTING TODO: