use xbasic::expr::ExprValue;
use xbasic::xbasic::XBasicBuilder;
mod common;
fn test_program_native_functions<T: 'static>(
functions: Vec<(&'static str, u8, T)>,
program: &'static str,
expected: &'static str,
) where
T: Fn(Vec<ExprValue>, &mut common::TestIO) -> ExprValue,
{
let tio = common::TestIO::new(expected);
let mut xbb = XBasicBuilder::new(tio);
for (name, arity, native_fn) in functions {
assert!(xbb
.define_function(name.to_owned(), arity, native_fn)
.is_ok());
}
let mut xb = xbb.build();
match xb.run(program) {
Ok(_) => (),
Err(_) => {
for error in xb.error_handler.errors {
println!("{}", error);
}
panic!();
}
}
xb.get_io().check();
}
#[test]
fn basic_native_function() {
test_program_native_functions(
vec![("test", 0, |_, _: &mut _| ExprValue::Decimal(123.4))],
"
print test()
",
"123.4\n",
);
}
#[test]
fn basic_native_function_argument_order() {
test_program_native_functions(
vec![("test", 2, |args: Vec<ExprValue>, _: &mut _| {
ExprValue::Decimal(args[0].clone().into_decimal() / args[1].clone().into_decimal())
})],
"
print test(300, 2)
",
"150\n",
);
}
#[test]
fn wrong_arity() {
let tio = common::TestIO::new("");
let mut xbb = XBasicBuilder::new(tio);
assert!(xbb
.define_function("arity".to_string(), 2, |_, _| ExprValue::Decimal(0.0))
.is_ok(),);
let mut xb = xbb.build();
assert!(xb
.run(
"
arity(1, 2, 3)
"
)
.is_err());
assert!(xb.error_handler.had_errors);
assert_eq!(
xb.error_handler.errors,
["[line 2] Error at 'arity': Expected 2 arguments, got 3."]
);
xb.get_io().check();
}
#[test]
fn duplicate_native_function() {
let tio = common::TestIO::new("");
let mut xbb = XBasicBuilder::new(tio);
assert!(xbb
.define_function("arity".to_string(), 2, |_, _| ExprValue::Decimal(0.0))
.is_ok());
assert!(xbb
.define_function("arity".to_string(), 2, |_, _| ExprValue::Decimal(5.0))
.is_err());
let mut xb = xbb.build();
assert!(xb
.run(
"
arity(1, 2, 3)
"
)
.is_err());
assert!(xb.error_handler.had_errors);
assert_eq!(
xb.error_handler.errors,
["[line 2] Error at 'arity': Expected 2 arguments, got 3."]
);
xb.get_io().check();
}
#[test]
fn sum_args() {
test_program_native_functions(
vec![("sum", 5, |args: Vec<ExprValue>, _: &mut _| {
let mut sum: f64 = 0.0;
for arg in args {
sum += arg.into_decimal();
}
ExprValue::Decimal(sum)
})],
"
print sum(1, 3.4, 5.6, 789, -204)
",
"595\n",
);
}
#[test]
fn native_same_name_function_as_user() {
test_program_native_functions(
vec![("sum", 5, |args: Vec<ExprValue>, _: &mut _| {
let mut sum: f64 = 0.0;
for arg in args {
sum += arg.into_decimal();
}
ExprValue::Decimal(sum)
})],
"
function sum()
print 1 - 2
end function
sum()
",
"-1\n",
);
}
#[test]
fn native_doesnt_corrupt_stack() {
test_program_native_functions(
vec![("sum", 5, |args: Vec<ExprValue>, _: &mut _| {
let mut sum: f64 = 0.0;
for arg in args {
sum += arg.into_decimal();
}
ExprValue::Decimal(sum)
})],
"
function sum(a, b, c, d, e)
return
end function
sum(1, 2, 3, 4, 5)
for x = 0 to 5
print x
next x
",
"0\n1\n2\n3\n4\n5\n",
);
}