mod func_tests { use relish::ast::lex; use relish::ast::{Args, Ctr, Seg, Symbol, ValueType}; use relish::ast::{SymTable, Type, UserFn}; use std::rc::Rc; #[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, docs: String::new(), 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 Ctr::Bool(b) = *syms .call_symbol(&"test_func_in".to_string(), &args, true) .unwrap() { assert!(b) } } #[test] fn decl_and_call_external_func_singlet() { let mut syms = SymTable::new(); let finner = lex(&"input".to_string()).unwrap(); let test_external_func: Symbol = Symbol { name: String::from("echo"), conditional_branches: false, args: Args::Lazy(1), docs: String::new(), 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); if let Ctr::Bool(b) = *syms .call_symbol(&"test_func_in".to_string(), &args, true) .unwrap() { assert!(b) } } #[test] fn decl_and_call_external_func_multi_body() { let mut syms = SymTable::new(); let finner = lex(&"input".to_string()).unwrap(); let test_external_func: Symbol = Symbol { name: String::from("echo_2"), conditional_branches: false, args: Args::Lazy(1), docs: String::new(), 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); assert_eq!( *syms .call_symbol(&"echo_2".to_string(), &args, true) .unwrap() .to_string(), "'test'".to_string() ); } #[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]), docs: String::new(), 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()) } }, )), }; let finner = lex(&"((test_inner true))".to_string()).unwrap(); let outer_func: Symbol = Symbol { name: String::from("test_outer"), conditional_branches: false, args: Args::Lazy(1), docs: String::new(), 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); assert_eq!( syms.call_symbol(&"test_outer".to_string(), &args, true) .unwrap() .to_string(), "'test'".to_string() ); } #[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]), docs: String::new(), 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); assert_eq!( syms.call_symbol(&"test_func_in".to_string(), &args, true) .err() .unwrap(), "arg 1 expected to be bool".to_string(), ); } #[test] fn too_many_args() { let mut syms = SymTable::new(); let finner = lex(&"(input)".to_string()).unwrap(); let test_external_func: Symbol = Symbol { name: String::from("echo"), conditional_branches: false, args: Args::Lazy(1), docs: String::new(), 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); assert_eq!( syms.call_symbol(&"test_func_in".to_string(), &args, true) .err() .unwrap(), "expected 1 args. Got 2.".to_string(), ); } #[test] fn too_few_args() { let mut syms = SymTable::new(); let finner = lex(&"(input)".to_string()).unwrap(); let test_external_func: Symbol = Symbol { name: String::from("echo"), conditional_branches: false, args: Args::Lazy(1), docs: String::new(), 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); assert_eq!( syms.call_symbol(&"test_func_in".to_string(), &args, true) .err() .unwrap(), "expected 1 args. Got 0.".to_string(), ); } #[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]), docs: String::new(), 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); assert_eq!( syms.call_symbol(&"test_func_in".to_string(), &args, true) .err() .unwrap(), "error evaluating args: undefined symbol: undefined-symbol".to_string(), ); } }