strykelang 0.9.1

A highly parallel Perl 5 interpreter written in Rust
Documentation
//! Parsed `Program` shape checks (explicit tests; no batching).

use stryke::ast::{ExprKind, StmtKind};

#[test]
fn empty_source_yields_empty_statement_list() {
    let p = stryke::parse("").expect("parse");
    assert!(p.statements.is_empty());
}

#[test]
fn semicolons_only_parse_to_program_without_panicking() {
    let p = stryke::parse(";;").expect("parse");
    assert!(
        p.statements.is_empty()
            || p.statements
                .iter()
                .all(|s| matches!(s.kind, StmtKind::Empty))
    );
}

#[test]
fn three_expression_statements_distinct_lines() {
    let p = stryke::parse("1;\n2;\n3").expect("parse");
    assert_eq!(p.statements.len(), 3);
    for (i, stmt) in p.statements.iter().enumerate() {
        let StmtKind::Expression(expr) = &stmt.kind else {
            panic!("stmt {i}: expected Expression");
        };
        let ExprKind::Integer(n) = &expr.kind else {
            panic!("stmt {i}: expected integer literal");
        };
        assert_eq!(*n, (i as i64) + 1);
    }
}

#[test]
fn sub_decl_then_call_without_semicolon_is_two_statements() {
    let p = stryke::parse("fn foo { return 5 } foo()").expect("parse");
    assert_eq!(
        p.statements.len(),
        2,
        "expected sub stmt then call stmt; got {:?}",
        p.statements
            .iter()
            .map(|s| std::mem::discriminant(&s.kind))
            .collect::<Vec<_>>()
    );
}

#[test]
fn sub_declaration_statement_kind() {
    let p = stryke::parse("fn foo { return 1; }").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::SubDecl { name, .. } = &p.statements[0].kind else {
        panic!("expected SubDecl");
    };
    assert_eq!(name, "foo");
}

#[test]
fn package_statement_kind() {
    let p = stryke::parse("package Bar::Baz").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::Package { name } = &p.statements[0].kind else {
        panic!("expected Package");
    };
    assert_eq!(name, "Bar::Baz");
}

#[test]
fn use_strict_statement_kind() {
    let p = stryke::parse("use strict").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::Use { module, .. } = &p.statements[0].kind else {
        panic!("expected Use");
    };
    assert_eq!(module, "strict");
}

#[test]
fn no_warnings_statement_kind() {
    let p = stryke::parse("no warnings").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::No { module, .. } = &p.statements[0].kind else {
        panic!("expected No");
    };
    assert_eq!(module, "warnings");
}

#[test]
fn my_scalar_declaration_statement_kind() {
    let p = stryke::parse("my $x = 10").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::My(decls) = &p.statements[0].kind else {
        panic!("expected My");
    };
    assert_eq!(decls.len(), 1);
    assert_eq!(decls[0].name, "x");
}

#[test]
fn if_statement_has_else_branch_in_ast() {
    let p = stryke::parse("if (0) { 1; } else { 2; }").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::If {
        else_block, elsifs, ..
    } = &p.statements[0].kind
    else {
        panic!("expected If");
    };
    assert!(elsifs.is_empty());
    assert!(else_block.is_some());
}

#[test]
fn while_loop_statement_kind() {
    let p = stryke::parse("while (0) { }").expect("parse");
    assert_eq!(p.statements.len(), 1);
    assert!(matches!(p.statements[0].kind, StmtKind::While { .. }));
}

#[test]
fn foreach_statement_kind() {
    let p = stryke::parse("foreach my $k (1, 2) { $k; }").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::Foreach { var, .. } = &p.statements[0].kind else {
        panic!("expected Foreach");
    };
    assert_eq!(var, "k");
}

#[test]
fn begin_block_statement_kind() {
    let p = stryke::parse("BEGIN { 1; }").expect("parse");
    assert_eq!(p.statements.len(), 1);
    assert!(matches!(p.statements[0].kind, StmtKind::Begin(_)));
}

#[test]
fn end_block_statement_kind() {
    let p = stryke::parse("END { 1; }").expect("parse");
    assert_eq!(p.statements.len(), 1);
    assert!(matches!(p.statements[0].kind, StmtKind::End(_)));
}

#[test]
fn return_statement_kind() {
    let p = stryke::parse("return 42").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::Return(Some(expr)) = &p.statements[0].kind else {
        panic!("expected Return with expr");
    };
    assert!(matches!(expr.kind, ExprKind::Integer(42)));
}

#[test]
fn bare_return_statement_kind() {
    let p = stryke::parse("return").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::Return(None) = &p.statements[0].kind else {
        panic!("expected Return without expr");
    };
}

#[test]
fn last_next_redo_statement_kinds() {
    let p = stryke::parse("last; next; redo").expect("parse");
    assert_eq!(p.statements.len(), 3);
    assert!(matches!(p.statements[0].kind, StmtKind::Last(None)));
    assert!(matches!(p.statements[1].kind, StmtKind::Next(None)));
    assert!(matches!(p.statements[2].kind, StmtKind::Redo(None)));
}

#[test]
fn binary_add_expression_in_statement() {
    let p = stryke::parse("7 + 8").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::Expression(expr) = &p.statements[0].kind else {
        panic!("expected Expression");
    };
    let ExprKind::BinOp { op, .. } = &expr.kind else {
        panic!("expected BinOp");
    };
    use stryke::ast::BinOp;
    assert_eq!(*op, BinOp::Add);
}

#[test]
fn regex_literal_expression_kind() {
    let p = stryke::parse("m/abc/").expect("parse");
    assert_eq!(p.statements.len(), 1);
    let StmtKind::Expression(expr) = &p.statements[0].kind else {
        panic!("expected Expression");
    };
    let ExprKind::Regex(_, flags) = &expr.kind else {
        panic!("expected Regex");
    };
    assert!(flags.is_empty());
}