add OP_LEN and allow telling luac a file name

This make debugging easier, since the bytecodes will match between
a terminal and lunar_wave
main
_ 2023-09-28 00:10:21 -05:00
parent 565fd19e66
commit 5649f38698
5 changed files with 49 additions and 7 deletions

View File

@ -31,6 +31,8 @@ pub enum Instruction {
// Jump // Jump
Jmp (i32), Jmp (i32),
Len (u8, u8),
// Load F (Float?) // Load F (Float?)
LoadF (u8, i32), LoadF (u8, i32),

View File

@ -8,12 +8,35 @@ use crate::{
} }
}; };
pub (crate) fn compile_bytecode_from_file (path: &str) -> Vec <u8> {
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 /// 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 /// `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_from_stdin (source: Vec <u8>) -> Vec <u8> {
use std::{ use std::{
io::Write, io::Write,
process::{ process::{
@ -87,6 +110,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
0x2e => Inst::MmBin (a, b, c), 0x2e => Inst::MmBin (a, b, c),
0x30 => Inst::MmBinK (a, b, c, k), 0x30 => Inst::MmBinK (a, b, c, k),
0x33 => Inst::Not (a, b), 0x33 => Inst::Not (a, b),
0x34 => Inst::Len (a, b),
0x3c => Inst::EqK (a, b, k), 0x3c => Inst::EqK (a, b, k),
0x3d => Inst::EqI (a, i_sb (buf)?, k), 0x3d => Inst::EqI (a, i_sb (buf)?, k),
0x38 => Inst::Jmp (s_j), 0x38 => Inst::Jmp (s_j),

View File

@ -11,6 +11,7 @@ mod tests;
fn main () { fn main () {
use state::State; use state::State;
let mut list_bytecode = false;
let mut pipe_bytecode = false; let mut pipe_bytecode = false;
let mut script = None; let mut script = None;
@ -19,6 +20,7 @@ fn main () {
while let Some (arg) = args.next () { while let Some (arg) = args.next () {
match arg.as_str () { match arg.as_str () {
"--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 ()),
"--" => break, "--" => break,
@ -26,9 +28,9 @@ fn main () {
} }
} }
let lua_file = if let Some (script) = script { let chunk = if let Some (script) = script {
let source = std::fs::read (script).expect ("couldn't load Lua source code"); let bytecode = loader::compile_bytecode_from_file (&script);
let bytecode = loader::compile_bytecode(source); dbg! (&bytecode [0..48]);
let mut rdr = std::io::Cursor::new (bytecode); let mut rdr = std::io::Cursor::new (bytecode);
loader::parse_chunk (&mut rdr).unwrap () loader::parse_chunk (&mut rdr).unwrap ()
} }
@ -40,6 +42,10 @@ fn main () {
unimplemented!(); unimplemented!();
}; };
if list_bytecode {
dbg! (&chunk);
}
let mut vm = State::default (); let mut vm = State::default ();
if std::env::var("LUA_DEBUG").is_ok() { if std::env::var("LUA_DEBUG").is_ok() {
vm.debug_print = true; vm.debug_print = true;
@ -52,5 +58,5 @@ fn main () {
program_counter: 0, program_counter: 0,
}); });
vm.execute_chunk (&lua_file, &upvalues); vm.execute_chunk (&chunk, &upvalues);
} }

View File

@ -8,12 +8,14 @@ use crate::{
}, },
}; };
#[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 upvalue_count: usize,
} }
#[derive (Debug)]
pub struct Chunk { pub struct Chunk {
pub blocks: Vec <Block>, pub blocks: Vec <Block>,
} }
@ -338,6 +340,14 @@ impl State {
*self.reg_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::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) => { Instruction::LoadF (a, sbx) => {
*self.reg_mut (*a) = Value::Float (*sbx as f64); *self.reg_mut (*a) = Value::Float (*sbx as f64);
} }

View File

@ -42,7 +42,7 @@ fn run_bytecode (args: &[&str], bc: &[u8]) -> Vec <Value> {
/// and returns the output /// and returns the output
fn run_source (args: &[&str], s: &str) -> Vec <Value> { fn run_source (args: &[&str], s: &str) -> Vec <Value> {
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) run_bytecode (args, &bc)
} }
@ -289,7 +289,7 @@ fn is_93 () {
end 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 (); let chunk = loader::parse_chunk_from_bytes (&bc).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));