diff --git a/src/instruction.rs b/src/instruction.rs index 36c815e..0ff3568 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -31,6 +31,8 @@ pub enum Instruction { // Jump Jmp (i32), + Len (u8, u8), + // Load F (Float?) LoadF (u8, i32), diff --git a/src/loader.rs b/src/loader.rs index 056eeef..ba67bbc 100644 --- a/src/loader.rs +++ b/src/loader.rs @@ -8,12 +8,35 @@ use crate::{ } }; +pub (crate) fn compile_bytecode_from_file (path: &str) -> Vec { + use std::{ + process::{ + Command, + Stdio, + }, + }; + + let child = Command::new ("luac5.4") + .arg ("-o") // Output to... + .arg ("-") // Standard output + .arg (path) + .stdout (Stdio::piped ()) + .spawn () + .expect ("failed to execute `luac5.4`. Is Lua installed?"); + + let output = child + .wait_with_output () + .expect ("failed to wait on child"); + + output.stdout.as_slice ().to_vec () +} + /// Invoke `luac` as a subprocess /// 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 ) -> Vec { +pub (crate) fn compile_bytecode_from_stdin (source: Vec ) -> Vec { use std::{ io::Write, process::{ @@ -87,6 +110,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option 0x2e => Inst::MmBin (a, b, c), 0x30 => Inst::MmBinK (a, b, c, k), 0x33 => Inst::Not (a, b), + 0x34 => Inst::Len (a, b), 0x3c => Inst::EqK (a, b, k), 0x3d => Inst::EqI (a, i_sb (buf)?, k), 0x38 => Inst::Jmp (s_j), diff --git a/src/main.rs b/src/main.rs index f007ec5..6291171 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ mod tests; fn main () { use state::State; + let mut list_bytecode = false; let mut pipe_bytecode = false; let mut script = None; @@ -19,6 +20,7 @@ fn main () { while let Some (arg) = args.next () { match arg.as_str () { + "--list-bytecode" => list_bytecode = true, "--pipe-bytecode" => pipe_bytecode = true, "--script" => script = Some (args.next ().unwrap ()), "--" => break, @@ -26,9 +28,9 @@ fn main () { } } - let lua_file = if let Some (script) = script { - let source = std::fs::read (script).expect ("couldn't load Lua source code"); - let bytecode = loader::compile_bytecode(source); + let chunk = if let Some (script) = script { + let bytecode = loader::compile_bytecode_from_file (&script); + dbg! (&bytecode [0..48]); let mut rdr = std::io::Cursor::new (bytecode); loader::parse_chunk (&mut rdr).unwrap () } @@ -40,6 +42,10 @@ fn main () { unimplemented!(); }; + if list_bytecode { + dbg! (&chunk); + } + let mut vm = State::default (); if std::env::var("LUA_DEBUG").is_ok() { vm.debug_print = true; @@ -52,5 +58,5 @@ fn main () { program_counter: 0, }); - vm.execute_chunk (&lua_file, &upvalues); + vm.execute_chunk (&chunk, &upvalues); } diff --git a/src/state.rs b/src/state.rs index 58f6b75..7f953ce 100644 --- a/src/state.rs +++ b/src/state.rs @@ -8,12 +8,14 @@ use crate::{ }, }; +#[derive (Debug)] pub struct Block { pub instructions: Vec , pub constants: Vec , pub upvalue_count: usize, } +#[derive (Debug)] pub struct Chunk { pub blocks: Vec , } @@ -338,6 +340,14 @@ impl State { *self.reg_mut (*a) = upvalues [b].clone (); }, Instruction::Jmp (s_j) => next_pc += s_j, + Instruction::Len (a, b) => { + let len = match self.reg (*b) { + Value::String (s) => s.len (), + _ => unimplemented!(), + }; + + *self.reg_mut (*a) = len.into (); + } Instruction::LoadF (a, sbx) => { *self.reg_mut (*a) = Value::Float (*sbx as f64); } diff --git a/src/tests.rs b/src/tests.rs index 05a425b..6c81843 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -42,7 +42,7 @@ fn run_bytecode (args: &[&str], bc: &[u8]) -> Vec { /// and returns the output fn run_source (args: &[&str], s: &str) -> Vec { - let bc = loader::compile_bytecode (s.as_bytes ().to_vec ()); + let bc = loader::compile_bytecode_from_stdin (s.as_bytes ().to_vec ()); run_bytecode (args, &bc) } @@ -289,7 +289,7 @@ fn is_93 () { end "#; - let bc = loader::compile_bytecode (src.as_bytes ().to_vec ()); + let bc = loader::compile_bytecode_from_stdin (src.as_bytes ().to_vec ()); let chunk = loader::parse_chunk_from_bytes (&bc).unwrap (); assert_eq! (chunk.blocks [0].instructions [3], Inst::EqK (0, 1, false));