Compare commits
10 Commits
b639d02027
...
57486f7a65
Author | SHA1 | Date |
---|---|---|
_ | 57486f7a65 | |
_ | 2a0e02aec4 | |
_ | 24d576879b | |
_ | 35e62027e6 | |
_ | e499d27dfc | |
_ | a460b5a932 | |
_ | a9e14d0f47 | |
_ | f0d4f25cec | |
_ | 4da634a2aa | |
_ | 05b1d6e1f7 |
|
@ -251,19 +251,25 @@ pub fn parse_block <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>)
|
||||||
}
|
}
|
||||||
|
|
||||||
let upvalue_count = parse_int (rdr).unwrap () as usize;
|
let upvalue_count = parse_int (rdr).unwrap () as usize;
|
||||||
|
let mut upvalues = Vec::with_capacity (upvalue_count);
|
||||||
for _ in 0..upvalue_count {
|
for _ in 0..upvalue_count {
|
||||||
// Just ignore these
|
let in_stack = parse_byte (rdr).unwrap () == 1;
|
||||||
|
let idx = parse_byte (rdr).unwrap ();
|
||||||
|
let kind = parse_byte (rdr).unwrap ();
|
||||||
|
|
||||||
for _ in 0..3 {
|
let upvalue = crate::state::Upvalue {
|
||||||
parse_byte (rdr).unwrap ();
|
in_stack,
|
||||||
}
|
idx,
|
||||||
|
kind,
|
||||||
|
};
|
||||||
|
|
||||||
|
upvalues.push (upvalue);
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks.push (Block {
|
blocks.push (Block {
|
||||||
constants,
|
constants,
|
||||||
instructions,
|
instructions,
|
||||||
upvalue_count,
|
upvalues,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Recursion
|
// Recursion
|
||||||
|
|
74
src/main.rs
74
src/main.rs
|
@ -14,12 +14,24 @@ fn main () {
|
||||||
let mut list_bytecode = false;
|
let mut list_bytecode = false;
|
||||||
let mut pipe_bytecode = false;
|
let mut pipe_bytecode = false;
|
||||||
let mut script = None;
|
let mut script = None;
|
||||||
|
let mut breakpoints = vec![];
|
||||||
|
|
||||||
let mut args = std::env::args ();
|
let mut args = std::env::args ();
|
||||||
let exe_name = args.next ().unwrap ();
|
let exe_name = args.next ().unwrap ();
|
||||||
|
|
||||||
while let Some (arg) = args.next () {
|
while let Some (arg) = args.next () {
|
||||||
match arg.as_str () {
|
match arg.as_str () {
|
||||||
|
"--break" => {
|
||||||
|
let s = args.next ().unwrap ();
|
||||||
|
let (block_idx, program_counter) = s.split_once (":").unwrap ();
|
||||||
|
let block_idx = str::parse (block_idx).unwrap ();
|
||||||
|
let program_counter = str::parse (program_counter).unwrap ();
|
||||||
|
|
||||||
|
breakpoints.push (state::Breakpoint {
|
||||||
|
block_idx,
|
||||||
|
program_counter,
|
||||||
|
});
|
||||||
|
},
|
||||||
"--list-bytecode" => list_bytecode = true,
|
"--list-bytecode" => list_bytecode = true,
|
||||||
"--pipe-bytecode" => pipe_bytecode = true,
|
"--pipe-bytecode" => pipe_bytecode = true,
|
||||||
"--script" => script = Some (args.next ().unwrap ()),
|
"--script" => script = Some (args.next ().unwrap ()),
|
||||||
|
@ -45,17 +57,63 @@ fn main () {
|
||||||
dbg! (&chunk);
|
dbg! (&chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut vm = State::default ();
|
let upvalues = State::upvalues_from_args ([exe_name].into_iter ().chain (args));
|
||||||
if std::env::var("LUA_DEBUG").is_ok() {
|
|
||||||
|
let mut vm = State::new (&chunk, &upvalues);
|
||||||
|
if std::env::var("LWVM_DEBUG").is_ok() {
|
||||||
vm.debug_print = true;
|
vm.debug_print = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let upvalues = State::upvalues_from_args ([exe_name].into_iter ().chain (args));
|
let max_iters = 2000;
|
||||||
|
let mut in_break = false;
|
||||||
|
let mut last_input = String::new ();
|
||||||
|
|
||||||
vm.breakpoints.push (state::Breakpoint {
|
for _ in 0..max_iters {
|
||||||
block_idx: 3,
|
if in_break || breakpoints.iter ().any (|bp| vm.at_breakpoint (bp)) {
|
||||||
program_counter: 0,
|
in_break = true;
|
||||||
});
|
dbg! (&vm.stack);
|
||||||
|
|
||||||
vm.execute_chunk (&chunk, &upvalues);
|
let mut input = Default::default ();
|
||||||
|
std::io::stdin ().read_line (&mut input).unwrap ();
|
||||||
|
|
||||||
|
let input = if input == "" {
|
||||||
|
&last_input
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
last_input = input;
|
||||||
|
&last_input
|
||||||
|
};
|
||||||
|
|
||||||
|
match input.as_str ().trim_end () {
|
||||||
|
"c" => in_break = false,
|
||||||
|
"q" => return,
|
||||||
|
"registers" => {
|
||||||
|
dbg! (&vm.registers);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
"s" => {
|
||||||
|
match vm.step () {
|
||||||
|
None => (),
|
||||||
|
Some (state::StepOutput::ChunkReturned (x)) => {
|
||||||
|
dbg! (x);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
x => { dbg! (x); },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match vm.step () {
|
||||||
|
None => (),
|
||||||
|
Some (state::StepOutput::ChunkReturned (x)) => {
|
||||||
|
dbg! (x);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg! (vm);
|
||||||
|
panic! ("Hit max iterations before block returned");
|
||||||
}
|
}
|
||||||
|
|
238
src/state.rs
238
src/state.rs
|
@ -8,11 +8,18 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive (Debug)]
|
||||||
|
pub struct Upvalue {
|
||||||
|
pub in_stack: bool,
|
||||||
|
pub idx: u8,
|
||||||
|
pub kind: u8,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive (Debug)]
|
#[derive (Debug)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
pub instructions: Vec <Instruction>,
|
pub instructions: Vec <Instruction>,
|
||||||
pub constants: Vec <Value>,
|
pub constants: Vec <Value>,
|
||||||
pub upvalue_count: usize,
|
pub upvalues: Vec <Upvalue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Debug)]
|
#[derive (Debug)]
|
||||||
|
@ -21,7 +28,7 @@ pub struct Chunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Clone, Debug)]
|
#[derive (Clone, Debug)]
|
||||||
struct StackFrame {
|
pub struct StackFrame {
|
||||||
// i32 makes it a little easier to implement jumps
|
// i32 makes it a little easier to implement jumps
|
||||||
// Starts at 0 right after OP_CALL
|
// Starts at 0 right after OP_CALL
|
||||||
|
|
||||||
|
@ -40,34 +47,16 @@ pub struct Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Debug)]
|
#[derive (Debug)]
|
||||||
pub struct State {
|
pub struct State <'a> {
|
||||||
registers: Vec <Value>,
|
pub registers: Vec <Value>,
|
||||||
// Currently only used for native function calls
|
// Currently only used for native function calls
|
||||||
top: usize,
|
top: usize,
|
||||||
stack: Vec <StackFrame>,
|
pub stack: Vec <StackFrame>,
|
||||||
|
|
||||||
pub debug_print: bool,
|
pub debug_print: bool,
|
||||||
pub breakpoints: Vec <Breakpoint>,
|
|
||||||
step_count: u32,
|
step_count: u32,
|
||||||
}
|
chunk: &'a Chunk,
|
||||||
|
upvalues: &'a [Value],
|
||||||
impl Default for State {
|
|
||||||
fn default () -> Self {
|
|
||||||
Self {
|
|
||||||
registers: vec! [Value::Nil; 16],
|
|
||||||
top: 0,
|
|
||||||
stack: vec! [
|
|
||||||
StackFrame {
|
|
||||||
program_counter: 0,
|
|
||||||
block_idx: 0,
|
|
||||||
register_offset: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
debug_print: false,
|
|
||||||
breakpoints: Default::default(),
|
|
||||||
step_count: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lw_print (l: &mut State) -> i32 {
|
fn lw_print (l: &mut State) -> i32 {
|
||||||
|
@ -86,7 +75,34 @@ fn lw_print (l: &mut State) -> i32 {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
pub enum StepOutput {
|
||||||
|
ChunkReturned (Vec <Value>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'a> State <'a> {
|
||||||
|
pub fn new (chunk: &'a Chunk, upvalues: &'a [Value]) -> Self {
|
||||||
|
Self {
|
||||||
|
registers: vec! [Value::Nil; 16],
|
||||||
|
top: 0,
|
||||||
|
stack: vec! [
|
||||||
|
StackFrame {
|
||||||
|
program_counter: 0,
|
||||||
|
block_idx: 0,
|
||||||
|
register_offset: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
debug_print: false,
|
||||||
|
step_count: 0,
|
||||||
|
chunk,
|
||||||
|
upvalues,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn at_breakpoint (&self, bp: &Breakpoint) -> bool {
|
||||||
|
let frame = self.stack.last ().unwrap ();
|
||||||
|
frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter
|
||||||
|
}
|
||||||
|
|
||||||
pub fn upvalues_from_args <I: Iterator <Item = String>> (args: I) -> Vec <Value>
|
pub fn upvalues_from_args <I: Iterator <Item = String>> (args: I) -> Vec <Value>
|
||||||
{
|
{
|
||||||
let arg = args.map (|s| Value::from (s)).enumerate ();
|
let arg = args.map (|s| Value::from (s)).enumerate ();
|
||||||
|
@ -124,23 +140,13 @@ impl State {
|
||||||
self.top - self.stack.last ().unwrap ().register_offset
|
self.top - self.stack.last ().unwrap ().register_offset
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_chunk (&mut self, chunk: &Chunk, upvalues: &[Value])
|
pub fn step (&mut self) -> Option <StepOutput> {
|
||||||
-> Vec <Value> {
|
let chunk = self.chunk;
|
||||||
let max_iters = 2000;
|
|
||||||
|
|
||||||
for _ in 0..max_iters {
|
|
||||||
self.step_count += 1;
|
self.step_count += 1;
|
||||||
|
|
||||||
let frame = self.stack.last_mut ().unwrap ().clone ();
|
let frame = self.stack.last_mut ().unwrap ().clone ();
|
||||||
let block = chunk.blocks.get (frame.block_idx).unwrap ();
|
let block = chunk.blocks.get (frame.block_idx).unwrap ();
|
||||||
|
|
||||||
for bp in &self.breakpoints {
|
|
||||||
if frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter
|
|
||||||
{
|
|
||||||
dbg! (&self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
||||||
|
@ -160,15 +166,17 @@ impl State {
|
||||||
let v_b = self.reg (*b);
|
let v_b = self.reg (*b);
|
||||||
let v_c = self.reg (*c);
|
let v_c = self.reg (*c);
|
||||||
|
|
||||||
let sum = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ())
|
let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ())
|
||||||
{
|
{
|
||||||
Value::from (v_b + v_c)
|
Value::from (v_b + v_c)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Value::from (v_b.as_float ().unwrap () + v_c.as_float ().unwrap ())
|
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) = sum;
|
*self.reg_mut (*a) = x;
|
||||||
},
|
},
|
||||||
Instruction::Call (a, b, _c) => {
|
Instruction::Call (a, b, _c) => {
|
||||||
let b = usize::from (*b);
|
let b = usize::from (*b);
|
||||||
|
@ -188,7 +196,7 @@ impl State {
|
||||||
|
|
||||||
match v_a {
|
match v_a {
|
||||||
Value::BogusClosure (rc) => {
|
Value::BogusClosure (rc) => {
|
||||||
let idx = rc.idx;
|
let idx = rc.borrow ().idx;
|
||||||
|
|
||||||
let block_idx = frame.block_idx;
|
let block_idx = frame.block_idx;
|
||||||
let target_block = idx;
|
let target_block = idx;
|
||||||
|
@ -208,7 +216,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip the PC increment at the bottom of the loop
|
// Skip the PC increment at the bottom of the loop
|
||||||
continue;
|
return None;
|
||||||
},
|
},
|
||||||
Value::RsFunc (x) => {
|
Value::RsFunc (x) => {
|
||||||
let current_frame = self.stack.last ().unwrap ();
|
let current_frame = self.stack.last ().unwrap ();
|
||||||
|
@ -241,10 +249,25 @@ impl State {
|
||||||
Instruction::Closure (a, b) => {
|
Instruction::Closure (a, b) => {
|
||||||
let b = usize::try_from (*b).unwrap ();
|
let b = usize::try_from (*b).unwrap ();
|
||||||
|
|
||||||
*self.reg_mut (*a) = Value::BogusClosure (BogusClosure {
|
let idx = frame.block_idx + b + 1;
|
||||||
idx: b + frame.block_idx + 1,
|
let block = &chunk.blocks [idx];
|
||||||
upvalues: vec! [],
|
|
||||||
}.into ());
|
let mut new_upvalues = Vec::with_capacity (block.upvalues.len ());
|
||||||
|
for uv in &block.upvalues {
|
||||||
|
let val = if uv.in_stack {
|
||||||
|
self.reg (uv.idx).clone ()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO: This isn't really correct
|
||||||
|
self.upvalues [usize::from (uv.idx)].clone ()
|
||||||
|
};
|
||||||
|
new_upvalues.push (val);
|
||||||
|
}
|
||||||
|
|
||||||
|
*self.reg_mut (*a) = Value::from (BogusClosure {
|
||||||
|
idx,
|
||||||
|
upvalues: new_upvalues,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
Instruction::EqI (a, sb, k_flag) => {
|
Instruction::EqI (a, sb, k_flag) => {
|
||||||
if (self.reg (*a).as_int ().unwrap () == *sb as i64) != *k_flag
|
if (self.reg (*a).as_int ().unwrap () == *sb as i64) != *k_flag
|
||||||
|
@ -317,7 +340,24 @@ impl State {
|
||||||
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 ();
|
||||||
|
|
||||||
let table = upvalues.get (b).unwrap ().as_table ().expect ("GetTabUp only works on tables").borrow ();
|
// If we're inside a closure, use its upvalues
|
||||||
|
// instead of the chunk's upvalues
|
||||||
|
|
||||||
|
let frame = self.stack.last ().unwrap ();
|
||||||
|
let value = if frame.register_offset == 0 {
|
||||||
|
self.upvalues.get (b).unwrap ().clone ()
|
||||||
|
}
|
||||||
|
else if let Some (cell) = self.registers [frame.register_offset - 1].as_closure ()
|
||||||
|
{
|
||||||
|
let closure = cell.borrow ();
|
||||||
|
let value = closure.upvalues.get (b).unwrap ();
|
||||||
|
value.clone ()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.upvalues.get (b).unwrap ().clone ()
|
||||||
|
};
|
||||||
|
|
||||||
|
let table = value.as_table ().expect ("GetTabUp only works on tables").borrow ();
|
||||||
|
|
||||||
let key = match k.get (c).unwrap () {
|
let key = match k.get (c).unwrap () {
|
||||||
Value::String (s) => String::from (s.as_ref()),
|
Value::String (s) => String::from (s.as_ref()),
|
||||||
|
@ -338,14 +378,21 @@ impl State {
|
||||||
},
|
},
|
||||||
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;
|
||||||
let upvalues = match &self.registers [this_func] {
|
let closure = match &self.registers [this_func] {
|
||||||
Value::BogusClosure (rc) => &rc.upvalues,
|
Value::BogusClosure (rc) => rc.clone (),
|
||||||
_ => panic! ("Can't do GetUpVal outside a closure"),
|
_ => panic! ("Can't do GetUpVal outside a closure"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let b = usize::try_from (*b).unwrap ();
|
let b = usize::try_from (*b).unwrap ();
|
||||||
|
|
||||||
*self.reg_mut (*a) = upvalues [b].clone ();
|
let upvalue = match closure.borrow ().upvalues.get (b) {
|
||||||
|
Some (x) => x.clone (),
|
||||||
|
None => {
|
||||||
|
dbg! (chunk, &self);
|
||||||
|
panic! ("Missing upvalue");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*self.reg_mut (*a) = upvalue;
|
||||||
},
|
},
|
||||||
Instruction::Jmp (s_j) => next_pc += s_j,
|
Instruction::Jmp (s_j) => next_pc += s_j,
|
||||||
Instruction::Len (a, b) => {
|
Instruction::Len (a, b) => {
|
||||||
|
@ -397,9 +444,27 @@ impl State {
|
||||||
*self.reg_mut (*a) = (b % c).into ();
|
*self.reg_mut (*a) = (b % c).into ();
|
||||||
},
|
},
|
||||||
Instruction::Move (a, b) => {
|
Instruction::Move (a, b) => {
|
||||||
|
// If the value in b is deleted instead of duplicated,
|
||||||
|
// a bunch of tests fail
|
||||||
|
|
||||||
*self.reg_mut (*a) = self.reg (*b).clone ();
|
*self.reg_mut (*a) = self.reg (*b).clone ();
|
||||||
},
|
},
|
||||||
Instruction::Mul (_a, _b, _c) => unimplemented!(),
|
Instruction::Mul (a, b, c) => {
|
||||||
|
let v_b = self.reg (*b);
|
||||||
|
let v_c = self.reg (*c);
|
||||||
|
|
||||||
|
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::NewTable (a) => {
|
Instruction::NewTable (a) => {
|
||||||
*self.reg_mut (*a) = Value::Table (Default::default ());
|
*self.reg_mut (*a) = Value::Table (Default::default ());
|
||||||
},
|
},
|
||||||
|
@ -412,24 +477,25 @@ impl State {
|
||||||
|
|
||||||
let popped_frame = self.stack.pop ().unwrap ();
|
let popped_frame = self.stack.pop ().unwrap ();
|
||||||
|
|
||||||
// Build closure if needed
|
// Build closure if needed. No point building if we're
|
||||||
if *k {
|
// popping the last frame and exiting the program.
|
||||||
|
|
||||||
|
if *k && ! self.stack.is_empty () {
|
||||||
let closure_idx = match &self.registers [popped_frame.register_offset + a] {
|
let closure_idx = match &self.registers [popped_frame.register_offset + a] {
|
||||||
Value::BogusClosure (rc) => rc.idx,
|
Value::BogusClosure (rc) => rc.borrow ().idx,
|
||||||
_ => panic! ("Impossible"),
|
_ => panic! ("Impossible"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let upvalue_count = chunk.blocks [closure_idx].upvalue_count;
|
let upvalue_count = chunk.blocks [closure_idx].upvalues.len ();
|
||||||
|
|
||||||
let start_reg = a + popped_frame.register_offset - upvalue_count;
|
let start_reg = a + popped_frame.register_offset - upvalue_count;
|
||||||
|
|
||||||
let upvalues = self.registers [start_reg..start_reg+upvalue_count].iter ().cloned ().collect ();
|
let upvalues = self.registers [start_reg..start_reg+upvalue_count].iter ().cloned ().collect ();
|
||||||
|
|
||||||
self.registers [a + popped_frame.register_offset] = Value::BogusClosure (BogusClosure {
|
self.registers [a + popped_frame.register_offset] = Value::from (BogusClosure {
|
||||||
idx: closure_idx,
|
idx: closure_idx,
|
||||||
upvalues,
|
upvalues,
|
||||||
}.into ());
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.debug_print {
|
if self.debug_print {
|
||||||
|
@ -457,10 +523,13 @@ impl State {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Return from the entire program
|
// Return from the entire program
|
||||||
return self.registers [a..(a + b - 1)].to_vec();
|
return Some (StepOutput::ChunkReturned (self.registers [a..(a + b - 1)].to_vec()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::Return0 => unimplemented! (),
|
Instruction::Return0 => {
|
||||||
|
self.stack.pop ();
|
||||||
|
next_pc = self.stack.last ().unwrap ().program_counter;
|
||||||
|
},
|
||||||
Instruction::Return1 (a) => {
|
Instruction::Return1 (a) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let a = usize::try_from (*a).unwrap ();
|
||||||
let popped_frame = self.stack.pop ().unwrap ();
|
let popped_frame = self.stack.pop ().unwrap ();
|
||||||
|
@ -534,7 +603,36 @@ 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) => {
|
||||||
|
assert! (!k, "closing over values in tail calls not implemented");
|
||||||
|
|
||||||
|
// Shift closure and inputs into place
|
||||||
|
let a = usize::from (*a);
|
||||||
|
let b = usize::from (*b);
|
||||||
|
let offset = frame.register_offset - 1;
|
||||||
|
for i in (offset)..(offset + b) {
|
||||||
|
self.registers [i] = self.registers [i + a + 1].take ();
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = &self.registers [offset];
|
||||||
|
let closure = if let Some (x) = value.as_closure () {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dbg! (self);
|
||||||
|
panic! ("OP_TAILCALL only implemented for closures");
|
||||||
|
};
|
||||||
|
let closure = closure.borrow ();
|
||||||
|
|
||||||
|
// Jump into the other function
|
||||||
|
|
||||||
|
let frame = self.stack.last_mut ().unwrap ();
|
||||||
|
frame.program_counter = 0;
|
||||||
|
frame.block_idx = closure.idx;
|
||||||
|
|
||||||
|
// Skip the PC increment
|
||||||
|
return None;
|
||||||
|
},
|
||||||
Instruction::Test (a, _k) => {
|
Instruction::Test (a, _k) => {
|
||||||
if self.reg (*a).is_truthy() {
|
if self.reg (*a).is_truthy() {
|
||||||
next_pc += 1;
|
next_pc += 1;
|
||||||
|
@ -548,8 +646,26 @@ impl State {
|
||||||
let frame = self.stack.last_mut ().unwrap ();
|
let frame = self.stack.last_mut ().unwrap ();
|
||||||
frame.program_counter = next_pc;
|
frame.program_counter = next_pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn execute_chunk (&mut self, breakpoints: &[Breakpoint])
|
||||||
|
-> Vec <Value> {
|
||||||
|
let max_iters = 2000;
|
||||||
|
|
||||||
|
for _ in 0..max_iters {
|
||||||
|
if breakpoints.iter ().any (|bp| self.at_breakpoint (bp)) {
|
||||||
|
dbg! (&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.step () {
|
||||||
|
None => (),
|
||||||
|
Some (StepOutput::ChunkReturned (x)) => return x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg! (self);
|
||||||
panic! ("Hit max iterations before block returned");
|
panic! ("Hit max iterations before block returned");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
33
src/tests.rs
33
src/tests.rs
|
@ -23,9 +23,9 @@ fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
||||||
/// and returns the output
|
/// and returns the output
|
||||||
|
|
||||||
fn run_chunk (args: &[&str], chunk: &Chunk) -> Vec <Value> {
|
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 ()));
|
let upvalues = State::upvalues_from_args (args.into_iter ().map (|s| s.to_string ()));
|
||||||
vm.execute_chunk (chunk, &upvalues)
|
let mut vm = State::new (chunk, &upvalues);
|
||||||
|
vm.execute_chunk (&[])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes arguments and Lua bytecode, loads it, runs it,
|
/// Takes arguments and Lua bytecode, loads it, runs it,
|
||||||
|
@ -93,7 +93,7 @@ fn bools () {
|
||||||
"arg".into (),
|
"arg".into (),
|
||||||
"print".into (),
|
"print".into (),
|
||||||
],
|
],
|
||||||
upvalue_count: 1,
|
upvalues: vec! [],
|
||||||
},
|
},
|
||||||
Block {
|
Block {
|
||||||
instructions: vec! [
|
instructions: vec! [
|
||||||
|
@ -107,7 +107,7 @@ fn bools () {
|
||||||
Inst::Return0,
|
Inst::Return0,
|
||||||
],
|
],
|
||||||
constants: vec! [],
|
constants: vec! [],
|
||||||
upvalue_count: 0,
|
upvalues: vec! [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -118,10 +118,9 @@ fn bools () {
|
||||||
] {
|
] {
|
||||||
let expected: Vec <Value> = expected;
|
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 upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
||||||
|
let mut vm = State::new (&chunk, &upvalues);
|
||||||
let actual = vm.execute_chunk (&chunk, &upvalues);
|
let actual = vm.execute_chunk (&[]);
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +163,7 @@ fn floats () {
|
||||||
0.5.into (),
|
0.5.into (),
|
||||||
"print".into (),
|
"print".into (),
|
||||||
],
|
],
|
||||||
upvalue_count: 1,
|
upvalues: vec! [],
|
||||||
};
|
};
|
||||||
let chunk = Chunk {
|
let chunk = Chunk {
|
||||||
blocks: vec! [block],
|
blocks: vec! [block],
|
||||||
|
@ -175,9 +174,9 @@ fn floats () {
|
||||||
(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 mut vm = State::default ();
|
|
||||||
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
||||||
let actual = vm.execute_chunk (&chunk, &upvalues);
|
let mut vm = State::new (&chunk, &upvalues);
|
||||||
|
let actual = vm.execute_chunk (&[]);
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -185,19 +184,21 @@ fn floats () {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fma () {
|
fn fma () {
|
||||||
let bytecode = include_bytes! ("../test_vectors/fma.luac");
|
let source = include_bytes! ("../test_vectors/fma.lua");
|
||||||
let mut rdr = std::io::Cursor::new (bytecode);
|
let bytecode = &crate::loader::compile_bytecode_from_stdin (source.to_vec ());
|
||||||
let file = crate::loader::parse_chunk (&mut rdr).unwrap ();
|
let chunk = crate::loader::parse_chunk_from_bytes (bytecode).unwrap ();
|
||||||
assert_eq! (file.blocks.len (), 4);
|
assert_eq! (chunk.blocks.len (), 5);
|
||||||
|
|
||||||
|
assert_eq! (chunk.blocks [3].upvalues.len (), 2);
|
||||||
|
|
||||||
for (arg, expected) in [
|
for (arg, expected) in [
|
||||||
(vec! ["_exe_name"], vec! [122.into ()]),
|
(vec! ["_exe_name"], vec! [122.into ()]),
|
||||||
(vec! ["_exe_name"], vec! [122.into ()]),
|
(vec! ["_exe_name"], vec! [122.into ()]),
|
||||||
] {
|
] {
|
||||||
let expected: Vec <Value> = expected;
|
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 upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
||||||
let actual = vm.execute_chunk (&file, &upvalues);
|
let mut vm = State::new (&chunk, &upvalues);
|
||||||
|
let actual = vm.execute_chunk (&[]);
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
|
|
47
src/value.rs
47
src/value.rs
|
@ -11,11 +11,12 @@ use std::{
|
||||||
|
|
||||||
#[derive (Debug, Eq, PartialEq)]
|
#[derive (Debug, Eq, PartialEq)]
|
||||||
pub struct BogusClosure {
|
pub struct BogusClosure {
|
||||||
|
// I'm pretty sure this should be absolute?
|
||||||
pub idx: usize,
|
pub idx: usize,
|
||||||
pub upvalues: Vec <Value>,
|
pub upvalues: Vec <Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Clone, Debug, PartialEq)]
|
#[derive (Clone, PartialEq)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Nil,
|
Nil,
|
||||||
Boolean (bool),
|
Boolean (bool),
|
||||||
|
@ -32,7 +33,24 @@ pub enum Value {
|
||||||
// These are all bogus, I haven't figured out how to implement
|
// These are all bogus, I haven't figured out how to implement
|
||||||
// closures yet
|
// closures yet
|
||||||
|
|
||||||
BogusClosure (Rc <BogusClosure>),
|
BogusClosure (Rc <RefCell <BogusClosure>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Value {
|
||||||
|
fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Value::Nil => write! (f, "nil"),
|
||||||
|
Value::Boolean (false) => write! (f, "false"),
|
||||||
|
Value::Boolean (true) => write! (f, "true"),
|
||||||
|
Value::Float (x) => write! (f, "{:?}", x),
|
||||||
|
Value::Integer (x) => write! (f, "{}", x),
|
||||||
|
Value::RsFunc (x) => write! (f, "function: {:?}", x),
|
||||||
|
Value::String (s) => write! (f, "\"{}\"", s),
|
||||||
|
Value::Table (t) => write! (f, "{:?}", t.borrow ()),
|
||||||
|
|
||||||
|
Value::BogusClosure (x) => write! (f, "{:?}", x.borrow ()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Value {
|
impl Default for Value {
|
||||||
|
@ -53,11 +71,17 @@ impl fmt::Display for Value {
|
||||||
Value::String (s) => write! (f, "{}", s),
|
Value::String (s) => write! (f, "{}", s),
|
||||||
Value::Table (t) => write! (f, "table: {:?}", std::rc::Rc::as_ptr (t)),
|
Value::Table (t) => write! (f, "table: {:?}", std::rc::Rc::as_ptr (t)),
|
||||||
|
|
||||||
Value::BogusClosure (_) => write! (f, "BogusClosure"),
|
Value::BogusClosure (x) => write! (f, "BogusClosure: {:?}", std::rc::Rc::as_ptr (x)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From <BogusClosure> for Value {
|
||||||
|
fn from (x: BogusClosure) -> Self {
|
||||||
|
Self::BogusClosure (Rc::new (RefCell::new (x)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From <bool> for Value {
|
impl From <bool> for Value {
|
||||||
fn from (x: bool) -> Self {
|
fn from (x: bool) -> Self {
|
||||||
Self::Boolean (x)
|
Self::Boolean (x)
|
||||||
|
@ -156,6 +180,13 @@ impl PartialEq <i64> for Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
|
pub fn as_closure (&self) -> Option <&Rc <RefCell <BogusClosure>>> {
|
||||||
|
match self {
|
||||||
|
Self::BogusClosure (x) => Some (x),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Coerces ints to float
|
/// Coerces ints to float
|
||||||
|
|
||||||
pub fn as_float (&self) -> Option <f64> {
|
pub fn as_float (&self) -> Option <f64> {
|
||||||
|
@ -200,12 +231,20 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Debug, Default, Eq, PartialEq)]
|
#[derive (Default, Eq, PartialEq)]
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
array: Vec <Value>,
|
array: Vec <Value>,
|
||||||
hash: HashMap <Value, Value>,
|
hash: HashMap <Value, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Table {
|
||||||
|
fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write! (f, "Table {:#?}", self.hash)?;
|
||||||
|
|
||||||
|
Ok (())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Table {
|
impl Table {
|
||||||
fn get_inner (&self, key: &Value) -> Value {
|
fn get_inner (&self, key: &Value) -> Value {
|
||||||
self.hash.get (key).cloned ().unwrap_or_default ()
|
self.hash.get (key).cloned ().unwrap_or_default ()
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
local function make_closure (x)
|
local function make_closure (t)
|
||||||
print (x)
|
|
||||||
return function (y)
|
return function (y)
|
||||||
return x + y
|
return t.x + y
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local f = make_closure (11)
|
local f = make_closure ({ x = 11 })
|
||||||
|
local t = {}
|
||||||
local x = f (12)
|
local x = f (12)
|
||||||
print (x)
|
print (x)
|
||||||
return x
|
return x
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
-- This one fails :( I haven't implemented closures properly yet.
|
-- This one fails :( I haven't implemented closures properly yet.
|
||||||
|
|
||||||
|
local ii = "bogus"
|
||||||
|
|
||||||
local function add (aa, bb)
|
local function add (aa, bb)
|
||||||
return aa + bb
|
return aa + bb
|
||||||
end
|
end
|
||||||
|
@ -12,6 +14,13 @@ local function fma (ee, ff, gg)
|
||||||
return add (mul (ee, ff), gg)
|
return add (mul (ee, ff), gg)
|
||||||
end
|
end
|
||||||
|
|
||||||
local hh = fma (10, 11, 12)
|
local function run ()
|
||||||
print (hh)
|
local hh = fma (10, 11, 12)
|
||||||
|
print (hh)
|
||||||
|
return hh
|
||||||
|
end
|
||||||
|
|
||||||
|
local hh = run ()
|
||||||
|
print (ii)
|
||||||
|
|
||||||
return hh
|
return hh
|
||||||
|
|
Loading…
Reference in New Issue