plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


plum-checker/tests/monomorphize_tests.rs
#![allow(non_snake_case)]
use plum_checker::monomorphize::*;
use plum_checker::types::PlumType;
use plum_core::ast;

#[test]
fn isGenericParamNameAcceptsSingleUppercaseLettersOnly() {
    assert!(isGenericParamName("T"));
    assert!(isGenericParamName("W"));
    assert!(!isGenericParamName("Int"));
    assert!(!isGenericParamName("TU"));
    assert!(!isGenericParamName("a"));
    assert!(!isGenericParamName(""));
}

#[test]
fn classGenericParamsReadsDeclaredGenericsList() {
    let c = ast::Class {
        name: "Box".to_string(),
        implements: vec![],
        generics: vec![ast::GenericParam { name: "T".to_string(), bounds: vec![] }],
        fields: vec![ast::Field { name: "value".to_string(), ty: ast::Type { name: "T".to_string(), generics: vec![] } }],
    };
    assert_eq!(classGenericParams(&c), vec!["T".to_string()]);
}

#[test]
fn fnGenericParamsDetectsImplicitUppercaseLetterTypesInOrder() {
    let f = ast::Fn {
        is_extern: false,
        name: "pair".to_string(),
        type_param: None,
        params: vec![
            ast::Param { name: "first".to_string(), ty: ast::ParamType::Type(ast::Type { name: "T".to_string(), generics: vec![] }), default: None },
            ast::Param { name: "second".to_string(), ty: ast::ParamType::Type(ast::Type { name: "U".to_string(), generics: vec![] }), default: None },
        ],
        returns: Some(ast::Type { name: "Bool".to_string(), generics: vec![] }),
        body: ast::FnBody::Block(ast::Block { stmts: vec![] }),
    };
    assert_eq!(fnGenericParams(&f), vec!["T".to_string(), "U".to_string()]);
}

#[test]
fn enumGenericParamsDetectsImplicitUppercaseLetterVariantFields() {
    let e = ast::Enum {
        name: "Option".to_string(),
        params: vec![],
        variants: vec![
            ast::EnumVariant { name: "Some".to_string(), fields: vec!["T".to_string()], values: vec![] },
            ast::EnumVariant { name: "None".to_string(), fields: vec![], values: vec![] },
        ],
    };
    assert_eq!(enumGenericParams(&e), vec!["T".to_string()]);
}

#[test]
fn mangleJoinsBaseNameAndTypeArgs() {
    assert_eq!(mangle("Box", &[PlumType::TInt]), "Box$Int");
    assert_eq!(mangle("Pair", &[PlumType::TInt, PlumType::TStr]), "Pair$Int$Str");
    assert_eq!(mangle("Green", &[]), "Green");
}

#[test]
fn specializeClassSubstitutesGenericFieldTypesAndClearsGenericsList() {
    let c = ast::Class {
        name: "Box".to_string(),
        implements: vec![],
        generics: vec![ast::GenericParam { name: "T".to_string(), bounds: vec![] }],
        fields: vec![ast::Field { name: "value".to_string(), ty: ast::Type { name: "T".to_string(), generics: vec![] } }],
    };
    let mut bindings = std::collections::BTreeMap::new();
    bindings.insert("T".to_string(), PlumType::TInt);
    let specialized = specializeClass(&c, &Substitution(bindings), "Box$Int");
    assert_eq!(specialized.name, "Box$Int");
    assert!(specialized.generics.is_empty());
    assert_eq!(specialized.fields[0].ty.name, "Int");
}

#[test]
fn specializeFnSubstitutesGenericParamAndReturnTypes() {
    let f = ast::Fn {
        is_extern: false,
        name: "wrap".to_string(),
        type_param: None,
        params: vec![ast::Param { name: "value".to_string(), ty: ast::ParamType::Type(ast::Type { name: "T".to_string(), generics: vec![] }), default: None }],
        returns: Some(ast::Type { name: "T".to_string(), generics: vec![] }),
        body: ast::FnBody::Expr(ast::Expr::Var("value".to_string())),
    };
    let mut bindings = std::collections::BTreeMap::new();
    bindings.insert("T".to_string(), PlumType::TInt);
    let specialized = specializeFn(&f, &Substitution(bindings), "wrap$Int", None);
    assert_eq!(specialized.name, "wrap$Int");
    match &specialized.params[0].ty {
        ast::ParamType::Type(t) => assert_eq!(t.name, "Int"),
        _ => panic!("expected ParamType::Type"),
    }
    assert_eq!(specialized.returns.unwrap().name, "Int");
}

#[test]
fn specializeFnSetsNewReceiverForAMethod() {
    let f = ast::Fn {
        is_extern: false,
        name: "getValue".to_string(),
        type_param: Some("Box".to_string()),
        params: vec![],
        returns: Some(ast::Type { name: "T".to_string(), generics: vec![] }),
        body: ast::FnBody::Expr(ast::Expr::Self_),
    };
    let mut bindings = std::collections::BTreeMap::new();
    bindings.insert("T".to_string(), PlumType::TStr);
    let specialized = specializeFn(&f, &Substitution(bindings), "getValue", Some("Box$Str".to_string()));
    assert_eq!(specialized.name, "getValue");
    assert_eq!(specialized.type_param, Some("Box$Str".to_string()));
    assert_eq!(specialized.returns.unwrap().name, "Str");
}

#[test]
fn specializeEnumSubstitutesGenericVariantFieldNames() {
    let e = ast::Enum {
        name: "Option".to_string(),
        params: vec![],
        variants: vec![
            ast::EnumVariant { name: "Some".to_string(), fields: vec!["T".to_string()], values: vec![] },
            ast::EnumVariant { name: "None".to_string(), fields: vec![], values: vec![] },
        ],
    };
    let mut bindings = std::collections::BTreeMap::new();
    bindings.insert("T".to_string(), PlumType::TInt);
    let specialized = specializeEnum(&e, &Substitution(bindings), "Option$Int");
    assert_eq!(specialized.name, "Option$Int");
    assert_eq!(specialized.variants[0].fields, vec!["Int".to_string()]);
    assert!(specialized.variants[1].fields.is_empty());
}