brainterpreter 0.1.2

An interpreter for a Bauble toy programming language
Documentation
use std::cell::RefCell;
use std::error::Error;
use std::rc::Rc;

use brainterpreter::compiler::Compiler;
use brainterpreter::lexer::Lexer;
use brainterpreter::parser::Parser;
use brainterpreter::vm::Vm;

#[test]
fn expression_with_negative_numbers() {
    let io = interpret("print -1;").unwrap();
    assert_eq!(io.as_slice(), "-1\n".as_bytes());
}

#[test]
fn calculation_precedence() {
    let io = interpret("print 2 + 2 * 2 - (3 + 3);").unwrap();
    let out = String::from_utf8(io).unwrap();
    assert_eq!(out, "0\n");
}

#[test]
fn conditionals() {
    let io = interpret(
        r#"
    let input = 11;
    if (input > 10) {
        print 3;
    } else if (input > 5) {
        print 2;
    } else {
        print 1;
    }
    "#,
    )
    .unwrap();
    let out = String::from_utf8(io).unwrap();
    assert_eq!(out, "3\n");

    let io = interpret(
        r#"
    let input = 6;
    if (input > 10) {
        print 3;
    } else if (input > 5) {
        print 2;
    } else {
        print 1;
    }
    "#,
    )
    .unwrap();
    let out = String::from_utf8(io).unwrap();
    assert_eq!(out, "2\n");

    let io = interpret(
        r#"
    let input = 3;
    if (input > 10) {
        print 3;
    } else if (input > 5) {
        print 2;
    } else {
        print 1;
    }
    "#,
    )
    .unwrap();
    let out = String::from_utf8(io).unwrap();
    assert_eq!(out, "1\n");
}

#[test]
fn while_loop() {
    let source = r#"
    let i = 5;
    while (i > 0) {
        print i;
        i = i - 1;
    }
    print 100;
    "#;
    let io = interpret(source).unwrap();
    let out = String::from_utf8(io).unwrap();

    assert_eq!(out, "5\n4\n3\n2\n1\n100\n");
}

#[test]
fn function_call() {
    let source = r#"
    fun add(a, b) {
        return a + b;
    }

    print add(1, 2);
    "#;
    let io = interpret(source).unwrap();
    let out = String::from_utf8(io).unwrap();

    assert_eq!(out, "3\n");
}

#[test]
fn assign_variables() {
    let source = r#"
    let a = 1;
    print a;
    let a = a + 1;
    print a;
    {
        let a = 3;
        print a;
    }
    print a;
    "#;
    let io = interpret(source).unwrap();
    let out = String::from_utf8(io).unwrap();

    assert_eq!(out, "1\n2\n3\n2\n");
}

#[test]
fn read_string_index() {
    let source = r#"
    let w = "Rust";    
    print w[0];
    "#;
    let io = interpret(source).unwrap();
    let out = String::from_utf8(io).unwrap();

    assert_eq!(out, "R\n");
}

#[test]
fn change_string_index() {
    let source = r#"
    let w = "Rust";
    w[0] = "D";
    print w;
    "#;
    let io = interpret(source).unwrap();
    let out = String::from_utf8(io).unwrap();

    assert_eq!(out, "Dust\n");
}

#[test]
fn built_in_function() {
    let source = r#"
    let str = "Rust";
    let l = len(str);
    print l;
    "#;
    let io = interpret(source).unwrap();
    let out = String::from_utf8(io).unwrap();

    assert_eq!(out, "4\n");
}

#[test]
fn iterate_over_characters() {
    let source = r#"
    let str = "Rust";
    let i = 0;
    while (i < len(str)) {
        print str[i];
        i = i + 1;
    }
    "#;
    let io = interpret(source).unwrap();
    let out = String::from_utf8(io).unwrap();

    assert_eq!(out, "R\nu\ns\nt\n");
}

#[test]
fn report_number_of_characters() {
    let source = r#"
    fun report_number_of_characters(str) {
        return str + " has " + as_string(len(str)) + " characters.";
    }
    print report_number_of_characters("Rust");
    "#;
    let io = interpret(source).unwrap();
    let out = String::from_utf8(io).unwrap();

    assert_eq!(out, "Rust has 4 characters.\n");
}

#[test]
fn number_array() {
    let source = r#"
    let memory = [0;10];
    print memory[0];
    memory[0] = 1;
    print memory[0];
    print memory[1];
    "#;
    let io = interpret(source).unwrap();
    let out = String::from_utf8(io).unwrap();

    assert_eq!(out, "0\n1\n0\n");
}

pub fn interpret(source: &str) -> Result<Vec<u8>, Box<dyn Error>> {
    let io = Rc::new(RefCell::new(vec![]));
    {
        let lexer = Lexer::new(source);
        let mut parser = Parser::new(lexer);
        let ast = parser.parse_program()?;
        let mut compiler = Compiler::default();
        let script = compiler.compile_script(ast)?;
        let mut vm = Vm::with_io(io.clone());
        vm.run_script(script)?;
    }

    let output = io.borrow();
    Ok(output.clone())
}