Significant improvement to eval routine and tests

This commit is contained in:
Aidan 2021-06-22 00:50:37 -07:00
parent 9b981921b4
commit df5cb47cb4
No known key found for this signature in database
GPG key ID: 327711E983899316
4 changed files with 397 additions and 11 deletions

View file

@ -30,10 +30,107 @@ pub fn eval(
vars: Rc<RefCell<VTable>>, vars: Rc<RefCell<VTable>>,
funcs: Rc<RefCell<FTable>>, funcs: Rc<RefCell<FTable>>,
sym_loose: bool sym_loose: bool
) -> Result<Ast, String> { ) -> Result<Ctr, String> {
eval_inner(ast, vars, funcs, sym_loose, true) 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( fn eval_inner(
ast: Ast, ast: Ast,
vars: Rc<RefCell<VTable>>, vars: Rc<RefCell<VTable>>,
@ -54,6 +151,8 @@ fn eval_inner(
let res_car; let res_car;
let res_cdr; let res_cdr;
// Handle Function Case
match process_ctr( match process_ctr(
ast.borrow().clone().car, ast.borrow().clone().car,
vars.clone(), vars.clone(),
@ -113,8 +212,10 @@ fn process_ctr(
match ctr.clone() { match ctr.clone() {
Ctr::Symbol(token) => { Ctr::Symbol(token) => {
print!("[+] Detected Symbol: {}\n", token);
let mut tok = token; let mut tok = token;
if let Some(s) = vars.borrow().get(&tok) { if let Some(s) = vars.borrow().get(&tok) {
print!("[+] Found symbol value\n");
// is function, or variable alias? // is function, or variable alias?
let mut pass = false; let mut pass = false;
@ -141,12 +242,15 @@ fn process_ctr(
if first_item { if first_item {
if let Some(f) = funcs.borrow().get(&tok) { if let Some(f) = funcs.borrow().get(&tok) {
if let Ctr::Seg(args) = rest { if let Ctr::Seg(args) = rest {
print!("[+] Calling function: {}\n", tok);
match func_call(f.clone(), args, vars.clone(), funcs.clone()) { 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) Err(s) => return Err(s)
} }
} else { } else {
println!("AC::: {:?}\n", rest);
return Err("evaluation: args to function not a list".to_string()) return Err("evaluation: args to function not a list".to_string())
} }
} else { } else {
@ -160,7 +264,7 @@ fn process_ctr(
}, },
Ctr::Seg(tree) => { Ctr::Seg(tree) => {
// if list is_car then we need to set first_item to true // 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(ast) => {
Ok(Ctr::Seg(ast)) Ok(Ctr::Seg(ast))
}, },
@ -172,3 +276,4 @@ fn process_ctr(
_ => Ok(ctr.clone()) _ => Ok(ctr.clone())
} }
} }
*/

View file

@ -81,7 +81,13 @@ pub fn func_call(
let mut n_args: Ast = args.clone(); let mut n_args: Ast = args.clone();
if !called_func.eval_lazy { if !called_func.eval_lazy {
match eval(args, vars.clone(), funcs.clone(), called_func.loose_syms) { 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( Err(s) => return Err(
format!( format!(
"error evaluating args to {}: {}", "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() { for n in 0..f.arg_syms.len() {
vars.borrow_mut().remove(&f.arg_syms[n].clone()); vars.borrow_mut().remove(&f.arg_syms[n].clone());
} }
match result { return Ok(result)
Ok(r) => Ok(r.borrow().clone().car),
Err(e) => Err(e)
}
} }
} }
} }

228
tests/test_eval.rs Normal file
View file

@ -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
}
}
}
}
}*/
}

View file

@ -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: /* TESTING TODO: