use rasm_core::{
    isa::{Operand, Registers, Scale, directive::DirectiveOperand},
    lexer::Lexer,
    parser::{
        Parser,
        ast::{AST, Statement},
        err::ParserError,
    },
    src::SourceCode,
    str_pool::StrPool,
};

pub fn get_ast(code: &str) -> Result<AST, Vec<ParserError>> {
    let source = SourceCode::new(code.to_string());

    let mut str_pool = StrPool::from_source(&source);
    let mut lexer = Lexer::new(&mut str_pool, source);
    lexer.tokenize();

    assert!(lexer.errors.is_empty(), "{:?}", lexer.errors);

    let mut parser = Parser::new(&lexer.tokens, &mut str_pool);
    let ast = parser.parse();

    if !parser.errors.is_empty() {
        Err(parser.errors)
    } else {
        Ok(ast)
    }
}

#[test]
fn test_ast_empty_program() {
    let ast = get_ast("").unwrap();
    // Пустая программа должна возвращать пустой вектор statement-ов
    assert_eq!(ast.program.len(), 0);
}

#[test]
fn test_ast_label_sequence() {
    let ast = get_ast(
        "
    _start:
    loop_start:
    ",
    )
    .unwrap();

    // Ожидаем ровно 2 утверждения
    assert_eq!(ast.program.len(), 2);

    // Проверяем, что оба узла — это именно метки (Label)
    assert!(matches!(
        ast.program[0],
        Statement::Label { span: _, name: _ }
    ));
    assert!(matches!(
        ast.program[1],
        Statement::Label { span: _, name: _ }
    ));
}

#[test]
fn test_ast_mixed_statements() {
    let ast = get_ast(
        "
    .text
    _start:
        ret
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 3);

    // Проверяем строгий порядок типов в AST: Directive -> Label -> Instruction
    assert!(matches!(
        ast.program[0],
        Statement::Directive {
            span: _,
            directive: _
        }
    ));
    assert!(matches!(
        ast.program[1],
        Statement::Label { span: _, name: _ }
    ));
    assert!(matches!(
        ast.program[2],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    ));
}

#[test]
fn test_ast_instruction_heavy() {
    let ast = get_ast(
        "
        mov rax, rbx
        syscall
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 2);
    assert!(matches!(
        ast.program[0],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    ));
    assert!(matches!(
        ast.program[1],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    ));
}

#[test]
fn test_ast_detailed_instruction_operands() {
    // Проверяем типы операндов: регистр и число
    let ast = get_ast("mov rax, 42").unwrap();

    assert_eq!(ast.program.len(), 1);

    if let Statement::Instruction {
        span: _,
        instruction: instr,
    } = &ast.program[0]
    {
        // Проверяем, что операнда ровно два
        assert_eq!(instr.operands.len(), 2);

        // Первый операнд — регистр
        assert!(matches!(instr.operands[0], Operand::Register(_)));

        // Второй операнд — число (Immediate) со значением 42
        assert!(matches!(instr.operands[1], Operand::Immediate(42)));
    } else {
        panic!("Ожидалась инструкция");
    }
}

#[test]
fn test_ast_negative_immediate() {
    // Проверяем, что парсер корректно упаковывает отрицательные числа в Immediate
    let ast = get_ast("mov rbx, -100").unwrap();

    if let Statement::Instruction {
        span: _,
        instruction: instr,
    } = &ast.program[0]
    {
        assert!(matches!(instr.operands[1], Operand::Immediate(-100)));
    } else {
        panic!("Ожидалась инструкция");
    }
}

#[test]
fn test_ast_detailed_memory_sib() {
    // Проверяем сложную адресацию памяти: [base + index * scale + disp]
    let ast = get_ast("mov rcx, qword ptr [rax + rbx*4 + 15]").unwrap();

    assert_eq!(ast.program.len(), 1);

    if let Statement::Instruction {
        span: _,
        instruction: instr,
    } = &ast.program[0]
    {
        // Нас интересует второй операнд (память)
        if let Operand::Memory(mem) = &instr.operands[1] {
            // Проверяем, что базовый регистр на месте
            assert!(mem.base.is_some());

            // Проверяем индексный регистр
            assert!(mem.index.is_some());

            // Проверяем масштаб (scale = 4 -> Scale::Four)
            assert!(matches!(mem.scale, Some(Scale::Four)));

            // Проверяем смещение (displacement = 15)
            assert_eq!(mem.displacement, 15);
        } else {
            panic!("Второй операнд должен быть Memory адресацией");
        }
    }
}

