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