Browse Source

Give IO ownership to XBasic object

master
Stephen 5 days ago
parent
commit
ebd5010fff
19 changed files with 381 additions and 371 deletions
  1. +1
    -1
      Cargo.lock
  2. +1
    -1
      Cargo.toml
  3. +2
    -2
      benches/benchmark.rs
  4. +4
    -4
      src/bin/main.rs
  5. +14
    -16
      src/stmt.rs
  6. +5
    -5
      src/vm.rs
  7. +21
    -9
      src/xbasic.rs
  8. +12
    -12
      tests/bedmas.rs
  9. +15
    -15
      tests/call_function_from_rust.rs
  10. +10
    -11
      tests/common.rs
  11. +41
    -41
      tests/control.rs
  12. +11
    -11
      tests/errors.rs
  13. +123
    -123
      tests/functions.rs
  14. +6
    -6
      tests/input.rs
  15. +9
    -9
      tests/limits.rs
  16. +22
    -22
      tests/logical.rs
  17. +59
    -59
      tests/native_functions.rs
  18. +22
    -22
      tests/unary.rs
  19. +3
    -2
      tests/variables.rs

+ 1
- 1
Cargo.lock View File

@@ -784,7 +784,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

[[package]]
name = "xbasic"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"criterion",
"num-derive",


+ 1
- 1
Cargo.toml View File

@@ -1,7 +1,7 @@
[package]
name = "xbasic"
description = "A library that allows adding a scripting language onto your project with ease. This lets your users write their own arbitrary logic."
version = "0.1.5"
version = "0.2.0"
authors = ["Stephen <stephen@stephendownward.ca>"]
edition = "2018"
readme = "README.md"


+ 2
- 2
benches/benchmark.rs View File

@@ -31,8 +31,8 @@ impl BasicIO for TestIO {
}

fn recursive_benchmark(c: &mut Criterion) {
let mut test_io = TestIO::new("");
let mut xb = XBasic::new(&mut test_io);
let test_io = TestIO::new("");
let mut xb = XBasic::new(test_io);
c.bench_function("fib 20", |b| {
b.iter(|| {
xb.run(


+ 4
- 4
src/bin/main.rs View File

@@ -29,8 +29,8 @@ fn main() {
}

fn run_file(path: &str) {
let mut io = ShellIO {};
let mut xb = XBasic::new(&mut io);
let io = ShellIO {};
let mut xb = XBasic::new(io);
match xb.run(&fs::read_to_string(path).unwrap()) {
Ok(_) => {}
Err(_) => {
@@ -42,8 +42,8 @@ fn run_file(path: &str) {
}

fn run_prompt() {
let mut io = ShellIO {};
let mut xb = XBasic::new(&mut io);
let io = ShellIO {};
let mut xb = XBasic::new(io);
loop {
let mut input = String::new();
print!("> ");


+ 14
- 16
src/stmt.rs View File

@@ -2,8 +2,6 @@ use crate::expr::Expr;
use crate::tokens::Token;
use crate::visitor::StmtVisitor;

// TODO make everything in here pub(crate)

#[derive(Clone)]
pub(crate) struct Stmt {
content: StmtContent,
@@ -119,12 +117,12 @@ impl Stmt {

#[derive(Clone)]
pub(crate) struct PrintStmt {
pub(crate) values: Vec<Expr>,
pub values: Vec<Expr>,
}

#[derive(Clone)]
pub(crate) struct InputStmt {
pub(crate) variable: Token,
pub variable: Token,
}

#[derive(Clone)]
@@ -134,32 +132,32 @@ pub(crate) struct ExpressionStmt {

#[derive(Clone)]
pub(crate) struct AssignStmt {
pub(crate) name: Token,
pub(crate) value: Expr,
pub name: Token,
pub value: Expr,
}

#[derive(Clone)]
pub(crate) struct IfStmt {
pub(crate) condition: Expr,
pub(crate) then_stmts: Vec<Stmt>,
pub(crate) else_stmts: Vec<Stmt>,
pub condition: Expr,
pub then_stmts: Vec<Stmt>,
pub else_stmts: Vec<Stmt>,
}

#[derive(Clone)]
pub(crate) struct WhileStmt {
pub(crate) condition: Expr,
pub(crate) body: Vec<Stmt>,
pub condition: Expr,
pub body: Vec<Stmt>,
}

#[derive(Clone)]
pub(crate) struct ForStmt {
pub(crate) variable: Token,
pub(crate) min_value: Expr,
pub(crate) max_value: Expr,
pub(crate) body: Vec<Stmt>,
pub variable: Token,
pub min_value: Expr,
pub max_value: Expr,
pub body: Vec<Stmt>,
}

#[derive(Clone)]
pub(crate) struct ReturnStmt {
pub(crate) return_value: Expr,
pub return_value: Expr,
}

+ 5
- 5
src/vm.rs View File

@@ -24,10 +24,10 @@ impl CallFrame {

// TODO we need a way to clean up the stack when there's an error
// Probably we can just pop off the stack until we're only left with the # of variables?
pub(crate) struct VirtualMachine<'a, T: 'static> {
pub(crate) struct VirtualMachine<T: 'static> {
ip: usize,
stack: Vec<ExprValue>,
stdio: &'a mut T,
pub stdio: T,
error_handler: ErrorHandler,
chunks: Vec<Chunk>, // Holds core + function chunks
cur_chunk: usize,
@@ -39,12 +39,12 @@ pub(crate) struct VirtualMachine<'a, T: 'static> {
compute_limit: usize,
}

impl<'a, T> VirtualMachine<'a, T>
impl<T> VirtualMachine<T>
where
T: BasicIO,
{
pub(crate) fn new(
stdio: &'a mut T,
stdio: T,
native_functions: Vec<NativeFunction<T>>,
compute_limit: usize,
) -> Self {
@@ -393,7 +393,7 @@ where
}
args.reverse();
let function = &self.native_functions[function_id];
let result = (function.function)(args, self.stdio);
let result = (function.function)(args, &mut self.stdio);
self.push(result);
}
}


+ 21
- 9
src/xbasic.rs View File

@@ -18,13 +18,13 @@ use std::collections::HashMap;
/// There are two main reasons to use this:
/// 1. To define custom native functions
/// 2. To set a compute limit
pub struct XBasicBuilder<'a, T: 'static> {
stdio: &'a mut T,
pub struct XBasicBuilder<T: 'static> {
stdio: T,
native_functions: HashMap<String, NativeFunction<T>>,
compute_limit: usize,
}

impl<'a, T> XBasicBuilder<'a, T>
impl<T> XBasicBuilder<T>
where
T: BasicIO,
{
@@ -33,7 +33,7 @@ where
/// # Arguments
///
/// * `stdio` - An instance of a struct which implements the `BasicIO` trait.
pub fn new(stdio: &'a mut T) -> Self {
pub fn new(stdio: T) -> Self {
let mut xbb = Self {
stdio,
native_functions: HashMap::new(),
@@ -81,7 +81,7 @@ where
}

/// Consume the builder, producing an XBasic instance.
pub fn build(self) -> XBasic<'a, T> {
pub fn build(self) -> XBasic<T> {
let native_function_definitions = self
.native_functions
.iter()
@@ -103,22 +103,22 @@ where
}

/// Represents an xBASIC Interpreter.
pub struct XBasic<'a, T: 'static> {
pub struct XBasic<T: 'static> {
/// Keeps track of errors that have been encountered while interpreting source code.
/// Errors are recorded as user-friendly strings so that they can be passed directly to the user.
pub error_handler: ErrorHandler,
functions: Vec<Function>,
native_functions: Vec<NativeFunctionDefinition>,
compiler: Compiler,
vm: VirtualMachine<'a, T>,
vm: VirtualMachine<T>,
}

impl<'a, T> XBasic<'a, T>
impl<T> XBasic<T>
where
T: BasicIO,
{
/// Creates a new `XBasic` struct with the given `BasicIO` instance.
pub fn new(stdio: &'a mut T) -> Self {
pub fn new(stdio: T) -> Self {
let xbb = XBasicBuilder::new(stdio);
xbb.build()
}
@@ -284,4 +284,16 @@ where

Err(())
}

/// Gets a reference to the IO object.
/// Useful if you are using the IO object for some kind of state.
pub fn get_io(&self) -> &T {
&self.vm.stdio
}

/// Gets a mutable reference to the IO object.
/// Useful if you are using the IO object for some kind of state.
pub fn get_io_mut(&mut self) -> &mut T {
&mut self.vm.stdio
}
}

+ 12
- 12
tests/bedmas.rs View File

@@ -4,20 +4,20 @@ mod common;

#[test]
fn order_of_operations_1() {
let mut tio = common::TestIO::new("23\n");
{
let mut xb = XBasic::new(&mut tio);
xb.run("print 3 + 4 * 5\n").unwrap();
}
tio.check();
let tio = common::TestIO::new("23\n");
let mut xb = XBasic::new(tio);
xb.run("print 3 + 4 * 5\n").unwrap();
xb.get_io().check();
}

#[test]
fn order_of_operations_2() {
let mut tio = common::TestIO::new("35\n");
{
let mut xb = XBasic::new(&mut tio);
xb.run("print (3 + 4) * 5\n").unwrap();
}
tio.check();
let tio = common::TestIO::new("35\n");
let mut xb = XBasic::new(tio);
xb.run("print (3 + 4) * 5\n").unwrap();
xb.get_io().check();
}

+ 15
- 15
tests/call_function_from_rust.rs View File

@@ -5,8 +5,8 @@ use xbasic::xbasic::XBasic;

#[test]
fn wrong_arity() {
let mut tio = common::TestIO::new("");
let mut xb = XBasic::new(&mut tio);
let tio = common::TestIO::new("");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
@@ -17,13 +17,13 @@ end function
)
.is_ok());
assert!(xb.call_function("a", &[]).is_err());
tio.check();
xb.get_io().check();
}

#[test]
fn no_arguments() {
let mut tio = common::TestIO::new("Hello World!\n");
let mut xb = XBasic::new(&mut tio);
let tio = common::TestIO::new("Hello World!\n");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
@@ -34,13 +34,13 @@ end function
)
.is_ok());
assert!(xb.call_function("a", &[]).is_ok());
tio.check();
xb.get_io().check();
}

#[test]
fn multiple_arguments() {
let mut tio = common::TestIO::new("abc 3.4\n");
let mut xb = XBasic::new(&mut tio);
let tio = common::TestIO::new("abc 3.4\n");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
@@ -56,13 +56,13 @@ end function
&[ExprValue::String("abc".to_owned()), ExprValue::Decimal(3.4)]
)
.is_ok());
tio.check();
xb.get_io().check();
}

#[test]
fn return_value() {
let mut tio = common::TestIO::new("");
let mut xb = XBasic::new(&mut tio);
let tio = common::TestIO::new("");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
@@ -76,13 +76,13 @@ end function
Ok(ExprValue::Integer(3)),
xb.call_function("func", &[ExprValue::Integer(5), ExprValue::Integer(2)])
);
tio.check();
xb.get_io().check();
}

#[test]
fn multiple_calls() {
let mut tio = common::TestIO::new("");
let mut xb = XBasic::new(&mut tio);
let tio = common::TestIO::new("");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
@@ -112,5 +112,5 @@ end function
Ok(ExprValue::Integer(3)),
xb.call_function("func", &[ExprValue::Integer(6), ExprValue::Integer(3)])
);
tio.check();
xb.get_io().check();
}

+ 10
- 11
tests/common.rs View File

@@ -31,18 +31,17 @@ impl BasicIO for TestIO {

#[allow(dead_code)]
pub fn test_program(program: &'static str, expected: &'static str) {
let mut tio = TestIO::new(expected);
{
let mut xb = XBasic::new(&mut tio);
match xb.run(program) {
Ok(_) => (),
Err(_) => {
for error in xb.error_handler.errors {
println!("{}", error);
}
panic!();
let tio = TestIO::new(expected);

let mut xb = XBasic::new(tio);
match xb.run(program) {
Ok(_) => (),
Err(_) => {
for error in xb.error_handler.errors {
println!("{}", error);
}
panic!();
}
}
tio.check();
xb.get_io().check();
}

+ 41
- 41
tests/control.rs View File

@@ -20,17 +20,17 @@ fn if_test_false() {

#[test]
fn if_no_terminator() {
let mut tio = common::TestIO::new("");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb.run("if 3 = 4 then\n").is_err());
assert!(xb.error_handler.had_errors);
assert_eq!(
xb.error_handler.errors,
["[line 2] Error at end: Expected END IF after IF statement."]
);
}
tio.check();
let tio = common::TestIO::new("");
let mut xb = XBasic::new(tio);
assert!(xb.run("if 3 = 4 then\n").is_err());
assert!(xb.error_handler.had_errors);
assert_eq!(
xb.error_handler.errors,
["[line 2] Error at end: Expected END IF after IF statement."]
);
xb.get_io().check();
}

// TODO
@@ -135,26 +135,26 @@ fn if_elseif_else() {

#[test]
fn if_elseif_missing_then() {
let mut tio = common::TestIO::new("");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb
.run(
"if 5 = 3 then
let tio = common::TestIO::new("");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"if 5 = 3 then
print \"hi\"
elseif 3 = 5
print \"bye\"\
end if
"
)
.is_err());
assert!(xb.error_handler.had_errors);
assert_eq!(
xb.error_handler.errors,
["[line 3] Error at newline: Expected THEN after ELSEIF statement condition."]
);
}
tio.check();
)
.is_err());
assert!(xb.error_handler.had_errors);
assert_eq!(
xb.error_handler.errors,
["[line 3] Error at newline: Expected THEN after ELSEIF statement condition."]
);
xb.get_io().check();
}

#[test]
@@ -204,24 +204,24 @@ next x

#[test]
fn for_loop_invalid_next() {
let mut tio = common::TestIO::new("");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb
.run(
"for x = 0 to 10
let tio = common::TestIO::new("");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"for x = 0 to 10
print x
next y
"
)
.is_err());
assert!(xb.error_handler.had_errors);
assert_eq!(
xb.error_handler.errors,
["[line 3] Error at 'y': Incorrect variable after NEXT."]
);
}
tio.check();
)
.is_err());
assert!(xb.error_handler.had_errors);
assert_eq!(
xb.error_handler.errors,
["[line 3] Error at 'y': Incorrect variable after NEXT."]
);
xb.get_io().check();
}

#[test]


+ 11
- 11
tests/errors.rs View File

@@ -4,15 +4,15 @@ mod common;

#[test]
fn runtime_line_count() {
let mut tio = common::TestIO::new("hello world\n");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb.run("print \"hello world\"\n\"what\" and 3\n").is_err());
assert!(xb.error_handler.had_runtime_error);
assert_eq!(
xb.error_handler.errors,
["[line 2] Error : Cannot cast string to boolean."]
);
}
tio.check();
let tio = common::TestIO::new("hello world\n");
let mut xb = XBasic::new(tio);
assert!(xb.run("print \"hello world\"\n\"what\" and 3\n").is_err());
assert!(xb.error_handler.had_runtime_error);
assert_eq!(
xb.error_handler.errors,
["[line 2] Error : Cannot cast string to boolean."]
);
xb.get_io().check();
}

+ 123
- 123
tests/functions.rs View File

@@ -104,96 +104,96 @@ end function

#[test]
fn recursion_stack_overflow() {
let mut tio = common::TestIO::new("");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb
.run(
"
let tio = common::TestIO::new("");
let mut xb = XBasic::new(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();
)
.is_err());
assert!(xb.error_handler.had_errors);
assert_eq!(
xb.error_handler.errors,
["[line 3] Error : Stack overflow."]
);
xb.get_io().check();
}

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

#[test]
fn function_already_defined() {
let mut tio = common::TestIO::new("");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb
.run(
"
let tio = common::TestIO::new("");
let mut xb = XBasic::new(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();
)
.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."]
);
xb.get_io().check();
}

#[test]
fn function_mismatched_arity() {
let mut tio = common::TestIO::new("");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb
.run(
"
let tio = common::TestIO::new("");
let mut xb = XBasic::new(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();
)
.is_err());
assert!(xb.error_handler.had_errors);
assert_eq!(
xb.error_handler.errors,
["[line 2] Error at 'a': Expected 3 arguments, got 4."]
);
xb.get_io().check();
}

#[test]
@@ -264,62 +264,62 @@ 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(
"
let tio = common::TestIO::new("5\n");
let mut xb = XBasic::new(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();
)
.is_ok());
assert!(xb.run("print a(5)\n").is_ok());
assert!(!xb.error_handler.had_errors);
xb.get_io().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(
"
let tio = common::TestIO::new("5\n");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
function a(b)
return b
end function
"
)
.is_ok());
assert!(xb
.run(
"
)
.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();
)
.is_err());
xb.clear_errors();
assert!(xb.run("print a(5)\n").is_ok());
assert!(!xb.error_handler.had_errors);
xb.get_io().check();
}

#[test]
fn function_variable_edge_case() {
let mut tio = common::TestIO::new("done\n");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb
.run(
"
let tio = common::TestIO::new("done\n");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
function func(b)
a = 0
end function
@@ -329,21 +329,21 @@ z = 0
b = 0
print \"done\"
",
)
.is_ok());
assert!(!xb.error_handler.had_errors);
}
tio.check();
)
.is_ok());
assert!(!xb.error_handler.had_errors);
xb.get_io().check();
}

#[test]
fn function_argument_edge_case() {
let mut tio = common::TestIO::new("3\ndone\n");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb
.run(
"
let tio = common::TestIO::new("3\ndone\n");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
a = test
function func(b)
c = 0
@@ -354,21 +354,21 @@ end function
z = 0
print \"done\"
",
)
.is_ok());
assert!(!xb.error_handler.had_errors);
}
tio.check();
)
.is_ok());
assert!(!xb.error_handler.had_errors);
xb.get_io().check();
}

#[test]
fn function_doesnt_corrupt_stack() {
let mut tio = common::TestIO::new("3\n0\n1\n2\n3\n4\n5\n");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb
.run(
"
let tio = common::TestIO::new("3\n0\n1\n2\n3\n4\n5\n");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
function func(b)
print b
end function
@@ -378,9 +378,9 @@ for x = 0 to 5
print x
next x
",
)
.is_ok());
assert!(!xb.error_handler.had_errors);
}
tio.check();
)
.is_ok());
assert!(!xb.error_handler.had_errors);
xb.get_io().check();
}

+ 6
- 6
tests/input.rs View File

@@ -40,8 +40,8 @@ impl BasicIO for TestIO {

#[test]
fn input_test() {
let mut tio = TestIO::new("Line 1\nLine 2\n3", "3\nLine 2\nLine 1\n");
let mut xb = XBasic::new(&mut tio);
let tio = TestIO::new("Line 1\nLine 2\n3", "3\nLine 2\nLine 1\n");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
@@ -54,13 +54,13 @@ print a
"
)
.is_ok());
tio.check();
xb.get_io().check();
}

#[test]
fn input_overwrite() {
let mut tio = TestIO::new("Line 1", "Line 1\n");
let mut xb = XBasic::new(&mut tio);
let tio = TestIO::new("Line 1", "Line 1\n");
let mut xb = XBasic::new(tio);
assert!(xb
.run(
"
@@ -70,5 +70,5 @@ print a
"
)
.is_ok());
tio.check();
xb.get_io().check();
}

+ 9
- 9
tests/limits.rs View File

@@ -4,8 +4,8 @@ mod common;

#[test]
fn below_compute_limit() {
let mut tio = common::TestIO::new("3240\n");
let mut xbb = XBasicBuilder::new(&mut tio);
let tio = common::TestIO::new("3240\n");
let mut xbb = XBasicBuilder::new(tio);
xbb.compute_limit(1000);
let mut xb = xbb.build();
assert!(xb
@@ -19,13 +19,13 @@ print b
)
.is_ok());
assert!(!xb.error_handler.had_runtime_error);
tio.check();
xb.get_io().check();
}

#[test]
fn above_compute_limit() {
let mut tio = common::TestIO::new("");
let mut xbb = XBasicBuilder::new(&mut tio);
let tio = common::TestIO::new("");
let mut xbb = XBasicBuilder::new(tio);
xbb.compute_limit(1000);
let mut xb = xbb.build();
assert!(xb
@@ -43,13 +43,13 @@ print b
"[line 2] Error : Compute limit exceeded.",
xb.error_handler.errors[0]
);
tio.check();
xb.get_io().check();
}

