use xbasic::expr::ExprValue; use xbasic::xbasic::XBasicBuilder; mod common; fn test_program_native_functions( functions: Vec<(&'static str, u8, T)>, program: &'static str, expected: &'static str, ) where T: Fn(Vec, &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, _: &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, _: &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, _: &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, _: &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", ); }