#[test]
fn test_ast_directive_with_operands() {
    // Проверяем, как парсер разбирает директивы с числами
    let ast = get_ast(".quad 10, 20, 30").unwrap();

    assert_eq!(ast.program.len(), 1);

    if let Statement::Directive {
        span: _,
        directive: dir,
    } = &ast.program[0]
    {
        assert_eq!(dir.operands.len(), 3);

        // Все операнды должны быть числами
        assert!(matches!(dir.operands[0], DirectiveOperand::Number(10)));
        assert!(matches!(dir.operands[1], DirectiveOperand::Number(20)));
        assert!(matches!(dir.operands[2], DirectiveOperand::Number(30)));
    } else {
        panic!("Ожидалась директива");
    }
}

#[test]
fn test_ast_directive_string() {
    // Проверяем парсинг строковых директив
    let ast = get_ast(".ascii \"Hello\"").unwrap();

    if let Statement::Directive {
        span: _,
        directive: dir,
    } = &ast.program[0]
    {
        assert_eq!(dir.operands.len(), 1);
        // Должен создаться StringLiteral с неким StrId внутри
        assert!(matches!(
            dir.operands[0],
            DirectiveOperand::StringLiteral(_)
        ));
    } else {
        panic!("Ожидалась директива .ascii");
    }
}

#[test]
fn test_ast_complex_loop_with_nested_local_labels() {
    // Код с глобальными и локальными метками, условными переходами и циклами
    let code = "
    _start:
        mov rcx, 10
    outer_loop:
        push rcx
        mov rbx, 5
    inner_loop:
        dec rbx
        jnz inner_loop
        pop rcx
        dec rcx
        jnz outer_loop
        ret
    ";
    let ast = get_ast(code).unwrap();
    assert_eq!(ast.program.len(), 12);

    // Выродочные проверки структуры по индексам
    assert!(matches!(
        ast.program[0],
        Statement::Label { span: _, name: _ }
    )); // _start:
    assert!(matches!(
        ast.program[1],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    )); // mov
    assert!(matches!(
        ast.program[2],
        Statement::Label { span: _, name: _ }
    )); // .outer_loop:
    assert!(matches!(
        ast.program[5],
        Statement::Label { span: _, name: _ }
    )); // .inner_loop:
    assert!(matches!(
        ast.program[11],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    )); // ret
}

#[test]
fn test_ast_consecutive_labels_without_instructions() {
    // Сложная цепочка меток, идущих друг за другом (алиасы адресов)
    let ast = get_ast(
        "
    global_alias:
    _main:
    alias:
    ;; пустые строки между ними
    
    another_label:
        nop
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 5);
    assert!(matches!(
        ast.program[0],
        Statement::Label { span: _, name: _ }
    ));
    assert!(matches!(
        ast.program[1],
        Statement::Label { span: _, name: _ }
    ));
    assert!(matches!(
        ast.program[2],
        Statement::Label { span: _, name: _ }
    ));
    assert!(matches!(
        ast.program[3],
        Statement::Label { span: _, name: _ }
    ));
    assert!(matches!(
        ast.program[4],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    ));
}

#[test]
fn test_ast_memory_sib_all_scale_variants() {
    // Комплексный тест на проверку всех возможных масштабов (Scale) в SIB
    let ast = get_ast(
        "
        mov rax, [rbx + rsi*1]
        mov rbx, [rcx + rdx*2 + 8]
        mov rcx, [rdi + rbp*4 + 16]
        mov rdx, [r8 + r9*8 + 32]
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 4);

    let scales = [Scale::One, Scale::Two, Scale::Four, Scale::Eight];
    let disps = [0, 8, 16, 32];

    for (i, (expected_scale, expected_disp)) in scales.iter().zip(disps.iter()).enumerate() {
        if let Statement::Instruction {
            span: _,
            instruction: ins,
        } = &ast.program[i]
        {
            if let Operand::Memory(mem) = &ins.operands[1] {
                assert!(mem.base.is_some());
                assert!(mem.index.is_some());
                assert!(mem.scale.is_some());
                assert!(matches!(mem.scale.unwrap(), _ if mem.scale.unwrap() == *expected_scale));
                assert_eq!(mem.displacement, *expected_disp);
            } else {
                panic!("Ожидался операнд памяти");
            }
        }
    }
}

