Significant improvement to eval routine and tests
This commit is contained in:
parent
9b981921b4
commit
df5cb47cb4
4 changed files with 397 additions and 11 deletions
115
src/eval.rs
115
src/eval.rs
|
|
@ -30,10 +30,107 @@ pub fn eval(
|
|||
vars: Rc<RefCell<VTable>>,
|
||||
funcs: Rc<RefCell<FTable>>,
|
||||
sym_loose: bool
|
||||
) -> Result<Ast, String> {
|
||||
eval_inner(ast, vars, funcs, sym_loose, true)
|
||||
) -> Result<Ctr, String> {
|
||||
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<RefCell<VTable>>,
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
35
src/func.rs
35
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
228
tests/test_eval.rs
Normal file
228
tests/test_eval.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue