♻️ refactor: extract `State::eval` for testing bugs the REPL finds
parent
f9e8f26ac3
commit
8baea40e82
|
@ -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"
|
||||||
|
|
|
@ -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),
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
|
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;
|
||||||
|
|
|
@ -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>) -> Result <Vec <u8>, String> {
|
pub fn compile_bytecode (source: Vec <u8>) -> Result <Vec <u8>, Error> {
|
||||||
use std::{
|
use std::{
|
||||||
io::Write,
|
io::Write,
|
||||||
process::{
|
process::{
|
||||||
|
@ -67,7 +73,7 @@ pub fn compile_bytecode (source: Vec <u8>) -> Result <Vec <u8>, String> {
|
||||||
Ok (output.stdout)
|
Ok (output.stdout)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Err (String::from_utf8 (output.stderr).unwrap ())
|
Err (Error::Compile (String::from_utf8 (output.stderr).unwrap ()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +81,7 @@ pub fn compile_bytecode (source: Vec <u8>) -> Result <Vec <u8>, String> {
|
||||||
/// 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>) -> Result <Vec <u8>, String> {
|
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 Ok (buffer);
|
return Ok (buffer);
|
||||||
|
|
|
@ -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 {
|
||||||
frame: StackFrame,
|
#[error ("generic")]
|
||||||
inst: Instruction,
|
Generic {
|
||||||
msg: &'static str,
|
frame: StackFrame,
|
||||||
|
inst: Instruction,
|
||||||
|
msg: &'static str,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
@ -217,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,
|
||||||
|
@ -900,6 +903,15 @@ impl State {
|
||||||
Ok (None)
|
Ok (None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
pub fn execute (&mut self)
|
||||||
-> Result <Vec <Value>, StepError> {
|
-> Result <Vec <Value>, StepError> {
|
||||||
let max_iters = 2000;
|
let max_iters = 2000;
|
||||||
|
|
|
@ -206,29 +206,9 @@ fn function_calls () {
|
||||||
|
|
||||||
let mut vm = crate::State::new (crate::Chunk::default (), upvalues);
|
let mut vm = crate::State::new (crate::Chunk::default (), upvalues);
|
||||||
|
|
||||||
{
|
vm.eval ("print (x ())").ok ();
|
||||||
let bc = crate::compile_bytecode (b"print (x ())".to_vec ()).unwrap ();
|
vm.eval ("x = function () return 5 end").ok ();
|
||||||
let chunk = crate::parse_chunk (&bc).unwrap ();
|
vm.eval ("print (x ())").ok ();
|
||||||
|
|
||||||
vm.set_chunk (chunk);
|
|
||||||
vm.execute ().unwrap ();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let bc = crate::compile_bytecode (b"x = function () return 5 end".to_vec ()).unwrap ();
|
|
||||||
let chunk = crate::parse_chunk (&bc).unwrap ();
|
|
||||||
|
|
||||||
vm.set_chunk (chunk);
|
|
||||||
vm.execute ().unwrap ();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let bc = crate::compile_bytecode (b"print (x ())".to_vec ()).unwrap ();
|
|
||||||
let chunk = crate::parse_chunk (&bc).unwrap ();
|
|
||||||
|
|
||||||
vm.set_chunk (chunk);
|
|
||||||
vm.execute ().unwrap ();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in New Issue