runmat-parser 0.5.0

Parser for RunMat (MATLAB/Octave) producing HIR from token streams
Documentation
use runmat_parser::{Expr, LValue, MultiAssignTarget, Stmt};

mod parse;
use parse::parse;

#[test]
fn multi_assign_parses() {
    let program = parse("[a,b] = f(x)").unwrap();
    assert_eq!(program.body.len(), 1);
    match &program.body[0] {
        Stmt::MultiAssign(targets, rhs, suppressed, _) => {
            let expected = vec![
                MultiAssignTarget::LValue(LValue::Var("a".into())),
                MultiAssignTarget::LValue(LValue::Var("b".into())),
            ];
            assert_eq!(targets, &expected);
            assert!(!suppressed);
            match rhs {
                Expr::FuncCall(name, args, _) => {
                    assert_eq!(name, "f");
                    assert_eq!(args.len(), 1);
                    assert!(matches!(args[0], Expr::Ident(ref n, _) if n == "x"));
                }
                _ => panic!("expected function call"),
            }
        }
        _ => panic!("expected multi-assign"),
    }
}

#[test]
fn multi_assign_semicolon_and_newline_behavior() {
    let program = parse("[a,b] = f(x);\n[c, d] = g(y)").unwrap();
    assert_eq!(program.body.len(), 2);

    match &program.body[0] {
        Stmt::MultiAssign(targets, rhs, suppressed, _) => {
            assert_eq!(
                targets,
                &vec![
                    MultiAssignTarget::LValue(LValue::Var("a".into())),
                    MultiAssignTarget::LValue(LValue::Var("b".into()))
                ]
            );
            assert!(*suppressed);
            if let Expr::FuncCall(name, _, _) = rhs {
                assert_eq!(name, "f");
            } else {
                panic!("expected first RHS as function call");
            }
        }
        other => panic!("unexpected first stmt: {other:?}"),
    }

    match &program.body[1] {
        Stmt::MultiAssign(targets, rhs, suppressed, _) => {
            assert_eq!(
                targets,
                &vec![
                    MultiAssignTarget::LValue(LValue::Var("c".into())),
                    MultiAssignTarget::LValue(LValue::Var("d".into()))
                ]
            );
            assert!(!*suppressed);
            if let Expr::FuncCall(name, _, _) = rhs {
                assert_eq!(name, "g");
            } else {
                panic!("expected second RHS as function call");
            }
        }
        other => panic!("unexpected second stmt: {other:?}"),
    }
}

#[test]
fn multi_assign_index_cell_target_parses() {
    let program = parse("[varargout{1:nargout}] = f(x)").unwrap();
    assert_eq!(program.body.len(), 1);
    match &program.body[0] {
        Stmt::MultiAssign(targets, rhs, suppressed, _) => {
            assert!(!suppressed);
            assert_eq!(targets.len(), 1);
            match &targets[0] {
                MultiAssignTarget::LValue(LValue::IndexCell(base, indices)) => {
                    assert!(matches!(**base, Expr::Ident(ref n, _) if n == "varargout"));
                    assert_eq!(indices.len(), 1);
                }
                other => panic!("expected cell-index target, got {other:?}"),
            }
            assert!(matches!(rhs, Expr::FuncCall(ref name, _, _) if name == "f"));
        }
        other => panic!("expected multi-assign, got {other:?}"),
    }
}