wavepeek 1.0.1

Command-line tool for RTL waveform inspection with deterministic machine-friendly output.
Documentation
use crate::expr::Span;
use crate::expr::ast::{IntegralBase, IntegralLiteral, RealLiteral, StringLiteral};
use crate::expr::lexer::lex_logical_expr;

use super::*;

#[test]
fn derive_tokens_and_logical_token_kinds() {
    let token = Token {
        kind: TokenKind::KeywordIff,
        span: Span::new(1, 4),
        lexeme: "iff".to_string(),
    };
    assert_eq!(token.clone(), token);
    assert!(format!("{token:?}").contains("KeywordIff"));

    for kind in [
        TokenKind::Identifier,
        TokenKind::KeywordOr,
        TokenKind::KeywordPosedge,
        TokenKind::KeywordNegedge,
        TokenKind::KeywordEdge,
        TokenKind::Star,
        TokenKind::Comma,
        TokenKind::LeftParen,
        TokenKind::RightParen,
    ] {
        assert_eq!(kind.clone(), kind);
        assert!(format!("{kind:?}").len() > 1);
    }

    let kinds = vec![
        LogicalTokenKind::Identifier("top.sig".to_string()),
        LogicalTokenKind::IntegralLiteral(IntegralLiteral {
            width: Some(4),
            signed: false,
            base: IntegralBase::Binary,
            digits: "1010".to_string(),
            span: Span::new(0, 6),
        }),
        LogicalTokenKind::RealLiteral(RealLiteral {
            text: "1.5".to_string(),
            span: Span::new(0, 3),
        }),
        LogicalTokenKind::StringLiteral(StringLiteral {
            value: "hello".to_string(),
            span: Span::new(0, 7),
        }),
        LogicalTokenKind::LeftParen,
        LogicalTokenKind::RightParen,
        LogicalTokenKind::LeftBracket,
        LogicalTokenKind::RightBracket,
        LogicalTokenKind::LeftBrace,
        LogicalTokenKind::RightBrace,
        LogicalTokenKind::Comma,
        LogicalTokenKind::Colon,
        LogicalTokenKind::Question,
        LogicalTokenKind::Dot,
        LogicalTokenKind::Apostrophe,
        LogicalTokenKind::DoubleColon,
        LogicalTokenKind::Plus,
        LogicalTokenKind::Minus,
        LogicalTokenKind::Star,
        LogicalTokenKind::Power,
        LogicalTokenKind::Slash,
        LogicalTokenKind::Percent,
        LogicalTokenKind::Bang,
        LogicalTokenKind::Tilde,
        LogicalTokenKind::Amp,
        LogicalTokenKind::Pipe,
        LogicalTokenKind::Caret,
        LogicalTokenKind::TildeAmp,
        LogicalTokenKind::TildePipe,
        LogicalTokenKind::TildeCaret,
        LogicalTokenKind::CaretTilde,
        LogicalTokenKind::AndAnd,
        LogicalTokenKind::OrOr,
        LogicalTokenKind::Lt,
        LogicalTokenKind::Le,
        LogicalTokenKind::Gt,
        LogicalTokenKind::Ge,
        LogicalTokenKind::EqEq,
        LogicalTokenKind::NotEq,
        LogicalTokenKind::EqEqEq,
        LogicalTokenKind::NotEqEq,
        LogicalTokenKind::EqWildcard,
        LogicalTokenKind::NotEqWildcard,
        LogicalTokenKind::ShiftLeft,
        LogicalTokenKind::ShiftRight,
        LogicalTokenKind::ShiftArithLeft,
        LogicalTokenKind::ShiftArithRight,
        LogicalTokenKind::PlusColon,
        LogicalTokenKind::MinusColon,
        LogicalTokenKind::KeywordInside,
        LogicalTokenKind::Eof,
    ];
    for kind in kinds {
        let token = LogicalToken {
            kind: kind.clone(),
            span: Span::new(2, 3),
        };
        assert_eq!(token.clone(), token);
        assert!(format!("{kind:?}").len() > 1);
        assert!(format!("{token:?}").contains("span"));
    }
}

#[test]
fn lexer_exercises_multi_operator_and_cast_surface() {
    let tokens = lex_logical_expr(
        "lhs ==? rhs && mid !=? other && shift <<< 1 >>> 2 && bus[0 +: 2] == bus[1 -: 2] && signed'(data) == bit'(flag)",
        0,
    )
    .expect("lexer should accept the operator surface");
    let kinds = tokens
        .into_iter()
        .map(|token| token.kind)
        .collect::<Vec<_>>();

    assert!(kinds.contains(&LogicalTokenKind::EqWildcard));
    assert!(kinds.contains(&LogicalTokenKind::NotEqWildcard));
    assert!(kinds.contains(&LogicalTokenKind::ShiftArithLeft));
    assert!(kinds.contains(&LogicalTokenKind::ShiftArithRight));
    assert!(kinds.contains(&LogicalTokenKind::PlusColon));
    assert!(kinds.contains(&LogicalTokenKind::MinusColon));
    assert!(
        kinds
            .iter()
            .filter(|kind| matches!(kind, LogicalTokenKind::Apostrophe))
            .count()
            >= 2
    );
}

#[test]
fn lexer_exercises_based_literals_and_malformed_numeric_edges() {
    let tokens = lex_logical_expr("4'b1010 == bit'(flag) && 8'shff == signed'(data)", 0)
        .expect("lexer should keep based literals distinct from casts");
    assert!(
        tokens
            .iter()
            .any(|token| matches!(token.kind, LogicalTokenKind::IntegralLiteral(_)))
    );
    assert!(
        tokens
            .iter()
            .filter(|token| matches!(token.kind, LogicalTokenKind::Apostrophe))
            .count()
            >= 2
    );

    for source in ["8'sh", "12'", "4'b102"] {
        assert!(
            lex_logical_expr(source, 0).is_err(),
            "{source} should be rejected"
        );
    }
}