xbasic 0.1.1

A library that allows adding a scripting language onto your project with ease. This lets your users write their own arbitrary logic.
Documentation
use xbasic::xbasic::XBasic;

mod common;

#[test]
fn function_empty() {
	common::test_program(
		"
a()

function a()
end function
		",
		"",
	)
}

#[test]
fn function_uncalled() {
	common::test_program(
		"
function a()
    print \"Hello world\"
end function
		",
		"",
	)
}

#[test]
fn function_no_return() {
	common::test_program(
		"
a()

function a()
    print \"hi from function\"
end function
		",
		"hi from function\n",
	)
}

#[test]
fn function_parameter() {
	common::test_program(
		"
a(\"world\", \"hello\")

function a(b, c)
    print c b
end function
		",
		"hello world\n",
	)
}

#[test]
fn function_variable_same_name() {
	common::test_program(
		"
a = 3
print a()
print a

function a()
    return 3 + 4
end function
		",
		"7\n3\n",
	)
}

#[test]
fn function_return() {
	common::test_program(
		"
print a()

function a()
    return 3 + 4
end function
		",
		"7\n",
	)
}

#[test]
fn recursion_ok() {
	common::test_program(
		"
print fib(20)

function fib(n)
    if n = 1 or n = 2 then
        return 1
    end if
    return fib(n - 1) + fib(n - 2)
end function
",
		"6765\n",
	)
}

#[test]
fn recursion_stack_overflow() {
	let mut tio = common::TestIO::new("");
	{
		let mut xb = XBasic::new(&mut tio);
		assert!(xb
			.run(
				"
function a()
    a()
end function

a()
	"
			)
			.is_err());
		assert!(xb.error_handler.had_errors);
		assert_eq!(
			xb.error_handler.errors,
			["[line 3] Error : Stack overflow."]
		);
	}
	tio.check();
}

#[test]
fn function_does_not_exist() {
	let mut tio = common::TestIO::new("");
	{
		let mut xb = XBasic::new(&mut tio);
		assert!(xb
			.run(
				"
a()
	"
			)
			.is_err());
		assert!(xb.error_handler.had_errors);
		assert_eq!(
			xb.error_handler.errors,
			["[line 2] Error at 'a': Not a function."]
		);
	}
	tio.check();
}

#[test]
fn function_already_defined() {
	let mut tio = common::TestIO::new("");
	{
		let mut xb = XBasic::new(&mut tio);
		assert!(xb
			.run(
				"
function a()
end function

function a()
end function
	"
			)
			.is_err());
		assert!(xb.error_handler.had_errors);
		assert_eq!(
			xb.error_handler.errors,
			["[line 5] Error at 'a': Function is already defined on line 2."]
		);
	}
	tio.check();
}

#[test]
fn function_mismatched_arity() {
	let mut tio = common::TestIO::new("");
	{
		let mut xb = XBasic::new(&mut tio);
		assert!(xb
			.run(
				"
a(1, 2, 3, 4)
function a(b, c, d)
end function
	"
			)
			.is_err());
		assert!(xb.error_handler.had_errors);
		assert_eq!(
			xb.error_handler.errors,
			["[line 2] Error at 'a': Expected 3 arguments, got 4."]
		);
	}
	tio.check();
}

#[test]
fn function_named_same_as_param() {
	common::test_program(
		"
print a(3)

function a(a)
    return a + 4
end function
		",
		"7\n",
	)
}

#[test]
fn nested_functions() {
	common::test_program(
		"
print a(3)

function a(a)
    return b(a) + 2
end function

function b(c)
    return c + 5
end function
		",
		"10\n",
	)
}

#[test]
fn stack_cool_after_function() {
	common::test_program(
		"
c = 4
print a(3)

b = 3
print b
print c
print b + c

function a(a)
    return b(a) + 2
end function

function b(c)
    return c + 5
end function
		",
		"10\n3\n4\n7\n",
	)
}

#[test]
fn return_in_main() {
	common::test_program(
		"
return
		",
		"",
	)
}

#[test]
fn function_across_multiple_calls() {
	let mut tio = common::TestIO::new("5\n");
	{
		let mut xb = XBasic::new(&mut tio);
		assert!(xb
			.run(
				"
function a(b)
    return b
end function
	"
			)
			.is_ok());
		assert!(xb.run("print a(5)\n").is_ok());
		assert!(!xb.error_handler.had_errors);
	}
	tio.check();
}

#[test]
fn function_overwrite_across_runs() {
	let mut tio = common::TestIO::new("5\n");
	{
		let mut xb = XBasic::new(&mut tio);
		assert!(xb
			.run(
				"
function a(b)
    return b
end function
	"
			)
			.is_ok());
		assert!(xb
			.run(
				"
function a(b)
    return b * 2
end function
	"
			)
			.is_err());
		xb.clear_errors();
		assert!(xb.run("print a(5)\n").is_ok());
		assert!(!xb.error_handler.had_errors);
	}
	tio.check();
}