Compare commits
	
		
			2 Commits 
		
	
	
		
			b8dd59cd7c
			...
			8baea40e82
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								 | 
						8baea40e82 | |
| 
							
							
								 | 
						f9e8f26ac3 | 
| 
						 | 
				
			
			@ -12,3 +12,61 @@ dependencies = [
 | 
			
		|||
[[package]]
 | 
			
		||||
name = "lunar_wave_vm"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "thiserror",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "proc-macro2"
 | 
			
		||||
version = "1.0.67"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "unicode-ident",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "quote"
 | 
			
		||||
version = "1.0.33"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "syn"
 | 
			
		||||
version = "2.0.37"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "unicode-ident",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "thiserror"
 | 
			
		||||
version = "1.0.49"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "thiserror-impl",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "thiserror-impl"
 | 
			
		||||
version = "1.0.49"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "unicode-ident"
 | 
			
		||||
version = "1.0.12"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,9 @@
 | 
			
		|||
// cargo run -- --script lunar_wave_vm/test_vectors/fizz_buzz.lua
 | 
			
		||||
 | 
			
		||||
use std::io::Read;
 | 
			
		||||
use std::io::{
 | 
			
		||||
	Read,
 | 
			
		||||
	Write,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use lunar_wave_vm as lwvm;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -36,9 +39,8 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError
 | 
			
		|||
			"-" => {
 | 
			
		||||
				let mut buf = vec! [];
 | 
			
		||||
				std::io::stdin ().read_to_end (&mut buf).unwrap ();
 | 
			
		||||
				let bc = lwvm::ensure_bytecode (buf);
 | 
			
		||||
				let mut rdr = std::io::Cursor::new (bc);
 | 
			
		||||
				chunk = Some (lwvm::parse_chunk (&mut rdr).unwrap ());
 | 
			
		||||
				let bc = lwvm::ensure_bytecode (buf).unwrap ();
 | 
			
		||||
				chunk = Some (lwvm::parse_chunk (&bc).unwrap ());
 | 
			
		||||
				
 | 
			
		||||
				lua_args = vec! ["-".to_string ()];
 | 
			
		||||
			},
 | 
			
		||||
| 
						 | 
				
			
			@ -49,8 +51,7 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError
 | 
			
		|||
				}
 | 
			
		||||
				else if chunk.is_none () {
 | 
			
		||||
					let bc = lwvm::compile_bytecode_from_file (x);
 | 
			
		||||
					let mut rdr = std::io::Cursor::new (bc);
 | 
			
		||||
					chunk = Some (lwvm::parse_chunk (&mut rdr).unwrap ());
 | 
			
		||||
					chunk = Some (lwvm::parse_chunk (&bc).unwrap ());
 | 
			
		||||
					
 | 
			
		||||
					lua_args = vec! [x.to_string ()];
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -61,24 +62,55 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	let chunk = chunk.unwrap ();
 | 
			
		||||
	
 | 
			
		||||
	if list_bytecode {
 | 
			
		||||
		dbg! (&chunk);
 | 
			
		||||
	match chunk {
 | 
			
		||||
		Some (chunk) => debugger (DebuggerParams {
 | 
			
		||||
			breakpoints,
 | 
			
		||||
			chunk,
 | 
			
		||||
			list_bytecode,
 | 
			
		||||
			lua_args,
 | 
			
		||||
		}),
 | 
			
		||||
		None => repl (ReplParams {
 | 
			
		||||
			list_bytecode,
 | 
			
		||||
			lua_args,
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct DebuggerParams {
 | 
			
		||||
	breakpoints: Vec <lwvm::Breakpoint>,
 | 
			
		||||
	chunk: lwvm::Chunk,
 | 
			
		||||
	list_bytecode: bool,
 | 
			
		||||
	lua_args: Vec <String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ReplParams {
 | 
			
		||||
	list_bytecode: bool,
 | 
			
		||||
	lua_args: Vec <String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// The interpreter mode, which has optional debugging abilities
 | 
			
		||||
/// sort of like a cut-down gdb.
 | 
			
		||||
 | 
			
		||||
fn debugger (params: DebuggerParams) -> Result <Vec <lwvm::Value>, lwvm::StepError> 
 | 
			
		||||
{
 | 
			
		||||
	if params.list_bytecode {
 | 
			
		||||
		dbg! (¶ms.chunk);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	let upvalues = lwvm::State::upvalues_from_args (lua_args.into_iter ());
 | 
			
		||||
	let upvalues = lwvm::State::upvalues_from_args (params.lua_args.into_iter ());
 | 
			
		||||
	
 | 
			
		||||
	let mut vm = lwvm::State::new (chunk, upvalues);
 | 
			
		||||
	let mut vm = lwvm::State::new (params.chunk, upvalues);
 | 
			
		||||
	if std::env::var("LWVM_DEBUG").is_ok() {
 | 
			
		||||
		vm.debug_print = true;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Variables for interactive debugging
 | 
			
		||||
	
 | 
			
		||||
	let mut in_break = false;
 | 
			
		||||
	let mut last_input = String::new ();
 | 
			
		||||
	
 | 
			
		||||
	loop {
 | 
			
		||||
		if in_break || breakpoints.iter ().any (|bp| vm.at_breakpoint (bp)) {
 | 
			
		||||
		if in_break || params.breakpoints.iter ().any (|bp| vm.at_breakpoint (bp)) {
 | 
			
		||||
			in_break = true;
 | 
			
		||||
			dbg! (&vm.stack);
 | 
			
		||||
			
 | 
			
		||||
| 
						 | 
				
			
			@ -122,3 +154,52 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A REPL that's sort of like the PUC Lua or LuaJIT REPL, 
 | 
			
		||||
/// but with fewer features.
 | 
			
		||||
/// It still have to cheat and run `luac5.4` as a subprocess.
 | 
			
		||||
 | 
			
		||||
fn repl (params: ReplParams) -> Result <Vec <lwvm::Value>, lwvm::StepError> 
 | 
			
		||||
{
 | 
			
		||||
	let upvalues = lwvm::State::upvalues_from_args (params.lua_args.into_iter ());
 | 
			
		||||
	
 | 
			
		||||
	let mut vm = lwvm::State::new (lwvm::Chunk::default (), upvalues);
 | 
			
		||||
	
 | 
			
		||||
	println! ("Lunar Wave 0.1.0-modified  Copyright (C) 2023 ReactorScram (implements Lua 5.4  Copyright (C) 1994-2022 Lua.org, PUC-Rio");
 | 
			
		||||
	
 | 
			
		||||
	loop {
 | 
			
		||||
		{
 | 
			
		||||
			let mut stdout = std::io::stdout ().lock ();
 | 
			
		||||
			stdout.write_all (b"> ").unwrap ();
 | 
			
		||||
			stdout.flush ().unwrap ();
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		let mut input = Default::default ();
 | 
			
		||||
		std::io::stdin ().read_line (&mut input).unwrap ();
 | 
			
		||||
		if input.is_empty () {
 | 
			
		||||
			println! ();
 | 
			
		||||
			return Ok (vec! []);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		let bytecode = match lwvm::compile_bytecode (input.into_bytes ()) {
 | 
			
		||||
			Ok (x) => x,
 | 
			
		||||
			Err (e) => {
 | 
			
		||||
				eprintln! ("Compile error from luac subprocess:");
 | 
			
		||||
				eprintln! ("{}", e);
 | 
			
		||||
				continue;
 | 
			
		||||
			},
 | 
			
		||||
		};
 | 
			
		||||
		let chunk = lwvm::parse_chunk (&bytecode).unwrap ();
 | 
			
		||||
		
 | 
			
		||||
		if params.list_bytecode {
 | 
			
		||||
			dbg! (&chunk);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		vm.set_chunk (chunk);
 | 
			
		||||
		match vm.execute () {
 | 
			
		||||
			Ok (x) => if ! x.is_empty () {
 | 
			
		||||
				println! ("{x:?}")
 | 
			
		||||
			},
 | 
			
		||||
			Err (e) => println! ("{e:?}"),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,3 +4,6 @@ description = "A Lua virtual machine implementation"
 | 
			
		|||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
authors = ["ReactorScram"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
thiserror = "1.0.49"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
#[derive (Debug, thiserror::Error)]
 | 
			
		||||
pub enum Error {
 | 
			
		||||
	#[error ("loader")]
 | 
			
		||||
	Loader (#[from] crate::loader::Error),
 | 
			
		||||
	#[error ("VM step")]
 | 
			
		||||
	VmStep (#[from] crate::state::StepError),
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ pub enum Instruction {
 | 
			
		|||
	
 | 
			
		||||
	Call (u8, u8, u8),
 | 
			
		||||
	Closure (u8, u32),
 | 
			
		||||
	Concat (u8, u8),
 | 
			
		||||
	
 | 
			
		||||
	Div (u8, u8, u8),
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +85,7 @@ pub enum Instruction {
 | 
			
		|||
	
 | 
			
		||||
	SetList (u8, u8, u8, bool),
 | 
			
		||||
	
 | 
			
		||||
	SetTabUp (u8, u8, u8),
 | 
			
		||||
	SetTabUp (u8, u8, u8, bool),
 | 
			
		||||
	
 | 
			
		||||
	Sub (u8, u8, u8),
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,17 @@
 | 
			
		|||
mod error;
 | 
			
		||||
mod instruction;
 | 
			
		||||
mod loader;
 | 
			
		||||
mod state;
 | 
			
		||||
mod value;
 | 
			
		||||
 | 
			
		||||
pub use error::Error as Error;
 | 
			
		||||
pub use loader::compile_bytecode_from_file as compile_bytecode_from_file;
 | 
			
		||||
pub use loader::compile_bytecode as compile_bytecode;
 | 
			
		||||
pub use loader::ensure_bytecode as ensure_bytecode;
 | 
			
		||||
pub use loader::parse_chunk as parse_chunk;
 | 
			
		||||
pub use loader::parse_chunk_from_reader as parse_chunk_from_reader;
 | 
			
		||||
pub use state::Breakpoint as Breakpoint;
 | 
			
		||||
pub use state::Chunk as Chunk;
 | 
			
		||||
pub use state::State as State;
 | 
			
		||||
pub use state::StepError as StepError;
 | 
			
		||||
pub use state::StepOutput as StepOutput;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,12 +29,18 @@ pub fn compile_bytecode_from_file (path: &str) -> Vec <u8> {
 | 
			
		|||
	output.stdout.as_slice ().to_vec ()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive (Debug, thiserror::Error)]
 | 
			
		||||
pub enum Error {
 | 
			
		||||
	#[error ("compile")]
 | 
			
		||||
	Compile (String)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 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 fn compile_bytecode (source: Vec <u8>) -> Vec <u8> {
 | 
			
		||||
pub fn compile_bytecode (source: Vec <u8>) -> Result <Vec <u8>, Error> {
 | 
			
		||||
	use std::{
 | 
			
		||||
		io::Write,
 | 
			
		||||
		process::{
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +54,7 @@ pub fn compile_bytecode (source: Vec <u8>) -> Vec <u8> {
 | 
			
		|||
	.arg ("-")  // Standard output
 | 
			
		||||
	.arg ("-")  // Input from standard input
 | 
			
		||||
	.stdin (Stdio::piped ())
 | 
			
		||||
	.stderr (Stdio::piped ())
 | 
			
		||||
	.stdout (Stdio::piped ())
 | 
			
		||||
	.spawn ()
 | 
			
		||||
	.expect ("failed to execute `luac5.4`. Is Lua installed?");
 | 
			
		||||
| 
						 | 
				
			
			@ -61,17 +68,23 @@ pub fn compile_bytecode (source: Vec <u8>) -> Vec <u8> {
 | 
			
		|||
	.wait_with_output ()
 | 
			
		||||
	.expect ("failed to wait on child");
 | 
			
		||||
	
 | 
			
		||||
	output.stdout.as_slice ().to_vec ()
 | 
			
		||||
	if output.status.success () && output.status.code () == Some (0) 
 | 
			
		||||
	{
 | 
			
		||||
		Ok (output.stdout)
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		Err (Error::Compile (String::from_utf8 (output.stderr).unwrap ()))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Checks whether the input is already bytecode, or is possibly
 | 
			
		||||
/// Lua source code. If it's source code, compiles and returns bytecode.
 | 
			
		||||
/// If it's bytecode, just returns the input.
 | 
			
		||||
 | 
			
		||||
pub fn ensure_bytecode (buffer: Vec <u8>) -> Vec <u8> {
 | 
			
		||||
pub fn ensure_bytecode (buffer: Vec <u8>) -> Result <Vec <u8>, Error> {
 | 
			
		||||
	let bytecode_header = &[0x1b, 0x4c, 0x75, 0x61, 0x54, 0x00, 0x19, 0x93];
 | 
			
		||||
	if buffer.starts_with (bytecode_header) {
 | 
			
		||||
		return buffer;
 | 
			
		||||
		return Ok (buffer);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	compile_bytecode (buffer)
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +129,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
 | 
			
		|||
		0x0c => Inst::GetTable (a, b, c),
 | 
			
		||||
		0x0d => Inst::GetI (a, b, c),
 | 
			
		||||
		0x0e => Inst::GetField (a, b, c),
 | 
			
		||||
		0x0f => Inst::SetTabUp (a, b, c),
 | 
			
		||||
		0x0f => Inst::SetTabUp (a, b, c, k),
 | 
			
		||||
		0x11 => Inst::SetI (a, b, c, k),
 | 
			
		||||
		0x12 => Inst::SetField (a, b, c, k),
 | 
			
		||||
		0x13 => Inst::NewTable (a),
 | 
			
		||||
| 
						 | 
				
			
			@ -133,6 +146,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
 | 
			
		|||
		0x31 => Inst::UnM (a, b),
 | 
			
		||||
		0x33 => Inst::Not (a, b),
 | 
			
		||||
		0x34 => Inst::Len (a, b),
 | 
			
		||||
		0x35 => Inst::Concat (a, b),
 | 
			
		||||
		0x3c => Inst::EqK (a, b, k),
 | 
			
		||||
		0x3d => Inst::EqI (a, i_sb (buf)?, k),
 | 
			
		||||
		0x38 => Inst::Jmp (s_j),
 | 
			
		||||
| 
						 | 
				
			
			@ -333,8 +347,12 @@ pub fn parse_block <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>)
 | 
			
		|||
	Some (())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn parse_chunk (buf: &[u8]) -> Option <Chunk> {
 | 
			
		||||
	let mut rdr = std::io::Cursor::new (buf);
 | 
			
		||||
	parse_chunk_from_reader (&mut rdr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn parse_chunk <R: Read> (rdr: &mut R) -> Option <Chunk> {
 | 
			
		||||
pub fn parse_chunk_from_reader <R: Read> (rdr: &mut R) -> Option <Chunk> {
 | 
			
		||||
	// Discard 32 bytes from the start of the file.
 | 
			
		||||
	// This is magic number, version number, etc.
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			@ -352,11 +370,6 @@ 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)]
 | 
			
		||||
mod tests {
 | 
			
		||||
	#[test]
 | 
			
		||||
| 
						 | 
				
			
			@ -447,8 +460,7 @@ mod tests {
 | 
			
		|||
		}
 | 
			
		||||
		
 | 
			
		||||
		if false {
 | 
			
		||||
			let mut rdr = std::io::Cursor::new (bytecode.clone ());
 | 
			
		||||
			let file = crate::loader::parse_chunk (&mut rdr).unwrap ();
 | 
			
		||||
			let file = crate::loader::parse_chunk (bytecode).unwrap ();
 | 
			
		||||
			
 | 
			
		||||
			assert_eq! (file.blocks.len (), 5);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,12 +20,12 @@ pub struct Block {
 | 
			
		|||
	pub upvalues: Vec <Upvalue>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive (Clone, Debug)]
 | 
			
		||||
#[derive (Clone, Debug, Default)]
 | 
			
		||||
pub struct Chunk {
 | 
			
		||||
	pub blocks: Vec <Block>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive (Clone, Debug)]
 | 
			
		||||
#[derive (Clone, Debug, Default)]
 | 
			
		||||
pub struct StackFrame {
 | 
			
		||||
	// i32 makes it a little easier to implement jumps
 | 
			
		||||
	// Starts at 0 right after OP_CALL
 | 
			
		||||
| 
						 | 
				
			
			@ -133,11 +133,14 @@ pub enum StepOutput {
 | 
			
		|||
	ChunkReturned (Vec <Value>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive (Debug)]
 | 
			
		||||
pub struct StepError {
 | 
			
		||||
	frame: StackFrame,
 | 
			
		||||
	inst:  Instruction,
 | 
			
		||||
	msg: &'static str,
 | 
			
		||||
#[derive (Debug, thiserror::Error)]
 | 
			
		||||
pub enum StepError {
 | 
			
		||||
	#[error ("generic")]
 | 
			
		||||
	Generic {
 | 
			
		||||
		frame: StackFrame,
 | 
			
		||||
		inst:  Instruction,
 | 
			
		||||
		msg: &'static str,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl State {
 | 
			
		||||
| 
						 | 
				
			
			@ -148,11 +151,7 @@ impl State {
 | 
			
		|||
			registers: vec! [Value::Nil; 256],
 | 
			
		||||
			top: 0,
 | 
			
		||||
			stack: vec! [
 | 
			
		||||
				StackFrame {
 | 
			
		||||
					program_counter: 0,
 | 
			
		||||
					block_idx: 0,
 | 
			
		||||
					register_offset: 0,
 | 
			
		||||
				},
 | 
			
		||||
				StackFrame::default (),
 | 
			
		||||
			],
 | 
			
		||||
			debug_print: false,
 | 
			
		||||
			step_count: 0,
 | 
			
		||||
| 
						 | 
				
			
			@ -221,7 +220,7 @@ impl State {
 | 
			
		|||
	
 | 
			
		||||
	fn make_step_error (&self, msg: &'static str, inst: &Instruction) -> StepError
 | 
			
		||||
	{
 | 
			
		||||
		StepError {
 | 
			
		||||
		StepError::Generic {
 | 
			
		||||
			frame: self.stack.last ().unwrap ().clone (),
 | 
			
		||||
			inst: inst.clone (),
 | 
			
		||||
			msg,
 | 
			
		||||
| 
						 | 
				
			
			@ -386,6 +385,9 @@ impl State {
 | 
			
		|||
					upvalues: new_upvalues,
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
			Instruction::Concat (a, b) => {
 | 
			
		||||
				unimplemented! ("OP_CONCAT")
 | 
			
		||||
			},
 | 
			
		||||
			Instruction::Div (a, b, c) => {
 | 
			
		||||
				let v_b = self.reg (b);
 | 
			
		||||
				let v_c = self.reg (c);
 | 
			
		||||
| 
						 | 
				
			
			@ -760,7 +762,22 @@ impl State {
 | 
			
		|||
					dst.insert_int (i64::from (c + i), src.clone ());
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			Instruction::SetTabUp (_a, _b, _c) => unimplemented! (),
 | 
			
		||||
			Instruction::SetTabUp (a, b, c, k_flag) => {
 | 
			
		||||
				let a = usize::try_from (a).unwrap ();
 | 
			
		||||
				let b = usize::try_from (b).unwrap ();
 | 
			
		||||
				
 | 
			
		||||
				let value = if k_flag {
 | 
			
		||||
					&k [usize::from (c)]
 | 
			
		||||
				}
 | 
			
		||||
				else {
 | 
			
		||||
					self.reg (c)
 | 
			
		||||
				}
 | 
			
		||||
				.clone ();
 | 
			
		||||
				
 | 
			
		||||
				let table = self.upvalues.get_mut (a).unwrap ().as_table ().unwrap ();
 | 
			
		||||
				let key = k.get (b).unwrap ().as_str ().expect ("SetTabUp K[B] must be a string");
 | 
			
		||||
				table.borrow_mut ().insert_str (key, value);
 | 
			
		||||
			},
 | 
			
		||||
			Instruction::Sub (a, b, c) => {
 | 
			
		||||
				let v_b = self.reg (b);
 | 
			
		||||
				let v_c = self.reg (c);
 | 
			
		||||
| 
						 | 
				
			
			@ -886,15 +903,20 @@ impl State {
 | 
			
		|||
		Ok (None)
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	pub fn execute_chunk (&mut self, breakpoints: &[Breakpoint]) 
 | 
			
		||||
	pub fn eval (&mut self, src: &str) -> Result <Vec <Value>, crate::Error>
 | 
			
		||||
	{
 | 
			
		||||
		let bytecode = crate::compile_bytecode (src.as_bytes ().to_vec ())?;
 | 
			
		||||
		let chunk = crate::parse_chunk (&bytecode).unwrap ();
 | 
			
		||||
		
 | 
			
		||||
		self.set_chunk (chunk);
 | 
			
		||||
		Ok (self.execute ()?)
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	pub fn execute (&mut self) 
 | 
			
		||||
	-> Result <Vec <Value>, StepError> {
 | 
			
		||||
		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 Ok (x),
 | 
			
		||||
| 
						 | 
				
			
			@ -904,4 +926,9 @@ impl State {
 | 
			
		|||
		dbg! (self);
 | 
			
		||||
		panic! ("Hit max iterations before block returned");
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	pub fn set_chunk (&mut self, chunk: Chunk) {
 | 
			
		||||
		self.stack = vec! [Default::default ()];
 | 
			
		||||
		self.chunk = chunk;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,14 +25,14 @@ fn calculate_hash<T: Hash>(t: &T) -> u64 {
 | 
			
		|||
fn run_chunk (args: &[&str], chunk: Chunk) -> Vec <Value> {
 | 
			
		||||
	let upvalues = State::upvalues_from_args (args.into_iter ().map (|s| s.to_string ()));
 | 
			
		||||
	let mut vm = State::new (chunk, upvalues);
 | 
			
		||||
	vm.execute_chunk (&[]).unwrap ()
 | 
			
		||||
	vm.execute ().unwrap ()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 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 ();
 | 
			
		||||
	let chunk = loader::parse_chunk (&bc).unwrap ();
 | 
			
		||||
	run_chunk (args, chunk)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ fn run_bytecode (args: &[&str], bc: &[u8]) -> Vec <Value> {
 | 
			
		|||
/// and returns the output
 | 
			
		||||
 | 
			
		||||
fn run_source (args: &[&str], s: &str) -> Vec <Value> {
 | 
			
		||||
	let bc = loader::compile_bytecode (s.as_bytes ().to_vec ());
 | 
			
		||||
	let bc = loader::compile_bytecode (s.as_bytes ().to_vec ()).unwrap ();
 | 
			
		||||
	run_bytecode (args, &bc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +120,7 @@ fn bools () {
 | 
			
		|||
		
 | 
			
		||||
		let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
 | 
			
		||||
		let mut vm = State::new (chunk.clone (), upvalues);
 | 
			
		||||
		let actual = vm.execute_chunk (&[]).unwrap ();
 | 
			
		||||
		let actual = vm.execute ().unwrap ();
 | 
			
		||||
		assert_eq! (actual, expected);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -128,8 +128,8 @@ fn bools () {
 | 
			
		|||
#[test]
 | 
			
		||||
fn closure () {
 | 
			
		||||
	let source = include_bytes! ("../test_vectors/closure.lua");
 | 
			
		||||
	let bytecode = &crate::loader::compile_bytecode (source.to_vec ());
 | 
			
		||||
	let chunk = crate::loader::parse_chunk_from_bytes (bytecode).unwrap ();
 | 
			
		||||
	let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
 | 
			
		||||
	let chunk = crate::loader::parse_chunk (bytecode).unwrap ();
 | 
			
		||||
	
 | 
			
		||||
	assert_eq! (run_chunk (&["_exe_name"], chunk), vec! [Value::from (23i64)]);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -176,7 +176,7 @@ fn floats () {
 | 
			
		|||
		let expected: Vec <Value> = expected;
 | 
			
		||||
		let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
 | 
			
		||||
		let mut vm = State::new (chunk.clone (), upvalues);
 | 
			
		||||
		let actual = vm.execute_chunk (&[]).unwrap ();
 | 
			
		||||
		let actual = vm.execute ().unwrap ();
 | 
			
		||||
		
 | 
			
		||||
		assert_eq! (actual, expected);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -185,8 +185,8 @@ fn floats () {
 | 
			
		|||
#[test]
 | 
			
		||||
fn fma () {
 | 
			
		||||
	let source = include_bytes! ("../test_vectors/fma.lua");
 | 
			
		||||
	let bytecode = &crate::loader::compile_bytecode (source.to_vec ());
 | 
			
		||||
	let chunk = crate::loader::parse_chunk_from_bytes (bytecode).unwrap ();
 | 
			
		||||
	let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
 | 
			
		||||
	let chunk = crate::loader::parse_chunk (bytecode).unwrap ();
 | 
			
		||||
	assert_eq! (chunk.blocks.len (), 5);
 | 
			
		||||
	
 | 
			
		||||
	assert_eq! (chunk.blocks [3].upvalues.len (), 2);
 | 
			
		||||
| 
						 | 
				
			
			@ -194,12 +194,23 @@ fn fma () {
 | 
			
		|||
	let arg = vec! ["_exe_name"];
 | 
			
		||||
	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 (&[]).unwrap ();
 | 
			
		||||
	let actual = vm.execute ().unwrap ();
 | 
			
		||||
	let expected = vec! [Value::from (122)];
 | 
			
		||||
	
 | 
			
		||||
	assert_eq! (actual, expected);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn function_calls () {
 | 
			
		||||
	let upvalues = crate::State::upvalues_from_args (vec! ["_exe_name".to_string ()].into_iter ());
 | 
			
		||||
	
 | 
			
		||||
	let mut vm = crate::State::new (crate::Chunk::default (), upvalues);
 | 
			
		||||
	
 | 
			
		||||
	vm.eval ("print (x ())").ok ();
 | 
			
		||||
	vm.eval ("x = function () return 5 end").ok ();
 | 
			
		||||
	vm.eval ("print (x ())").ok ();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn heap () {
 | 
			
		||||
	use std::{
 | 
			
		||||
| 
						 | 
				
			
			@ -265,7 +276,7 @@ fn is_93 () {
 | 
			
		|||
	assert_ne! (calculate_hash (&Value::from ("94")), calculate_hash (&Value::from ("93")));
 | 
			
		||||
	assert_ne! (Value::Nil, Value::from ("93"));
 | 
			
		||||
	
 | 
			
		||||
	let src = r#"
 | 
			
		||||
	let src = br#"
 | 
			
		||||
	if arg [1] == "93" then
 | 
			
		||||
		print "it's 93"
 | 
			
		||||
		return 0
 | 
			
		||||
| 
						 | 
				
			
			@ -275,8 +286,8 @@ fn is_93 () {
 | 
			
		|||
	end
 | 
			
		||||
	"#;
 | 
			
		||||
	
 | 
			
		||||
	let bc = loader::compile_bytecode (src.as_bytes ().to_vec ());
 | 
			
		||||
	let chunk = loader::parse_chunk_from_bytes (&bc).unwrap ();
 | 
			
		||||
	let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
 | 
			
		||||
	let chunk = loader::parse_chunk (&bc).unwrap ();
 | 
			
		||||
	
 | 
			
		||||
	assert_eq! (chunk.blocks [0].instructions [3], Inst::EqK (0, 1, false));
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			@ -359,12 +370,12 @@ fn tables_2 () {
 | 
			
		|||
fn tailcall () {
 | 
			
		||||
	use crate::instruction::Instruction;
 | 
			
		||||
	
 | 
			
		||||
	let src = r#"
 | 
			
		||||
	let src = br#"
 | 
			
		||||
	return tonumber ("5")
 | 
			
		||||
	"#;
 | 
			
		||||
	
 | 
			
		||||
	let bc = loader::compile_bytecode (src.as_bytes ().to_vec ());
 | 
			
		||||
	let chunk = loader::parse_chunk_from_bytes (&bc).unwrap ();
 | 
			
		||||
	let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
 | 
			
		||||
	let chunk = loader::parse_chunk (&bc).unwrap ();
 | 
			
		||||
	
 | 
			
		||||
	assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false));
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,9 +19,8 @@ fn embedding () {
 | 
			
		|||
		1
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	let bytecode = lwvm::compile_bytecode (src.to_vec ());
 | 
			
		||||
	let mut rdr = std::io::Cursor::new (bytecode);
 | 
			
		||||
	let chunk = lwvm::parse_chunk (&mut rdr).unwrap ();
 | 
			
		||||
	let bc = lwvm::compile_bytecode (src.to_vec ()).unwrap ();
 | 
			
		||||
	let chunk = lwvm::parse_chunk (&bc).unwrap ();
 | 
			
		||||
	
 | 
			
		||||
	let host_lib = [
 | 
			
		||||
		("add", Value::RsFunc (host_add)),
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +35,7 @@ fn embedding () {
 | 
			
		|||
	];
 | 
			
		||||
	
 | 
			
		||||
	let mut vm = State::new (chunk, upvalues);
 | 
			
		||||
	let output = vm.execute_chunk (&vec! []).unwrap ();
 | 
			
		||||
	let output = vm.execute ().unwrap ();
 | 
			
		||||
	
 | 
			
		||||
	assert_eq! (output, vec! [Value::from (2019)]);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue