basic-lang 0.7.1

The BASIC programming language as it was in the 8-bit era.
Documentation
use basic::lang::{ast::*, lex, parse};

fn parse_str(s: &str) -> Option<Statement> {
    let (lin, tokens) = lex(s);
    match parse(lin, &tokens) {
        Ok(mut v) => v.pop(),
        Err(_) => None,
    }
}

#[test]
fn test_let_foo_eq_bar() {
    let answer = Statement::Let(
        0..3,
        Variable::Unary(4..7, Ident::Plain("TER".into())),
        Expression::Variable(Variable::Unary(8..11, Ident::Plain("BAR".into()))),
    );
    assert_eq!(parse_str("letter=bar:"), Some(answer));
    let answer = Statement::Let(
        0..3,
        Variable::Unary(0..3, Ident::Plain("TER".into())),
        Expression::Variable(Variable::Unary(4..7, Ident::Plain("BAR".into()))),
    );
    assert_eq!(parse_str("ter=bar:"), Some(answer));
}

#[test]
fn test_literals() {
    let answer = Statement::Let(
        0..1,
        Variable::Unary(0..1, Ident::Plain("A".into())),
        Expression::Integer(2..4, 12),
    );
    assert_eq!(parse_str("A=12"), Some(answer));
    let answer = Statement::Let(
        0..1,
        Variable::Unary(0..1, Ident::Plain("A".into())),
        Expression::Single(2..5, 12.0),
    );
    assert_eq!(parse_str("A=12!"), Some(answer));
    let answer = Statement::Let(
        0..1,
        Variable::Unary(0..1, Ident::Plain("A".into())),
        Expression::Double(2..6, 12e4),
    );
    assert_eq!(parse_str("A=12d4"), Some(answer));
    let answer = Statement::Let(
        0..1,
        Variable::Unary(0..1, Ident::Plain("A".into())),
        Expression::String(2..8, "food".into()),
    );
    assert_eq!(parse_str("A=\"food\""), Some(answer));
    let answer = Statement::Let(
        0..1,
        Variable::Unary(0..1, Ident::Plain("A".into())),
        Expression::Double(2..58, 0.0),
    );
    assert_eq!(
        parse_str("A=798347598234765983475983248592d-234721398742391847982344"),
        Some(answer)
    );
}

#[test]
fn test_unary() {
    let answer = Statement::Let(
        0..1,
        Variable::Unary(0..1, Ident::Plain("A".into())),
        Expression::Negation(
            2..3,
            Box::new(Expression::Add(
                5..6,
                Box::new(Expression::Integer(4..5, 1)),
                Box::new(Expression::Integer(7..8, 1)),
            )),
        ),
    );
    assert_eq!(parse_str("A=-(1++1)"), Some(answer));
}

#[test]
fn test_functions() {
    let answer = Statement::Let(
        0..1,
        Variable::Unary(0..1, Ident::Plain("A".into())),
        Expression::Variable(Variable::Array(
            2..11,
            Ident::Plain("COS".into()),
            vec![Expression::Single(6..10, 3.11)],
        )),
    );
    assert_eq!(parse_str("A=cos(3.11)"), Some(answer));
}

#[test]
fn test_precedence_and_paren() {
    let answer = Statement::Let(
        0..3,
        Variable::Unary(4..5, Ident::Plain("A".into())),
        Expression::Subtract(
            8..9,
            Box::new(Expression::Integer(7..8, 2)),
            Box::new(Expression::Multiply(
                22..23,
                Box::new(Expression::Add(
                    11..12,
                    Box::new(Expression::Integer(10..11, 3)),
                    Box::new(Expression::Variable(Variable::Array(
                        12..21,
                        Ident::Plain("COS".into()),
                        vec![Expression::Single(16..20, 3.11)],
                    ))),
                )),
                Box::new(Expression::Integer(23..24, 4)),
            )),
        ),
    );
    assert_eq!(parse_str("let A=(2-(3+cos(3.11))*4)"), Some(answer));
}

#[test]
fn test_printer_list() {
    let (lin, tokens) = lex("? 1 2,3;:?0.0");
    assert_eq!(
        parse(lin, &tokens).ok(),
        Some(vec!(
            Statement::Print(
                0..5,
                vec![
                    Expression::Integer(6..7, 1),
                    Expression::Integer(8..9, 2),
                    Expression::Variable(Variable::Array(
                        9..10,
                        Ident::String("TAB".into()),
                        vec![Expression::Integer(9..10, -14)]
                    )),
                    Expression::Integer(10..11, 3),
                ]
            ),
            Statement::Print(
                13..18,
                vec![
                    Expression::Single(19..22, 0.0),
                    Expression::String(22..22, "\n".into())
                ]
            )
        ))
    );
}

#[test]
fn test_remarks() {
    let (lin, tokens) = lex("10 PRINT REMARK");
    assert_eq!(
        parse(lin, &tokens).ok(),
        Some(vec!(Statement::Print(
            0..5,
            vec![Expression::String(6..6, "\n".into())]
        )))
    );
    let (lin, tokens) = lex("10 PRINT 'REMARK");
    assert_eq!(
        parse(lin, &tokens).ok(),
        Some(vec!(Statement::Print(
            0..5,
            vec![Expression::String(6..6, "\n".into())]
        )))
    );
}