#[test]
fn test_ast_memory_complex_pointer_size_directives() {
    // Инструкции со спецификаторами размера данных qword ptr, dword ptr и т.д.
    let ast = get_ast(
        "
        mov qword ptr [rax], rbx
        mov dword ptr [rcx + 4], rdx
        mov word ptr [rsi], rdi
        mov byte ptr [rdi], rax
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 4);
    for stmt in &ast.program {
        if let Statement::Instruction {
            span: _,
            instruction: ins,
        } = stmt
        {
            assert_eq!(ins.operands.len(), 2);
            assert!(matches!(ins.operands[0], Operand::Memory(_)));
            assert!(matches!(ins.operands[1], Operand::Register(_)));
        }
    }
}

#[test]
fn test_ast_memory_negative_and_boundary_displacements() {
    // Проверка граничных и отрицательных смещений памяти
    let ast = get_ast(
        "
        mov rax, [rbx - 1]
        mov rcx, [rdx + 2147483647]
        mov rsi, [rdi - 2147483648]
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 3);

    let expected_disps: [i64; 3] = [-1, 2147483647, -2147483648];
    for (i, disp) in expected_disps.iter().enumerate() {
        if let Statement::Instruction {
            span: _,
            instruction: ins,
        } = &ast.program[i]
            && let Operand::Memory(mem) = &ins.operands[1]
        {
            assert_eq!(mem.displacement as i64, *disp);
        }
    }
}

