From 9b981921b4c115e0f8845818462a81dd7552a633 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sun, 6 Jun 2021 10:24:19 -0700 Subject: [PATCH] add more unit tests for function calls --- src/eval.rs | 20 ++- src/func.rs | 24 ++-- src/segment.rs | 4 +- tests/test_func.rs | 310 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 339 insertions(+), 19 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index 5b37a07..ca71fed 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -51,6 +51,8 @@ fn eval_inner( * 5. return new ast */ let ret = new_ast(Ctr::None, Ctr::None); + let res_car; + let res_cdr; match process_ctr( ast.borrow().clone().car, @@ -59,10 +61,10 @@ fn eval_inner( sym_loose, first_item, true, - ret.borrow().clone().cdr + ast.borrow().clone().cdr ) { Ok(ctr) => { - ret.borrow_mut().car = ctr; + res_car = ctr; }, Err(err) => { return Err(err) @@ -79,13 +81,16 @@ fn eval_inner( Ctr::None ) { Ok(ctr) => { - ret.borrow_mut().cdr = ctr; + res_cdr = ctr; }, Err(err) => { return Err(err) } } + ret.borrow_mut().car = res_car; + ret.borrow_mut().cdr = res_cdr; + return Ok(ret) } @@ -112,16 +117,18 @@ fn process_ctr( if let Some(s) = vars.borrow().get(&tok) { // is function, or variable alias? + let mut pass = false; if first_item { if let Ctr::Symbol(t) = &*(s.clone()) { if let Some(_f) = funcs.borrow().get(t) { // leave this set for the function call case below tok = t.clone(); + pass = true; } } + } - // is a basic value. - } else { + if !pass { return Ok((*s.clone()).clone()) } @@ -139,6 +146,7 @@ fn process_ctr( Err(s) => return Err(s) } } else { + println!("AC::: {:?}\n", rest); return Err("evaluation: args to function not a list".to_string()) } } else { @@ -152,7 +160,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) { + match eval_inner(tree, vars, funcs, sym_loose, is_car && first_item) { Ok(ast) => { Ok(Ctr::Seg(ast)) }, diff --git a/src/func.rs b/src/func.rs index be1b7f4..331b013 100644 --- a/src/func.rs +++ b/src/func.rs @@ -32,9 +32,9 @@ pub struct ExternalOperation { // TODO: Intermediate evaluation to simplify branches with no argument in them // Simplified branches must not have side effects. // TODO: Apply Memoization? - ast: Ast, + pub ast: Ast, // list of argument string tokens - arg_syms: Vec + pub arg_syms: Vec } /* A stored function may either be a pointer to a function @@ -94,11 +94,16 @@ pub fn func_call( match &called_func.args { Args::Lazy(num) => { - if *num < 0 { - } - - if !(*num == (list_len(n_args.clone()) as i128 - 1)) { - return Err(format!("expected {} args in call to {}", num, called_func.name)) + let called_arg_count = list_len(n_args.clone()) as i128; + if *num > -1 && (*num != called_arg_count) { + return Err( + format!( + "expected {} args in call to {}. Got {}.", + num, + called_func.name, + called_arg_count + ) + ) } }, @@ -151,10 +156,13 @@ pub fn func_call( match &called_func.function { Operation::Internal(f) => Ok((f)(n_args, vars, funcs)), Operation::External(f) => { + for n in 0..f.arg_syms.len() { + let iter_arg = list_idx(n_args.clone(), n as u128); + vars.borrow_mut().insert( f.arg_syms[n].clone(), - Rc::new(list_idx(n_args.clone(), n as u128)) + Rc::new(iter_arg) ); } diff --git a/src/segment.rs b/src/segment.rs index bf6f4cd..539e247 100644 --- a/src/segment.rs +++ b/src/segment.rs @@ -23,6 +23,7 @@ pub type Ast = Rc>; // Container #[derive(Clone)] +#[derive(Debug)] pub enum Ctr { Symbol(String), String(String), @@ -53,6 +54,7 @@ pub enum Type { * how important RefCells were in Rust */ #[derive(Clone)] +#[derive(Debug)] pub struct Seg { /* "Contents of Address Register" * Historical way of referring to the first value in a cell. @@ -215,7 +217,7 @@ pub fn list_idx(tree: Ast, idx: u128) -> Ctr { match inner.car { Ctr::None => Ctr::None, _ => { - inner.cdr.clone() + inner.car.clone() } } } diff --git a/tests/test_func.rs b/tests/test_func.rs index e03b91e..736d1dd 100644 --- a/tests/test_func.rs +++ b/tests/test_func.rs @@ -3,14 +3,14 @@ mod func_tests { use std::cell::RefCell; use relish::ast::{Ast, Type, Ctr, new_ast}; use relish::ast::VTable; - use relish::ast::{Function, Operation, FTable, Args, func_declare, func_call}; + use relish::ast::{Function, Operation, FTable, Args, func_declare, func_call, ExternalOperation}; #[test] fn decl_and_call_internal_func() { let test_internal_func: Function = Function{ - name: String::from("test_func_in"), + name: String::from("test_func_in"), loose_syms: false, - eval_lazy: true, + eval_lazy: false, args: Args::Strict(vec![Type::Bool]), function: Operation::Internal( |a: Ast, _b: Rc>, _c: Rc>| -> Ctr { @@ -29,7 +29,7 @@ mod func_tests { let args = new_ast(Ctr::Bool(true), Ctr::None); if let Some(s) = func_declare(ft.clone(), Rc::new(RefCell::new(test_internal_func))) { - print!("{}", s); + print!("Error declaring internal func: {}", s); assert!(false); } @@ -55,4 +55,306 @@ mod func_tests { assert!(false); } } + + #[test] + fn decl_and_call_external_func_singlet() { + 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::Symbol("input".to_string()), Ctr::None) + } + ) + }; + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + let args = new_ast(Ctr::String("test".to_string()), Ctr::None); + if let Some(s) = func_declare(ft.clone(), + Rc::new(RefCell::new(test_external_func))) { + print!("Error declaring external func: {}", s); + assert!(false); + } + + let func: Rc>; + if let Some(f) = ft.borrow().get(&"echo".to_string()) { + func = f.clone(); + } else { + print!("failed to retrieve function!"); + assert!(false); + return; + } + + match func_call(func, args, vt, ft) { + Ok(ret) => { + match ret { + Ctr::String(b) => assert!(b == "test"), + _ => { + print!("Invalid return from func. Got {:?}\n", ret); + assert!(false); + } + } + }, + Err(e) => { + print!("Call to function failed: {}\n", e); + assert!(false); + } + } + } + + #[test] + fn decl_and_call_external_func_dual_ret() { + let test_external_func: Function = Function{ + name: String::from("echox2"), + 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::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())); + let args = new_ast(Ctr::String("test".to_string()), Ctr::None); + if let Some(s) = func_declare(ft.clone(), + Rc::new(RefCell::new(test_external_func))) { + print!("Error declaring external func: {}", s); + assert!(false); + } + + let func: Rc>; + if let Some(f) = ft.borrow().get(&"echox2".to_string()) { + func = f.clone(); + } else { + print!("failed to retrieve function!"); + assert!(false); + return; + } + + match func_call(func, args, vt, ft) { + Ok(ret) => { + match ret { + Ctr::Seg(b) => { + match b.borrow().clone().car { + Ctr::String(s) => assert!(s == "test"), + _ => { + print!("first elem wrong. expected \"test\", got: {:?}\n", b.borrow().car); + assert!(false); + } + } + + match b.borrow().clone().cdr { + Ctr::Seg(b2) => { + match b2.borrow().clone().car { + Ctr::String(s) => assert!(s == "test"), + _ => { + print!("second elem wrong. expected \"test\", got: {:?}\n", b2.borrow().car); + assert!(false) + } + } + + match b2.borrow().cdr { + Ctr::None => (), + _ => { + print!("there should not be a third element\n"); + assert!(false) + } + } + }, + _ => { + print!("first elem cdr should be a list. got: {:?}\n", b.borrow().cdr) + } + } + }, + _ => { + print!("Invalid return from func. Got {:?}\n", ret); + assert!(false); + } + } + }, + Err(e) => { + print!("Call to function failed: {}\n", e); + assert!(false); + } + } + } + + #[test] + fn decl_and_call_func_with_nested_call() { + let inner_func: Function = Function{ + name: String::from("test_inner"), + loose_syms: false, + eval_lazy: false, + args: Args::Strict(vec![Type::Bool]), + function: Operation::Internal( + |a: Ast, _b: Rc>, _c: Rc>| -> Ctr { + let inner = a.borrow(); + if let Ctr::Bool(b) = &inner.car { + if *b { + Ctr::String("test".to_string()) + } else { + Ctr::None + } + } else { + Ctr::None + } + } + ) + }; + + let outer_func: Function = Function{ + name: String::from("test_outer"), + loose_syms: false, + eval_lazy: false, + args: Args::Lazy(1), + function: Operation::External( + ExternalOperation{ + arg_syms: vec!["input".to_string()], + ast: new_ast(Ctr::Symbol("test_inner".to_string()), + Ctr::Seg(new_ast(Ctr::Symbol("input".to_string()), + Ctr::None))) + } + ) + }; + + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + let args = new_ast(Ctr::Bool(true), Ctr::None); + + if let Some(s) = func_declare(ft.clone(), + Rc::new(RefCell::new(inner_func))) { + print!("Error declaring inner func: {}", s); + assert!(false); + } + if let Some(s) = func_declare(ft.clone(), + Rc::new(RefCell::new(outer_func))) { + print!("Error declaring outer func: {}", s); + assert!(false); + } + + let func: Rc>; + if let Some(f) = ft.borrow().get(&"test_outer".to_string()) { + func = f.clone(); + } else { + print!("failed to retrieve function!"); + assert!(false); + return; + } + + match func_call(func, args, vt, ft) { + Ok(ret) => { + match ret { + Ctr::String(b) => assert!(b == "test"), + _ => { + print!("Invalid return from func. Got {:?}\n", ret); + assert!(false); + } + } + }, + Err(e) => { + print!("Call to function failed: {}\n", e); + assert!(false); + } + } + } + + /* This test removed because it would make more sense to test this functionality in eval tests + * this function tested the evaluation of a complex argument ast that could be reduced in eval + + #[test] + fn decl_and_call_func_eval_arg() { + let is_true_func: Function = Function{ + name: String::from("is_true?"), + loose_syms: false, + eval_lazy: false, + args: Args::Strict(vec![Type::Bool]), + function: Operation::Internal( + |a: Ast, _b: Rc>, _c: Rc>| -> Ctr { + let inner = a.borrow(); + if let Ctr::Bool(b) = &inner.car { + if *b { + Ctr::String("test".to_string()) + } else { + Ctr::None + } + } else { + Ctr::None + } + } + ) + }; + + let echo_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::Symbol("input".to_string()), Ctr::None) + } + ) + }; + + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + let args = new_ast( + Ctr::Seg(new_ast( + Ctr::Symbol("is_true?".to_string()), + Ctr::Seg(new_ast( + Ctr::Bool(true), + Ctr::None)))), + Ctr::None); + + if let Some(s) = func_declare(ft.clone(), + Rc::new(RefCell::new(is_true_func))) { + print!("Error declaring inner func: {}", s); + assert!(false); + } + if let Some(s) = func_declare(ft.clone(), + Rc::new(RefCell::new(echo_func))) { + print!("Error declaring outer func: {}", s); + assert!(false); + } + + let func: Rc>; + if let Some(f) = ft.borrow().get(&"echo".to_string()) { + func = f.clone(); + } else { + print!("failed to retrieve function!"); + assert!(false); + return; + } + + match func_call(func, args, vt, ft) { + Ok(ret) => { + match ret { + Ctr::String(b) => assert!(b == "test"), + _ => { + print!("Invalid return from func. Got {:?}\n", ret); + assert!(false); + } + } + }, + Err(e) => { + print!("Call to function failed: {}\n", e); + assert!(false); + } + } + } + */ } + +/* TESTING TODO: + * - Test functions that eval_lazy + * - Test functions that sym_loose + */