#[test]
fn compute_error_line_one() {
let mut tio = common::TestIO::new("");
let mut xbb = XBasicBuilder::new(&mut tio);
let tio = common::TestIO::new("");
let mut xbb = XBasicBuilder::new(tio);
xbb.compute_limit(1000);
let mut xb = xbb.build();
assert!(xb
@@ -65,5 +65,5 @@ wend
"[line 2] Error : Compute limit exceeded.",
xb.error_handler.errors[0]
);
tio.check();
xb.get_io().check();
}

+ 22
- 22
tests/logical.rs View File

@@ -19,17 +19,17 @@ fn and_3() {

#[test]
fn and_4() {
let mut tio = common::TestIO::new("");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb.run("print \"test\" and 4\n").is_err());
assert!(xb.error_handler.had_runtime_error);
assert_eq!(
xb.error_handler.errors,
["[line 1] Error : Cannot cast string to boolean."]
);
}
tio.check();
let tio = common::TestIO::new("");
let mut xb = XBasic::new(tio);
assert!(xb.run("print \"test\" and 4\n").is_err());
assert!(xb.error_handler.had_runtime_error);
assert_eq!(
xb.error_handler.errors,
["[line 1] Error : Cannot cast string to boolean."]
);
xb.get_io().check();
}

#[test]
@@ -49,15 +49,15 @@ fn or_3() {

#[test]
fn or_4() {
let mut tio = common::TestIO::new("");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb.run("print \"test\" or 4\n").is_err());
assert!(xb.error_handler.had_runtime_error);
assert_eq!(
xb.error_handler.errors,
["[line 1] Error : Cannot cast string to boolean."]
);
}
tio.check();
let tio = common::TestIO::new("");
let mut xb = XBasic::new(tio);
assert!(xb.run("print \"test\" or 4\n").is_err());
assert!(xb.error_handler.had_runtime_error);
assert_eq!(
xb.error_handler.errors,
["[line 1] Error : Cannot cast string to boolean."]
);
xb.get_io().check();
}

