veryl-parser 0.20.0

A modern hardware description language
Documentation
use crate::Parser;
use miette::Diagnostic;

#[track_caller]
fn success(code: &str) {
    let code = format!("module A {{ {} }}", code);
    let parser = Parser::parse(&code, &"");
    dbg!(code);
    assert!(parser.is_ok());
}

#[track_caller]
fn failure(code: &str) {
    let code = format!("module A {{ {} }}", code);
    let parser = Parser::parse(&code, &"");
    dbg!(code);
    assert!(parser.is_err());
}

#[track_caller]
fn help_message(code: &str) -> String {
    let parser = Parser::parse(&code, &"");
    let err = parser.err().unwrap();
    err.help().unwrap().to_string()
}

#[test]
fn comment() {
    success("// aaaaa \n");
    success("/* aaaaa */");
    success("/* aa \n a \n aa */");
}

#[test]
fn number() {
    // integer
    success("let a: u32 = 0123456789;");
    success("let a: u32 = 0_1_23456789;");
    success("let a: u32 = _0_1_23456789;"); // identifier
    failure("let a: u32 = 0_1__23456789;");

    // binary
    success("let a: u32 = 32'b01xzXZ;");
    success("let a: u32 = 32'b01_xz_XZ;");
    failure("let a: u32 = 32'b01__xz_XZ;");

    // octal
    success("let a: u32 = 32'o01234567xzXZ;");
    success("let a: u32 = 32'o01234567_xz_XZ;");
    failure("let a: u32 = 32'o01234567__xz_XZ;");

    // decimal
    success("let a: u32 = 32'd0123456789xzXZ;");
    success("let a: u32 = 32'd0123456789_xz_XZ;");
    failure("let a: u32 = 32'd0123456789__xz_XZ;");

    // hex
    success("let a: u32 = 32'h0123456789abcdefABCDEFxzXZ;");
    success("let a: u32 = 32'h0123456789abcdefABCDEF_xz_XZ;");
    failure("let a: u32 = 32'h0123456789abcdefABCDEF__xz_XZ;");

    // all0, all1
    success("let a: u32 = '0;");
    success("let a: u32 = '1;");
    failure("let a: u32 = '2;");

    // floating point
    success("let a: u32 = 0.1;");
    success("let a: u32 = 0_1_23.4_5_67;");
    failure("let a: u32 = 0_1__23.4_5_67;");

    // exponent
    success("let a: u32 = 0.1e10;");
    success("let a: u32 = 0.1e+10;");
    success("let a: u32 = 0.1e-10;");
    success("let a: u32 = 0.1E+10;");
    success("let a: u32 = 0.1E-10;");
    failure("let a: u32 = 0.1e++10;");
    failure("let a: u32 = 0.1e10.0;");
}

#[test]
fn identifier() {
    success("var a: u32;");
    success("var _abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_: u32;");
    failure("var 0a: u32;");
}

#[test]
fn expression() {
    success("let a: u32 = 1 && 1 || 1 & 1 ^ 1 ~^ 1 ^~ 1 | 1;");
    success("let a: u32 = 1 <: 1 <= 1 >: 1 >= 1 == 1 != 1 ==? 1 !=? 1;");
    success("let a: u32 = 1 << 1 >> 1 <<< 1 >>> 1;");
    success("let a: u32 = 1 ** 1 * 1 / 1 % 1 + 1 - 1;");
    success("let a: u32 = +-!~&|^~&~|~^^~1;");
    success("let a: u32 = ( (1 && 1) || 1) & (1 ^ 1 ~^ 1) ^~ 1 | 1;");
    failure("let a: u32 = ( (1 && 1) || 1 & (1 ^ 1 ~^ 1) ^~ 1 | 1;");
}

#[test]
fn function_call() {
    success("let a: u32 = a();");
    success("let a: u32 = $a();");
    success("let a: u32 = a.a.a();");
    success("let a: u32 = a::a::a();");
    success("let a: u32 = a::a::a.a.a();");
    success("let a: u32 = a(1, 1, 1);");
    success("let a: u32 = a(1, 1, 1,);");
    failure("let a: u32 = a(1 1, 1,);");
}

#[test]
fn range() {
    success("let a: u32 = a[1];");
    success("let a: u32 = a[1:0];");
    success("let a: u32 = a[1+:1];");
    success("let a: u32 = a[1-:1];");
    success("let a: u32 = a[1 step 1];");
}

#[test]
fn r#type() {
    success("var a: logic;");
    success("var a: bit;");
    success("var a: u32;");
    success("var a: u64;");
    success("var a: i32;");
    success("var a: i64;");
    success("var a: f32;");
    success("var a: f64;");
    success("var a: a::a;");

    success("var a: logic<10, 10>;");
    success("var a: bit<10, 10>;");
    success("var a: u32[10, 10];");
    success("var a: u64[10, 10];");
    success("var a: i32[10, 10];");
    success("var a: i64[10, 10];");
    success("var a: f32[10, 10];");
    success("var a: f64[10, 10];");
    success("var a: a::a<10, 10>;");
}

#[test]
fn assignment_statement() {
    success("always_comb { a = 1; }");
    success("always_comb { a.a.a = 1; }");
    success("always_comb { a += 1; }");
    success("always_comb { a -= 1; }");
    success("always_comb { a *= 1; }");
    success("always_comb { a /= 1; }");
    success("always_comb { a %= 1; }");
    success("always_comb { a &= 1; }");
    success("always_comb { a |= 1; }");
    success("always_comb { a ^= 1; }");
    success("always_comb { a <<= 1; }");
    success("always_comb { a >>= 1; }");
    success("always_comb { a <<<= 1; }");
    success("always_comb { a >>>= 1; }");
}

#[test]
fn embed() {
    let code = r#"
    let a: logic = {{{1'b0}}};
    embed (inline) sv {{{
        initial begin
            $display("a = %0d", \{ a \});
        end
    }}}
    "#;
    success(code);
}

#[test]
fn generic_arg() {
    let code = r#"
    inst u: ModuleA::<Pkg::C[0]>;
    "#;
    failure(code);
}

#[test]
fn parse_error_help() {
    let code = r#"
    module ModuleA {
        let a: logic = 1 < 0;
    }
    "#;

    assert_eq!(
        &help_message(code),
        "If you mean \"less than operator\", please use '<:'"
    );

    let code = r#"
    module ModuleA {
        let a: logic = 1 > 0;
    }
    "#;

    assert_eq!(
        &help_message(code),
        "If you mean \"greater than operator\", please use '>:'"
    );

    let code = r#"
    module ModuleA {
        for i in 0..10 {
        }
    }
    "#;

    assert_eq!(
        &help_message(code),
        "The first arm of generate-if declaration needs label (e.g. 'if x :label {')"
    );

    let code = r#"
    module ModuleA {
        always_comb {
            case a {
                1: {e, f} = g;
            }
        }
    }
    "#;

    assert_eq!(
        &help_message(code),
        "single case statement with bit concatenation at the left-hand side is not allowed,\nplease surround it by '{}' (e.g. 'x: { {a, b} = 1; }')"
    );

    let code = r#"
    module ModuleA {
        if x {
        }
    }
    "#;

    assert_eq!(
        &help_message(code),
        "The first arm of generate-if declaration needs label (e.g. 'if x :label {')"
    );

    let code = r#"
    module ModuleA {
        var if: logic;
    }
    "#;

    assert_eq!(
        &help_message(code),
        "'if' is a reserved keyword and cannot be used as an identifier"
    );
}