use crate::{
    VM, Linker, Assembler
};

#[test]
fn test_vm() {
    let mut vm = VM::new(0x1000);
    let mut linker = Linker::new();

    linker.add_procedure("@",
        crate::Assembler::new()
            .inst( crate::VM_Instruction::Fetch )
            .build());
    linker.add_procedure("!",
        crate::Assembler::new()
            .inst( crate::VM_Instruction::Store )
            .build());

    linker.add_procedure("int-add",
        crate::Assembler::new()
            .inst( crate::VM_Instruction::IntAdd )
            .build());

    linker.add_procedure("int-sub",
        crate::Assembler::new()
            .inst( crate::VM_Instruction::BitNeg )
            .lit(1)
            .inst( crate::VM_Instruction::IntAdd )
            .inst( crate::VM_Instruction::IntAdd )
            .build());

    // declare variable 'x' at address 0x86
    linker.add_procedure("x",
        crate::Assembler::new()
            .lit(0x86)
            .build());

    /*
     * compile & run the following program:
     *
     * x = 100 + 23;
     * if( x != 123 ) {
     *    emit('*');
     *    333
     * } else {
     *    emit('+');
     *    666
     * }
     */
    linker.add_procedure("main",
        crate::Assembler::new()
            // x = 123
            .lit(100).lit(23).call("int-add")
            .call("x").call("!")

            // if ( 123 - x ) { emit '*' } else { emit '+' }
            .lit(123)
            .call("x").call("@")
            .call("int-sub")
            .branch(
                crate::Assembler::new()
                    .lit(42).inst(crate::VM_Instruction::Emit)
                    .lit(111),
                crate::Assembler::new()
                    .lit(43).inst(crate::VM_Instruction::Emit)
                    .lit(222))
            .build());

    let main_addr = linker.get_link_addr(&"main".into()).expect("main not foudn");

    let bytecode = linker.link_total().expect("link error");
    vm.load( bytecode );
    vm.execute( main_addr );

    assert_eq!( vm.data_stack, vec![ 222 ] );
    println!("\nvm.datastack = {:?}", vm.data_stack);
}