use crate::state::{ Block, Chunk, Instruction as Inst, State, Value, }; #[test] fn bools () { /* local function bool_to_x (b) if b then return 99 else return 98 end end local x = bool_to_x (not not arg [1]) print (x) return x */ let chunk = Chunk { blocks: vec! [ Block { instructions: vec! [ Inst::VarArgPrep (0), Inst::Closure (0, 0), Inst::Move (1, 0), Inst::LoadFalse (2), Inst::Call (1, 2, 1), Inst::Move (1, 0), Inst::LoadTrue (2), Inst::Call (1, 2, 1), Inst::Move (1, 0), Inst::GetTabUp (2, 0, 0), Inst::GetI (2, 2, 1), Inst::Not (2, 2), Inst::Not (2, 2), Inst::Call (1, 2, 2), Inst::GetTabUp (2, 0, 1), Inst::Move (3, 1), Inst::Call (2, 2, 1), Inst::Return (1, 2, 1, false), Inst::Return (2, 1, 1, false), ], constants: vec! [ "arg".into (), "print".into (), ], upvalue_count: 1, }, Block { instructions: vec! [ Inst::Test (0, 0), Inst::Jmp (3), Inst::LoadI (1, 99), Inst::Return1 (1), Inst::Jmp (2), Inst::LoadI (1, 98), Inst::Return1 (1), Inst::Return0, ], constants: vec! [], upvalue_count: 0, }, ], }; for (arg, expected) in [ (vec! ["_exe_name"], vec! [98.into ()]), (vec! ["_exe_name", "asdf"], vec! [99.into ()]), ] { let mut vm = State::default (); let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); let actual = vm.execute_chunk (&chunk, &upvalues); assert_eq! (actual, expected); } } #[test] fn floats () { /* local a = 0.5 local b = 3 local x = a + b print (x) return x */ let block = Block { instructions: vec! [ Inst::VarArgPrep (0), Inst::LoadK (0, 0), Inst::LoadI (1, 3), Inst::Add (2, 0, 1), Inst::MmBin (0, 1, 6), Inst::GetTabUp (3, 0, 1), Inst::Move (4, 2), Inst::Call (3, 2, 1), Inst::Return (2, 2, 1, false), Inst::Return (3, 1, 1, false), ], constants: vec! [ 0.5.into (), "print".into (), ], upvalue_count: 1, }; let chunk = Chunk { blocks: vec! [block], }; for (arg, expected) in [ (vec! ["_exe_name"], vec! [3.5.into ()]), (vec! ["_exe_name", " "], vec! [3.5.into ()]), ] { let mut vm = State::default (); let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); let actual = vm.execute_chunk (&chunk, &upvalues); assert_eq! (actual, expected); } } #[test] fn fma () { /* */ let chunk = Chunk { blocks: vec! [ Block { instructions: vec! [ Inst::VarArgPrep (0), Inst::Closure (0, 0), Inst::Closure (1, 1), Inst::Closure (2, 2), Inst::Move (3, 2), Inst::LoadI (4, 10), Inst::LoadI (5, 11), Inst::LoadI (6, 12), Inst::Call (3, 4, 2), Inst::GetTabUp (4, 0, 0), Inst::Move (5, 3), Inst::Call (4, 2, 1), Inst::Return (3, 2, 1, false), // k? Inst::Return (3, 2, 1, false), // k? ], constants: vec! [ "print".into (), ], upvalue_count: 1, }, Block { instructions: vec! [ Inst::Add (2, 0, 1), Inst::MmBin (0, 1, 6), Inst::Return1 (2), Inst::Return0, ], constants: vec! [], upvalue_count: 0, }, Block { instructions: vec! [ Inst::Mul (2, 0, 1), Inst::MmBin (0, 1, 8), Inst::Return1 (2), Inst::Return0, ], constants: vec! [], upvalue_count: 0, }, Block { instructions: vec! [ Inst::GetUpVal (3, 0), // add Inst::GetUpVal (4, 1), // mul Inst::Move (5, 0), Inst::Move (6, 1), Inst::Call (4, 3, 2), Inst::Move (5, 2), Inst::TailCall (3, 3, 0), Inst::Return (3, 0, 0, false), Inst::Return0, ], constants: vec! [], upvalue_count: 2, }, ], }; for (arg, expected) in [ (vec! ["_exe_name"], vec! [122.into ()]), (vec! ["_exe_name"], vec! [122.into ()]), ] { let mut vm = State::default (); let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); let actual = vm.execute_chunk (&chunk, &upvalues); assert_eq! (actual, expected); } } #[test] fn is_93 () { /* if arg [1] == "93" then print "it's 93" return 0 else print "it's not 93" return 1 end */ let block = Block { instructions: vec! [ Inst::VarArgPrep (0), Inst::GetTabUp (1, 0, 0), Inst::GetI (1, 1, 1), Inst::EqK (1, 1, 0), Inst::Jmp (6), Inst::GetTabUp (1, 0, 2), Inst::LoadK (2, 3), Inst::Call (1, 2, 1), Inst::LoadI (1, 0), Inst::Return (1, 2, 1, false), Inst::Jmp (5), Inst::GetTabUp (1, 0, 2), Inst::LoadK (2, 4), Inst::Call (1, 2, 1), Inst::LoadI (1, 1), Inst::Return (1, 2, 1, false), Inst::Return (1, 1, 1, false), ], constants: vec! [ "arg", "93", "print", "it's 93", "it's not 93", ].into_iter ().map (Value::from).collect (), upvalue_count: 1, }; let chunk = Chunk { blocks: vec! [block], }; for (arg, expected) in [ (vec! ["_exe_name"], vec! [1.into ()]), (vec! ["_exe_name", "93"], vec! [0.into ()]), (vec! ["_exe_name", "94"], vec! [1.into ()]), ] { let mut vm = State::default (); let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); let actual = vm.execute_chunk (&chunk, &upvalues); assert_eq! (actual, expected); } } #[test] fn loader () { let bytecode = include_bytes! ("../test_vectors/closure.luac"); let mut rdr = std::io::Cursor::new (bytecode); let file = crate::loader::parse_file (&mut rdr).unwrap (); assert_eq! (file.file_name, "@test_vectors/closure.lua"); assert_eq! (file.blocks.len (), 3); }