mod func_tests { use std::rc::Rc; use relish::ast::lex; use relish::ast::{SymTable, Type, UserFn}; use relish::ast::{Args, Symbol, Ctr, Seg, ValueType}; #[test] fn decl_and_call_internal_func() { let mut syms = SymTable::new(); let test_internal_func: Symbol = Symbol { name: String::from("test_func_in"), conditional_branches: false, args: Args::Strict(vec![Type::Bool]), value: ValueType::Internal(Rc::new( |a: &Seg, _: &mut SymTable| -> Result { let inner = a; let mut is_bool = false; if let Ctr::Bool(_) = *inner.car { is_bool = true; } Ok(Ctr::Bool(is_bool)) }, )), }; let args = Seg::from( Box::new(Ctr::Bool(true)), Box::new(Ctr::None) ); syms.insert(String::from("test_func_in"), test_internal_func); if let Ok(ret) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { match *ret { Ctr::Bool(b) => assert!(b), _ => { print!("invalid return from func!"); assert!(false); } } } else { print!("call to function failed!"); assert!(false); } } #[test] fn decl_and_call_external_func_singlet() { let mut syms = SymTable::new(); match lex(&"input".to_string()) { Err(e) => panic!("{}", e), Ok(finner) => { let test_external_func: Symbol = Symbol { name: String::from("echo"), conditional_branches: false, args: Args::Lazy(1), value: ValueType::FuncForm(UserFn{ arg_syms: vec!["input".to_string()], ast: finner, }), }; let args = Seg::from( Box::new(Ctr::String("test".to_string())), Box::new(Ctr::None) ); syms.insert(String::from("test_func_in"), test_external_func); match syms.call_symbol(&"test_func_in".to_string(), &args, true) { 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_multi_body() { let mut syms = SymTable::new(); match lex(&"(input input)".to_string()) { Err(e) => panic!("{}", e), Ok(finner) => { let test_external_func: Symbol = Symbol{ name: String::from("echo_2"), conditional_branches: false, args: Args::Lazy(1), value: ValueType::FuncForm(UserFn{ arg_syms: vec!["input".to_string()], ast: finner, }), }; let args = Seg::from( Box::new(Ctr::String("test".to_string())), Box::new(Ctr::None) ); syms.insert(String::from("echo_2"), test_external_func); match syms.call_symbol(&"echo_2".to_string(), &args, true) { Ok(ret) => assert_eq!(ret.to_string(), "'test'"), Err(e) => { print!("Call to function failed: {}\n", e); assert!(false); } } } } } #[test] fn decl_and_call_func_with_nested_call() { let mut syms = SymTable::new(); let inner_func: Symbol = Symbol { name: String::from("test_inner"), conditional_branches: false, args: Args::Strict(vec![Type::Bool]), value: ValueType::Internal(Rc::new( |a: &Seg, _: &mut SymTable| -> Result { let inner = a; if let Ctr::Bool(b) = *inner.car { if b { Ok(Ctr::String("test".to_string())) } else { Ok(Ctr::None) } } else { Err("not a bool".to_string()) } }, )), }; match lex(&"((test_inner true))".to_string()) { Err(e) => panic!("{}", e), Ok(finner) => { let outer_func: Symbol = Symbol { name: String::from("test_outer"), conditional_branches: false, args: Args::Lazy(1), value: ValueType::FuncForm(UserFn{ arg_syms: vec!["input".to_string()], ast: finner, }), }; let args = Seg::from( Box::new(Ctr::Bool(true)), Box::new(Ctr::None) ); syms.insert(String::from("test_inner"), inner_func); syms.insert(String::from("test_outer"), outer_func); match syms.call_symbol(&"test_outer".to_string(), &args, true) { 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 arg_type_mismatch() { let mut syms = SymTable::new(); let test_internal_func: Symbol = Symbol { name: String::from("test_func_in"), conditional_branches: false, args: Args::Strict(vec![Type::Bool]), value: ValueType::Internal(Rc::new( |a: &Seg, _: &mut SymTable| -> Result { let inner = a; let mut is_bool = false; if let Ctr::Bool(_) = *inner.car { is_bool = true; } Ok(Ctr::Bool(is_bool)) }, )), }; let args = Seg::from( Box::new(Ctr::Integer(1)), Box::new(Ctr::None) ); syms.insert(String::from("test_func_in"), test_internal_func); if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { assert_eq!(s, "failure to call test_func_in: arg 1 expected to be bool"); } else { print!("call to function succeeded (shouldnt have)"); assert!(false); } } #[test] fn too_many_args() { let mut syms = SymTable::new(); match lex(&"(input)".to_string()) { Err(e) => panic!("{}", e), Ok(finner) => { let test_external_func: Symbol = Symbol { name: String::from("echo"), conditional_branches: false, args: Args::Lazy(1), value: ValueType::FuncForm(UserFn{ arg_syms: vec!["input".to_string()], ast: finner, }), }; let args = Seg::from( Box::new(Ctr::String("test".to_string())), Box::new(Ctr::Seg(Seg::from_mono(Box::new(Ctr::Integer(1))))) ); syms.insert(String::from("test_func_in"), test_external_func); if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { assert_eq!(s, "failure to call echo: expected 1 args. Got 2."); } else { print!("call to function succeeded (shouldnt have)"); assert!(false); } } } } #[test] fn too_few_args() { let mut syms = SymTable::new(); match lex(&"(input)".to_string()) { Err(e) => panic!("{}", e), Ok(finner) => { let test_external_func: Symbol = Symbol { name: String::from("echo"), conditional_branches: false, args: Args::Lazy(1), value: ValueType::FuncForm(UserFn{ arg_syms: vec!["input".to_string()], ast: finner, }), }; let args = Seg::new(); syms.insert(String::from("test_func_in"), test_external_func); if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { assert_eq!(s, "failure to call echo: expected 1 args. Got 0."); } else { print!("call to function succeeded (shouldnt have)"); assert!(false); } } } } #[test] fn arg_cant_eval() { let mut syms = SymTable::new(); let test_internal_func: Symbol = Symbol { name: String::from("test_func_in"), conditional_branches: false, args: Args::Strict(vec![Type::Bool]), value: ValueType::Internal(Rc::new( |a: &Seg, _: &mut SymTable| -> Result { let inner = a; let mut is_bool = false; if let Ctr::Bool(_) = *inner.car { is_bool = true; } Ok(Ctr::Bool(is_bool)) }, )), }; let args = Seg::from( Box::new(Ctr::Symbol("undefined-symbol".to_string())), Box::new(Ctr::None) ); syms.insert(String::from("test_func_in"), test_internal_func); if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { assert_eq!(s, "error in call to undefined-symbol: undefined symbol: undefined-symbol"); } else { print!("call to function succeeded (shouldnt have)"); assert!(false); } } }