down to 4.2x slower than PUC Lua, but the code became ugly to behold
parent
eb32a53d18
commit
130330b688
|
@ -55,8 +55,10 @@ pub struct State {
|
||||||
pub debug_print: bool,
|
pub debug_print: bool,
|
||||||
step_count: u32,
|
step_count: u32,
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
upvalues: Vec <Value>,
|
pub upvalues: Vec <Value>,
|
||||||
pub si: Interner,
|
pub si: Interner,
|
||||||
|
register_offset: usize,
|
||||||
|
block_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lw_io_write (l: &mut State, num_args: usize) -> usize {
|
fn lw_io_write (l: &mut State, num_args: usize) -> usize {
|
||||||
|
@ -194,6 +196,8 @@ impl State {
|
||||||
chunk,
|
chunk,
|
||||||
upvalues,
|
upvalues,
|
||||||
si: Default::default (),
|
si: Default::default (),
|
||||||
|
register_offset: 0,
|
||||||
|
block_idx: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,6 +217,8 @@ impl State {
|
||||||
chunk,
|
chunk,
|
||||||
upvalues,
|
upvalues,
|
||||||
si,
|
si,
|
||||||
|
register_offset: 0,
|
||||||
|
block_idx: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +227,7 @@ impl State {
|
||||||
frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter
|
frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upvalues_from_args <I: Iterator <Item = String>> (si: &mut Interner, args: I) -> Vec <Value>
|
pub fn upvalues_from_args <I: Iterator <Item = String>> (si: &mut Interner, args: I) -> Vec <Value>
|
||||||
{
|
{
|
||||||
let arg = args.map (|s| si.intern (&s)).enumerate ();
|
let arg = args.map (|s| si.intern (&s)).enumerate ();
|
||||||
let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), Value::String (v))));
|
let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), Value::String (v))));
|
||||||
|
@ -266,13 +272,11 @@ impl State {
|
||||||
/// Short form to get access to a register within our window
|
/// Short form to get access to a register within our window
|
||||||
|
|
||||||
pub fn reg (&self, i: u8) -> &Value {
|
pub fn reg (&self, i: u8) -> &Value {
|
||||||
let frame = self.stack.last ().unwrap ();
|
&self.registers [self.register_offset + i as usize]
|
||||||
&self.registers [frame.register_offset + i as usize]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reg_mut (&mut self, i: u8) -> &mut Value {
|
pub fn reg_mut (&mut self, i: u8) -> &mut Value {
|
||||||
let frame = self.stack.last ().unwrap ();
|
&mut self.registers [self.register_offset + i as usize]
|
||||||
&mut self.registers [frame.register_offset + i as usize]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For native functions to check how many args they got
|
// For native functions to check how many args they got
|
||||||
|
@ -289,18 +293,120 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn op_add (&mut self, a: u8, b: u8, c: u8) -> bool {
|
||||||
|
let v_b = self.reg (b);
|
||||||
|
let v_c = self.reg (c);
|
||||||
|
|
||||||
|
*self.reg_mut (a) = match (v_b, v_c) {
|
||||||
|
(Value::Float (b), Value::Float (c)) => Value::from (b + c),
|
||||||
|
(Value::Integer (b), Value::Integer (c)) => Value::from (b + c),
|
||||||
|
(Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 + c),
|
||||||
|
(Value::Float (b), Value::Integer (c)) => Value::from (b + *c as f64),
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn op_get_field (&mut self, a: u8, b: u8, c: u8) {
|
||||||
|
let block = self.chunk.blocks.get (self.block_idx).unwrap ();
|
||||||
|
let constants = &block.constants;
|
||||||
|
|
||||||
|
let key = match &constants [usize::from (c)] {
|
||||||
|
Value::String (s) => s,
|
||||||
|
_ => panic! ("K[C] must be a string"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let val = match &self.registers [self.register_offset + usize::from (b)] {
|
||||||
|
Value::Nil => panic! ("R[B] must not be nil"),
|
||||||
|
Value::Table (t) => t.borrow ().get_str (*key).clone (),
|
||||||
|
_ => panic! ("R[B] must be a table"),
|
||||||
|
};
|
||||||
|
|
||||||
|
*self.reg_mut (a) = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn op_get_table (&mut self, a: u8, b: u8, c: u8) {
|
||||||
|
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.borrow ().get (key.clone ()).clone ();
|
||||||
|
|
||||||
|
*self.reg_mut (a) = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn op_mmbin (&mut self, a: u8, b: u8, _c: u8) {
|
||||||
|
let a = self.reg (a);
|
||||||
|
let b = self.reg (b);
|
||||||
|
|
||||||
|
if a.as_float().is_some() && b.as_float().is_some () {
|
||||||
|
// No need for metamethods
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic! ("Not sure how to implement OP_MMBIN for these 2 values {a:?}, {b:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn op_mul (&mut self, a: u8, b: u8, c: u8) -> bool {
|
||||||
|
let v_b = self.reg (b);
|
||||||
|
let v_c = self.reg (c);
|
||||||
|
|
||||||
|
*self.reg_mut (a) = match (v_b, v_c) {
|
||||||
|
(Value::Float (b), Value::Float (c)) => Value::from (b * c),
|
||||||
|
(Value::Integer (b), Value::Integer (c)) => Value::from (b * c),
|
||||||
|
(Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 * c),
|
||||||
|
(Value::Float (b), Value::Integer (c)) => Value::from (b * *c as f64),
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn op_set_field (&mut self, a: u8, b: u8, c: u8, k: bool) {
|
||||||
|
let frame = self.stack.last ().unwrap ();
|
||||||
|
let block = self.chunk.blocks.get (frame.block_idx).unwrap ();
|
||||||
|
let constants = &block.constants;
|
||||||
|
|
||||||
|
let value = if k {
|
||||||
|
&constants [usize::from (c)]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.reg (c)
|
||||||
|
}
|
||||||
|
.clone ();
|
||||||
|
|
||||||
|
let b = usize::try_from (b).unwrap ();
|
||||||
|
|
||||||
|
let key = match constants.get (b).unwrap () {
|
||||||
|
Value::String (s) => *s,
|
||||||
|
_ => panic! ("SetField only supports string keys"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut dst = self.reg (a).as_table ()
|
||||||
|
.expect ("SetField only works on tables").borrow_mut ();
|
||||||
|
|
||||||
|
dst.insert_str (key, value);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn step (&mut self) -> Result <Option <StepOutput>, StepError>
|
pub fn step (&mut self) -> Result <Option <StepOutput>, StepError>
|
||||||
{
|
{
|
||||||
self.step_count += 1;
|
self.step_count += 1;
|
||||||
|
|
||||||
let frame = self.stack.last_mut ().unwrap ().clone ();
|
let frame = self.stack.last ().unwrap ();
|
||||||
let block = self.chunk.blocks.get (frame.block_idx).unwrap ();
|
self.block_idx = frame.block_idx;
|
||||||
|
self.register_offset = frame.register_offset;
|
||||||
|
let block_idx = frame.block_idx;
|
||||||
|
let block = self.chunk.blocks.get (block_idx).unwrap ();
|
||||||
|
|
||||||
let mut next_pc = frame.program_counter;
|
let mut next_pc = frame.program_counter;
|
||||||
|
|
||||||
let pc = usize::try_from (frame.program_counter).expect ("program_counter is not a valid usize");
|
let pc = usize::try_from (frame.program_counter).expect ("program_counter is not a valid usize");
|
||||||
let instruction = match block.instructions.get (pc) {
|
let instruction = match block.instructions.get (pc) {
|
||||||
Some (x) => x,
|
Some (x) => *x,
|
||||||
None => {
|
None => {
|
||||||
dbg! (&self.stack);
|
dbg! (&self.stack);
|
||||||
panic! ("program_counter went out of bounds");
|
panic! ("program_counter went out of bounds");
|
||||||
|
@ -311,25 +417,14 @@ impl State {
|
||||||
let k = &block.constants;
|
let k = &block.constants;
|
||||||
|
|
||||||
let make_step_error = |msg| {
|
let make_step_error = |msg| {
|
||||||
self.make_step_error (msg, instruction)
|
self.make_step_error (msg, &instruction)
|
||||||
};
|
};
|
||||||
|
|
||||||
match instruction.clone () {
|
match instruction {
|
||||||
Instruction::Add (a, b, c) => {
|
Instruction::Add (a, b, c) => {
|
||||||
let v_b = self.reg (b);
|
if self.op_add (a, b, c) {
|
||||||
let v_c = self.reg (c);
|
next_pc += 1;
|
||||||
|
|
||||||
let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ())
|
|
||||||
{
|
|
||||||
Value::from (v_b + v_c)
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}"));
|
|
||||||
let v_c = v_c.as_float ().unwrap_or_else (|| panic! ("{v_c}"));
|
|
||||||
Value::from (v_b + v_c)
|
|
||||||
};
|
|
||||||
|
|
||||||
*self.reg_mut (a) = x;
|
|
||||||
},
|
},
|
||||||
Instruction::AddI (a, b, s_c) => {
|
Instruction::AddI (a, b, s_c) => {
|
||||||
let v_b = self.reg (b);
|
let v_b = self.reg (b);
|
||||||
|
@ -365,15 +460,12 @@ impl State {
|
||||||
Value::BogusClosure (rc) => {
|
Value::BogusClosure (rc) => {
|
||||||
let idx = rc.borrow ().idx;
|
let idx = rc.borrow ().idx;
|
||||||
|
|
||||||
let block_idx = frame.block_idx;
|
|
||||||
let target_block = idx;
|
let target_block = idx;
|
||||||
|
|
||||||
let current_frame = self.stack.last ().unwrap ();
|
|
||||||
|
|
||||||
self.stack.push (StackFrame {
|
self.stack.push (StackFrame {
|
||||||
program_counter: 0,
|
program_counter: 0,
|
||||||
block_idx: target_block,
|
block_idx: target_block,
|
||||||
register_offset: current_frame.register_offset + a as usize + 1,
|
register_offset: self.register_offset + a as usize + 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.debug_print {
|
if self.debug_print {
|
||||||
|
@ -386,19 +478,19 @@ impl State {
|
||||||
return Ok (None);
|
return Ok (None);
|
||||||
},
|
},
|
||||||
Value::RsFunc (x) => {
|
Value::RsFunc (x) => {
|
||||||
let current_frame = self.stack.last ().unwrap ();
|
let old_offset = self.register_offset;
|
||||||
let new_offset = current_frame.register_offset + usize::from (a) + 1;
|
self.register_offset = old_offset + usize::from (a) + 1;
|
||||||
|
|
||||||
// Trash the stack frame so it doesn't point to a
|
// Trash the stack frame so it doesn't point to a
|
||||||
// valid Lua function
|
// valid Lua function
|
||||||
self.stack.push (StackFrame {
|
self.stack.push (StackFrame {
|
||||||
program_counter: 65535, // Bogus for native functions
|
program_counter: 65535, // Bogus for native functions
|
||||||
block_idx: 65535, // Bogus
|
block_idx: 65535, // Bogus
|
||||||
register_offset: new_offset,
|
register_offset: self.register_offset,
|
||||||
});
|
});
|
||||||
|
|
||||||
let num_args = if b == 0 {
|
let num_args = if b == 0 {
|
||||||
self.top - new_offset
|
self.top - self.register_offset
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
b - 1
|
b - 1
|
||||||
|
@ -408,7 +500,8 @@ impl State {
|
||||||
let num_results = x (self, num_args);
|
let num_results = x (self, num_args);
|
||||||
|
|
||||||
let popped_frame = self.stack.pop ().unwrap ();
|
let popped_frame = self.stack.pop ().unwrap ();
|
||||||
let offset = popped_frame.register_offset - 1;
|
self.register_offset = old_offset;
|
||||||
|
let offset = old_offset + usize::from (a);
|
||||||
|
|
||||||
for i in (offset)..(offset + usize::try_from (num_results).unwrap ()) {
|
for i in (offset)..(offset + usize::try_from (num_results).unwrap ()) {
|
||||||
self.registers [i] = self.registers [i + 1].take ();
|
self.registers [i] = self.registers [i + 1].take ();
|
||||||
|
@ -501,32 +594,10 @@ impl State {
|
||||||
*self.reg_mut (a + 3) = start.into ();
|
*self.reg_mut (a + 3) = start.into ();
|
||||||
},
|
},
|
||||||
Instruction::GetField (a, b, c) => {
|
Instruction::GetField (a, b, c) => {
|
||||||
let t = match self.reg (b) {
|
self.op_get_field (a, b, c);
|
||||||
Value::Nil => Err (make_step_error ("R[B] must not be nil"))?,
|
|
||||||
Value::Table (t) => t,
|
|
||||||
_ => Err (make_step_error ("R[B] must be a table"))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let key = match &k [usize::from (c)] {
|
|
||||||
Value::String (s) => *s,
|
|
||||||
_ => panic! ("K[C] must be a string"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let val = t.borrow ().get_str (key).clone ();
|
|
||||||
|
|
||||||
*self.reg_mut (a) = val;
|
|
||||||
},
|
},
|
||||||
Instruction::GetTable (a, b, c) => {
|
Instruction::GetTable (a, b, c) => {
|
||||||
let t = match self.reg (b) {
|
self.op_get_table (a, b, c);
|
||||||
Value::Table (t) => t,
|
|
||||||
_ => panic! ("R[B] must be a table"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let key = self.reg (c);
|
|
||||||
|
|
||||||
let val = t.borrow ().get (key.clone ());
|
|
||||||
|
|
||||||
*self.reg_mut (a) = val;
|
|
||||||
},
|
},
|
||||||
Instruction::GetTabUp (a, b, c) => {
|
Instruction::GetTabUp (a, b, c) => {
|
||||||
let b = usize::try_from (b).unwrap ();
|
let b = usize::try_from (b).unwrap ();
|
||||||
|
@ -563,7 +634,7 @@ impl State {
|
||||||
|
|
||||||
let value = {
|
let value = {
|
||||||
let table = self.reg (b).as_table ().expect ("GetI only works on tables").borrow ();
|
let table = self.reg (b).as_table ().expect ("GetI only works on tables").borrow ();
|
||||||
table.get_int (key)
|
table.get_int (key).clone ()
|
||||||
};
|
};
|
||||||
|
|
||||||
*self.reg_mut (a) = value;
|
*self.reg_mut (a) = value;
|
||||||
|
@ -621,16 +692,8 @@ impl State {
|
||||||
Instruction::LoadTrue (a) => {
|
Instruction::LoadTrue (a) => {
|
||||||
*self.reg_mut (a) = true.into ();
|
*self.reg_mut (a) = true.into ();
|
||||||
},
|
},
|
||||||
Instruction::MmBin (a, b, _c) => {
|
Instruction::MmBin (a, b, c) => {
|
||||||
let a = self.reg (a);
|
self.op_mmbin (a, b, c);
|
||||||
let b = self.reg (b);
|
|
||||||
|
|
||||||
if a.as_float().is_some() && b.as_float().is_some () {
|
|
||||||
// No need for metamethods
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
panic! ("Not sure how to implememtn OP_MMBIN for these 2 values {a:?}, {b:?}");
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Instruction::MmBinI (_a, _s_b, _c, _k) => {
|
Instruction::MmBinI (_a, _s_b, _c, _k) => {
|
||||||
// Ignore
|
// Ignore
|
||||||
|
@ -651,20 +714,12 @@ impl State {
|
||||||
*self.reg_mut (a) = self.reg (b).clone ();
|
*self.reg_mut (a) = self.reg (b).clone ();
|
||||||
},
|
},
|
||||||
Instruction::Mul (a, b, c) => {
|
Instruction::Mul (a, b, c) => {
|
||||||
let v_b = self.reg (b);
|
// If we handled the mul as a regular int or float,
|
||||||
let v_c = self.reg (c);
|
// skip the OP_MMBIN that probably comes after this
|
||||||
|
|
||||||
let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ())
|
if self.op_mul (a, b, c) {
|
||||||
{
|
next_pc += 1;
|
||||||
Value::from (v_b * v_c)
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}"));
|
|
||||||
let v_c = v_c.as_float ().unwrap_or_else (|| panic! ("{v_c}"));
|
|
||||||
Value::from (v_b * v_c)
|
|
||||||
};
|
|
||||||
|
|
||||||
*self.reg_mut (a) = x;
|
|
||||||
},
|
},
|
||||||
Instruction::MulK (a, b, c) => {
|
Instruction::MulK (a, b, c) => {
|
||||||
let v_b = self.reg (b);
|
let v_b = self.reg (b);
|
||||||
|
@ -776,25 +831,7 @@ impl State {
|
||||||
self.top = popped_frame.register_offset - 1 + 1;
|
self.top = popped_frame.register_offset - 1 + 1;
|
||||||
},
|
},
|
||||||
Instruction::SetField (a, b, c, k_flag) => {
|
Instruction::SetField (a, b, c, k_flag) => {
|
||||||
let value = if k_flag {
|
self.op_set_field (a, b, c, 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,
|
|
||||||
_ => panic! ("SetField only supports string keys"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut dst = self.reg (a).as_table ()
|
|
||||||
.expect ("SetField only works on tables").borrow_mut ();
|
|
||||||
|
|
||||||
dst.insert_str (key, value);
|
|
||||||
},
|
},
|
||||||
Instruction::SetI (a, b, c, k_flag) => {
|
Instruction::SetI (a, b, c, k_flag) => {
|
||||||
let value = if k_flag {
|
let value = if k_flag {
|
||||||
|
|
|
@ -23,17 +23,18 @@ fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
||||||
/// Takes arguments and a parsed Lua chunk, runs its,
|
/// Takes arguments and a parsed Lua chunk, runs its,
|
||||||
/// and returns the output
|
/// and returns the output
|
||||||
|
|
||||||
fn run_chunk (args: &[&str], chunk: Chunk) -> Vec <Value> {
|
fn run_chunk (vm: &mut State, args: &[&str], chunk: Chunk) -> Vec <Value> {
|
||||||
let mut vm = State::new_with_args (chunk, args.iter ());
|
vm.upvalues = State::upvalues_from_args(&mut vm.si, args.into_iter ().map (|s| s.to_string ()));
|
||||||
|
vm.set_chunk (chunk);
|
||||||
vm.execute ().unwrap ()
|
vm.execute ().unwrap ()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes arguments and Lua bytecode, loads it, runs it,
|
/// Takes arguments and Lua bytecode, loads it, runs it,
|
||||||
/// and return the output
|
/// and return the output
|
||||||
|
|
||||||
fn run_bytecode (args: &[&str], bc: &[u8], si: &mut Interner) -> Vec <Value> {
|
fn run_bytecode (vm: &mut State, args: &[&str], bc: &[u8]) -> Vec <Value> {
|
||||||
let chunk = loader::parse_chunk (&bc, si).unwrap ();
|
let chunk = loader::parse_chunk (&bc, &mut vm.si).unwrap ();
|
||||||
run_chunk (args, chunk)
|
run_chunk (vm, args, chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes arguments and Lua source code,
|
/// Takes arguments and Lua source code,
|
||||||
|
@ -41,9 +42,9 @@ fn run_bytecode (args: &[&str], bc: &[u8], si: &mut Interner) -> Vec <Value> {
|
||||||
/// runs it,
|
/// runs it,
|
||||||
/// and returns the output
|
/// and returns the output
|
||||||
|
|
||||||
fn run_source (args: &[&str], s: &str, si: &mut Interner) -> Vec <Value> {
|
fn run_source (vm: &mut State, args: &[&str], s: &str) -> Vec <Value> {
|
||||||
let bc = loader::compile_bytecode (s.as_bytes ().to_vec ()).unwrap ();
|
let bc = loader::compile_bytecode (s.as_bytes ().to_vec ()).unwrap ();
|
||||||
run_bytecode (args, &bc, si)
|
run_bytecode (vm, args, &bc)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -114,15 +115,15 @@ fn bools () {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
||||||
|
|
||||||
for (arg, expected) in [
|
for (arg, expected) in [
|
||||||
(vec! ["_exe_name"], vec! [98.into ()]),
|
(vec! ["_exe_name"], vec! [98.into ()]),
|
||||||
(vec! ["_exe_name", "asdf"], vec! [99.into ()]),
|
(vec! ["_exe_name", "asdf"], vec! [99.into ()]),
|
||||||
] {
|
] {
|
||||||
let expected: Vec <Value> = expected;
|
let expected: Vec <Value> = expected;
|
||||||
|
|
||||||
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
let actual = run_chunk (&mut vm, &arg, chunk.clone ());
|
||||||
let mut vm = State::new (chunk.clone (), upvalues);
|
|
||||||
let actual = vm.execute ().unwrap ();
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,9 +132,11 @@ fn bools () {
|
||||||
fn closure () {
|
fn closure () {
|
||||||
let source = include_bytes! ("../test_vectors/closure.lua");
|
let source = include_bytes! ("../test_vectors/closure.lua");
|
||||||
let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
|
let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
|
||||||
let chunk = crate::loader::parse_chunk (bytecode, &mut Interner::default ()).unwrap ();
|
let mut si = Interner::default ();
|
||||||
|
let chunk = crate::loader::parse_chunk (bytecode, &mut si).unwrap ();
|
||||||
|
|
||||||
assert_eq! (run_chunk (&["_exe_name"], chunk), vec! [Value::from (23i64)]);
|
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
||||||
|
assert_eq! (run_chunk (&mut vm, &["_exe_name"], chunk), vec! [Value::from (23i64)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -173,14 +176,14 @@ fn floats () {
|
||||||
blocks: vec! [block],
|
blocks: vec! [block],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
||||||
|
|
||||||
for (arg, expected) in [
|
for (arg, expected) in [
|
||||||
(vec! ["_exe_name"], vec! [3.5.into ()]),
|
(vec! ["_exe_name"], vec! [3.5.into ()]),
|
||||||
(vec! ["_exe_name", " "], vec! [3.5.into ()]),
|
(vec! ["_exe_name", " "], vec! [3.5.into ()]),
|
||||||
] {
|
] {
|
||||||
let expected: Vec <Value> = expected;
|
let expected: Vec <Value> = expected;
|
||||||
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
let actual = run_chunk (&mut vm, &arg, chunk.clone ());
|
||||||
let mut vm = State::new (chunk.clone (), upvalues);
|
|
||||||
let actual = vm.execute ().unwrap ();
|
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -189,15 +192,15 @@ fn floats () {
|
||||||
#[test]
|
#[test]
|
||||||
fn fma () {
|
fn fma () {
|
||||||
let source = include_bytes! ("../test_vectors/fma.lua");
|
let source = include_bytes! ("../test_vectors/fma.lua");
|
||||||
|
let mut si = Interner::default ();
|
||||||
let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
|
let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
|
||||||
let chunk = crate::loader::parse_chunk (bytecode, &mut Interner::default ()).unwrap ();
|
let chunk = crate::loader::parse_chunk (bytecode, &mut si).unwrap ();
|
||||||
assert_eq! (chunk.blocks.len (), 5);
|
assert_eq! (chunk.blocks.len (), 5);
|
||||||
|
|
||||||
assert_eq! (chunk.blocks [3].upvalues.len (), 2);
|
assert_eq! (chunk.blocks [3].upvalues.len (), 2);
|
||||||
|
|
||||||
let arg = vec! ["_exe_name"];
|
|
||||||
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
let mut vm = crate::State::new_with_args (chunk, si, vec! ["_exe_name".to_string ()].into_iter ());
|
||||||
let mut vm = State::new (chunk, upvalues);
|
|
||||||
let actual = vm.execute ().unwrap ();
|
let actual = vm.execute ().unwrap ();
|
||||||
let expected = vec! [Value::from (122)];
|
let expected = vec! [Value::from (122)];
|
||||||
|
|
||||||
|
@ -206,9 +209,8 @@ fn fma () {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_calls () {
|
fn function_calls () {
|
||||||
let upvalues = crate::State::upvalues_from_args (vec! ["_exe_name".to_string ()].into_iter ());
|
let si = Interner::default ();
|
||||||
|
let mut vm = crate::State::new_with_args (crate::Chunk::default (), si, vec! ["_exe_name".to_string ()].into_iter ());
|
||||||
let mut vm = crate::State::new (crate::Chunk::default (), upvalues);
|
|
||||||
|
|
||||||
vm.eval ("print (x ())").ok ();
|
vm.eval ("print (x ())").ok ();
|
||||||
vm.eval ("x = function () return 5 end").ok ();
|
vm.eval ("x = function () return 5 end").ok ();
|
||||||
|
@ -220,9 +222,8 @@ fn function_calls () {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_returns () {
|
fn function_returns () {
|
||||||
let upvalues = crate::State::upvalues_from_args (vec! ["_exe_name".to_string ()].into_iter ());
|
let si = Interner::default ();
|
||||||
|
let mut vm = crate::State::new_with_args (crate::Chunk::default (), si, vec! ["_exe_name".to_string ()].into_iter ());
|
||||||
let mut vm = crate::State::new (crate::Chunk::default (), upvalues);
|
|
||||||
|
|
||||||
assert_eq! (
|
assert_eq! (
|
||||||
vm.eval ("return ((function () return 5 end)())").unwrap (),
|
vm.eval ("return ((function () return 5 end)())").unwrap (),
|
||||||
|
@ -316,15 +317,17 @@ fn is_93 () {
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
|
let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
|
||||||
let chunk = loader::parse_chunk (&bc, &mut Interner::default ()).unwrap ();
|
let chunk = loader::parse_chunk (&bc, &mut si).unwrap ();
|
||||||
|
|
||||||
assert_eq! (chunk.blocks [0].instructions [3], Inst::EqK (0, 1, false));
|
assert_eq! (chunk.blocks [0].instructions [3], Inst::EqK (0, 1, false));
|
||||||
|
|
||||||
|
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
||||||
|
|
||||||
let run = run_chunk;
|
let run = run_chunk;
|
||||||
|
|
||||||
assert_eq! (run (&[""], chunk.clone ()), vec! [Value::from (1)]);
|
assert_eq! (run (&mut vm, &[""], chunk.clone ()), vec! [Value::from (1)]);
|
||||||
assert_eq! (run (&["", "93"], chunk.clone ()), vec! [Value::from (0)]);
|
assert_eq! (run (&mut vm, &["", "93"], chunk.clone ()), vec! [Value::from (0)]);
|
||||||
assert_eq! (run (&["", "94"], chunk.clone ()), vec! [Value::from (1)]);
|
assert_eq! (run (&mut vm, &["", "94"], chunk.clone ()), vec! [Value::from (1)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -377,7 +380,9 @@ fn tables_1 () {
|
||||||
print (t ["t"][1])
|
print (t ["t"][1])
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
run_source (&[], src, &mut Interner::default ());
|
let si = Interner::default ();
|
||||||
|
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
||||||
|
run_source (&mut vm, &[], src);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -392,23 +397,29 @@ fn tables_2 () {
|
||||||
print (a [2])
|
print (a [2])
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
run_source (&[], src, &mut Interner::default ());
|
let si = Interner::default ();
|
||||||
|
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
||||||
|
run_source (&mut vm, &[], src);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tailcall () {
|
fn tailcall () {
|
||||||
use crate::instruction::Instruction;
|
use crate::instruction::Instruction;
|
||||||
|
|
||||||
|
let mut si = Interner::default ();
|
||||||
|
|
||||||
let src = br#"
|
let src = br#"
|
||||||
return tonumber ("5")
|
return tonumber ("5")
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
|
let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
|
||||||
let chunk = loader::parse_chunk (&bc, &mut Interner::default ()).unwrap ();
|
let chunk = loader::parse_chunk (&bc, &mut si).unwrap ();
|
||||||
|
|
||||||
assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false));
|
assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false));
|
||||||
|
|
||||||
let actual = run_chunk (&[], chunk);
|
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
||||||
|
|
||||||
|
let actual = run_chunk (&mut vm, &[], chunk);
|
||||||
let expected = vec! [Value::from (5)];
|
let expected = vec! [Value::from (5)];
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
|
|
|
@ -259,20 +259,21 @@ impl fmt::Debug for Table {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Table {
|
impl Table {
|
||||||
fn get_inner (&self, key: &Value) -> Value {
|
fn get_inner (&self, key: &Value) -> &Value {
|
||||||
match key {
|
match key {
|
||||||
Value::Nil => Value::Nil,
|
Value::Nil => &NIL,
|
||||||
Value::String (x) => self.map.get (x).cloned ().unwrap_or_default (),
|
Value::String (x) => self.map.get (x).unwrap_or (&NIL),
|
||||||
x => self.hash.get (x).cloned ().unwrap_or_default (),
|
Value::Integer (x) => self.array.get (usize::try_from (*x).unwrap ()).unwrap_or (&NIL),
|
||||||
|
x => self.hash.get (x).unwrap_or (&NIL),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get <A: Into <Value>> (&self, key: A) -> Value {
|
pub fn get <A: Into <Value>> (&self, key: A) -> &Value {
|
||||||
self.get_inner (&(key.into ()))
|
self.get_inner (&(key.into ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_int (&self, key: i64) -> Value {
|
pub fn get_int (&self, key: i64) -> &Value {
|
||||||
self.get_inner (&(key.into ()))
|
self.array.get (usize::try_from (key).unwrap ()).unwrap_or (&NIL)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_str (&self, key: InternedString) -> &Value {
|
pub fn get_str (&self, key: InternedString) -> &Value {
|
||||||
|
@ -290,6 +291,7 @@ impl Table {
|
||||||
b: B,
|
b: B,
|
||||||
) {
|
) {
|
||||||
match a.into () {
|
match a.into () {
|
||||||
|
Value::Integer (x) => self.insert_int (x, b),
|
||||||
Value::Nil => (),
|
Value::Nil => (),
|
||||||
Value::String (x) => {
|
Value::String (x) => {
|
||||||
self.map.insert (x, b.into ());
|
self.map.insert (x, b.into ());
|
||||||
|
@ -304,7 +306,9 @@ impl Table {
|
||||||
|
|
||||||
pub fn insert_int <A: Into <Value>> (&mut self, k: i64, v: A)
|
pub fn insert_int <A: Into <Value>> (&mut self, k: i64, v: A)
|
||||||
{
|
{
|
||||||
self.insert (Value::from (k), v.into ())
|
let k = usize::try_from (k).unwrap ();
|
||||||
|
self.array.resize (k + 1, NIL);
|
||||||
|
self.array [k] = v.into ();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_str (&mut self, key: InternedString, v: Value) {
|
pub fn insert_str (&mut self, key: InternedString, v: Value) {
|
||||||
|
@ -313,7 +317,7 @@ impl Table {
|
||||||
|
|
||||||
pub fn length (&self) -> i64 {
|
pub fn length (&self) -> i64 {
|
||||||
for i in 1..i64::MAX {
|
for i in 1..i64::MAX {
|
||||||
if self.get (i) == Value::Nil {
|
if self.get (i) == &NIL {
|
||||||
return i - 1;
|
return i - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,16 +363,16 @@ mod tests {
|
||||||
|
|
||||||
let mut t = Table::default ();
|
let mut t = Table::default ();
|
||||||
|
|
||||||
assert_eq! (t.get (Value::from_str (&mut si, "a")), nil);
|
assert_eq! (t.get (si.intern ("a")), &nil);
|
||||||
assert_eq! (t.get (Value::from_str (&mut si, "b")), nil);
|
assert_eq! (t.get (si.intern ("b")), &nil);
|
||||||
|
|
||||||
t.insert (si.to_value ("a"), 1993);
|
t.insert (si.to_value ("a"), 1993);
|
||||||
t.insert (si.to_value ("b"), 2007);
|
t.insert (si.to_value ("b"), 2007);
|
||||||
|
|
||||||
assert_eq! (t.get (Value::from_str (&mut si, "a")), 1993);
|
assert_eq! (t.get (si.intern ("a")), &Value::from (1993));
|
||||||
assert_eq! (t.get (Value::from_str (&mut si, "b")), 2007);
|
assert_eq! (t.get (si.intern ("b")), &Value::from (2007));
|
||||||
|
|
||||||
t.insert (19, 93);
|
t.insert (19, 93);
|
||||||
assert_eq! (t.get (19), 93);
|
assert_eq! (t.get (19), &Value::from (93));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,16 +19,18 @@ fn embedding () {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut si = lwvm::Interner::default ();
|
||||||
|
|
||||||
let bc = lwvm::compile_bytecode (src.to_vec ()).unwrap ();
|
let bc = lwvm::compile_bytecode (src.to_vec ()).unwrap ();
|
||||||
let chunk = lwvm::parse_chunk (&bc).unwrap ();
|
let chunk = lwvm::parse_chunk (&bc, &mut si).unwrap ();
|
||||||
|
|
||||||
let host_lib = [
|
let host_lib = [
|
||||||
("add", Value::RsFunc (host_add)),
|
("add", Value::RsFunc (host_add)),
|
||||||
].into_iter ().map (|(k, v)| (k.to_string (), v));
|
].into_iter ().map (|(k, v)| (si.intern (k), v));
|
||||||
|
|
||||||
let env = [
|
let env = [
|
||||||
("host_lib", Value::from_iter (host_lib.into_iter ())),
|
("host_lib", Value::from_iter (host_lib.into_iter ())),
|
||||||
].into_iter ().map (|(k, v)| (k.to_string (), v));
|
].into_iter ().map (|(k, v)| (si.intern (k), v));
|
||||||
|
|
||||||
let upvalues = vec! [
|
let upvalues = vec! [
|
||||||
Value::from_iter (env.into_iter ()),
|
Value::from_iter (env.into_iter ()),
|
||||||
|
|
Loading…
Reference in New Issue