♻️ refactor: make register access easier to read
parent
2bd1ff3d59
commit
5466e6ec56
|
@ -8,6 +8,8 @@ use crate::state::{
|
||||||
|
|
||||||
/// Invoke `luac` as a subprocess
|
/// Invoke `luac` as a subprocess
|
||||||
/// Luckily luac is single-pass, so we can just pipe in and out
|
/// Luckily luac is single-pass, so we can just pipe in and out
|
||||||
|
///
|
||||||
|
/// `source` is a Vec because we move it to a worker thread
|
||||||
|
|
||||||
pub (crate) fn compile_bytecode (source: Vec <u8>) -> Vec <u8> {
|
pub (crate) fn compile_bytecode (source: Vec <u8>) -> Vec <u8> {
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -51,8 +53,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
|
||||||
(((buf [1] >> 7) as u32) << 0) |
|
(((buf [1] >> 7) as u32) << 0) |
|
||||||
((buf [2] as u32) << 1) |
|
((buf [2] as u32) << 1) |
|
||||||
((buf [3] as u32) << 9);
|
((buf [3] as u32) << 9);
|
||||||
let bx = bx.try_into().ok ()?;
|
let sbx = i32::try_from (bx).ok ()? - 65535;
|
||||||
let sbx = bx - 65535;
|
|
||||||
let k = (buf [1] & 0x80) >> 7 == 1;
|
let k = (buf [1] & 0x80) >> 7 == 1;
|
||||||
let s_j = a as i32 + ((b as i32) << 8) + 1;
|
let s_j = a as i32 + ((b as i32) << 8) + 1;
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
|
||||||
0x24 => Inst::Mul (a, b, c),
|
0x24 => Inst::Mul (a, b, c),
|
||||||
0x2e => Inst::MmBin (a, b, c),
|
0x2e => Inst::MmBin (a, b, c),
|
||||||
0x33 => Inst::Not (a, b),
|
0x33 => Inst::Not (a, b),
|
||||||
0x3c => Inst::EqK (a, b, c),
|
0x3c => Inst::EqK (a, b, k),
|
||||||
0x38 => Inst::Jmp (s_j),
|
0x38 => Inst::Jmp (s_j),
|
||||||
0x42 => Inst::Test (a, k),
|
0x42 => Inst::Test (a, k),
|
||||||
0x44 => Inst::Call (a, b, c),
|
0x44 => Inst::Call (a, b, c),
|
||||||
|
@ -230,6 +231,11 @@ pub fn parse_chunk <R: Read> (rdr: &mut R) -> Option <Chunk> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_chunk_from_bytes (b: &[u8]) -> Option <Chunk> {
|
||||||
|
let mut rdr = std::io::Cursor::new (b);
|
||||||
|
parse_chunk (&mut rdr)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg (test)]
|
#[cfg (test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -264,7 +270,7 @@ mod tests {
|
||||||
([0x48, 0x00, 0x02, 0x00], Inst::Return1 (0)),
|
([0x48, 0x00, 0x02, 0x00], Inst::Return1 (0)),
|
||||||
([0x47, 0x00, 0x01, 0x00], Inst::Return0),
|
([0x47, 0x00, 0x01, 0x00], Inst::Return0),
|
||||||
([0x8d, 0x00, 0x01, 0x01], Inst::GetI (1, 1, 1)),
|
([0x8d, 0x00, 0x01, 0x01], Inst::GetI (1, 1, 1)),
|
||||||
([0xbc, 0x00, 0x01, 0x00], Inst::EqK (1, 1, 0)),
|
([0xbc, 0x00, 0x01, 0x00], Inst::EqK (1, 1, false)),
|
||||||
([0xb8, 0x02, 0x00, 0x80], Inst::Jmp (6)),
|
([0xb8, 0x02, 0x00, 0x80], Inst::Jmp (6)),
|
||||||
([0x38, 0x02, 0x00, 0x80], Inst::Jmp (5)),
|
([0x38, 0x02, 0x00, 0x80], Inst::Jmp (5)),
|
||||||
([0x52, 0x00, 0x00, 0x00], Inst::ExtraArg (0)),
|
([0x52, 0x00, 0x00, 0x00], Inst::ExtraArg (0)),
|
||||||
|
|
135
src/state.rs
135
src/state.rs
|
@ -11,10 +11,10 @@ pub enum Instruction {
|
||||||
Add (u8, u8, u8),
|
Add (u8, u8, u8),
|
||||||
|
|
||||||
Call (u8, u8, u8),
|
Call (u8, u8, u8),
|
||||||
Closure (u8, i32),
|
Closure (u8, u32),
|
||||||
|
|
||||||
// Equals Constant?
|
// Equals Constant?
|
||||||
EqK (u8, u8, u8),
|
EqK (u8, u8, bool),
|
||||||
|
|
||||||
ExtraArg (u32),
|
ExtraArg (u32),
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ pub enum Instruction {
|
||||||
LoadI (u8, i32),
|
LoadI (u8, i32),
|
||||||
|
|
||||||
// Load Constant
|
// Load Constant
|
||||||
LoadK (u8, i32),
|
LoadK (u8, u32),
|
||||||
|
|
||||||
LoadNil (u8),
|
LoadNil (u8),
|
||||||
|
|
||||||
|
@ -150,10 +150,22 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_window_mut (&mut self) -> &mut [Value] {
|
fn register_window_mut (&mut self) -> &mut [Value] {
|
||||||
let frame = self.stack.last_mut ().unwrap ();
|
let frame = self.stack.last ().unwrap ();
|
||||||
&mut self.registers [frame.register_offset..]
|
&mut self.registers [frame.register_offset..]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Short form to get access to a register within our window
|
||||||
|
|
||||||
|
fn reg (&self, i: u8) -> &Value {
|
||||||
|
let frame = self.stack.last ().unwrap ();
|
||||||
|
&self.registers [frame.register_offset + i as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_mut (&mut self, i: u8) -> &mut Value {
|
||||||
|
let frame = self.stack.last ().unwrap ();
|
||||||
|
&mut self.registers [frame.register_offset + i as usize]
|
||||||
|
}
|
||||||
|
|
||||||
pub fn execute_chunk (&mut self, chunk: &Chunk, upvalues: &[Value])
|
pub fn execute_chunk (&mut self, chunk: &Chunk, upvalues: &[Value])
|
||||||
-> Vec <Value> {
|
-> Vec <Value> {
|
||||||
let max_iters = 2000;
|
let max_iters = 2000;
|
||||||
|
@ -187,16 +199,10 @@ impl State {
|
||||||
|
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::Add (a, b, c) => {
|
Instruction::Add (a, b, c) => {
|
||||||
let r = self.register_window_mut ();
|
let v_b = self.reg (*b).as_float ().unwrap ();
|
||||||
|
let v_c = self.reg (*c).as_float ().unwrap ();
|
||||||
|
|
||||||
let a = usize::try_from (*a).unwrap ();
|
*self.reg_mut (*a) = Value::from (v_b + v_c);
|
||||||
let b = usize::try_from (*b).unwrap ();
|
|
||||||
let c = usize::try_from (*c).unwrap ();
|
|
||||||
|
|
||||||
let v_b = r [b].as_float ().unwrap ();
|
|
||||||
let v_c = r [c].as_float ().unwrap ();
|
|
||||||
|
|
||||||
r [a] = (v_b + v_c).into ();
|
|
||||||
},
|
},
|
||||||
Instruction::Call (a, _b, c) => {
|
Instruction::Call (a, _b, c) => {
|
||||||
// Take arguments from registers [a + 1, a + b)
|
// Take arguments from registers [a + 1, a + b)
|
||||||
|
@ -209,9 +215,7 @@ impl State {
|
||||||
|
|
||||||
// TODO: Only implement printing values for now
|
// TODO: Only implement printing values for now
|
||||||
|
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let v_a = self.reg (*a);
|
||||||
let r = self.register_window_mut ();
|
|
||||||
let v_a = r.get (a).unwrap ();
|
|
||||||
|
|
||||||
match v_a {
|
match v_a {
|
||||||
Value::BogusClosure (rc) => {
|
Value::BogusClosure (rc) => {
|
||||||
|
@ -225,7 +229,7 @@ impl State {
|
||||||
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 + 1,
|
register_offset: current_frame.register_offset + *a as usize + 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.debug_print {
|
if self.debug_print {
|
||||||
|
@ -244,10 +248,10 @@ impl State {
|
||||||
// assert_eq! (*b, 2);
|
// assert_eq! (*b, 2);
|
||||||
assert_eq! (*c, 1);
|
assert_eq! (*c, 1);
|
||||||
|
|
||||||
let value = r.get (a + 1).unwrap ();
|
let value = self.reg (a + 1);
|
||||||
println! ("{}", value);
|
println! ("{}", value);
|
||||||
|
|
||||||
r [a] = r [a + 1].take ();
|
*self.reg_mut (*a) = self.reg_mut (*a + 1).take ();
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let stack = &self.stack;
|
let stack = &self.stack;
|
||||||
|
@ -256,30 +260,18 @@ impl State {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::Closure (a, b) => {
|
Instruction::Closure (a, b) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
|
||||||
let b = usize::try_from (*b).unwrap ();
|
let b = usize::try_from (*b).unwrap ();
|
||||||
|
|
||||||
if self.debug_print {
|
*self.reg_mut (*a) = Value::BogusClosure (BogusClosure {
|
||||||
println! ("OP_CLOSURE {a} {b}");
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = self.register_window_mut ();
|
|
||||||
r [a] = Value::BogusClosure (BogusClosure {
|
|
||||||
idx: b + frame.block_idx + 1,
|
idx: b + frame.block_idx + 1,
|
||||||
upvalues: vec! [],
|
upvalues: vec! [],
|
||||||
}.into ());
|
}.into ());
|
||||||
},
|
},
|
||||||
Instruction::EqK (a, b, c_k) => {
|
Instruction::EqK (a, b, k_flag) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let b = usize::from (*b);
|
||||||
let b = usize::try_from (*b).unwrap ();
|
|
||||||
let r = self.register_window ();
|
|
||||||
|
|
||||||
let equal = r [a] == k [b];
|
if (*self.reg (*a) == k [b]) != *k_flag {
|
||||||
|
next_pc += 1;
|
||||||
match (equal, c_k) {
|
|
||||||
(true, 0) => next_pc += 1,
|
|
||||||
(false, 1) => next_pc += 1,
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::ExtraArg (ax) => {
|
Instruction::ExtraArg (ax) => {
|
||||||
|
@ -289,7 +281,6 @@ impl State {
|
||||||
assert_eq! (*ax, 0, "implemented only for ax == 0");
|
assert_eq! (*ax, 0, "implemented only for ax == 0");
|
||||||
},
|
},
|
||||||
Instruction::GetTabUp (a, b, c) => {
|
Instruction::GetTabUp (a, b, c) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
|
||||||
let b = usize::try_from (*b).unwrap ();
|
let b = usize::try_from (*b).unwrap ();
|
||||||
let c = usize::try_from (*c).unwrap ();
|
let c = usize::try_from (*c).unwrap ();
|
||||||
|
|
||||||
|
@ -303,26 +294,19 @@ impl State {
|
||||||
_ => panic! ("GetTabUp only supports string keys"),
|
_ => panic! ("GetTabUp only supports string keys"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let value = env.get (key).unwrap ();
|
let value = env.get (key).unwrap ();
|
||||||
|
*self.reg_mut (*a) = value.clone ();
|
||||||
|
|
||||||
self.register_window_mut() [a] = value.clone ();
|
|
||||||
},
|
},
|
||||||
Instruction::GetI (a, b, c) => {
|
Instruction::GetI (a, b, c) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
|
||||||
let b = usize::try_from (*b).unwrap ();
|
|
||||||
let c = usize::try_from (*c).unwrap ();
|
let c = usize::try_from (*c).unwrap ();
|
||||||
|
|
||||||
let r = self.register_window_mut ();
|
let table = self.reg (*b);
|
||||||
|
|
||||||
let table = r.get (b).unwrap ();
|
|
||||||
let value = match table {
|
let value = match table {
|
||||||
Value::BogusArg (arg) => arg.get (c).map (|x| x.as_str().into ()).unwrap_or_default(),
|
Value::BogusArg (arg) => arg.get (c).map (|x| x.as_str().into ()).unwrap_or_default(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
r [a] = value;
|
*self.reg_mut (*a) = value;
|
||||||
},
|
},
|
||||||
Instruction::GetUpVal (a, b) => {
|
Instruction::GetUpVal (a, b) => {
|
||||||
let this_func = self.stack.last ().unwrap ().register_offset - 1;
|
let this_func = self.stack.last ().unwrap ().register_offset - 1;
|
||||||
|
@ -331,46 +315,34 @@ impl State {
|
||||||
_ => panic! ("Can't do GetUpVal outside a closure"),
|
_ => panic! ("Can't do GetUpVal outside a closure"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let a = usize::try_from (*a).unwrap ();
|
|
||||||
let b = usize::try_from (*b).unwrap ();
|
let b = usize::try_from (*b).unwrap ();
|
||||||
|
|
||||||
self.register_window_mut ()[a] = upvalues [b].clone ();
|
*self.reg_mut (*a) = upvalues [b].clone ();
|
||||||
},
|
},
|
||||||
Instruction::Jmp (s_j) => next_pc += s_j,
|
Instruction::Jmp (s_j) => next_pc += s_j,
|
||||||
Instruction::LoadF (a, sbx) => {
|
Instruction::LoadF (a, sbx) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
*self.reg_mut (*a) = Value::Float (*sbx as f64);
|
||||||
self.register_window_mut ()[a] = Value::Float (*sbx as f64);
|
|
||||||
}
|
}
|
||||||
Instruction::LoadFalse (a) => {
|
Instruction::LoadFalse (a) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
*self.reg_mut (*a) = false.into ();
|
||||||
self.register_window_mut ()[a] = Value::Boolean (false);
|
|
||||||
},
|
},
|
||||||
Instruction::LoadI (a, sbx) => {
|
Instruction::LoadI (a, sbx) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
*self.reg_mut (*a) = Value::Integer (*sbx as i64);
|
||||||
|
|
||||||
self.register_window_mut ()[a] = Value::Integer (*sbx as i64);
|
|
||||||
},
|
},
|
||||||
Instruction::LoadK (a, bx) => {
|
Instruction::LoadK (a, bx) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
|
||||||
let bx = usize::try_from (*bx).unwrap ();
|
let bx = usize::try_from (*bx).unwrap ();
|
||||||
|
|
||||||
self.register_window_mut ()[a] = k [bx].clone ();
|
*self.reg_mut (*a) = k [bx].clone ();
|
||||||
},
|
},
|
||||||
Instruction::LoadNil (a) => {
|
Instruction::LoadNil (a) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
*self.reg_mut (*a) = Value::Nil;
|
||||||
self.register_window_mut ()[a] = Value::Nil;
|
|
||||||
},
|
},
|
||||||
Instruction::LoadTrue (a) => {
|
Instruction::LoadTrue (a) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
*self.reg_mut (*a) = true.into ();
|
||||||
self.register_window_mut ()[a] = Value::Boolean (true);
|
|
||||||
},
|
},
|
||||||
Instruction::MmBin (a, b, _c) => {
|
Instruction::MmBin (a, b, _c) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let a = self.reg (*a);
|
||||||
let b = usize::try_from (*b).unwrap ();
|
let b = self.reg (*b);
|
||||||
|
|
||||||
let r = self.register_window();
|
|
||||||
let a = &r [a];
|
|
||||||
let b = &r [b];
|
|
||||||
|
|
||||||
if a.as_float().is_some() && b.as_float().is_some () {
|
if a.as_float().is_some() && b.as_float().is_some () {
|
||||||
// No need for metamethods
|
// No need for metamethods
|
||||||
|
@ -380,27 +352,18 @@ impl State {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::Move (a, b) => {
|
Instruction::Move (a, b) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
*self.reg_mut (*a) = self.reg (*b).clone ();
|
||||||
let b = usize::try_from (*b).unwrap ();
|
|
||||||
|
|
||||||
let r = self.register_window_mut();
|
|
||||||
r [a] = r [b].clone ();
|
|
||||||
},
|
},
|
||||||
Instruction::Mul (_a, _b, _c) => unimplemented!(),
|
Instruction::Mul (_a, _b, _c) => unimplemented!(),
|
||||||
Instruction::NewTable (a) => {
|
Instruction::NewTable (a) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
*self.reg_mut (*a) = Value::Table (Default::default ());
|
||||||
self.register_window_mut ()[a] = Value::Table (Default::default ());
|
|
||||||
},
|
},
|
||||||
Instruction::Not (a, b) => {
|
Instruction::Not (a, b) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
*self.reg_mut (*a) = Value::Boolean (! self.reg (*b).is_truthy());
|
||||||
let b = usize::try_from (*b).unwrap ();
|
|
||||||
let r = self.register_window_mut();
|
|
||||||
r [a] = Value::Boolean (! r [b].is_truthy());
|
|
||||||
}
|
}
|
||||||
Instruction::Return (a, b, c, k) => {
|
Instruction::Return (a, b, _c, k) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let a = usize::try_from (*a).unwrap ();
|
||||||
let b = usize::try_from (*b).unwrap ();
|
let b = usize::try_from (*b).unwrap ();
|
||||||
let _c = usize::try_from (*c).unwrap ();
|
|
||||||
|
|
||||||
let popped_frame = self.stack.pop ().unwrap ();
|
let popped_frame = self.stack.pop ().unwrap ();
|
||||||
|
|
||||||
|
@ -479,15 +442,7 @@ impl State {
|
||||||
Instruction::SetTabUp (_a, _b, _c) => unimplemented! (),
|
Instruction::SetTabUp (_a, _b, _c) => unimplemented! (),
|
||||||
Instruction::TailCall (_a, _b, _c, _k) => unimplemented! (),
|
Instruction::TailCall (_a, _b, _c, _k) => unimplemented! (),
|
||||||
Instruction::Test (a, _k) => {
|
Instruction::Test (a, _k) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
if self.reg (*a).is_truthy() {
|
||||||
|
|
||||||
let a = self.register_window ().get (a).unwrap ();
|
|
||||||
|
|
||||||
if self.debug_print {
|
|
||||||
println! ("Test {a:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.is_truthy() {
|
|
||||||
next_pc += 1;
|
next_pc += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
78
src/tests.rs
78
src/tests.rs
|
@ -1,4 +1,7 @@
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
loader,
|
||||||
state::{
|
state::{
|
||||||
Block,
|
Block,
|
||||||
Chunk,
|
Chunk,
|
||||||
|
@ -8,6 +11,41 @@ use crate::{
|
||||||
value::Value,
|
value::Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
||||||
|
use std::hash::Hasher;
|
||||||
|
|
||||||
|
let mut s = std::collections::hash_map::DefaultHasher::new ();
|
||||||
|
t.hash(&mut s);
|
||||||
|
s.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes arguments and a parsed Lua chunk, runs its,
|
||||||
|
/// and returns the output
|
||||||
|
|
||||||
|
fn run_chunk (args: &[&str], chunk: &Chunk) -> Vec <Value> {
|
||||||
|
let mut vm = State::default ();
|
||||||
|
let upvalues = State::upvalues_from_args (args.into_iter ().map (|s| s.to_string ()));
|
||||||
|
vm.execute_chunk (chunk, &upvalues)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes arguments and Lua bytecode, loads it, runs it,
|
||||||
|
/// and return the output
|
||||||
|
|
||||||
|
fn run_bytecode (args: &[&str], bc: &[u8]) -> Vec <Value> {
|
||||||
|
let chunk = loader::parse_chunk_from_bytes (&bc).unwrap ();
|
||||||
|
run_chunk (args, &chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes arguments and Lua source code,
|
||||||
|
/// invokes `luac` to compile it to bytecode,
|
||||||
|
/// runs it,
|
||||||
|
/// and returns the output
|
||||||
|
|
||||||
|
fn run_source (args: &[&str], s: &str) -> Vec <Value> {
|
||||||
|
let bc = loader::compile_bytecode (s.as_bytes ().to_vec ());
|
||||||
|
run_bytecode (args, &bc)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bools () {
|
fn bools () {
|
||||||
/*
|
/*
|
||||||
|
@ -178,27 +216,31 @@ fn fma () {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_93 () {
|
fn is_93 () {
|
||||||
let source = include_bytes! ("../test_vectors/is_93.lua");
|
assert_eq! (Value::from ("93"), Value::from ("93"));
|
||||||
assert_eq! (&blake3::hash (source).to_hex (), "a058869ed5142cbc8ccb2372991ef8b37657af38dc3f5e34814a7274b14d1e52");
|
assert_ne! (Value::from ("94"), Value::from ("93"));
|
||||||
|
assert_ne! (calculate_hash (&Value::from ("94")), calculate_hash (&Value::from ("93")));
|
||||||
|
assert_ne! (Value::Nil, Value::from ("93"));
|
||||||
|
|
||||||
let bytecode = crate::loader::compile_bytecode (source.to_vec ());
|
let src = r#"
|
||||||
assert_eq! (&blake3::hash (&bytecode).to_hex (), "b59ce3e054500beb109c247e784cc4a0ff09ac42f8086dc5cdbf711bce1ba071");
|
if arg [1] == "93" then
|
||||||
|
print "it's 93"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
print "it's not 93"
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
"#;
|
||||||
|
|
||||||
let mut rdr = std::io::Cursor::new (bytecode);
|
let bc = loader::compile_bytecode (src.as_bytes ().to_vec ());
|
||||||
let file = crate::loader::parse_chunk (&mut rdr).unwrap ();
|
let chunk = loader::parse_chunk_from_bytes (&bc).unwrap ();
|
||||||
|
|
||||||
for (arg, expected) in [
|
assert_eq! (chunk.blocks [0].instructions [3], Inst::EqK (0, 1, false));
|
||||||
(vec! ["_exe_name"], vec! [1.into ()]),
|
|
||||||
(vec! ["_exe_name", "93"], vec! [0.into ()]),
|
let run = run_chunk;
|
||||||
(vec! ["_exe_name", "94"], vec! [1.into ()]),
|
|
||||||
] {
|
assert_eq! (run (&[""], &chunk), vec! [Value::from (1)]);
|
||||||
let expected: Vec <Value> = expected;
|
assert_eq! (run (&["", "93"], &chunk), vec! [Value::from (0)]);
|
||||||
let mut vm = State::default ();
|
assert_eq! (run (&["", "94"], &chunk), vec! [Value::from (1)]);
|
||||||
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
|
||||||
let actual = vm.execute_chunk (&file, &upvalues);
|
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -61,6 +61,12 @@ impl fmt::Display for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From <bool> for Value {
|
||||||
|
fn from (x: bool) -> Self {
|
||||||
|
Self::Boolean (x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From <String> for Value {
|
impl From <String> for Value {
|
||||||
fn from (x: String) -> Self {
|
fn from (x: String) -> Self {
|
||||||
Self::String (x.into ())
|
Self::String (x.into ())
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
local function unused_fn ()
|
|
||||||
print "unused"
|
|
||||||
end
|
|
||||||
|
|
||||||
if arg [1] == "93" then
|
|
||||||
print "it's 93"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
print "it's not 93"
|
|
||||||
return 1
|
|
||||||
end
|
|
Loading…
Reference in New Issue