Compare commits
2 Commits
b8dd59cd7c
...
8baea40e82
Author | SHA1 | Date |
---|---|---|
_ | 8baea40e82 | |
_ | f9e8f26ac3 |
|
@ -12,3 +12,61 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lunar_wave_vm"
|
name = "lunar_wave_vm"
|
||||||
version = "0.1.0"
|
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
|
// 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;
|
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! [];
|
let mut buf = vec! [];
|
||||||
std::io::stdin ().read_to_end (&mut buf).unwrap ();
|
std::io::stdin ().read_to_end (&mut buf).unwrap ();
|
||||||
let bc = lwvm::ensure_bytecode (buf);
|
let bc = lwvm::ensure_bytecode (buf).unwrap ();
|
||||||
let mut rdr = std::io::Cursor::new (bc);
|
chunk = Some (lwvm::parse_chunk (&bc).unwrap ());
|
||||||
chunk = Some (lwvm::parse_chunk (&mut rdr).unwrap ());
|
|
||||||
|
|
||||||
lua_args = vec! ["-".to_string ()];
|
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 () {
|
else if chunk.is_none () {
|
||||||
let bc = lwvm::compile_bytecode_from_file (x);
|
let bc = lwvm::compile_bytecode_from_file (x);
|
||||||
let mut rdr = std::io::Cursor::new (bc);
|
chunk = Some (lwvm::parse_chunk (&bc).unwrap ());
|
||||||
chunk = Some (lwvm::parse_chunk (&mut rdr).unwrap ());
|
|
||||||
|
|
||||||
lua_args = vec! [x.to_string ()];
|
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 ();
|
match chunk {
|
||||||
|
Some (chunk) => debugger (DebuggerParams {
|
||||||
|
breakpoints,
|
||||||
|
chunk,
|
||||||
|
list_bytecode,
|
||||||
|
lua_args,
|
||||||
|
}),
|
||||||
|
None => repl (ReplParams {
|
||||||
|
list_bytecode,
|
||||||
|
lua_args,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if list_bytecode {
|
struct DebuggerParams {
|
||||||
dbg! (&chunk);
|
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() {
|
if std::env::var("LWVM_DEBUG").is_ok() {
|
||||||
vm.debug_print = true;
|
vm.debug_print = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Variables for interactive debugging
|
||||||
|
|
||||||
let mut in_break = false;
|
let mut in_break = false;
|
||||||
let mut last_input = String::new ();
|
let mut last_input = String::new ();
|
||||||
|
|
||||||
loop {
|
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;
|
in_break = true;
|
||||||
dbg! (&vm.stack);
|
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"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["ReactorScram"]
|
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),
|
Call (u8, u8, u8),
|
||||||
Closure (u8, u32),
|
Closure (u8, u32),
|
||||||
|
Concat (u8, u8),
|
||||||
|
|
||||||
Div (u8, u8, u8),
|
Div (u8, u8, u8),
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ pub enum Instruction {
|
||||||
|
|
||||||
SetList (u8, u8, u8, bool),
|
SetList (u8, u8, u8, bool),
|
||||||
|
|
||||||
SetTabUp (u8, u8, u8),
|
SetTabUp (u8, u8, u8, bool),
|
||||||
|
|
||||||
Sub (u8, u8, u8),
|
Sub (u8, u8, u8),
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
|
mod error;
|
||||||
mod instruction;
|
mod instruction;
|
||||||
mod loader;
|
mod loader;
|
||||||
mod state;
|
mod state;
|
||||||
mod value;
|
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_from_file as compile_bytecode_from_file;
|
||||||
pub use loader::compile_bytecode as compile_bytecode;
|
pub use loader::compile_bytecode as compile_bytecode;
|
||||||
pub use loader::ensure_bytecode as ensure_bytecode;
|
pub use loader::ensure_bytecode as ensure_bytecode;
|
||||||
pub use loader::parse_chunk as parse_chunk;
|
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::Breakpoint as Breakpoint;
|
||||||
|
pub use state::Chunk as Chunk;
|
||||||
pub use state::State as State;
|
pub use state::State as State;
|
||||||
pub use state::StepError as StepError;
|
pub use state::StepError as StepError;
|
||||||
pub use state::StepOutput as StepOutput;
|
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 ()
|
output.stdout.as_slice ().to_vec ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive (Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error ("compile")]
|
||||||
|
Compile (String)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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 fn compile_bytecode (source: Vec <u8>) -> Vec <u8> {
|
pub fn compile_bytecode (source: Vec <u8>) -> Result <Vec <u8>, Error> {
|
||||||
use std::{
|
use std::{
|
||||||
io::Write,
|
io::Write,
|
||||||
process::{
|
process::{
|
||||||
|
@ -48,6 +54,7 @@ pub fn compile_bytecode (source: Vec <u8>) -> Vec <u8> {
|
||||||
.arg ("-") // Standard output
|
.arg ("-") // Standard output
|
||||||
.arg ("-") // Input from standard input
|
.arg ("-") // Input from standard input
|
||||||
.stdin (Stdio::piped ())
|
.stdin (Stdio::piped ())
|
||||||
|
.stderr (Stdio::piped ())
|
||||||
.stdout (Stdio::piped ())
|
.stdout (Stdio::piped ())
|
||||||
.spawn ()
|
.spawn ()
|
||||||
.expect ("failed to execute `luac5.4`. Is Lua installed?");
|
.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 ()
|
.wait_with_output ()
|
||||||
.expect ("failed to wait on child");
|
.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
|
/// Checks whether the input is already bytecode, or is possibly
|
||||||
/// Lua source code. If it's source code, compiles and returns bytecode.
|
/// Lua source code. If it's source code, compiles and returns bytecode.
|
||||||
/// If it's bytecode, just returns the input.
|
/// 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];
|
let bytecode_header = &[0x1b, 0x4c, 0x75, 0x61, 0x54, 0x00, 0x19, 0x93];
|
||||||
if buffer.starts_with (bytecode_header) {
|
if buffer.starts_with (bytecode_header) {
|
||||||
return buffer;
|
return Ok (buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
compile_bytecode (buffer)
|
compile_bytecode (buffer)
|
||||||
|
@ -116,7 +129,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
|
||||||
0x0c => Inst::GetTable (a, b, c),
|
0x0c => Inst::GetTable (a, b, c),
|
||||||
0x0d => Inst::GetI (a, b, c),
|
0x0d => Inst::GetI (a, b, c),
|
||||||
0x0e => Inst::GetField (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),
|
0x11 => Inst::SetI (a, b, c, k),
|
||||||
0x12 => Inst::SetField (a, b, c, k),
|
0x12 => Inst::SetField (a, b, c, k),
|
||||||
0x13 => Inst::NewTable (a),
|
0x13 => Inst::NewTable (a),
|
||||||
|
@ -133,6 +146,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
|
||||||
0x31 => Inst::UnM (a, b),
|
0x31 => Inst::UnM (a, b),
|
||||||
0x33 => Inst::Not (a, b),
|
0x33 => Inst::Not (a, b),
|
||||||
0x34 => Inst::Len (a, b),
|
0x34 => Inst::Len (a, b),
|
||||||
|
0x35 => Inst::Concat (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),
|
||||||
|
@ -333,8 +347,12 @@ pub fn parse_block <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>)
|
||||||
Some (())
|
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.
|
// Discard 32 bytes from the start of the file.
|
||||||
// This is magic number, version number, etc.
|
// 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)]
|
#[cfg (test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -447,8 +460,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
let mut rdr = std::io::Cursor::new (bytecode.clone ());
|
let file = crate::loader::parse_chunk (bytecode).unwrap ();
|
||||||
let file = crate::loader::parse_chunk (&mut rdr).unwrap ();
|
|
||||||
|
|
||||||
assert_eq! (file.blocks.len (), 5);
|
assert_eq! (file.blocks.len (), 5);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,12 @@ pub struct Block {
|
||||||
pub upvalues: Vec <Upvalue>,
|
pub upvalues: Vec <Upvalue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Clone, Debug)]
|
#[derive (Clone, Debug, Default)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
pub blocks: Vec <Block>,
|
pub blocks: Vec <Block>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Clone, Debug)]
|
#[derive (Clone, Debug, Default)]
|
||||||
pub 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
|
||||||
|
@ -133,11 +133,14 @@ pub enum StepOutput {
|
||||||
ChunkReturned (Vec <Value>),
|
ChunkReturned (Vec <Value>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Debug)]
|
#[derive (Debug, thiserror::Error)]
|
||||||
pub struct StepError {
|
pub enum StepError {
|
||||||
|
#[error ("generic")]
|
||||||
|
Generic {
|
||||||
frame: StackFrame,
|
frame: StackFrame,
|
||||||
inst: Instruction,
|
inst: Instruction,
|
||||||
msg: &'static str,
|
msg: &'static str,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
@ -148,11 +151,7 @@ impl State {
|
||||||
registers: vec! [Value::Nil; 256],
|
registers: vec! [Value::Nil; 256],
|
||||||
top: 0,
|
top: 0,
|
||||||
stack: vec! [
|
stack: vec! [
|
||||||
StackFrame {
|
StackFrame::default (),
|
||||||
program_counter: 0,
|
|
||||||
block_idx: 0,
|
|
||||||
register_offset: 0,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
debug_print: false,
|
debug_print: false,
|
||||||
step_count: 0,
|
step_count: 0,
|
||||||
|
@ -221,7 +220,7 @@ impl State {
|
||||||
|
|
||||||
fn make_step_error (&self, msg: &'static str, inst: &Instruction) -> StepError
|
fn make_step_error (&self, msg: &'static str, inst: &Instruction) -> StepError
|
||||||
{
|
{
|
||||||
StepError {
|
StepError::Generic {
|
||||||
frame: self.stack.last ().unwrap ().clone (),
|
frame: self.stack.last ().unwrap ().clone (),
|
||||||
inst: inst.clone (),
|
inst: inst.clone (),
|
||||||
msg,
|
msg,
|
||||||
|
@ -386,6 +385,9 @@ impl State {
|
||||||
upvalues: new_upvalues,
|
upvalues: new_upvalues,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
Instruction::Concat (a, b) => {
|
||||||
|
unimplemented! ("OP_CONCAT")
|
||||||
|
},
|
||||||
Instruction::Div (a, b, c) => {
|
Instruction::Div (a, b, c) => {
|
||||||
let v_b = self.reg (b);
|
let v_b = self.reg (b);
|
||||||
let v_c = self.reg (c);
|
let v_c = self.reg (c);
|
||||||
|
@ -760,7 +762,22 @@ impl State {
|
||||||
dst.insert_int (i64::from (c + i), src.clone ());
|
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) => {
|
Instruction::Sub (a, b, c) => {
|
||||||
let v_b = self.reg (b);
|
let v_b = self.reg (b);
|
||||||
let v_c = self.reg (c);
|
let v_c = self.reg (c);
|
||||||
|
@ -886,15 +903,20 @@ impl State {
|
||||||
Ok (None)
|
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> {
|
-> Result <Vec <Value>, StepError> {
|
||||||
let max_iters = 2000;
|
let max_iters = 2000;
|
||||||
|
|
||||||
for _ in 0..max_iters {
|
for _ in 0..max_iters {
|
||||||
if breakpoints.iter ().any (|bp| self.at_breakpoint (bp)) {
|
|
||||||
dbg! (&self);
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.step ()? {
|
match self.step ()? {
|
||||||
None => (),
|
None => (),
|
||||||
Some (StepOutput::ChunkReturned (x)) => return Ok (x),
|
Some (StepOutput::ChunkReturned (x)) => return Ok (x),
|
||||||
|
@ -904,4 +926,9 @@ impl State {
|
||||||
dbg! (self);
|
dbg! (self);
|
||||||
panic! ("Hit max iterations before block returned");
|
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> {
|
fn run_chunk (args: &[&str], chunk: Chunk) -> Vec <Value> {
|
||||||
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 ()));
|
||||||
let mut vm = State::new (chunk, upvalues);
|
let mut vm = State::new (chunk, upvalues);
|
||||||
vm.execute_chunk (&[]).unwrap ()
|
vm.execute ().unwrap ()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes arguments and Lua bytecode, loads it, runs it,
|
/// Takes arguments and Lua bytecode, loads it, runs it,
|
||||||
/// and return the output
|
/// and return the output
|
||||||
|
|
||||||
fn run_bytecode (args: &[&str], bc: &[u8]) -> Vec <Value> {
|
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)
|
run_chunk (args, chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 (s.as_bytes ().to_vec ()).unwrap ();
|
||||||
run_bytecode (args, &bc)
|
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 upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
||||||
let mut vm = State::new (chunk.clone (), upvalues);
|
let mut vm = State::new (chunk.clone (), upvalues);
|
||||||
let actual = vm.execute_chunk (&[]).unwrap ();
|
let actual = vm.execute ().unwrap ();
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,8 +128,8 @@ fn bools () {
|
||||||
#[test]
|
#[test]
|
||||||
fn closure () {
|
fn closure () {
|
||||||
let source = include_bytes! ("../test_vectors/closure.lua");
|
let source = include_bytes! ("../test_vectors/closure.lua");
|
||||||
let bytecode = &crate::loader::compile_bytecode (source.to_vec ());
|
let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
|
||||||
let chunk = crate::loader::parse_chunk_from_bytes (bytecode).unwrap ();
|
let chunk = crate::loader::parse_chunk (bytecode).unwrap ();
|
||||||
|
|
||||||
assert_eq! (run_chunk (&["_exe_name"], chunk), vec! [Value::from (23i64)]);
|
assert_eq! (run_chunk (&["_exe_name"], chunk), vec! [Value::from (23i64)]);
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ fn floats () {
|
||||||
let expected: Vec <Value> = expected;
|
let expected: Vec <Value> = expected;
|
||||||
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.clone (), upvalues);
|
let mut vm = State::new (chunk.clone (), upvalues);
|
||||||
let actual = vm.execute_chunk (&[]).unwrap ();
|
let actual = vm.execute ().unwrap ();
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -185,8 +185,8 @@ fn floats () {
|
||||||
#[test]
|
#[test]
|
||||||
fn fma () {
|
fn fma () {
|
||||||
let source = include_bytes! ("../test_vectors/fma.lua");
|
let source = include_bytes! ("../test_vectors/fma.lua");
|
||||||
let bytecode = &crate::loader::compile_bytecode (source.to_vec ());
|
let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
|
||||||
let chunk = crate::loader::parse_chunk_from_bytes (bytecode).unwrap ();
|
let chunk = crate::loader::parse_chunk (bytecode).unwrap ();
|
||||||
assert_eq! (chunk.blocks.len (), 5);
|
assert_eq! (chunk.blocks.len (), 5);
|
||||||
|
|
||||||
assert_eq! (chunk.blocks [3].upvalues.len (), 2);
|
assert_eq! (chunk.blocks [3].upvalues.len (), 2);
|
||||||
|
@ -194,12 +194,23 @@ fn fma () {
|
||||||
let arg = vec! ["_exe_name"];
|
let arg = vec! ["_exe_name"];
|
||||||
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 mut vm = State::new (chunk, upvalues);
|
||||||
let actual = vm.execute_chunk (&[]).unwrap ();
|
let actual = vm.execute ().unwrap ();
|
||||||
let expected = vec! [Value::from (122)];
|
let expected = vec! [Value::from (122)];
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
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]
|
#[test]
|
||||||
fn heap () {
|
fn heap () {
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -265,7 +276,7 @@ fn is_93 () {
|
||||||
assert_ne! (calculate_hash (&Value::from ("94")), calculate_hash (&Value::from ("93")));
|
assert_ne! (calculate_hash (&Value::from ("94")), calculate_hash (&Value::from ("93")));
|
||||||
assert_ne! (Value::Nil, Value::from ("93"));
|
assert_ne! (Value::Nil, Value::from ("93"));
|
||||||
|
|
||||||
let src = r#"
|
let src = br#"
|
||||||
if arg [1] == "93" then
|
if arg [1] == "93" then
|
||||||
print "it's 93"
|
print "it's 93"
|
||||||
return 0
|
return 0
|
||||||
|
@ -275,8 +286,8 @@ fn is_93 () {
|
||||||
end
|
end
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let bc = loader::compile_bytecode (src.as_bytes ().to_vec ());
|
let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
|
||||||
let chunk = loader::parse_chunk_from_bytes (&bc).unwrap ();
|
let chunk = loader::parse_chunk (&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));
|
||||||
|
|
||||||
|
@ -359,12 +370,12 @@ fn tables_2 () {
|
||||||
fn tailcall () {
|
fn tailcall () {
|
||||||
use crate::instruction::Instruction;
|
use crate::instruction::Instruction;
|
||||||
|
|
||||||
let src = r#"
|
let src = br#"
|
||||||
return tonumber ("5")
|
return tonumber ("5")
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let bc = loader::compile_bytecode (src.as_bytes ().to_vec ());
|
let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
|
||||||
let chunk = loader::parse_chunk_from_bytes (&bc).unwrap ();
|
let chunk = loader::parse_chunk (&bc).unwrap ();
|
||||||
|
|
||||||
assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false));
|
assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false));
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,8 @@ fn embedding () {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytecode = lwvm::compile_bytecode (src.to_vec ());
|
let bc = lwvm::compile_bytecode (src.to_vec ()).unwrap ();
|
||||||
let mut rdr = std::io::Cursor::new (bytecode);
|
let chunk = lwvm::parse_chunk (&bc).unwrap ();
|
||||||
let chunk = lwvm::parse_chunk (&mut rdr).unwrap ();
|
|
||||||
|
|
||||||
let host_lib = [
|
let host_lib = [
|
||||||
("add", Value::RsFunc (host_add)),
|
("add", Value::RsFunc (host_add)),
|
||||||
|
@ -36,7 +35,7 @@ fn embedding () {
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut vm = State::new (chunk, upvalues);
|
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)]);
|
assert_eq! (output, vec! [Value::from (2019)]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue