can create an empty table

main
_ 2023-09-26 15:49:12 -05:00
parent e12d749c7c
commit 72b2d6e656
6 changed files with 189 additions and 27 deletions

View File

@ -45,6 +45,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
let a = (buf [0] >> 7) | ((buf [1] & 0x7f) << 1);
let b = buf [2];
let ax = a as u32 + ((b as u32) << 8);
let c = buf [3];
let bx =
(((buf [1] >> 7) as u32) << 0) |
@ -67,6 +68,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
0x0b => Inst::GetTabUp (a, b, c),
0x0d => Inst::GetI (a, b, c),
0x0f => Inst::SetTabUp (a, b, c),
0x13 => Inst::NewTable (a),
0x22 => Inst::Add (a, b, c),
0x24 => Inst::Mul (a, b, c),
0x2e => Inst::MmBin (a, b, c),
@ -81,6 +83,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
0x48 => Inst::Return1 (a),
0x4f => Inst::Closure (a, bx),
0x51 => Inst::VarArgPrep (a.into ()),
0x52 => Inst::ExtraArg (ax),
_ => return None,
})
}
@ -264,6 +267,7 @@ mod tests {
([0xbc, 0x00, 0x01, 0x00], Inst::EqK (1, 1, 0)),
([0xb8, 0x02, 0x00, 0x80], Inst::Jmp (6)),
([0x38, 0x02, 0x00, 0x80], Inst::Jmp (5)),
([0x52, 0x00, 0x00, 0x00], Inst::ExtraArg (0)),
] {
let actual = super::parse_inst (input).unwrap ();
assert_eq!(actual, expected);

View File

@ -1,7 +1,8 @@
use std::collections::BTreeMap;
use std::collections::HashMap;
use crate::value::{
BogusClosure,
Table,
Value,
};
@ -15,6 +16,8 @@ pub enum Instruction {
// Equals Constant?
EqK (u8, u8, u8),
ExtraArg (u32),
// Get Immediate?
GetI (u8, u8, u8),
@ -48,6 +51,8 @@ pub enum Instruction {
Mul (u8, u8, u8),
NewTable (u8),
Not (u8, u8),
// (A, B, _C, k) Return B - 1 registers starting with A
@ -129,7 +134,7 @@ impl State {
{
let arg: Vec <_> = args.map (|s| s.to_string ()).collect ();
let env = BTreeMap::from_iter ([
let env = HashMap::from_iter ([
("arg", Value::BogusArg (arg.into ())),
("print", Value::BogusPrint),
].map (|(k, v)| (k.to_string (), v)));
@ -247,6 +252,7 @@ impl State {
Value::Float (x) => println! ("{:?}", x),
Value::Integer (x) => println! ("{}", x),
Value::String (s) => println! ("{}", s),
Value::Table (t) => println! ("table: {:?}", std::rc::Rc::as_ptr (t)),
_ => unimplemented! (),
};
@ -285,6 +291,12 @@ impl State {
_ => (),
}
},
Instruction::ExtraArg (ax) => {
// This is used for NewTable. Maybe it's for reserving
// capacity in the array or something?
assert_eq! (*ax, 0, "implemented only for ax == 0");
},
Instruction::GetTabUp (a, b, c) => {
let a = usize::try_from (*a).unwrap ();
let b = usize::try_from (*b).unwrap ();
@ -383,6 +395,11 @@ impl State {
let r = self.register_window_mut();
r [a] = r [b].clone ();
},
Instruction::Mul (_a, _b, _c) => unimplemented!(),
Instruction::NewTable (a) => {
let a = usize::try_from (*a).unwrap ();
self.register_window_mut ()[a] = Value::Table (Default::default ());
},
Instruction::Not (a, b) => {
let a = usize::try_from (*a).unwrap ();
let b = usize::try_from (*b).unwrap ();
@ -444,6 +461,7 @@ impl State {
return self.registers [a..(a + b - 1)].to_vec();
}
},
Instruction::Return0 => unimplemented! (),
Instruction::Return1 (a) => {
let a = usize::try_from (*a).unwrap ();
let popped_frame = self.stack.pop ().unwrap ();
@ -467,6 +485,8 @@ impl State {
let offset = popped_frame.register_offset;
self.registers [offset - 1] = self.registers [offset + a].take ();
},
Instruction::SetTabUp (_a, _b, _c) => unimplemented! (),
Instruction::TailCall (_a, _b, _c, _k) => unimplemented! (),
Instruction::Test (a, _k) => {
let a = usize::try_from (*a).unwrap ();
@ -481,7 +501,6 @@ impl State {
}
},
Instruction::VarArgPrep (_) => (),
x => panic! ("Unimplemented instruction {x:?}"),
}
next_pc += 1;

View File

@ -1,8 +1,11 @@
use crate::state::{
Block,
Chunk,
Instruction as Inst,
State,
use crate::{
state::{
Block,
Chunk,
Instruction as Inst,
State,
},
value::Value,
};
#[test]
@ -75,6 +78,8 @@ fn bools () {
(vec! ["_exe_name"], vec! [98.into ()]),
(vec! ["_exe_name", "asdf"], vec! [99.into ()]),
] {
let expected: Vec <Value> = expected;
let mut vm = State::default ();
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
@ -94,6 +99,7 @@ fn closure () {
(vec! ["_exe_name"], vec! [23.0.into ()]),
(vec! ["_exe_name"], vec! [23.0.into ()]),
] {
let expected: Vec <Value> = expected;
let mut vm = State::default ();
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
let actual = vm.execute_chunk (&file, &upvalues);
@ -141,6 +147,7 @@ fn floats () {
(vec! ["_exe_name"], vec! [3.5.into ()]),
(vec! ["_exe_name", " "], vec! [3.5.into ()]),
] {
let expected: Vec <Value> = expected;
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);
@ -160,6 +167,7 @@ fn fma () {
(vec! ["_exe_name"], vec! [122.into ()]),
(vec! ["_exe_name"], vec! [122.into ()]),
] {
let expected: Vec <Value> = expected;
let mut vm = State::default ();
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
let actual = vm.execute_chunk (&file, &upvalues);
@ -184,6 +192,7 @@ fn is_93 () {
(vec! ["_exe_name", "93"], vec! [0.into ()]),
(vec! ["_exe_name", "94"], vec! [1.into ()]),
] {
let expected: Vec <Value> = expected;
let mut vm = State::default ();
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
let actual = vm.execute_chunk (&file, &upvalues);
@ -200,28 +209,16 @@ fn value_size () {
// Lua's tagged union values are 12-16 bytes on a 32-bit system
// with 64-bit floats
//
// It is very nice if LunarWaveVM is the same or better.
// There is some exploratory things in this test, too
// It would be nice if LunarWaveVM is the same or better.
// There are some exploratory things in this test, too
use std::{
mem::size_of,
rc::Rc,
};
assert! (size_of::<Box <()>> () <= 8);
assert! (size_of::<std::rc::Rc <()>> () <= 8);
enum Value {
Nil,
Boolean (bool),
Float (f64),
String (Rc <String>),
}
let sz = size_of::<Value> ();
let expected = 16;
assert! (sz <= expected, "{sz} > {expected}");
let sz = size_of::<crate::value::Value> ();
let expected = 16;
assert! (sz <= expected, "{sz} > {expected}");

View File

@ -1,9 +1,13 @@
use std::{
collections::BTreeMap,
cmp::{
Eq,
PartialEq,
},
collections::HashMap,
rc::Rc,
};
#[derive (Clone, Debug, Hash, PartialEq)]
#[derive (Debug, Eq, PartialEq)]
pub struct BogusClosure {
pub idx: usize,
pub upvalues: Vec <Value>,
@ -13,16 +17,21 @@ pub struct BogusClosure {
pub enum Value {
Nil,
Boolean (bool),
// Rust is very strict about float equality, so some of my code
// here is probably wrong in subtle ways.
Float (f64),
Integer (i64),
String (Rc <String>),
Table (Rc <Table>),
// These are all bogus, I haven't figured out how to implement
// tables and function pointers yet
BogusArg (Rc <Vec <String>>),
BogusClosure (Rc <BogusClosure>),
BogusEnv (Rc <BTreeMap <String, Value>>),
BogusEnv (Rc <HashMap <String, Value>>),
BogusPrint,
}
@ -46,7 +55,13 @@ impl From <&str> for Value {
impl From <i32> for Value {
fn from (x: i32) -> Self {
Self::Integer (i64::try_from (x).unwrap ())
Self::Integer (i64::from (x))
}
}
impl From <i64> for Value {
fn from (x: i64) -> Self {
Self::Integer (x)
}
}
@ -56,6 +71,14 @@ impl From <f64> for Value {
}
}
impl From <Table> for Value {
fn from (x: Table) -> Self {
Self::Table (x.into ())
}
}
impl Eq for Value {}
impl std::hash::Hash for Value {
fn hash <H: std::hash::Hasher> (&self, state: &mut H) {
// Per https://doc.rust-lang.org/std/hash/trait.Hash.html#prefix-collisions
@ -67,7 +90,11 @@ impl std::hash::Hash for Value {
Self::Boolean (x) => x.hash (state),
Self::Float (x) => x.to_ne_bytes ().hash (state),
Self::Integer (x) => x.hash (state),
Self::String (x) => x.as_ptr ().hash (state),
// TODO: Implement string interning so we don't hash the whole string here
Self::String (x) => x.hash (state),
Self::Table (x) => Rc::as_ptr (&x).hash (state),
Self::BogusArg (_) => panic! ("can't hash Bogus values"),
Self::BogusClosure (_) => panic! ("can't hash Bogus values"),
Self::BogusEnv (_) => panic! ("can't hash Bogus values"),
@ -76,6 +103,12 @@ impl std::hash::Hash for Value {
}
}
impl PartialEq <i64> for Value {
fn eq (&self, rhs: &i64) -> bool {
*self == Value::from (*rhs)
}
}
impl Value {
pub fn as_float (&self) -> Option <f64> {
match self {
@ -102,3 +135,74 @@ impl Value {
x
}
}
#[derive (Debug, Default, Eq, PartialEq)]
pub struct Table {
array: Vec <Value>,
hash: HashMap <Value, Value>,
}
impl Table {
fn get_inner (&self, key: &Value) -> Value {
self.hash.get (key).cloned ().unwrap_or_default ()
}
pub fn get <A: Into <Value>> (&self, key: A) -> Value {
self.get_inner (&(key.into ()))
}
fn insert_inner (&mut self, a: Value, b: Value) {
self.hash.insert (a, b);
}
pub fn insert <A: Into <Value>, B: Into <Value>> (
&mut self,
a: A,
b: B,
) {
self.insert_inner (a.into (), b.into ())
}
}
#[cfg (test)]
mod tests {
use std::collections::HashMap;
use super::{
Table,
Value,
};
#[test]
fn smoke () {
let v_a = Value::from (5.0);
let v_b = Value::from (5);
assert_eq! (v_a.as_float (), Some (5.0));
assert_eq! (v_b.as_float (), Some (5.0));
}
#[test]
fn tables () {
let nil = Value::Nil;
assert_ne! (Value::from (18.0), Value::from (19.0));
let mut t = HashMap::new ();
t.insert (Value::from ("x"), Value::from (19.0));
assert_eq! (t.get (&Value::from ("x")), Some (&Value::from (19.0)));
let mut t = Table::default ();
assert_eq! (t.get ("a"), nil);
assert_eq! (t.get ("b"), nil);
t.insert ("a", 1993);
t.insert ("b", 2007);
assert_eq! (t.get ("a"), 1993);
assert_eq! (t.get ("b"), 2007);
t.insert (19, 93);
assert_eq! (t.get (19), 93);
}
}

View File

@ -4,3 +4,6 @@ print (true)
print (1993)
print (1993.00)
print "Hello."
local t = {}
print (t)

35
test_vectors/hello.txt Normal file
View File

@ -0,0 +1,35 @@
main <test_vectors/hello.lua:0,0> (25 instructions at 0x55938922ecd0)
0+ params, 3 slots, 1 upvalue, 1 local, 2 constants, 0 functions
1 [1] VARARGPREP 0
2 [1] GETTABUP 0 0 0 ; _ENV "print"
3 [1] LOADNIL 1 0 ; 1 out
4 [1] CALL 0 2 1 ; 1 in 0 out
5 [2] GETTABUP 0 0 0 ; _ENV "print"
6 [2] LOADFALSE 1
7 [2] CALL 0 2 1 ; 1 in 0 out
8 [3] GETTABUP 0 0 0 ; _ENV "print"
9 [3] LOADTRUE 1
10 [3] CALL 0 2 1 ; 1 in 0 out
11 [4] GETTABUP 0 0 0 ; _ENV "print"
12 [4] LOADI 1 1993
13 [4] CALL 0 2 1 ; 1 in 0 out
14 [5] GETTABUP 0 0 0 ; _ENV "print"
15 [5] LOADF 1 1993
16 [5] CALL 0 2 1 ; 1 in 0 out
17 [6] GETTABUP 0 0 0 ; _ENV "print"
18 [6] LOADK 1 1 ; "Hello."
19 [6] CALL 0 2 1 ; 1 in 0 out
20 [8] NEWTABLE 0 0 0 ; 0
21 [8] EXTRAARG 0
22 [9] GETTABUP 1 0 0 ; _ENV "print"
23 [9] MOVE 2 0
24 [9] CALL 1 2 1 ; 1 in 0 out
25 [9] RETURN 1 1 1 ; 0 out
constants (2) for 0x55938922ecd0:
0 S "print"
1 S "Hello."
locals (1) for 0x55938922ecd0:
0 t 22 26
upvalues (1) for 0x55938922ecd0:
0 _ENV 1 0