#[test]
fn test_ast_data_section_with_massive_arrays() {
    // Имитация сложной инициализации глобальных массивов в секции данных
    let ast = get_ast(
        "
    .data
    .quad 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    .quad 0xFFFFFFFF, 0xAAAAAAAA, 1
    .quad 1000, 2000, 3000
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 4);
    assert!(matches!(
        ast.program[0],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .data

    if let Statement::Directive {
        span: _,
        directive: dir,
    } = &ast.program[1]
    {
        // .quad
        assert_eq!(dir.operands.len(), 10);
    }
    if let Statement::Directive {
        span: _,
        directive: dir,
    } = &ast.program[2]
    {
        // .quad
        assert_eq!(dir.operands.len(), 3);
        assert!(matches!(dir.operands[2], DirectiveOperand::Number(1)));
    }
}

#[test]
fn test_ast_string_directives_with_escapes() {
    // Проверка директив строк с разным внутренним содержимым
    let ast = get_ast(
        "
    .ascii \"Hello, world!\\n\"
    .asciz \"Embedded \\\"quotes\\\" inside\"
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 2);
    for stmt in &ast.program {
        if let Statement::Directive {
            span: _,
            directive: dir,
        } = stmt
        {
            assert_eq!(dir.operands.len(), 1);
            assert!(matches!(
                dir.operands[0],
                DirectiveOperand::StringLiteral(_)
            ));
        }
    }
}

#[test]
fn test_ast_alternating_text_and_data_sections() {
    // Переключение контекста секций компилятора туда-обратно
    let ast = get_ast(
        "
    .text
        mov rax, 1
    .data
        .quad 42
    .text
        syscall
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 6);
    assert!(matches!(
        ast.program[0],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .text
    assert!(matches!(
        ast.program[1],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    )); // mov
    assert!(matches!(
        ast.program[2],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .data
    assert!(matches!(
        ast.program[3],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .quad
    assert!(matches!(
        ast.program[4],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .text
    assert!(matches!(
        ast.program[5],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    )); // syscall
}

#[test]
fn test_ast_function_prologue_epilogue_pattern() {
    // Проверка классического паттерна выделения и освобождения стек-фрейма функции
    let ast = get_ast(
        "
    my_function:
        push rbp
        mov rbp, rsp
        sub rsp, 48
        
        ; тело функции
        xor rax, rax
        
        mov rsp, rbp
        pop rbp
        ret
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 8);
    assert!(matches!(
        ast.program[0],
        Statement::Label { span: _, name: _ }
    ));
    for i in 1..8 {
        assert!(matches!(
            ast.program[i],
            Statement::Instruction {
                span: _,
                instruction: _
            }
        ));
    }
}

#[test]
fn test_ast_full_syscall_wrapper_block() {
    // Заполнение регистров под системный вызов x86_64 Linux (write)
    let ast = get_ast(
        "
        mov rax, 1        ; sys_write
        mov rdi, 1        ; fd: stdout
        mov rsi, [rbp - 8]; buffer ptr
        mov rdx, 14       ; count
        syscall
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 5);
    for i in 0..4 {
        if let Statement::Instruction {
            span: _,
            instruction: ins,
        } = &ast.program[i]
        {
            assert_eq!(ins.operands.len(), 2);
        }
    }
    assert!(matches!(
        ast.program[4],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    )); // syscall (0 операндов)
}

#[test]
fn test_ast_stack_heavy_manipulation() {
    // Интенсивная последовательность работы со стеком push/pop
    let ast = get_ast(
        "
        push rax
        push rbx
        push qword ptr [rcx]
        pop rdx
        pop rsi
        pop rdi
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 6);
    for stmt in &ast.program {
        assert!(matches!(
            stmt,
            Statement::Instruction {
                span: _,
                instruction: _
            }
        ));
    }
}

#[test]
fn test_ast_heavy_arithmetic_and_logical_chain() {
    // Математический и логический конвейер обработки данных
    let ast = get_ast(
        "
        add rax, rbx
        sub rcx, rdx
        and rsi, rdi
        or r8, r9
        xor r10, r10
        shl r11, 4
        shr r12, 2
        not r13
        neg r14
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 9);
    // Проверяем, что бинарные имеют 2 операнда, а унарные (not, neg) — один
    for i in 0..7 {
        if let Statement::Instruction {
            span: _,
            instruction: ins,
        } = &ast.program[i]
        {
            assert_eq!(ins.operands.len(), 2);
        }
    }
    for i in 7..9 {
        if let Statement::Instruction {
            span: _,
            instruction: ins,
        } = &ast.program[i]
        {
            assert_eq!(ins.operands.len(), 1);
        }
    }
}

#[test]
fn test_ast_chaotic_whitespaces_and_comments_everywhere() {
    // Усложненное форматирование с кучей комментариев внутри и между инструкциями
    let code = "
    ;;; Начальный комментарий блока
    
    _start:      ; Комментарий метки
    
        mov    rax   ,    [rbx]    ; Много пробелов везде
        
        \t\tinc\t\trax\t\t; Табуляции
        
        ;; Промежуточный комментарий на всю строку
        ret
        
    ;;; Конечный комментарий
    ";
    let ast = get_ast(code).unwrap();
    assert_eq!(ast.program.len(), 4);
    assert!(matches!(
        ast.program[0],
        Statement::Label { span: _, name: _ }
    ));
    assert!(matches!(
        ast.program[1],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    ));
    assert!(matches!(
        ast.program[2],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    ));
    assert!(matches!(
        ast.program[3],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    ));
}

#[test]
fn test_ast_string_and_numeric_directives_interleaved() {
    // Перемешанные строки данных и числовые константы в одной секции
    let ast = get_ast(
        "
    .data
    label_str:  .ascii \"Data block\\0\"
    label_val:  .quad 0x1122334455667788
                .quad 0xFF
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 6);
    assert!(matches!(
        ast.program[0],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .data
    assert!(matches!(
        ast.program[1],
        Statement::Label { span: _, name: _ }
    )); // label_str:
    assert!(matches!(
        ast.program[2],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .ascii
    assert!(matches!(
        ast.program[3],
        Statement::Label { span: _, name: _ }
    )); // label_val:
    assert!(matches!(
        ast.program[4],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .quad
    assert!(matches!(
        ast.program[5],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .quad
}

#[test]
fn test_ast_leaf_node_string_length_algorithm() {
    // Реализация классического алгоритма подсчета длины строки (strlen)
    let code = "
    strlen:
        xor rax, rax
    loop:
        cmp byte ptr [rdi + rax], 0
        je done
        inc rax
        jmp loop
    done:
        ret
    ";
    let ast = get_ast(code).unwrap();
    assert_eq!(ast.program.len(), 9);
    assert!(matches!(
        ast.program[0],
        Statement::Label { span: _, name: _ }
    )); // strlen:
    assert!(matches!(
        ast.program[2],
        Statement::Label { span: _, name: _ }
    )); // loop:
    assert!(matches!(
        ast.program[7],
        Statement::Label { span: _, name: _ }
    )); // done:
}

#[test]
fn test_ast_complex_jump_target_types() {
    // Различные варианты вызовов и прыжков: по меткам, регистрам и адресам памяти
    let ast = get_ast(
        "
        call _printf
        jmp rax
        call qword ptr [rbx + 8]
        jmp local_handler
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 4);
    for stmt in &ast.program {
        if let Statement::Instruction {
            span: _,
            instruction: ins,
        } = stmt
        {
            assert_eq!(ins.operands.len(), 1);
        }
    }
}

#[test]
fn test_ast_memory_no_base_index_scale_only() {
    // Хитрая адресация без базового регистра, только индекс и масштаб: [index * scale]
    let ast = get_ast("mov rax, [rcx*4]").unwrap();
    assert_eq!(ast.program.len(), 1);
    if let Statement::Instruction {
        span: _,
        instruction: ins,
    } = &ast.program[0]
        && let Operand::Memory(mem) = &ins.operands[1]
    {
        assert!(mem.base.is_none());
        assert!(mem.index.is_some());
        assert!(matches!(mem.scale, Some(Scale::Four)));
        assert_eq!(mem.displacement, 0);
    }
}

#[test]
fn test_ast_memory_no_index_base_and_displacement() {
    // Адресация только с базой и смещением, без индекса: [base + disp]
    let ast = get_ast("mov [rbp + 32], rcx").unwrap();
    assert_eq!(ast.program.len(), 1);
    if let Statement::Instruction {
        span: _,
        instruction: ins,
    } = &ast.program[0]
        && let Operand::Memory(mem) = &ins.operands[0]
    {
        assert!(mem.base.is_some());
        assert!(mem.index.is_none());
        assert!(mem.scale.is_none());
        assert_eq!(mem.displacement, 32);
    }
}

#[test]
fn test_ast_memory_rip_relative_label() {
    let ast = get_ast(
        "
    _start:
        lea rsi, [rip + msg]
    msg:
        .ascii \"Hello\"
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 4);

    if let Statement::Instruction {
        span: _,
        instruction: ins,
    } = &ast.program[1]
    {
        if let Operand::Memory(mem) = &ins.operands[1] {
            assert!(matches!(mem.base, Some(Registers::Rip)));
            assert!(mem.index.is_none());
            assert!(mem.scale.is_none());
            assert_eq!(mem.displacement, 0);
            assert!(mem.label.is_some());
        } else {
            panic!("expected RIP-relative memory operand");
        }
    } else {
        panic!("expected instruction");
    }
}

#[test]
fn test_ast_directive_multiple_empty_and_filled_strings() {
    // Секвенция пустых строк и обычных внутри директив компилятора
    let ast = get_ast(
        "
        .ascii \"\"
        .ascii \"a\"
        .ascii \"abc\"
    ",
    )
    .unwrap();
    assert_eq!(ast.program.len(), 3);
    for stmt in &ast.program {
        assert!(matches!(
            stmt,
            Statement::Directive {
                span: _,
                directive: _
            }
        ));
    }
}

#[test]
fn test_ast_all_statement_types_interleaving_massively() {
    // Сверхплотное хаотичное перемешивание всех типов Statement-ов
    let ast = get_ast(
        "
    .text
    label_one:
        nop
    .data
    label_two:
        .quad 1
    .text
        ret
    ",
    )
    .unwrap();

    assert_eq!(ast.program.len(), 8);
    assert!(matches!(
        ast.program[0],
        Statement::Directive {
            span: _,
            directive: _
        }
    ));
    assert!(matches!(
        ast.program[1],
        Statement::Label { span: _, name: _ }
    ));
    assert!(matches!(
        ast.program[2],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    ));
    assert!(matches!(
        ast.program[3],
        Statement::Directive {
            span: _,
            directive: _
        }
    ));
    assert!(matches!(
        ast.program[4],
        Statement::Label { span: _, name: _ }
    ));
    assert!(matches!(
        ast.program[5],
        Statement::Directive {
            span: _,
            directive: _
        }
    ));
    assert!(matches!(
        ast.program[6],
        Statement::Directive {
            span: _,
            directive: _
        }
    ));
    assert!(matches!(
        ast.program[7],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    ));
}

#[test]
fn test_ast_monolithic_full_program_validation() {
    // Финальный гигантский тест, имитирующий законченную микропрограмму
    let code = "
    .data
        val: .quad 0x100
    
    .text
    .global _start
    
    _start:
        mov rax, 0
        mov rbx, qword ptr [rip + 0]
    process:
        cmp rbx, 0
        je exit
        add rax, rbx
        dec rbx
        jmp process
    exit:
        ret
    ";

    let ast = get_ast(code).unwrap();

    // Вся программа должна содержать ровно 14 элементов
    assert_eq!(ast.program.len(), 16);

    // Точечно проверяем критически важные узлы
    assert!(matches!(
        ast.program[0],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .data
    assert!(matches!(
        ast.program[1],
        Statement::Label { span: _, name: _ }
    )); // val:
    assert!(matches!(
        ast.program[2],
        Statement::Directive {
            span: _,
            directive: _
        }
    )); // .quad 0x100
    assert!(matches!(
        ast.program[5],
        Statement::Label { span: _, name: _ }
    )); // _start:
    assert!(matches!(
        ast.program[8],
        Statement::Label { span: _, name: _ }
    )); // process:
    assert!(matches!(
        ast.program[14],
        Statement::Label { span: _, name: _ }
    )); // exit:
    assert!(matches!(
        ast.program[15],
        Statement::Instruction {
            span: _,
            instruction: _
        }
    )); // ret
}
