enum Instruction { VarArgPrep (i32), GetTabUp (u8, u8, u8), GetI (u8, u8, u8), EqK (u8, u8, u8), Jmp (i32), LoadK (u8, i32), Call (u8, u8, u8), Closure (u8, i32), Return (u8, u8, u8), } #[derive (Clone, Debug, PartialEq)] enum Value { Nil, False, True, Float (f64), String (String), // These are all bogus, I haven't figured out how to implement // tables and function pointers yet BogusArg, BogusPrint, } impl Default for Value { fn default () -> Self { Self::Nil } } impl From for Value { fn from (x: String) -> Self { Self::String (x) } } impl From <&str> for Value { fn from (x: &str) -> Self { Self::from (String::from (x)) } } struct Chunk { instructions: Vec , constants: Vec , } fn main() { let arg: Vec <_> = std::env::args ().collect (); let chunk = Chunk { instructions: vec! [ Instruction::VarArgPrep (0), Instruction::GetTabUp (0, 0, 0), Instruction::GetI (0, 0, 1), Instruction::EqK (0, 1, 0), Instruction::Jmp (4), Instruction::GetTabUp (0, 0, 2), Instruction::LoadK (1, 3), Instruction::Call (0, 2, 1), Instruction::Jmp (3), Instruction::GetTabUp (0, 0, 2), Instruction::LoadK (1, 4), Instruction::Call (0, 2, 1), Instruction::Return (1, 1, 1), ], constants: vec! [ "arg", "93", "print", "it's 93", "it's not 93", ].into_iter ().map (|s| Value::from (s)).collect (), }; let mut registers = vec! [Value::default (); 256]; let mut program_counter = 0i32; let max_iters = 2000; for _ in 0..max_iters { let instruction = chunk.instructions.get (usize::try_from (program_counter).unwrap ()).unwrap (); let r = &mut registers; let k = &chunk.constants; match instruction { Instruction::Call (a, b, c) => { // Take arguments from registers [a + 1, a + b) // Call the function in register [a] // Return values in registers [a, a + c - 1) // // That is, call a with b - 1 arguments and expect c returns // // e.g. CALL 0 2 1 mean "Call 0 with 1 argument, return 1 value", like for printing a constant // TODO: Only implement printing constants for now let a = usize::try_from (*a).unwrap (); assert_eq! (*b, 2); assert_eq! (*c, 1); println! ("{:?}", r [a + 1]); }, Instruction::EqK (a, b, c_k) => { let a = usize::try_from (*a).unwrap (); let b = usize::try_from (*b).unwrap (); let equal = r [a] == k [b]; match (equal, c_k) { (true, 0) => program_counter += 1, (false, 1) => program_counter += 1, _ => (), } }, Instruction::GetTabUp (a, b, c) => { let a = usize::try_from (*a).unwrap (); let b = usize::try_from (*b).unwrap (); let c = usize::try_from (*c).unwrap (); // Only supported upvalue is `_ENV` assert_eq! (b, 0); let key = k.get (c).unwrap (); let value = match key { Value::String (s) => match s.as_str() { "arg" => Value::BogusArg, "print" => Value::BogusPrint, _ => panic! ("key not in _ENV upvalue"), }, _ => unimplemented!(), }; r [a] = value; }, Instruction::GetI (a, b, c) => { let a = usize::try_from (*a).unwrap (); let b = usize::try_from (*b).unwrap (); let c = usize::try_from (*c).unwrap (); let table = r.get (b).unwrap (); let value = match table { Value::BogusArg => arg.get (c).unwrap ().as_str().into (), _ => unimplemented!(), }; r [a] = value; }, Instruction::Jmp (sJ) => program_counter += sJ, Instruction::LoadK (a, bx) => { let a = usize::try_from (*a).unwrap (); let bx = usize::try_from (*bx).unwrap (); r [a] = k [bx].clone (); }, Instruction::Return (_a, _b, _c) => { break; }, Instruction::VarArgPrep (_) => (), _ => (), } program_counter += 1; } }