♻️ refactor: extract `State::eval` for testing bugs the REPL finds
							parent
							
								
									f9e8f26ac3
								
							
						
					
					
						commit
						8baea40e82
					
				|  | @ -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" | ||||
|  |  | |||
|  | @ -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), | ||||
| } | ||||
|  | @ -1,8 +1,10 @@ | |||
| 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; | ||||
|  |  | |||
|  | @ -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>) -> Result <Vec <u8>, String> { | ||||
| pub fn compile_bytecode (source: Vec <u8>) -> Result <Vec <u8>, Error> { | ||||
| 	use std::{ | ||||
| 		io::Write, | ||||
| 		process::{ | ||||
|  | @ -67,7 +73,7 @@ pub fn compile_bytecode (source: Vec <u8>) -> Result <Vec <u8>, String> { | |||
| 		Ok (output.stdout) | ||||
| 	} | ||||
| 	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.
 | ||||
| /// 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]; | ||||
| 	if buffer.starts_with (bytecode_header) { | ||||
| 		return Ok (buffer); | ||||
|  |  | |||
|  | @ -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 { | ||||
|  | @ -217,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, | ||||
|  | @ -900,6 +903,15 @@ impl State { | |||
| 		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) 
 | ||||
| 	-> Result <Vec <Value>, StepError> { | ||||
| 		let max_iters = 2000; | ||||
|  |  | |||
|  | @ -206,29 +206,9 @@ fn function_calls () { | |||
| 	
 | ||||
| 	let mut vm = crate::State::new (crate::Chunk::default (), upvalues); | ||||
| 	
 | ||||
| 	{ | ||||
| 		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 (); | ||||
| 	} | ||||
| 	
 | ||||
| 	{ | ||||
| 		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 (); | ||||
| 	} | ||||
| 	vm.eval ("print (x ())").ok (); | ||||
| 	vm.eval ("x = function () return 5 end").ok (); | ||||
| 	vm.eval ("print (x ())").ok (); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 _
						_