+ 59
- 59
tests/native_functions.rs View File

@@ -10,29 +10,29 @@ fn test_program_native_functions<T: 'static>(
) where
T: Fn(Vec<ExprValue>, &mut common::TestIO) -> ExprValue,
{
let mut tio = common::TestIO::new(expected);
{
let mut xbb = XBasicBuilder::new(&mut tio);
for (name, arity, native_fn) in functions {
assert!(xbb
.define_function(name.to_owned(), arity, native_fn)
.is_ok());
}
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();
let mut xb = xbb.build();

match xb.run(program) {
Ok(_) => (),
Err(_) => {
for error in xb.error_handler.errors {
println!("{}", error);
}
panic!();
match xb.run(program) {
Ok(_) => (),
Err(_) => {
for error in xb.error_handler.errors {
println!("{}", error);
}
panic!();
}
}
tio.check();

xb.get_io().check();
}

#[test]
@@ -61,55 +61,55 @@ print test(300, 2)

#[test]
fn wrong_arity() {
let mut tio = common::TestIO::new("");
{
let mut xbb = XBasicBuilder::new(&mut tio);
assert!(xbb
.define_function("arity".to_string(), 2, |_, _| ExprValue::Decimal(0.0))
.is_ok(),);
let mut xb = xbb.build();
assert!(xb
.run(
"
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."]
);
}
tio.check();
)
.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 mut tio = common::TestIO::new("");
{
let mut xbb = XBasicBuilder::new(&mut 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(
"
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."]
);
}
tio.check();
)
.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]


+ 22
- 22
tests/unary.rs View File

@@ -14,17 +14,17 @@ fn not_2() {

#[test]
fn not_3() {
let mut tio = common::TestIO::new("");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb.run("print not \"test\"\n").is_err());
assert!(xb.error_handler.had_runtime_error);
assert_eq!(
xb.error_handler.errors,
["[line 1] Error : Cannot cast string to boolean."]
);
}
tio.check();
let tio = common::TestIO::new("");
let mut xb = XBasic::new(tio);
assert!(xb.run("print not \"test\"\n").is_err());
assert!(xb.error_handler.had_runtime_error);
assert_eq!(
xb.error_handler.errors,
["[line 1] Error : Cannot cast string to boolean."]
);
xb.get_io().check();
}

#[test]
@@ -39,15 +39,15 @@ fn neg_2() {

#[test]
fn neg_3() {
let mut tio = common::TestIO::new("");
{
let mut xb = XBasic::new(&mut tio);
assert!(xb.run("print -\"test\"\n").is_err());
assert!(xb.error_handler.had_runtime_error);
assert_eq!(
xb.error_handler.errors,
["[line 1] Error : Cannot cast string to number."]
);
}
tio.check();
let tio = common::TestIO::new("");
let mut xb = XBasic::new(tio);
assert!(xb.run("print -\"test\"\n").is_err());
assert!(xb.error_handler.had_runtime_error);
assert_eq!(
xb.error_handler.errors,
["[line 1] Error : Cannot cast string to number."]
);
xb.get_io().check();
}

+ 3
- 2
tests/variables.rs View File

@@ -14,12 +14,13 @@ fn initialized_variable() {

#[test]
fn variable_persists_runs() {
let mut tio = common::TestIO::new("3\n");
let mut xb = XBasic::new(&mut tio);
let tio = common::TestIO::new("3\n");
let mut xb = XBasic::new(tio);
assert!(xb.run("a = 3\n").is_ok());
assert!(xb.run("true and \"hi\"").is_err());
xb.clear_errors();
assert!(xb.run("print a\n").is_ok());
xb.get_io().check();
}

#[test]


Loading…
Cancel
Save