tables are working okay

Still missing a lot of stuff. And I'm pretty sure the Rc handling code
is totally wrong.

And they don't have the Lua 5.0 optimization of an internal array,
but that's probably easy to put in.
main
_ 2023-09-26 20:31:00 -05:00
parent 79e9df852c
commit 9d29aeb43b
5 changed files with 151 additions and 8 deletions

View File

@ -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),

View File

@ -69,8 +69,12 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
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 <Inst>
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 <R: Read> (rdr: &mut R) -> Option <u8>
Some (buf [0])
}
fn parse_float <R: Read> (rdr: &mut R) -> Option <f64> {
let mut buf = [0u8; 8];
rdr.read_exact (&mut buf).ok ()?;
Some (f64::from_ne_bytes(buf))
}
fn parse_int <R: Read> (rdr: &mut R) -> Option <u32>
{
Some ((parse_byte (rdr)? - 0x80) as u32)
}
fn parse_i64 <R: Read> (rdr: &mut R) -> Option <i64> {
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 <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>)
{
// 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 <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>)
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;

View File

@ -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) => {

View File

@ -145,6 +145,13 @@ impl Value {
}
}
pub fn as_table_mut (&mut self) -> Option <&mut Rc <Table>> {
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.

View File

@ -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])