use super::*;

fn lex(source: &str) -> Vec<Lexeme> {
    let (tokens, _comments, diags) = Lexer::new(source, 0).tokenize();
    assert!(diags.is_empty(), "unexpected errors: {:?}", diags);
    tokens.into_iter().map(|t| t.node).collect()
}

#[test]
fn test_keywords() {
    let tokens = lex("program fn let mut pub if else for in bounded return");
    assert_eq!(
        tokens,
        vec![
            Lexeme::Program,
            Lexeme::Fn,
            Lexeme::Let,
            Lexeme::Mut,
            Lexeme::Pub,
            Lexeme::If,
            Lexeme::Else,
            Lexeme::For,
            Lexeme::In,
            Lexeme::Bounded,
            Lexeme::Return,
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_types() {
    let tokens = lex("Field XField Bool U32 Digest");
    assert_eq!(
        tokens,
        vec![
            Lexeme::FieldTy,
            Lexeme::XFieldTy,
            Lexeme::BoolTy,
            Lexeme::U32Ty,
            Lexeme::DigestTy,
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_symbols() {
    let tokens = lex("( ) { } [ ] , : ; . .. -> = == + * *. < & ^ /% #");
    assert_eq!(
        tokens,
        vec![
            Lexeme::LParen,
            Lexeme::RParen,
            Lexeme::LBrace,
            Lexeme::RBrace,
            Lexeme::LBracket,
            Lexeme::RBracket,
            Lexeme::Comma,
            Lexeme::Colon,
            Lexeme::Semicolon,
            Lexeme::Dot,
            Lexeme::DotDot,
            Lexeme::Arrow,
            Lexeme::Eq,
            Lexeme::EqEq,
            Lexeme::Plus,
            Lexeme::Star,
            Lexeme::StarDot,
            Lexeme::Lt,
            Lexeme::Amp,
            Lexeme::Caret,
            Lexeme::SlashPercent,
            Lexeme::Hash,
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_integers() {
    let tokens = lex("0 1 42 18446744073709551615");
    assert_eq!(
        tokens,
        vec![
            Lexeme::Integer(0),
            Lexeme::Integer(1),
            Lexeme::Integer(42),
            Lexeme::Integer(u64::MAX),
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_identifiers() {
    let tokens = lex("foo bar_baz x1 _underscore");
    assert_eq!(
        tokens,
        vec![
            Lexeme::Ident("foo".into()),
            Lexeme::Ident("bar_baz".into()),
            Lexeme::Ident("x1".into()),
            Lexeme::Ident("_underscore".into()),
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_comments() {
    let tokens = lex("foo // this is a comment\nbar");
    assert_eq!(
        tokens,
        vec![
            Lexeme::Ident("foo".into()),
            Lexeme::Ident("bar".into()),
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_simple_program() {
    let tokens = lex("program test\n\nfn main() {\n    let a: Field = pub_read()\n}");
    assert_eq!(tokens[0], Lexeme::Program);
    assert_eq!(tokens[1], Lexeme::Ident("test".into()));
    assert_eq!(tokens[2], Lexeme::Fn);
    assert_eq!(tokens[3], Lexeme::Ident("main".into()));
}

#[test]
fn test_event_keywords() {
    let tokens = lex("event reveal seal");
    assert_eq!(
        tokens,
        vec![Lexeme::Event, Lexeme::Reveal, Lexeme::Seal, Lexeme::Eof,]
    );
}

#[test]
fn test_asm_block_basic() {
    let tokens = lex("asm { push 1\nadd }");
    assert_eq!(
        tokens,
        vec![
            Lexeme::AsmBlock {
                body: "push 1\nadd".to_string(),
                effect: 0,
                target: None,
            },
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_asm_block_positive_effect() {
    let tokens = lex("asm(+1) { push 42 }");
    assert_eq!(
        tokens,
        vec![
            Lexeme::AsmBlock {
                body: "push 42".to_string(),
                effect: 1,
                target: None,
            },
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_asm_block_negative_effect() {
    let tokens = lex("asm(-2) { pop 1\npop 1 }");
    assert_eq!(
        tokens,
        vec![
            Lexeme::AsmBlock {
                body: "pop 1\npop 1".to_string(),
                effect: -2,
                target: None,
            },
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_asm_block_with_negative_literal() {
    let tokens = lex("asm { push -1\nadd }");
    assert_eq!(
        tokens,
        vec![
            Lexeme::AsmBlock {
                body: "push -1\nadd".to_string(),
                effect: 0,
                target: None,
            },
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_asm_block_target_tag() {
    let tokens = lex("asm(triton) { push 1 }");
    assert_eq!(
        tokens,
        vec![
            Lexeme::AsmBlock {
                body: "push 1".to_string(),
                effect: 0,
                target: Some("triton".to_string()),
            },
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_asm_block_target_and_effect() {
    let tokens = lex("asm(triton, +2) { push 1\npush 2 }");
    assert_eq!(
        tokens,
        vec![
            Lexeme::AsmBlock {
                body: "push 1\npush 2".to_string(),
                effect: 2,
                target: Some("triton".to_string()),
            },
            Lexeme::Eof,
        ]
    );
}

#[test]
fn test_asm_block_in_function() {
    // fn main() { asm { ... } }
    // Tokens: Fn, Ident("main"), LParen, RParen, LBrace, AsmBlock, RBrace, Eof
    let tokens = lex("fn main() {\n    asm { dup 0\nadd }\n}");
    assert_eq!(tokens[0], Lexeme::Fn);
    assert!(matches!(tokens[5], Lexeme::AsmBlock { .. }));
    assert_eq!(tokens[6], Lexeme::RBrace);
}

#[test]
fn test_match_keyword() {
    let tokens = lex("match x { 0 => { } _ => { } }");
    assert_eq!(tokens[0], Lexeme::Match);
    assert_eq!(tokens[1], Lexeme::Ident("x".into()));
    assert_eq!(tokens[2], Lexeme::LBrace);
    assert_eq!(tokens[3], Lexeme::Integer(0));
    assert_eq!(tokens[4], Lexeme::FatArrow);
    assert_eq!(tokens[5], Lexeme::LBrace);
    assert_eq!(tokens[6], Lexeme::RBrace);
    assert_eq!(tokens[7], Lexeme::Underscore);
    assert_eq!(tokens[8], Lexeme::FatArrow);
}

#[test]
fn test_fat_arrow_vs_eq() {
    let tokens = lex("= => ==");
    assert_eq!(
        tokens,
        vec![Lexeme::Eq, Lexeme::FatArrow, Lexeme::EqEq, Lexeme::Eof]
    );
}

// --- Error path tests ---

fn lex_with_errors(source: &str) -> (Vec<Lexeme>, Vec<Diagnostic>) {
    let (tokens, _comments, diags) = Lexer::new(source, 0).tokenize();
    let lexemes = tokens.into_iter().map(|t| t.node).collect();
    (lexemes, diags)
}

#[test]
fn test_error_unexpected_character() {
    let (_tokens, diags) = lex_with_errors("@");
    assert!(!diags.is_empty(), "should produce an error for '@'");
    assert!(
        diags[0].message.contains("unexpected character '@'"),
        "error should name the character, got: {}",
        diags[0].message
    );
    assert!(
        diags[0].help.is_some(),
        "unexpected character error should have help text"
    );
}

#[test]
fn test_error_subtraction_operator() {
    let (_tokens, diags) = lex_with_errors("a - b");
    assert!(!diags.is_empty(), "should produce an error for '-'");
    assert!(
        diags[0].message.contains("no subtraction operator"),
        "should explain there is no subtraction, got: {}",
        diags[0].message
    );
    assert!(
        diags[0].help.as_deref().unwrap().contains("sub(a, b)"),
        "help should suggest sub() function"
    );
}

#[test]
fn test_error_division_operator() {
    let (_tokens, diags) = lex_with_errors("a / b");
    assert!(!diags.is_empty(), "should produce an error for '/'");
    assert!(
        diags[0].message.contains("no division operator"),
        "should explain there is no division, got: {}",
        diags[0].message
    );
    assert!(
        diags[0].help.as_deref().unwrap().contains("/%"),
        "help should suggest /% operator"
    );
}

#[test]
fn test_error_integer_too_large() {
    let (_tokens, diags) = lex_with_errors("99999999999999999999999");
    assert!(
        !diags.is_empty(),
        "should produce an error for huge integer"
    );
    assert!(
        diags[0].message.contains("too large"),
        "should say the integer is too large, got: {}",
        diags[0].message
    );
    assert!(
        diags[0].help.is_some(),
        "integer overflow error should have help text"
    );
}

#[test]
fn test_error_unterminated_asm_block() {
    let (_tokens, diags) = lex_with_errors("asm { push 1");
    assert!(
        !diags.is_empty(),
        "should produce an error for unterminated asm"
    );
    assert!(
        diags[0].message.contains("unterminated asm block"),
        "should report unterminated asm, got: {}",
        diags[0].message
    );
    assert!(
        diags[0].help.is_some(),
        "unterminated asm error should have help text"
    );
}

Dimensions

trident/src/deploy/tests.rs
cw-cyber/contracts/hub-skills/src/tests.rs
trident/src/lsp/semantic/tests.rs
trident/src/syntax/format/tests.rs
cw-cyber/contracts/hub-libs/src/tests.rs
cw-cyber/contracts/hub-channels/src/tests.rs
trident/src/package/registry/tests.rs
trident/src/cost/stack_verifier/tests.rs
cw-cyber/contracts/hub-networks/src/tests.rs
trident/src/verify/sym/tests.rs
cw-cyber/contracts/cw-cyber-subgraph/src/tests.rs
cw-cyber/contracts/cw-cyber-gift/src/tests.rs
trident/src/verify/report/tests.rs
trident/src/package/store/tests.rs
cw-cyber/contracts/hub-tokens/src/tests.rs
trident/src/config/scaffold/tests.rs
trident/src/verify/solve/tests.rs
trident/src/verify/smt/tests.rs
cw-cyber/contracts/graph-filter/src/tests.rs
trident/src/package/manifest/tests.rs
trident/src/verify/synthesize/tests.rs
cw-cyber/contracts/cw-cyber-passport/src/tests.rs
trident/src/verify/equiv/tests.rs
trident/src/lsp/util/tests.rs
trident/src/config/resolve/tests.rs
trident/src/package/hash/tests.rs
trident/src/ir/lir/tests.rs
cw-cyber/contracts/hub-protocols/src/tests.rs
trident/src/syntax/grammar/tests.rs
trident/src/ir/tir/optimize/tests.rs
trident/src/neural/data/tir_graph/tests.rs
trident/src/ir/tir/lower/tests.rs
trident/src/ir/tir/stack/tests.rs

Local Graph