flesh/tests/test_func.rs
Ava Hahn 789349df48 Error Messaging Redesign
This commit contains the following:

* New data types to support full tracebacks
* New traceback data type used across stl and ast
* Updates to tests
* fixes for error messaging in sym and some stl functions
2023-05-23 22:06:11 +00:00

283 lines
9.2 KiB
Rust

mod func_tests {
use relish::ast::lex;
use relish::ast::{Args, Ctr, Seg, Symbol, ValueType, Traceback};
use relish::ast::{SymTable, Type, UserFn, start_trace};
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<Ctr, Traceback> {
let inner = a;
let mut is_bool = false;
if let Ctr::Bool(_) = *inner.car {
is_bool = true;
}
Ok(Ctr::Bool(is_bool))
},
)),
..Default::default()
};
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,
}),
..Default::default()
};
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,
}),
..Default::default()
};
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<Ctr, Traceback> {
let inner = a;
if let Ctr::Bool(b) = *inner.car {
if b {
Ok(Ctr::String("test".to_string()))
} else {
Ok(Ctr::None)
}
} else {
Err(start_trace(("", "not a bool".to_string()).into()))
}
},
)),
..Default::default()
};
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,
}),
..Default::default()
};
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<Ctr, Traceback> {
let inner = a;
let mut is_bool = false;
if let Ctr::Bool(_) = *inner.car {
is_bool = true;
}
Ok(Ctr::Bool(is_bool))
},
)),
..Default::default()
};
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()
.0
.first()
.unwrap()
.message,
"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,
}),
..Default::default()
};
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()
.0
.first()
.unwrap()
.message,
"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,
}),
..Default::default()
};
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()
.0
.first()
.unwrap()
.message,
"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<Ctr, Traceback> {
let inner = a;
let mut is_bool = false;
if let Ctr::Bool(_) = *inner.car {
is_bool = true;
}
Ok(Ctr::Bool(is_bool))
},
)),
..Default::default()
};
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()
.0
.first()
.unwrap()
.message,
"(is an undefined symbol)".to_string(),
);
}
}