♻️ refactor: State owns its Chunk now
							parent
							
								
									700b273a11
								
							
						
					
					
						commit
						db84365c27
					
				|  | @ -51,7 +51,7 @@ fn main () -> Result <(), lwvm::StepError> { | ||||||
| 	
 | 	
 | ||||||
| 	let upvalues = lwvm::State::upvalues_from_args ([exe_name].into_iter ().chain (args)); | 	let upvalues = lwvm::State::upvalues_from_args ([exe_name].into_iter ().chain (args)); | ||||||
| 	
 | 	
 | ||||||
| 	let mut vm = lwvm::State::new (&chunk, &upvalues); | 	let mut vm = lwvm::State::new (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; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -9,11 +9,9 @@ use crate::{ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub fn compile_bytecode_from_file (path: &str) -> Vec <u8> { | pub fn compile_bytecode_from_file (path: &str) -> Vec <u8> { | ||||||
| 	use std::{ | 	use std::process::{ | ||||||
| 		process::{ | 		Command, | ||||||
| 			Command, | 		Stdio, | ||||||
| 			Stdio, |  | ||||||
| 		}, |  | ||||||
| 	}; | 	}; | ||||||
| 	
 | 	
 | ||||||
| 	let child = Command::new ("luac5.4") | 	let child = Command::new ("luac5.4") | ||||||
|  |  | ||||||
|  | @ -1,5 +1,3 @@ | ||||||
| use std::rc::Rc; |  | ||||||
| 
 |  | ||||||
| use crate::{ | use crate::{ | ||||||
| 	instruction::Instruction, | 	instruction::Instruction, | ||||||
| 	value::{ | 	value::{ | ||||||
|  | @ -8,21 +6,21 @@ use crate::{ | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[derive (Debug)] | #[derive (Clone, Debug)] | ||||||
| pub struct Upvalue { | pub struct Upvalue { | ||||||
| 	pub in_stack: bool, | 	pub in_stack: bool, | ||||||
| 	pub idx: u8, | 	pub idx: u8, | ||||||
| 	pub kind: u8, | 	pub kind: u8, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive (Debug)] | #[derive (Clone, Debug)] | ||||||
| pub struct Block { | pub struct Block { | ||||||
| 	pub instructions: Vec <Instruction>, | 	pub instructions: Vec <Instruction>, | ||||||
| 	pub constants: Vec <Value>, | 	pub constants: Vec <Value>, | ||||||
| 	pub upvalues: Vec <Upvalue>, | 	pub upvalues: Vec <Upvalue>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive (Debug)] | #[derive (Clone, Debug)] | ||||||
| pub struct Chunk { | pub struct Chunk { | ||||||
| 	pub blocks: Vec <Block>, | 	pub blocks: Vec <Block>, | ||||||
| } | } | ||||||
|  | @ -47,7 +45,7 @@ pub struct Breakpoint { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive (Debug)] | #[derive (Debug)] | ||||||
| pub struct State <'a> { | pub struct State { | ||||||
| 	pub registers: Vec <Value>, | 	pub registers: Vec <Value>, | ||||||
| 	// Currently only used for native function calls
 | 	// Currently only used for native function calls
 | ||||||
| 	top: usize, | 	top: usize, | ||||||
|  | @ -55,8 +53,8 @@ pub struct State <'a> { | ||||||
| 	
 | 	
 | ||||||
| 	pub debug_print: bool, | 	pub debug_print: bool, | ||||||
| 	step_count: u32, | 	step_count: u32, | ||||||
| 	chunk: &'a Chunk, | 	chunk: Chunk, | ||||||
| 	upvalues: &'a [Value], | 	upvalues: Vec <Value>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn lw_io_write (l: &mut State, num_args: usize) -> usize { | fn lw_io_write (l: &mut State, num_args: usize) -> usize { | ||||||
|  | @ -142,8 +140,8 @@ pub struct StepError { | ||||||
| 	msg: &'static str, | 	msg: &'static str, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl <'a> State <'a> { | impl State { | ||||||
| 	pub fn new (chunk: &'a Chunk, upvalues: &'a [Value]) -> Self { | 	pub fn new (chunk: Chunk, upvalues: Vec <Value>) -> Self { | ||||||
| 		Self { | 		Self { | ||||||
| 			// TODO: Stack is actually supposed to grow to a limit of
 | 			// TODO: Stack is actually supposed to grow to a limit of
 | ||||||
| 			// idk 10,000. I thought it was fixed at 256.
 | 			// idk 10,000. I thought it was fixed at 256.
 | ||||||
|  | @ -232,11 +230,10 @@ impl <'a> State <'a> { | ||||||
| 	
 | 	
 | ||||||
| 	pub fn step (&mut self) -> Result <Option <StepOutput>, StepError> 
 | 	pub fn step (&mut self) -> Result <Option <StepOutput>, StepError> 
 | ||||||
| 	{ | 	{ | ||||||
| 		let chunk = self.chunk; |  | ||||||
| 		self.step_count += 1; | 		self.step_count += 1; | ||||||
| 		
 | 		
 | ||||||
| 		let frame = self.stack.last_mut ().unwrap ().clone (); | 		let frame = self.stack.last_mut ().unwrap ().clone (); | ||||||
| 		let block = chunk.blocks.get (frame.block_idx).unwrap (); | 		let block = self.chunk.blocks.get (frame.block_idx).unwrap (); | ||||||
| 		
 | 		
 | ||||||
| 		let mut next_pc = frame.program_counter; | 		let mut next_pc = frame.program_counter; | ||||||
| 		
 | 		
 | ||||||
|  | @ -256,10 +253,10 @@ impl <'a> State <'a> { | ||||||
| 			self.make_step_error (msg, instruction) | 			self.make_step_error (msg, instruction) | ||||||
| 		}; | 		}; | ||||||
| 		
 | 		
 | ||||||
| 		match instruction { | 		match instruction.clone () { | ||||||
| 			Instruction::Add (a, b, c) => { | 			Instruction::Add (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); | ||||||
| 				
 | 				
 | ||||||
| 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | ||||||
| 				{ | 				{ | ||||||
|  | @ -271,24 +268,24 @@ impl <'a> State <'a> { | ||||||
| 					Value::from (v_b + v_c) | 					Value::from (v_b + v_c) | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = x; | 				*self.reg_mut (a) = x; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::AddI (a, b, s_c) => { | 			Instruction::AddI (a, b, s_c) => { | ||||||
| 				let v_b = self.reg (*b); | 				let v_b = self.reg (b); | ||||||
| 				
 | 				
 | ||||||
| 				let x = if let Some (v_b) = v_b.as_int () 
 | 				let x = if let Some (v_b) = v_b.as_int () 
 | ||||||
| 				{ | 				{ | ||||||
| 					Value::from (v_b + *s_c as i64) | 					Value::from (v_b + s_c as i64) | ||||||
| 				} | 				} | ||||||
| 				else { | 				else { | ||||||
| 					let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}")); | 					let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}")); | ||||||
| 					Value::from (v_b + f64::from (*s_c)) | 					Value::from (v_b + f64::from (s_c)) | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = x; | 				*self.reg_mut (a) = x; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::Call (a, b, c) => { | 			Instruction::Call (a, b, c) => { | ||||||
| 				let b = usize::from (*b); | 				let b = usize::from (b); | ||||||
| 				
 | 				
 | ||||||
| 				// Take arguments from registers [a + 1, a + b)
 | 				// Take arguments from registers [a + 1, a + b)
 | ||||||
| 				// Call the function in register [a]
 | 				// Call the function in register [a]
 | ||||||
|  | @ -301,7 +298,7 @@ impl <'a> State <'a> { | ||||||
| 				// Do a clone here to avoid a borow problem.
 | 				// Do a clone here to avoid a borow problem.
 | ||||||
| 				// Should be fixable with more clever code.
 | 				// Should be fixable with more clever code.
 | ||||||
| 				
 | 				
 | ||||||
| 				let v_a = self.reg (*a).clone (); | 				let v_a = self.reg (a).clone (); | ||||||
| 				
 | 				
 | ||||||
| 				match v_a { | 				match v_a { | ||||||
| 					Value::BogusClosure (rc) => { | 					Value::BogusClosure (rc) => { | ||||||
|  | @ -315,7 +312,7 @@ impl <'a> State <'a> { | ||||||
| 						self.stack.push (StackFrame { | 						self.stack.push (StackFrame { | ||||||
| 							program_counter: 0, | 							program_counter: 0, | ||||||
| 							block_idx: target_block, | 							block_idx: target_block, | ||||||
| 							register_offset: current_frame.register_offset + *a as usize + 1, | 							register_offset: current_frame.register_offset + a as usize + 1, | ||||||
| 						}); | 						}); | ||||||
| 						
 | 						
 | ||||||
| 						if self.debug_print { | 						if self.debug_print { | ||||||
|  | @ -329,7 +326,7 @@ impl <'a> State <'a> { | ||||||
| 					}, | 					}, | ||||||
| 					Value::RsFunc (x) => { | 					Value::RsFunc (x) => { | ||||||
| 						let current_frame = self.stack.last ().unwrap (); | 						let current_frame = self.stack.last ().unwrap (); | ||||||
| 						let new_offset = current_frame.register_offset + usize::from (*a) + 1; | 						let new_offset = current_frame.register_offset + usize::from (a) + 1; | ||||||
| 						
 | 						
 | ||||||
| 						// Trash the stack frame so it doesn't point to a
 | 						// Trash the stack frame so it doesn't point to a
 | ||||||
| 						// valid Lua function
 | 						// valid Lua function
 | ||||||
|  | @ -340,7 +337,7 @@ impl <'a> State <'a> { | ||||||
| 						}); | 						}); | ||||||
| 						
 | 						
 | ||||||
| 						let num_args = if b == 0 { | 						let num_args = if b == 0 { | ||||||
| 							self.top - *a as usize | 							self.top - a as usize | ||||||
| 						} | 						} | ||||||
| 						else { | 						else { | ||||||
| 							b - 1 | 							b - 1 | ||||||
|  | @ -357,7 +354,7 @@ impl <'a> State <'a> { | ||||||
| 						} | 						} | ||||||
| 						
 | 						
 | ||||||
| 						// Set up top for the next call
 | 						// Set up top for the next call
 | ||||||
| 						if *c == 0 { | 						if c == 0 { | ||||||
| 							self.top = popped_frame.register_offset - 1 + num_results; | 							self.top = popped_frame.register_offset - 1 + num_results; | ||||||
| 						} | 						} | ||||||
| 					}, | 					}, | ||||||
|  | @ -367,10 +364,10 @@ impl <'a> State <'a> { | ||||||
| 				} | 				} | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::Closure (a, b) => { | 			Instruction::Closure (a, b) => { | ||||||
| 				let b = usize::try_from (*b).unwrap (); | 				let b = usize::try_from (b).unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				let idx = frame.block_idx + b + 1; | 				let idx = frame.block_idx + b + 1; | ||||||
| 				let block = &chunk.blocks [idx]; | 				let block = &self.chunk.blocks [idx]; | ||||||
| 				
 | 				
 | ||||||
| 				let mut new_upvalues = Vec::with_capacity (block.upvalues.len ()); | 				let mut new_upvalues = Vec::with_capacity (block.upvalues.len ()); | ||||||
| 				for uv in &block.upvalues { | 				for uv in &block.upvalues { | ||||||
|  | @ -384,31 +381,31 @@ impl <'a> State <'a> { | ||||||
| 					new_upvalues.push (val); | 					new_upvalues.push (val); | ||||||
| 				} | 				} | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = Value::from (BogusClosure { | 				*self.reg_mut (a) = Value::from (BogusClosure { | ||||||
| 					idx, | 					idx, | ||||||
| 					upvalues: new_upvalues, | 					upvalues: new_upvalues, | ||||||
| 				}); | 				}); | ||||||
| 			}, | 			}, | ||||||
| 			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); | ||||||
| 				
 | 				
 | ||||||
| 				let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}")); | 				let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}")); | ||||||
| 				
 | 				
 | ||||||
| 				let v_c = v_c.as_float ().ok_or_else (|| make_step_error ("C must be a number"))?; | 				let v_c = v_c.as_float ().ok_or_else (|| make_step_error ("C must be a number"))?; | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = Value::from (v_b / v_c); | 				*self.reg_mut (a) = Value::from (v_b / v_c); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::EqI (a, sb, k_flag) => { | 			Instruction::EqI (a, sb, k_flag) => { | ||||||
| 				if (self.reg (*a).as_int ().unwrap () == *sb as i64) != *k_flag 
 | 				if (self.reg (a).as_int ().unwrap () == sb as i64) != k_flag 
 | ||||||
| 				{ | 				{ | ||||||
| 					next_pc += 1; | 					next_pc += 1; | ||||||
| 				} | 				} | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::EqK (a, b, k_flag) => { | 			Instruction::EqK (a, b, k_flag) => { | ||||||
| 				let b = usize::from (*b); | 				let b = usize::from (b); | ||||||
| 				
 | 				
 | ||||||
| 				if (*self.reg (*a) == k [b]) != *k_flag { | 				if (*self.reg (a) == k [b]) != k_flag { | ||||||
| 					next_pc += 1; | 					next_pc += 1; | ||||||
| 				} | 				} | ||||||
| 			}, | 			}, | ||||||
|  | @ -416,60 +413,60 @@ impl <'a> State <'a> { | ||||||
| 				// This is used for NewTable. Maybe it's for reserving
 | 				// This is used for NewTable. Maybe it's for reserving
 | ||||||
| 				// capacity in the array or something?
 | 				// capacity in the array or something?
 | ||||||
| 				
 | 				
 | ||||||
| 				assert_eq! (*ax, 0, "implemented only for ax == 0"); | 				assert_eq! (ax, 0, "implemented only for ax == 0"); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::ForLoop (a, bx) => { | 			Instruction::ForLoop (a, bx) => { | ||||||
| 				let mut iter = self.reg (*a + 3).as_int ().unwrap (); | 				let mut iter = self.reg (a + 3).as_int ().unwrap (); | ||||||
| 				iter += 1; | 				iter += 1; | ||||||
| 				*self.reg_mut (*a + 3) = iter.into (); | 				*self.reg_mut (a + 3) = iter.into (); | ||||||
| 				
 | 				
 | ||||||
| 				let stop = self.reg (*a + 1).as_int ().unwrap (); | 				let stop = self.reg (a + 1).as_int ().unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				if iter <= stop { | 				if iter <= stop { | ||||||
| 					next_pc -= i32::try_from (*bx).unwrap (); | 					next_pc -= i32::try_from (bx).unwrap (); | ||||||
| 				} | 				} | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::ForPrep (a, bx) => { | 			Instruction::ForPrep (a, bx) => { | ||||||
| 				let start = self.reg (*a).as_int ().unwrap (); | 				let start = self.reg (a).as_int ().unwrap (); | ||||||
| 				let stop = self.reg (*a + 1).as_int ().unwrap (); | 				let stop = self.reg (a + 1).as_int ().unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				if start > stop { | 				if start > stop { | ||||||
| 					next_pc += i32::try_from (*bx).unwrap () + 1; | 					next_pc += i32::try_from (bx).unwrap () + 1; | ||||||
| 				} | 				} | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a + 3) = start.into (); | 				*self.reg_mut (a + 3) = start.into (); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::GetField (a, b, c) => { | 			Instruction::GetField (a, b, c) => { | ||||||
| 				let t = match self.reg (*b) { | 				let t = match self.reg (b) { | ||||||
| 					Value::Nil => Err (make_step_error ("R[B] must not be nil"))?, | 					Value::Nil => Err (make_step_error ("R[B] must not be nil"))?, | ||||||
| 					Value::Table (t) => t, | 					Value::Table (t) => t, | ||||||
| 					_ => Err (make_step_error ("R[B] must be a table"))?, | 					_ => Err (make_step_error ("R[B] must be a table"))?, | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				let key = match &k [usize::from (*c)] { | 				let key = match &k [usize::from (c)] { | ||||||
| 					Value::String (s) => s, | 					Value::String (s) => s, | ||||||
| 					_ => panic! ("K[C] must be a string"), | 					_ => panic! ("K[C] must be a string"), | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				let val = t.borrow ().get_str (key.as_str ()).clone (); | 				let val = t.borrow ().get_str (key.as_str ()).clone (); | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = val; | 				*self.reg_mut (a) = val; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::GetTable (a, b, c) => { | 			Instruction::GetTable (a, b, c) => { | ||||||
| 				let t = match self.reg (*b) { | 				let t = match self.reg (b) { | ||||||
| 					Value::Table (t) => t, | 					Value::Table (t) => t, | ||||||
| 					_ => panic! ("R[B] must be a table"), | 					_ => panic! ("R[B] must be a table"), | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				let key = self.reg (*c); | 				let key = self.reg (c); | ||||||
| 				
 | 				
 | ||||||
| 				let val = t.borrow ().get (key.clone ()); | 				let val = t.borrow ().get (key.clone ()); | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = val; | 				*self.reg_mut (a) = val; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::GetTabUp (a, b, c) => { | 			Instruction::GetTabUp (a, b, c) => { | ||||||
| 				let b = usize::try_from (*b).unwrap (); | 				let b = usize::try_from (b).unwrap (); | ||||||
| 				let c = usize::try_from (*c).unwrap (); | 				let c = usize::try_from (c).unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				// If we're inside a closure, use its upvalues
 | 				// If we're inside a closure, use its upvalues
 | ||||||
| 				// instead of the chunk's upvalues
 | 				// instead of the chunk's upvalues
 | ||||||
|  | @ -495,17 +492,17 @@ impl <'a> State <'a> { | ||||||
| 					_ => panic! ("GetTabUp only supports string keys"), | 					_ => panic! ("GetTabUp only supports string keys"), | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = table.get (key); | 				*self.reg_mut (a) = table.get (key); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::GetI (a, b, c) => { | 			Instruction::GetI (a, b, c) => { | ||||||
| 				let key = i64::try_from (*c).unwrap (); | 				let key = i64::try_from (c).unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				let value = { | 				let value = { | ||||||
| 					let table = self.reg (*b).as_table ().expect ("GetI only works on tables").borrow (); | 					let table = self.reg (b).as_table ().expect ("GetI only works on tables").borrow (); | ||||||
| 					table.get_int (key) | 					table.get_int (key) | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = value; | 				*self.reg_mut (a) = value; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::GetUpVal (a, b) => { | 			Instruction::GetUpVal (a, b) => { | ||||||
| 				let this_func = self.stack.last ().unwrap ().register_offset - 1; | 				let this_func = self.stack.last ().unwrap ().register_offset - 1; | ||||||
|  | @ -514,20 +511,20 @@ impl <'a> State <'a> { | ||||||
| 					_ => panic! ("Can't do GetUpVal outside a closure"), | 					_ => panic! ("Can't do GetUpVal outside a closure"), | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				let b  = usize::try_from  (*b).unwrap (); | 				let b  = usize::try_from  (b).unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				let upvalue = match closure.borrow ().upvalues.get (b) { | 				let upvalue = match closure.borrow ().upvalues.get (b) { | ||||||
| 					Some (x) => x.clone (), | 					Some (x) => x.clone (), | ||||||
| 					None => { | 					None => { | ||||||
| 						dbg! (chunk, &self); | 						dbg! (&self); | ||||||
| 						panic! ("Missing upvalue"); | 						panic! ("Missing upvalue"); | ||||||
| 					} | 					} | ||||||
| 				}; | 				}; | ||||||
| 				*self.reg_mut (*a) = upvalue; | 				*self.reg_mut (a) = upvalue; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::Jmp (s_j) => next_pc += s_j, | 			Instruction::Jmp (s_j) => next_pc += s_j, | ||||||
| 			Instruction::Len (a, b) => { | 			Instruction::Len (a, b) => { | ||||||
| 				let len = match self.reg (*b) { | 				let len = match self.reg (b) { | ||||||
| 					Value::BogusClosure (_) => Err (make_step_error ("attempt to get length of a function value"))?, | 					Value::BogusClosure (_) => Err (make_step_error ("attempt to get length of a function value"))?, | ||||||
| 					Value::Boolean (_) => Err (make_step_error ("attempt to get length of a boolean value"))?, | 					Value::Boolean (_) => Err (make_step_error ("attempt to get length of a boolean value"))?, | ||||||
| 					Value::Float (_) => Err (make_step_error ("attempt to get length of a number value"))?, | 					Value::Float (_) => Err (make_step_error ("attempt to get length of a number value"))?, | ||||||
|  | @ -538,31 +535,31 @@ impl <'a> State <'a> { | ||||||
| 					Value::Table (t) => t.borrow ().length ().into (), | 					Value::Table (t) => t.borrow ().length ().into (), | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = len; | 				*self.reg_mut (a) = len; | ||||||
| 			} | 			} | ||||||
| 			Instruction::LoadF (a, sbx) => { | 			Instruction::LoadF (a, sbx) => { | ||||||
| 				*self.reg_mut (*a) = Value::Float (*sbx as f64); | 				*self.reg_mut (a) = Value::Float (sbx as f64); | ||||||
| 			} | 			} | ||||||
| 			Instruction::LoadFalse (a) => { | 			Instruction::LoadFalse (a) => { | ||||||
| 				*self.reg_mut (*a) = false.into (); | 				*self.reg_mut (a) = false.into (); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::LoadI (a, sbx) => { | 			Instruction::LoadI (a, sbx) => { | ||||||
| 				*self.reg_mut (*a) = Value::Integer (*sbx as i64); | 				*self.reg_mut (a) = Value::Integer (sbx as i64); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::LoadK (a, bx) => { | 			Instruction::LoadK (a, bx) => { | ||||||
| 				let bx = usize::try_from (*bx).unwrap (); | 				let bx = usize::try_from (bx).unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = k [bx].clone (); | 				*self.reg_mut (a) = k [bx].clone (); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::LoadNil (a) => { | 			Instruction::LoadNil (a) => { | ||||||
| 				*self.reg_mut (*a) = Value::Nil; | 				*self.reg_mut (a) = Value::Nil; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::LoadTrue (a) => { | 			Instruction::LoadTrue (a) => { | ||||||
| 				*self.reg_mut (*a) = true.into (); | 				*self.reg_mut (a) = true.into (); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::MmBin (a, b, _c) => { | 			Instruction::MmBin (a, b, _c) => { | ||||||
| 				let a = self.reg (*a); | 				let a = self.reg (a); | ||||||
| 				let b = self.reg (*b); | 				let b = self.reg (b); | ||||||
| 				
 | 				
 | ||||||
| 				if a.as_float().is_some() && b.as_float().is_some () { | 				if a.as_float().is_some() && b.as_float().is_some () { | ||||||
| 					// No need for metamethods
 | 					// No need for metamethods
 | ||||||
|  | @ -578,20 +575,20 @@ impl <'a> State <'a> { | ||||||
| 				// Ignore
 | 				// Ignore
 | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::ModK (a, b, c) => { | 			Instruction::ModK (a, b, c) => { | ||||||
| 				let b = self.reg (*b).as_int().unwrap (); | 				let b = self.reg (b).as_int().unwrap (); | ||||||
| 				let c = k [usize::from (*c)].as_int ().unwrap (); | 				let c = k [usize::from (c)].as_int ().unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = (b % c).into (); | 				*self.reg_mut (a) = (b % c).into (); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::Move (a, b) => { | 			Instruction::Move (a, b) => { | ||||||
| 				// If the value in b is deleted instead of duplicated,
 | 				// If the value in b is deleted instead of duplicated,
 | ||||||
| 				// a bunch of tests fail
 | 				// a bunch of tests fail
 | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = self.reg (*b).clone (); | 				*self.reg_mut (a) = self.reg (b).clone (); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::Mul (a, b, c) => { | 			Instruction::Mul (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); | ||||||
| 				
 | 				
 | ||||||
| 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | ||||||
| 				{ | 				{ | ||||||
|  | @ -603,11 +600,11 @@ impl <'a> State <'a> { | ||||||
| 					Value::from (v_b * v_c) | 					Value::from (v_b * v_c) | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = x; | 				*self.reg_mut (a) = x; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::MulK (a, b, c) => { | 			Instruction::MulK (a, b, c) => { | ||||||
| 				let v_b = self.reg (*b); | 				let v_b = self.reg (b); | ||||||
| 				let v_c = &k [usize::from (*c)]; | 				let v_c = &k [usize::from (c)]; | ||||||
| 				
 | 				
 | ||||||
| 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | ||||||
| 				{ | 				{ | ||||||
|  | @ -619,30 +616,30 @@ impl <'a> State <'a> { | ||||||
| 					Value::from (v_b * v_c) | 					Value::from (v_b * v_c) | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = x; | 				*self.reg_mut (a) = x; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::NewTable (a) => { | 			Instruction::NewTable (a) => { | ||||||
| 				*self.reg_mut (*a) = Value::Table (Default::default ()); | 				*self.reg_mut (a) = Value::Table (Default::default ()); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::Not (a, b) => { | 			Instruction::Not (a, b) => { | ||||||
| 				*self.reg_mut (*a) = Value::Boolean (! self.reg (*b).is_truthy()); | 				*self.reg_mut (a) = Value::Boolean (! self.reg (b).is_truthy()); | ||||||
| 			} | 			} | ||||||
| 			Instruction::Return (a, b, _c, k) => { | 			Instruction::Return (a, b, _c, k_flag) => { | ||||||
| 				let a = usize::try_from (*a).unwrap (); | 				let a = usize::try_from (a).unwrap (); | ||||||
| 				let b = usize::try_from (*b).unwrap (); | 				let b = usize::try_from (b).unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				let popped_frame = self.stack.pop ().unwrap (); | 				let popped_frame = self.stack.pop ().unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				// Build closure if needed. No point building if we're
 | 				// Build closure if needed. No point building if we're
 | ||||||
| 				// popping the last frame and exiting the program.
 | 				// popping the last frame and exiting the program.
 | ||||||
| 				
 | 				
 | ||||||
| 				if *k && ! self.stack.is_empty () { | 				if k_flag && ! self.stack.is_empty () { | ||||||
| 					let closure_idx = match &self.registers [popped_frame.register_offset + a] { | 					let closure_idx = match &self.registers [popped_frame.register_offset + a] { | ||||||
| 						Value::BogusClosure (rc) => rc.borrow ().idx, | 						Value::BogusClosure (rc) => rc.borrow ().idx, | ||||||
| 						_ => panic! ("Impossible"), | 						_ => panic! ("Impossible"), | ||||||
| 					}; | 					}; | ||||||
| 					
 | 					
 | ||||||
| 					let upvalue_count = chunk.blocks [closure_idx].upvalues.len (); | 					let upvalue_count = self.chunk.blocks [closure_idx].upvalues.len (); | ||||||
| 					
 | 					
 | ||||||
| 					let start_reg = a + popped_frame.register_offset - upvalue_count; | 					let start_reg = a + popped_frame.register_offset - upvalue_count; | ||||||
| 					
 | 					
 | ||||||
|  | @ -690,7 +687,7 @@ impl <'a> State <'a> { | ||||||
| 				self.top = popped_frame.register_offset - 1 + 0; | 				self.top = popped_frame.register_offset - 1 + 0; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::Return1 (a) => { | 			Instruction::Return1 (a) => { | ||||||
| 				let a = usize::try_from (*a).unwrap (); | 				let a = usize::try_from (a).unwrap (); | ||||||
| 				let popped_frame = self.stack.pop ().unwrap (); | 				let popped_frame = self.stack.pop ().unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				
 | 				
 | ||||||
|  | @ -715,58 +712,58 @@ impl <'a> State <'a> { | ||||||
| 				self.top = popped_frame.register_offset - 1 + 1; | 				self.top = popped_frame.register_offset - 1 + 1; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::SetField (a, b, c, k_flag) => { | 			Instruction::SetField (a, b, c, k_flag) => { | ||||||
| 				let value = if *k_flag { | 				let value = if k_flag { | ||||||
| 					&k [usize::from (*c)] | 					&k [usize::from (c)] | ||||||
| 				} | 				} | ||||||
| 				else { | 				else { | ||||||
| 					self.reg (*c) | 					self.reg (c) | ||||||
| 				} | 				} | ||||||
| 				.clone (); | 				.clone (); | ||||||
| 				
 | 				
 | ||||||
| 				let b = usize::try_from (*b).unwrap (); | 				let b = usize::try_from (b).unwrap (); | ||||||
| 				
 | 				
 | ||||||
| 				let key = match k.get (b).unwrap () { | 				let key = match k.get (b).unwrap () { | ||||||
| 					Value::String (s) => s.as_ref (), | 					Value::String (s) => s.as_ref (), | ||||||
| 					_ => panic! ("SetField only supports string keys"), | 					_ => panic! ("SetField only supports string keys"), | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				let mut dst = self.reg (*a).as_table () | 				let mut dst = self.reg (a).as_table () | ||||||
| 				.expect ("SetField only works on tables").borrow_mut (); | 				.expect ("SetField only works on tables").borrow_mut (); | ||||||
| 				
 | 				
 | ||||||
| 				dst.insert_str (key.as_str (), value); | 				dst.insert_str (key.as_str (), value); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::SetI (a, b, c, k_flag) => { | 			Instruction::SetI (a, b, c, k_flag) => { | ||||||
| 				let value = if *k_flag { | 				let value = if k_flag { | ||||||
| 					&k [usize::from (*c)] | 					&k [usize::from (c)] | ||||||
| 				} | 				} | ||||||
| 				else { | 				else { | ||||||
| 					self.reg (*c) | 					self.reg (c) | ||||||
| 				} | 				} | ||||||
| 				.clone (); | 				.clone (); | ||||||
| 				
 | 				
 | ||||||
| 				let mut dst = self.reg_mut (*a).as_table ().expect ("SetI only works on tables").borrow_mut (); | 				let mut dst = self.reg_mut (a).as_table ().expect ("SetI only works on tables").borrow_mut (); | ||||||
| 				
 | 				
 | ||||||
| 				dst.insert_int (i64::from (*b), value); | 				dst.insert_int (i64::from (b), value); | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::SetList (a, b, c, k) => { | 			Instruction::SetList (a, b, c, k_flag) => { | ||||||
| 				if *b == 0 { | 				if b == 0 { | ||||||
| 					panic! ("SetList with b == 0 not implemented"); | 					panic! ("SetList with b == 0 not implemented"); | ||||||
| 				} | 				} | ||||||
| 				if *k { | 				if k_flag { | ||||||
| 					panic! ("SetList with k = true not implemented"); | 					panic! ("SetList with k = true not implemented"); | ||||||
| 				} | 				} | ||||||
| 				
 | 				
 | ||||||
| 				let mut dst = self.reg (*a).as_table ().expect ("SetList only works on tables").borrow_mut (); | 				let mut dst = self.reg (a).as_table ().expect ("SetList only works on tables").borrow_mut (); | ||||||
| 				
 | 				
 | ||||||
| 				for i in 1..=*b { | 				for i in 1..=b { | ||||||
| 					let src = self.reg (*a + i); | 					let src = self.reg (a + i); | ||||||
| 					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) => unimplemented! (), | ||||||
| 			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); | ||||||
| 				
 | 				
 | ||||||
| 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | ||||||
| 				{ | 				{ | ||||||
|  | @ -778,10 +775,10 @@ impl <'a> State <'a> { | ||||||
| 					Value::from (v_b - v_c) | 					Value::from (v_b - v_c) | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = x; | 				*self.reg_mut (a) = x; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::TailCall (a, b, c, k) => { | 			Instruction::TailCall (a, b, c, k) => { | ||||||
| 				let a = usize::from (*a); | 				let a = usize::from (a); | ||||||
| 				assert! (!k, "closing over values in tail calls not implemented"); | 				assert! (!k, "closing over values in tail calls not implemented"); | ||||||
| 				
 | 				
 | ||||||
| 				let offset = frame.register_offset; | 				let offset = frame.register_offset; | ||||||
|  | @ -792,7 +789,7 @@ impl <'a> State <'a> { | ||||||
| 						
 | 						
 | ||||||
| 						// Shift inputs into place
 | 						// Shift inputs into place
 | ||||||
| 						
 | 						
 | ||||||
| 						let b = usize::from (*b); | 						let b = usize::from (b); | ||||||
| 						
 | 						
 | ||||||
| 						let num_args = if b == 0 { | 						let num_args = if b == 0 { | ||||||
| 							self.top - a | 							self.top - a | ||||||
|  | @ -816,7 +813,7 @@ impl <'a> State <'a> { | ||||||
| 					}, | 					}, | ||||||
| 					Value::RsFunc (x) => { | 					Value::RsFunc (x) => { | ||||||
| 						// Shift inputs into place
 | 						// Shift inputs into place
 | ||||||
| 						let b = usize::from (*b); | 						let b = usize::from (b); | ||||||
| 						for i in (offset)..(offset + b) { | 						for i in (offset)..(offset + b) { | ||||||
| 							self.registers [i] = self.registers [i + a + 1].take (); | 							self.registers [i] = self.registers [i + a + 1].take (); | ||||||
| 						} | 						} | ||||||
|  | @ -847,7 +844,7 @@ impl <'a> State <'a> { | ||||||
| 						} | 						} | ||||||
| 						else { | 						else { | ||||||
| 							// Set up top for the next call
 | 							// Set up top for the next call
 | ||||||
| 							if *c == 0 { | 							if c == 0 { | ||||||
| 								self.top = popped_frame.register_offset - 1 + num_results; | 								self.top = popped_frame.register_offset - 1 + num_results; | ||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
|  | @ -858,13 +855,13 @@ impl <'a> State <'a> { | ||||||
| 					}, | 					}, | ||||||
| 				} | 				} | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::Test (a, k) => { | 			Instruction::Test (a, k_flag) => { | ||||||
| 				if self.reg (*a).is_truthy() != *k { | 				if self.reg (a).is_truthy() != k_flag { | ||||||
| 					next_pc += 1; | 					next_pc += 1; | ||||||
| 				} | 				} | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::UnM (a, b) => { | 			Instruction::UnM (a, b) => { | ||||||
| 				let v_b = self.reg (*b); | 				let v_b = self.reg (b); | ||||||
| 				
 | 				
 | ||||||
| 				let x = if let Some (v_b) = v_b.as_int () 
 | 				let x = if let Some (v_b) = v_b.as_int () 
 | ||||||
| 				{ | 				{ | ||||||
|  | @ -875,7 +872,7 @@ impl <'a> State <'a> { | ||||||
| 					Value::from (-v_b) | 					Value::from (-v_b) | ||||||
| 				}; | 				}; | ||||||
| 				
 | 				
 | ||||||
| 				*self.reg_mut (*a) = x; | 				*self.reg_mut (a) = x; | ||||||
| 			}, | 			}, | ||||||
| 			Instruction::VarArgPrep (_) => (), | 			Instruction::VarArgPrep (_) => (), | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -22,9 +22,9 @@ fn calculate_hash<T: Hash>(t: &T) -> u64 { | ||||||
| /// Takes arguments and a parsed Lua chunk, runs its,
 | /// Takes arguments and a parsed Lua chunk, runs its,
 | ||||||
| /// and returns the output
 | /// and returns the output
 | ||||||
| 
 | 
 | ||||||
| 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_chunk (&[]).unwrap () | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -33,7 +33,7 @@ fn run_chunk (args: &[&str], chunk: &Chunk) -> Vec <Value> { | ||||||
| 
 | 
 | ||||||
| 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_from_bytes (&bc).unwrap (); | ||||||
| 	run_chunk (args, &chunk) | 	run_chunk (args, chunk) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Takes arguments and Lua source code, 
 | /// Takes arguments and Lua source code, 
 | ||||||
|  | @ -119,7 +119,7 @@ fn bools () { | ||||||
| 		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, &upvalues); | 		let mut vm = State::new (chunk.clone (), upvalues); | ||||||
| 		let actual = vm.execute_chunk (&[]).unwrap (); | 		let actual = vm.execute_chunk (&[]).unwrap (); | ||||||
| 		assert_eq! (actual, expected); | 		assert_eq! (actual, expected); | ||||||
| 	} | 	} | ||||||
|  | @ -131,7 +131,7 @@ fn closure () { | ||||||
| 	let bytecode = &crate::loader::compile_bytecode_from_stdin (source.to_vec ()); | 	let bytecode = &crate::loader::compile_bytecode_from_stdin (source.to_vec ()); | ||||||
| 	let chunk = crate::loader::parse_chunk_from_bytes (bytecode).unwrap (); | 	let chunk = crate::loader::parse_chunk_from_bytes (bytecode).unwrap (); | ||||||
| 	
 | 	
 | ||||||
| 	assert_eq! (run_chunk (&["_exe_name"], &chunk), vec! [Value::from (23i64)]); | 	assert_eq! (run_chunk (&["_exe_name"], chunk), vec! [Value::from (23i64)]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[test] | #[test] | ||||||
|  | @ -175,7 +175,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, &upvalues); | 		let mut vm = State::new (chunk.clone (), upvalues); | ||||||
| 		let actual = vm.execute_chunk (&[]).unwrap (); | 		let actual = vm.execute_chunk (&[]).unwrap (); | ||||||
| 		
 | 		
 | ||||||
| 		assert_eq! (actual, expected); | 		assert_eq! (actual, expected); | ||||||
|  | @ -191,17 +191,13 @@ fn fma () { | ||||||
| 	
 | 	
 | ||||||
| 	assert_eq! (chunk.blocks [3].upvalues.len (), 2); | 	assert_eq! (chunk.blocks [3].upvalues.len (), 2); | ||||||
| 	
 | 	
 | ||||||
| 	for (arg, expected) in [ | 	let arg = vec! ["_exe_name"]; | ||||||
| 		(vec! ["_exe_name"], vec! [122.into ()]), | 	let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); | ||||||
| 		(vec! ["_exe_name"], vec! [122.into ()]), | 	let mut vm = State::new (chunk, upvalues); | ||||||
| 	] { | 	let actual = vm.execute_chunk (&[]).unwrap (); | ||||||
| 		let expected: Vec <Value> = expected; | 	let expected = vec! [Value::from (122)]; | ||||||
| 		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 (); |  | ||||||
| 	
 | 	
 | ||||||
| 		assert_eq! (actual, expected); | 	assert_eq! (actual, expected); | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[test] | #[test] | ||||||
|  | @ -286,22 +282,22 @@ fn is_93 () { | ||||||
| 	
 | 	
 | ||||||
| 	let run = run_chunk; | 	let run = run_chunk; | ||||||
| 	
 | 	
 | ||||||
| 	assert_eq! (run (&[""], &chunk), vec! [Value::from (1)]); | 	assert_eq! (run (&[""], chunk.clone ()), vec! [Value::from (1)]); | ||||||
| 	assert_eq! (run (&["", "93"], &chunk), vec! [Value::from (0)]); | 	assert_eq! (run (&["", "93"], chunk.clone ()), vec! [Value::from (0)]); | ||||||
| 	assert_eq! (run (&["", "94"], &chunk), vec! [Value::from (1)]); | 	assert_eq! (run (&["", "94"], chunk.clone ()), vec! [Value::from (1)]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[test] | #[test] | ||||||
| fn native_functions () { | fn native_functions () { | ||||||
| 	fn add (l: &mut State) -> i32 { | 	fn add (_: &mut State) -> i32 { | ||||||
| 		0 | 		0 | ||||||
| 	} | 	} | ||||||
| 	
 | 	
 | ||||||
| 	fn multiply (l: &mut State) -> i32 { | 	fn multiply (_: &mut State) -> i32 { | ||||||
| 		0 | 		0 | ||||||
| 	} | 	} | ||||||
| 	
 | 	
 | ||||||
| 	fn greet (l: &mut State) -> i32 { | 	fn greet (_: &mut State) -> i32 { | ||||||
| 		0 | 		0 | ||||||
| 	} | 	} | ||||||
| 	
 | 	
 | ||||||
|  | @ -372,7 +368,7 @@ fn tailcall () { | ||||||
| 	
 | 	
 | ||||||
| 	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)); | ||||||
| 	
 | 	
 | ||||||
| 	let actual = run_chunk (&[], &chunk); | 	let actual = run_chunk (&[], chunk); | ||||||
| 	let expected = vec! [Value::from (5)]; | 	let expected = vec! [Value::from (5)]; | ||||||
| 	
 | 	
 | ||||||
| 	assert_eq! (actual, expected); | 	assert_eq! (actual, expected); | ||||||
|  |  | ||||||
|  | @ -35,7 +35,7 @@ fn embedding () { | ||||||
| 		Value::from_iter (env.into_iter ()), | 		Value::from_iter (env.into_iter ()), | ||||||
| 	]; | 	]; | ||||||
| 	
 | 	
 | ||||||
| 	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_chunk (&vec! []).unwrap (); | ||||||
| 	
 | 	
 | ||||||
| 	assert_eq! (output, vec! [Value::from (2019)]); | 	assert_eq! (output, vec! [Value::from (2019)]); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 _
						_