⭐ 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
parent
79e9df852c
commit
9d29aeb43b
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
96
src/state.rs
96
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) => {
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
||||
|
|
Loading…
Reference in New Issue