diff --git a/src/instruction.rs b/src/instruction.rs index e723f55..ed5b22d 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -10,9 +10,13 @@ pub enum Instruction { ExtraArg (u32), + GetField (u8, u8, u8), + // Get Immediate? GetI (u8, u8, u8), + GetTable (u8, u8, u8), + // Get Table, Upvalue GetTabUp (u8, u8, u8), @@ -55,6 +59,12 @@ pub enum Instruction { // Return just one register Return1 (u8), + SetField (u8, u8, u8, bool), + + SetI (u8, u8, u8, bool), + + SetList (u8, u8, u8, bool), + SetTabUp (u8, u8, u8), TailCall (u8, u8, u8, bool), diff --git a/src/loader.rs b/src/loader.rs index c112129..d3c09aa 100644 --- a/src/loader.rs +++ b/src/loader.rs @@ -69,8 +69,12 @@ pub fn parse_inst (buf: [u8; 4]) -> Option 0x08 => Inst::LoadNil (a), 0x09 => Inst::GetUpVal (a, b), 0x0b => Inst::GetTabUp (a, b, c), + 0x0c => Inst::GetTable (a, b, c), 0x0d => Inst::GetI (a, b, c), + 0x0e => Inst::GetField (a, b, c), 0x0f => Inst::SetTabUp (a, b, c), + 0x11 => Inst::SetI (a, b, c, k), + 0x12 => Inst::SetField (a, b, c, k), 0x13 => Inst::NewTable (a), 0x22 => Inst::Add (a, b, c), 0x24 => Inst::Mul (a, b, c), @@ -84,6 +88,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option 0x46 => Inst::Return (a, b, c, k), 0x47 => Inst::Return0, 0x48 => Inst::Return1 (a), + 0x4e => Inst::SetList (a, b, c, k), 0x4f => Inst::Closure (a, bx), 0x51 => Inst::VarArgPrep (a.into ()), 0x52 => Inst::ExtraArg (ax), @@ -118,11 +123,23 @@ fn parse_byte (rdr: &mut R) -> Option Some (buf [0]) } +fn parse_float (rdr: &mut R) -> Option { + let mut buf = [0u8; 8]; + rdr.read_exact (&mut buf).ok ()?; + Some (f64::from_ne_bytes(buf)) +} + fn parse_int (rdr: &mut R) -> Option { Some ((parse_byte (rdr)? - 0x80) as u32) } +fn parse_i64 (rdr: &mut R) -> Option { + let mut buf = [0u8; 8]; + rdr.read_exact (&mut buf).ok ()?; + Some (i64::from_ne_bytes(buf)) +} + // I'm doing this recursively so it's easy to match with the PUC Lua // code, but I don't like recursion in general, and I don't know // why PUC wrote it that way. @@ -132,6 +149,8 @@ pub fn parse_block (rdr: &mut R, blocks: &mut Vec ) { // Ignore things I haven't implemented yet + use crate::value::Value; + parse_string (rdr)?; // function name parse_int (rdr)?; // start line in source code parse_int (rdr)?; // last line in source code @@ -152,12 +171,18 @@ pub fn parse_block (rdr: &mut R, blocks: &mut Vec ) let mut constants = Vec::with_capacity (constant_count as usize); - for _ in 0..constant_count { + for i in 0..constant_count { + // LUA_TNIL and friends from `lua.h` in PUC Lua let const_type = parse_byte (rdr)?; - assert_eq! (const_type, 0x04); - let s = parse_string (rdr)?; - constants.push (s.into ()); + let val = match const_type { + 3 => Value::from (parse_i64 (rdr)?), + 4 => parse_string (rdr)?.into (), + 19 => Value::from (parse_float (rdr)?), + x => panic! ("Constant {} has type {}", i, x), + }; + + constants.push (val); } let upvalue_count = parse_int (rdr)? as usize; diff --git a/src/state.rs b/src/state.rs index 3db4ac5..3600467 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,4 +1,7 @@ -use std::collections::HashMap; +use std::{ + collections::HashMap, + rc::Rc, +}; use crate::{ instruction::Instruction, @@ -212,6 +215,31 @@ impl State { assert_eq! (*ax, 0, "implemented only for ax == 0"); }, + Instruction::GetField (a, b, c) => { + let t = match self.reg (*b) { + Value::Table (t) => t, + _ => panic! ("R[B] must be a table"), + }; + + let key = match &k [usize::from (*c)] { + Value::String (s) => s, + _ => panic! ("K[C] must be a string"), + }; + + *self.reg_mut (*a) = t.get (Value::String (Rc::clone (key))); + }, + Instruction::GetTable (a, b, c) => { + let t = match self.reg (*b) { + Value::Table (t) => t, + _ => panic! ("R[B] must be a table"), + }; + + let key = self.reg (*c); + + let val = t.get (key.clone ()); + + *self.reg_mut (*a) = val; + }, Instruction::GetTabUp (a, b, c) => { let b = usize::try_from (*b).unwrap (); let c = usize::try_from (*c).unwrap (); @@ -230,12 +258,16 @@ impl State { *self.reg_mut (*a) = value.clone (); }, Instruction::GetI (a, b, c) => { - let c = usize::try_from (*c).unwrap (); + let key = usize::try_from (*c).unwrap (); let table = self.reg (*b); let value = match table { - Value::BogusArg (arg) => arg.get (c).map (|x| x.as_str().into ()).unwrap_or_default(), - _ => unimplemented!(), + Value::BogusArg (arg) => arg.get (key).map (|x| x.as_str().into ()).unwrap_or_default(), + Value::Table (t) => { + let key = Value::from (i64::try_from (key).unwrap ()); + t.get (key) + }, + _ => unimplemented! (), }; *self.reg_mut (*a) = value; @@ -371,6 +403,62 @@ impl State { let offset = popped_frame.register_offset; self.registers [offset - 1] = self.registers [offset + a].take (); }, + Instruction::SetField (a, b, c, k_flag) => { + let value = if *k_flag { + &k [usize::from (*c)] + } + else { + self.reg (*c) + } + .clone (); + + let b = usize::try_from (*b).unwrap (); + + let key = match k.get (b).unwrap () { + Value::String (s) => s.as_ref (), + _ => panic! ("GetTabUp only supports string keys"), + }; + + let dst = self.reg_mut (*a).as_table_mut () + .expect ("SetField only works on tables"); + + Rc::get_mut (dst).expect ("shrug").insert (Value::from (key.as_str ()), value); + }, + Instruction::SetI (a, b, c, k_flag) => { + let value = if *k_flag { + &k [usize::from (*c)] + } + else { + self.reg (*c) + } + .clone (); + + let dst = self.reg_mut (*a).as_table_mut () + .expect ("SetI only works on tables"); + + Rc::get_mut (dst).expect ("shrug").insert (i64::from (*b), value); + }, + Instruction::SetList (a, b, c, k) => { + if *b == 0 { + panic! ("SetList with b == 0 not implemented"); + } + if *k { + panic! ("SetList with k = true not implemented"); + } + + // Temporarily move the table out + + let mut dst = Rc::into_inner (std::mem::replace (self.reg_mut (*a).as_table_mut ().expect ("SetList only works on tables"), Default::default ())).unwrap (); + + for i in 1..=*b { + let src = self.reg (*a + i); + dst.insert (Value::from (i64::from (*c + i)), src.clone ()); + } + + // Put the table back and pretend it's all okay + + *self.reg_mut (*a) = Value::Table (dst.into ()); + }, Instruction::SetTabUp (_a, _b, _c) => unimplemented! (), Instruction::TailCall (_a, _b, _c, _k) => unimplemented! (), Instruction::Test (a, _k) => { diff --git a/src/value.rs b/src/value.rs index 6c6ff68..2fbe524 100644 --- a/src/value.rs +++ b/src/value.rs @@ -145,6 +145,13 @@ impl Value { } } + pub fn as_table_mut (&mut self) -> Option <&mut Rc > { + match self { + Self::Table (t) => Some (t), + _ => None, + } + } + pub fn is_truthy (&self) -> bool { // And this is something Lua does better than JS and Python. diff --git a/test_vectors/hello.lua b/test_vectors/hello.lua index 838bf06..fb08d11 100644 --- a/test_vectors/hello.lua +++ b/test_vectors/hello.lua @@ -1,3 +1,5 @@ +print "# 1" + print (nil) print (false) print (true) @@ -5,5 +7,16 @@ print (1993) print (1993.00) print "Hello." +print "# 2" + local t = {} print (t) +print (t [1]) +t [1] = "asdf" +print (t [1]) +t.x = 1993 +print (t.x) + +t.t = { 3.14159 } +print (t ["t"][1]) +