2021-03-14 16:14:57 -07:00
|
|
|
mod func_tests {
|
2023-02-17 22:10:54 -08:00
|
|
|
use relish::ast::lex;
|
2023-03-01 11:38:02 -08:00
|
|
|
use relish::ast::{Args, Ctr, Seg, Symbol, ValueType};
|
2023-02-24 15:29:17 -08:00
|
|
|
use relish::ast::{SymTable, Type, UserFn};
|
2023-03-01 11:38:02 -08:00
|
|
|
use std::rc::Rc;
|
2021-03-14 16:14:57 -07:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn decl_and_call_internal_func() {
|
2023-02-24 15:29:17 -08:00
|
|
|
let mut syms = SymTable::new();
|
2023-02-17 22:10:54 -08:00
|
|
|
let test_internal_func: Symbol = Symbol {
|
2022-01-16 22:02:40 -08:00
|
|
|
name: String::from("test_func_in"),
|
2023-02-24 15:29:17 -08:00
|
|
|
conditional_branches: false,
|
2021-03-14 16:14:57 -07:00
|
|
|
args: Args::Strict(vec![Type::Bool]),
|
2023-02-27 22:53:54 -08:00
|
|
|
value: ValueType::Internal(Rc::new(
|
|
|
|
|
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
2023-02-17 22:10:54 -08:00
|
|
|
let inner = a;
|
2021-03-14 16:14:57 -07:00
|
|
|
let mut is_bool = false;
|
2023-02-17 22:10:54 -08:00
|
|
|
if let Ctr::Bool(_) = *inner.car {
|
2021-03-14 16:14:57 -07:00
|
|
|
is_bool = true;
|
|
|
|
|
}
|
2023-02-27 22:53:54 -08:00
|
|
|
Ok(Ctr::Bool(is_bool))
|
2022-01-16 22:02:40 -08:00
|
|
|
},
|
|
|
|
|
)),
|
2021-03-14 16:14:57 -07:00
|
|
|
};
|
2023-03-01 11:38:02 -08:00
|
|
|
let args = Seg::from(Box::new(Ctr::Bool(true)), Box::new(Ctr::None));
|
2023-02-17 22:10:54 -08:00
|
|
|
|
2023-02-24 15:29:17 -08:00
|
|
|
syms.insert(String::from("test_func_in"), test_internal_func);
|
2021-03-14 16:14:57 -07:00
|
|
|
|
2023-02-24 15:29:17 -08:00
|
|
|
if let Ok(ret) = syms.call_symbol(&"test_func_in".to_string(), &args, true) {
|
2023-02-17 22:10:54 -08:00
|
|
|
match *ret {
|
2021-03-14 16:14:57 -07:00
|
|
|
Ctr::Bool(b) => assert!(b),
|
|
|
|
|
_ => {
|
|
|
|
|
print!("invalid return from func!");
|
|
|
|
|
assert!(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
print!("call to function failed!");
|
|
|
|
|
assert!(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-06 10:24:19 -07:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn decl_and_call_external_func_singlet() {
|
2023-02-24 15:29:17 -08:00
|
|
|
let mut syms = SymTable::new();
|
2023-02-25 23:36:30 -08:00
|
|
|
match lex(&"input".to_string()) {
|
2021-11-02 20:12:14 -07:00
|
|
|
Err(e) => panic!("{}", e),
|
2021-07-19 23:59:03 -07:00
|
|
|
Ok(finner) => {
|
2023-02-17 22:10:54 -08:00
|
|
|
let test_external_func: Symbol = Symbol {
|
2021-07-19 23:59:03 -07:00
|
|
|
name: String::from("echo"),
|
2023-02-24 15:29:17 -08:00
|
|
|
conditional_branches: false,
|
2021-07-19 23:59:03 -07:00
|
|
|
args: Args::Lazy(1),
|
2023-03-01 11:38:02 -08:00
|
|
|
value: ValueType::FuncForm(UserFn {
|
2022-01-16 22:02:40 -08:00
|
|
|
arg_syms: vec!["input".to_string()],
|
|
|
|
|
ast: finner,
|
|
|
|
|
}),
|
2021-07-19 23:59:03 -07:00
|
|
|
};
|
2021-06-06 10:24:19 -07:00
|
|
|
|
2023-02-17 22:10:54 -08:00
|
|
|
let args = Seg::from(
|
|
|
|
|
Box::new(Ctr::String("test".to_string())),
|
2023-03-01 11:38:02 -08:00
|
|
|
Box::new(Ctr::None),
|
2023-02-17 22:10:54 -08:00
|
|
|
);
|
|
|
|
|
|
2023-02-24 15:29:17 -08:00
|
|
|
syms.insert(String::from("test_func_in"), test_external_func);
|
2021-06-06 10:24:19 -07:00
|
|
|
|
2023-02-24 15:29:17 -08:00
|
|
|
match syms.call_symbol(&"test_func_in".to_string(), &args, true) {
|
2023-02-17 22:10:54 -08:00
|
|
|
Ok(ret) => match *ret {
|
2022-01-16 22:02:40 -08:00
|
|
|
Ctr::String(b) => assert!(b == "test"),
|
|
|
|
|
_ => {
|
|
|
|
|
print!("Invalid return from func. Got {:?}\n", ret);
|
|
|
|
|
assert!(false);
|
2021-07-19 23:59:03 -07:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Err(e) => {
|
|
|
|
|
print!("Call to function failed: {}\n", e);
|
2021-06-06 10:24:19 -07:00
|
|
|
assert!(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-07-19 23:59:03 -07:00
|
|
|
fn decl_and_call_external_func_multi_body() {
|
2023-02-24 15:29:17 -08:00
|
|
|
let mut syms = SymTable::new();
|
2023-02-25 23:36:30 -08:00
|
|
|
match lex(&"(input input)".to_string()) {
|
2021-11-02 20:12:14 -07:00
|
|
|
Err(e) => panic!("{}", e),
|
2021-07-19 23:59:03 -07:00
|
|
|
Ok(finner) => {
|
2023-03-01 11:38:02 -08:00
|
|
|
let test_external_func: Symbol = Symbol {
|
2021-07-19 23:59:03 -07:00
|
|
|
name: String::from("echo_2"),
|
2023-02-24 15:29:17 -08:00
|
|
|
conditional_branches: false,
|
2021-07-19 23:59:03 -07:00
|
|
|
args: Args::Lazy(1),
|
2023-03-01 11:38:02 -08:00
|
|
|
value: ValueType::FuncForm(UserFn {
|
2022-01-16 22:02:40 -08:00
|
|
|
arg_syms: vec!["input".to_string()],
|
|
|
|
|
ast: finner,
|
|
|
|
|
}),
|
2021-07-19 23:59:03 -07:00
|
|
|
};
|
2021-06-06 10:24:19 -07:00
|
|
|
|
2023-02-17 22:10:54 -08:00
|
|
|
let args = Seg::from(
|
|
|
|
|
Box::new(Ctr::String("test".to_string())),
|
2023-03-01 11:38:02 -08:00
|
|
|
Box::new(Ctr::None),
|
2023-02-17 22:10:54 -08:00
|
|
|
);
|
|
|
|
|
|
2023-02-24 15:29:17 -08:00
|
|
|
syms.insert(String::from("echo_2"), test_external_func);
|
2023-02-20 19:42:48 -08:00
|
|
|
|
2023-02-24 15:29:17 -08:00
|
|
|
match syms.call_symbol(&"echo_2".to_string(), &args, true) {
|
2023-02-25 23:36:30 -08:00
|
|
|
Ok(ret) => assert_eq!(ret.to_string(), "'test'"),
|
2021-07-19 23:59:03 -07:00
|
|
|
Err(e) => {
|
|
|
|
|
print!("Call to function failed: {}\n", e);
|
2021-06-06 10:24:19 -07:00
|
|
|
assert!(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn decl_and_call_func_with_nested_call() {
|
2023-02-24 15:29:17 -08:00
|
|
|
let mut syms = SymTable::new();
|
2023-02-17 22:10:54 -08:00
|
|
|
let inner_func: Symbol = Symbol {
|
2021-06-06 10:24:19 -07:00
|
|
|
name: String::from("test_inner"),
|
2023-02-24 15:29:17 -08:00
|
|
|
conditional_branches: false,
|
2021-06-06 10:24:19 -07:00
|
|
|
args: Args::Strict(vec![Type::Bool]),
|
2023-02-27 22:53:54 -08:00
|
|
|
value: ValueType::Internal(Rc::new(
|
|
|
|
|
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
2023-02-17 22:10:54 -08:00
|
|
|
let inner = a;
|
|
|
|
|
if let Ctr::Bool(b) = *inner.car {
|
|
|
|
|
if b {
|
2023-02-27 22:53:54 -08:00
|
|
|
Ok(Ctr::String("test".to_string()))
|
2021-06-06 10:24:19 -07:00
|
|
|
} else {
|
2023-02-27 22:53:54 -08:00
|
|
|
Ok(Ctr::None)
|
2021-06-06 10:24:19 -07:00
|
|
|
}
|
|
|
|
|
} else {
|
2023-02-27 22:53:54 -08:00
|
|
|
Err("not a bool".to_string())
|
2021-06-06 10:24:19 -07:00
|
|
|
}
|
2022-01-16 22:02:40 -08:00
|
|
|
},
|
|
|
|
|
)),
|
2021-06-06 10:24:19 -07:00
|
|
|
};
|
|
|
|
|
|
2023-02-17 22:10:54 -08:00
|
|
|
match lex(&"((test_inner true))".to_string()) {
|
2021-11-02 20:12:14 -07:00
|
|
|
Err(e) => panic!("{}", e),
|
2021-07-19 23:59:03 -07:00
|
|
|
Ok(finner) => {
|
2023-02-17 22:10:54 -08:00
|
|
|
let outer_func: Symbol = Symbol {
|
2021-07-19 23:59:03 -07:00
|
|
|
name: String::from("test_outer"),
|
2023-02-24 15:29:17 -08:00
|
|
|
conditional_branches: false,
|
2021-07-19 23:59:03 -07:00
|
|
|
args: Args::Lazy(1),
|
2023-03-01 11:38:02 -08:00
|
|
|
value: ValueType::FuncForm(UserFn {
|
2022-01-16 22:02:40 -08:00
|
|
|
arg_syms: vec!["input".to_string()],
|
|
|
|
|
ast: finner,
|
|
|
|
|
}),
|
2021-07-19 23:59:03 -07:00
|
|
|
};
|
2021-06-06 10:24:19 -07:00
|
|
|
|
2023-03-01 11:38:02 -08:00
|
|
|
let args = Seg::from(Box::new(Ctr::Bool(true)), Box::new(Ctr::None));
|
2021-06-06 10:24:19 -07:00
|
|
|
|
2023-02-24 15:29:17 -08:00
|
|
|
syms.insert(String::from("test_inner"), inner_func);
|
|
|
|
|
syms.insert(String::from("test_outer"), outer_func);
|
2021-06-06 10:24:19 -07:00
|
|
|
|
2023-02-24 15:29:17 -08:00
|
|
|
match syms.call_symbol(&"test_outer".to_string(), &args, true) {
|
2023-02-17 22:10:54 -08:00
|
|
|
Ok(ret) => match *ret {
|
2022-01-16 22:02:40 -08:00
|
|
|
Ctr::String(b) => assert!(b == "test"),
|
|
|
|
|
_ => {
|
|
|
|
|
print!("Invalid return from func. Got {:?}\n", ret);
|
|
|
|
|
assert!(false);
|
2021-07-19 23:59:03 -07:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Err(e) => {
|
|
|
|
|
print!("Call to function failed: {}\n", e);
|
2021-06-06 10:24:19 -07:00
|
|
|
assert!(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-22 00:50:37 -07:00
|
|
|
#[test]
|
2023-02-24 16:05:10 -08:00
|
|
|
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]),
|
2023-02-27 22:53:54 -08:00
|
|
|
value: ValueType::Internal(Rc::new(
|
|
|
|
|
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
2023-02-24 16:05:10 -08:00
|
|
|
let inner = a;
|
|
|
|
|
let mut is_bool = false;
|
|
|
|
|
if let Ctr::Bool(_) = *inner.car {
|
|
|
|
|
is_bool = true;
|
|
|
|
|
}
|
2023-02-27 22:53:54 -08:00
|
|
|
Ok(Ctr::Bool(is_bool))
|
2023-02-24 16:05:10 -08:00
|
|
|
},
|
|
|
|
|
)),
|
|
|
|
|
};
|
2023-03-01 11:38:02 -08:00
|
|
|
let args = Seg::from(Box::new(Ctr::Integer(1)), Box::new(Ctr::None));
|
2021-06-22 00:50:37 -07:00
|
|
|
|
2023-02-24 16:05:10 -08:00
|
|
|
syms.insert(String::from("test_func_in"), test_internal_func);
|
2021-06-22 00:50:37 -07:00
|
|
|
|
2023-02-24 16:05:10 -08:00
|
|
|
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);
|
|
|
|
|
}
|
2021-06-22 00:50:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn too_many_args() {
|
2023-02-24 16:05:10 -08:00
|
|
|
let mut syms = SymTable::new();
|
2023-02-25 23:36:30 -08:00
|
|
|
match lex(&"(input)".to_string()) {
|
2023-02-24 16:05:10 -08:00
|
|
|
Err(e) => panic!("{}", e),
|
|
|
|
|
Ok(finner) => {
|
|
|
|
|
let test_external_func: Symbol = Symbol {
|
|
|
|
|
name: String::from("echo"),
|
|
|
|
|
conditional_branches: false,
|
|
|
|
|
args: Args::Lazy(1),
|
2023-03-01 11:38:02 -08:00
|
|
|
value: ValueType::FuncForm(UserFn {
|
2023-02-24 16:05:10 -08:00
|
|
|
arg_syms: vec!["input".to_string()],
|
|
|
|
|
ast: finner,
|
|
|
|
|
}),
|
|
|
|
|
};
|
2021-06-22 00:50:37 -07:00
|
|
|
|
2023-02-24 16:05:10 -08:00
|
|
|
let args = Seg::from(
|
|
|
|
|
Box::new(Ctr::String("test".to_string())),
|
2023-03-01 11:38:02 -08:00
|
|
|
Box::new(Ctr::Seg(Seg::from_mono(Box::new(Ctr::Integer(1))))),
|
2023-02-24 16:05:10 -08:00
|
|
|
);
|
2021-06-22 00:50:37 -07:00
|
|
|
|
2023-02-24 16:05:10 -08:00
|
|
|
syms.insert(String::from("test_func_in"), test_external_func);
|
2021-06-22 00:50:37 -07:00
|
|
|
|
2023-02-24 16:05:10 -08:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-22 00:50:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2023-02-24 16:05:10 -08:00
|
|
|
fn too_few_args() {
|
|
|
|
|
let mut syms = SymTable::new();
|
2023-02-25 23:36:30 -08:00
|
|
|
match lex(&"(input)".to_string()) {
|
2023-02-24 16:05:10 -08:00
|
|
|
Err(e) => panic!("{}", e),
|
|
|
|
|
Ok(finner) => {
|
|
|
|
|
let test_external_func: Symbol = Symbol {
|
|
|
|
|
name: String::from("echo"),
|
|
|
|
|
conditional_branches: false,
|
|
|
|
|
args: Args::Lazy(1),
|
2023-03-01 11:38:02 -08:00
|
|
|
value: ValueType::FuncForm(UserFn {
|
2023-02-24 16:05:10 -08:00
|
|
|
arg_syms: vec!["input".to_string()],
|
|
|
|
|
ast: finner,
|
|
|
|
|
}),
|
|
|
|
|
};
|
2021-06-22 00:50:37 -07:00
|
|
|
|
2023-02-24 16:05:10 -08:00
|
|
|
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) {
|
2023-02-25 23:36:30 -08:00
|
|
|
assert_eq!(s, "failure to call echo: expected 1 args. Got 0.");
|
2023-02-24 16:05:10 -08:00
|
|
|
} else {
|
|
|
|
|
print!("call to function succeeded (shouldnt have)");
|
|
|
|
|
assert!(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-22 00:50:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2023-02-24 16:05:10 -08:00
|
|
|
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]),
|
2023-02-27 22:53:54 -08:00
|
|
|
value: ValueType::Internal(Rc::new(
|
|
|
|
|
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
2023-02-24 16:05:10 -08:00
|
|
|
let inner = a;
|
|
|
|
|
let mut is_bool = false;
|
|
|
|
|
if let Ctr::Bool(_) = *inner.car {
|
|
|
|
|
is_bool = true;
|
|
|
|
|
}
|
2023-02-27 22:53:54 -08:00
|
|
|
Ok(Ctr::Bool(is_bool))
|
2023-02-24 16:05:10 -08:00
|
|
|
},
|
|
|
|
|
)),
|
|
|
|
|
};
|
|
|
|
|
let args = Seg::from(
|
|
|
|
|
Box::new(Ctr::Symbol("undefined-symbol".to_string())),
|
2023-03-01 11:38:02 -08:00
|
|
|
Box::new(Ctr::None),
|
2023-02-24 16:05:10 -08:00
|
|
|
);
|
2021-06-22 00:50:37 -07:00
|
|
|
|
2023-02-24 16:05:10 -08:00
|
|
|
syms.insert(String::from("test_func_in"), test_internal_func);
|
|
|
|
|
|
|
|
|
|
if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) {
|
2023-03-01 11:38:02 -08:00
|
|
|
assert_eq!(
|
|
|
|
|
s,
|
|
|
|
|
"error in call to undefined-symbol: undefined symbol: undefined-symbol"
|
|
|
|
|
);
|
2023-02-24 16:05:10 -08:00
|
|
|
} else {
|
|
|
|
|
print!("call to function succeeded (shouldnt have)");
|
|
|
|
|
assert!(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-14 16:14:57 -07:00
|
|
|
}
|