down to 4.2x slower than PUC Lua, but the code became ugly to behold
							parent
							
								
									eb32a53d18
								
							
						
					
					
						commit
						130330b688
					
				|  | @ -55,8 +55,10 @@ pub struct State { | |||
| 	pub debug_print: bool, | ||||
| 	step_count: u32, | ||||
| 	chunk: Chunk, | ||||
| 	upvalues: Vec <Value>, | ||||
| 	pub upvalues: Vec <Value>, | ||||
| 	pub si: Interner, | ||||
| 	register_offset: usize, | ||||
| 	block_idx: usize, | ||||
| } | ||||
| 
 | ||||
| fn lw_io_write (l: &mut State, num_args: usize) -> usize { | ||||
|  | @ -194,6 +196,8 @@ impl State { | |||
| 			chunk, | ||||
| 			upvalues, | ||||
| 			si: Default::default (), | ||||
| 			register_offset: 0, | ||||
| 			block_idx: 0, | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
|  | @ -213,6 +217,8 @@ impl State { | |||
| 			chunk, | ||||
| 			upvalues, | ||||
| 			si, | ||||
| 			register_offset: 0, | ||||
| 			block_idx: 0, | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
|  | @ -221,7 +227,7 @@ impl State { | |||
| 		frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter | ||||
| 	} | ||||
| 	
 | ||||
| 	fn upvalues_from_args <I: Iterator <Item = String>> (si: &mut Interner, args: I) -> Vec <Value> | ||||
| 	pub fn upvalues_from_args <I: Iterator <Item = String>> (si: &mut Interner, args: I) -> Vec <Value> | ||||
| 	{ | ||||
| 		let arg = args.map (|s| si.intern (&s)).enumerate (); | ||||
| 		let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), Value::String (v)))); | ||||
|  | @ -266,13 +272,11 @@ impl State { | |||
| 	/// Short form to get access to a register within our window
 | ||||
| 	
 | ||||
| 	pub fn reg (&self, i: u8) -> &Value { | ||||
| 		let frame = self.stack.last ().unwrap (); | ||||
| 		&self.registers [frame.register_offset + i as usize] | ||||
| 		&self.registers [self.register_offset + i as usize] | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn reg_mut (&mut self, i: u8) -> &mut Value { | ||||
| 		let frame = self.stack.last ().unwrap (); | ||||
| 		&mut self.registers [frame.register_offset + i as usize] | ||||
| 		&mut self.registers [self.register_offset + i as usize] | ||||
| 	} | ||||
| 	
 | ||||
| 	// For native functions to check how many args they got
 | ||||
|  | @ -289,18 +293,120 @@ impl State { | |||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	fn op_add (&mut self, a: u8, b: u8, c: u8) -> bool { | ||||
| 		let v_b = self.reg (b); | ||||
| 		let v_c = self.reg (c); | ||||
| 		
 | ||||
| 		*self.reg_mut (a) = match (v_b, v_c) { | ||||
| 			(Value::Float (b), Value::Float (c)) => Value::from (b + c), | ||||
| 			(Value::Integer (b), Value::Integer (c)) => Value::from (b + c), | ||||
| 			(Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 + c), | ||||
| 			(Value::Float (b), Value::Integer (c)) => Value::from (b + *c as f64), | ||||
| 			_ => return false, | ||||
| 		}; | ||||
| 		
 | ||||
| 		true | ||||
| 	} | ||||
| 	
 | ||||
| 	fn op_get_field (&mut self, a: u8, b: u8, c: u8) { | ||||
| 		let block = self.chunk.blocks.get (self.block_idx).unwrap (); | ||||
| 		let constants = &block.constants; | ||||
| 		
 | ||||
| 		let key = match &constants [usize::from (c)] { | ||||
| 			Value::String (s) => s, | ||||
| 			_ => panic! ("K[C] must be a string"), | ||||
| 		}; | ||||
| 		
 | ||||
| 		let val = match &self.registers [self.register_offset + usize::from (b)] { | ||||
| 			Value::Nil => panic! ("R[B] must not be nil"), | ||||
| 			Value::Table (t) => t.borrow ().get_str (*key).clone (), | ||||
| 			_ => panic! ("R[B] must be a table"), | ||||
| 		}; | ||||
| 		
 | ||||
| 		*self.reg_mut (a) = val; | ||||
| 	} | ||||
| 	
 | ||||
| 	fn op_get_table (&mut self, a: u8, b: u8, c: u8) { | ||||
| 		let t = match self.reg (b) { | ||||
| 			Value::Table (t) => t, | ||||
| 			_ => panic! ("R[B] must be a table"), | ||||
| 		}; | ||||
| 		
 | ||||
| 		let key = self.reg (c); | ||||
| 		
 | ||||
| 		let val = t.borrow ().get (key.clone ()).clone (); | ||||
| 		
 | ||||
| 		*self.reg_mut (a) = val; | ||||
| 	} | ||||
| 	
 | ||||
| 	fn op_mmbin (&mut self, a: u8, b: u8, _c: u8) { | ||||
| 		let a = self.reg (a); | ||||
| 		let b = self.reg (b); | ||||
| 		
 | ||||
| 		if a.as_float().is_some() && b.as_float().is_some () { | ||||
| 			// No need for metamethods
 | ||||
| 		} | ||||
| 		else { | ||||
| 			panic! ("Not sure how to implement OP_MMBIN for these 2 values {a:?}, {b:?}"); | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	fn op_mul (&mut self, a: u8, b: u8, c: u8) -> bool { | ||||
| 		let v_b = self.reg (b); | ||||
| 		let v_c = self.reg (c); | ||||
| 		
 | ||||
| 		*self.reg_mut (a) = match (v_b, v_c) { | ||||
| 			(Value::Float (b), Value::Float (c)) => Value::from (b * c), | ||||
| 			(Value::Integer (b), Value::Integer (c)) => Value::from (b * c), | ||||
| 			(Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 * c), | ||||
| 			(Value::Float (b), Value::Integer (c)) => Value::from (b * *c as f64), | ||||
| 			_ => return false, | ||||
| 		}; | ||||
| 		
 | ||||
| 		true | ||||
| 	} | ||||
| 	
 | ||||
| 	fn op_set_field (&mut self, a: u8, b: u8, c: u8, k: bool) { | ||||
| 		let frame = self.stack.last ().unwrap (); | ||||
| 		let block = self.chunk.blocks.get (frame.block_idx).unwrap (); | ||||
| 		let constants = &block.constants; | ||||
| 		
 | ||||
| 		let value = if k { | ||||
| 			&constants [usize::from (c)] | ||||
| 		} | ||||
| 		else { | ||||
| 			self.reg (c) | ||||
| 		} | ||||
| 		.clone (); | ||||
| 		
 | ||||
| 		let b = usize::try_from (b).unwrap (); | ||||
| 		
 | ||||
| 		let key = match constants.get (b).unwrap () { | ||||
| 			Value::String (s) => *s, | ||||
| 			_ => panic! ("SetField only supports string keys"), | ||||
| 		}; | ||||
| 		
 | ||||
| 		let mut dst = self.reg (a).as_table () | ||||
| 		.expect ("SetField only works on tables").borrow_mut (); | ||||
| 		
 | ||||
| 		dst.insert_str (key, value); | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn step (&mut self) -> Result <Option <StepOutput>, StepError> 
 | ||||
| 	{ | ||||
| 		self.step_count += 1; | ||||
| 		
 | ||||
| 		let frame = self.stack.last_mut ().unwrap ().clone (); | ||||
| 		let block = self.chunk.blocks.get (frame.block_idx).unwrap (); | ||||
| 		let frame = self.stack.last ().unwrap (); | ||||
| 		self.block_idx = frame.block_idx; | ||||
| 		self.register_offset = frame.register_offset; | ||||
| 		let block_idx = frame.block_idx; | ||||
| 		let block = self.chunk.blocks.get (block_idx).unwrap (); | ||||
| 		
 | ||||
| 		let mut next_pc = frame.program_counter; | ||||
| 		
 | ||||
| 		let pc = usize::try_from (frame.program_counter).expect ("program_counter is not a valid usize"); | ||||
| 		let instruction = match block.instructions.get (pc) { | ||||
| 			Some (x) => x, | ||||
| 			Some (x) => *x, | ||||
| 			None => { | ||||
| 				dbg! (&self.stack); | ||||
| 				panic! ("program_counter went out of bounds"); | ||||
|  | @ -311,25 +417,14 @@ impl State { | |||
| 		let k = &block.constants; | ||||
| 		
 | ||||
| 		let make_step_error = |msg| { | ||||
| 			self.make_step_error (msg, instruction) | ||||
| 			self.make_step_error (msg, &instruction) | ||||
| 		}; | ||||
| 		
 | ||||
| 		match instruction.clone () { | ||||
| 		match instruction { | ||||
| 			Instruction::Add (a, b, c) => { | ||||
| 				let v_b = self.reg (b); | ||||
| 				let v_c = self.reg (c); | ||||
| 				
 | ||||
| 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | ||||
| 				{ | ||||
| 					Value::from (v_b + v_c) | ||||
| 				if self.op_add (a, b, c) { | ||||
| 					next_pc += 1; | ||||
| 				} | ||||
| 				else { | ||||
| 					let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}")); | ||||
| 					let v_c = v_c.as_float ().unwrap_or_else (|| panic! ("{v_c}")); | ||||
| 					Value::from (v_b + v_c) | ||||
| 				}; | ||||
| 				
 | ||||
| 				*self.reg_mut (a) = x; | ||||
| 			}, | ||||
| 			Instruction::AddI (a, b, s_c) => { | ||||
| 				let v_b = self.reg (b); | ||||
|  | @ -365,15 +460,12 @@ impl State { | |||
| 					Value::BogusClosure (rc) => { | ||||
| 						let idx = rc.borrow ().idx; | ||||
| 						
 | ||||
| 						let block_idx = frame.block_idx; | ||||
| 						let target_block = idx; | ||||
| 						
 | ||||
| 						let current_frame = self.stack.last ().unwrap (); | ||||
| 						
 | ||||
| 						self.stack.push (StackFrame { | ||||
| 							program_counter: 0, | ||||
| 							block_idx: target_block, | ||||
| 							register_offset: current_frame.register_offset + a as usize + 1, | ||||
| 							register_offset: self.register_offset + a as usize + 1, | ||||
| 						}); | ||||
| 						
 | ||||
| 						if self.debug_print { | ||||
|  | @ -386,19 +478,19 @@ impl State { | |||
| 						return Ok (None); | ||||
| 					}, | ||||
| 					Value::RsFunc (x) => { | ||||
| 						let current_frame = self.stack.last ().unwrap (); | ||||
| 						let new_offset = current_frame.register_offset + usize::from (a) + 1; | ||||
| 						let old_offset = self.register_offset; | ||||
| 						self.register_offset = old_offset + usize::from (a) + 1; | ||||
| 						
 | ||||
| 						// Trash the stack frame so it doesn't point to a
 | ||||
| 						// valid Lua function
 | ||||
| 						self.stack.push (StackFrame { | ||||
| 							program_counter: 65535, // Bogus for native functions
 | ||||
| 							block_idx: 65535, // Bogus
 | ||||
| 							register_offset: new_offset, | ||||
| 							register_offset: self.register_offset, | ||||
| 						}); | ||||
| 						
 | ||||
| 						let num_args = if b == 0 { | ||||
| 							self.top - new_offset | ||||
| 							self.top - self.register_offset | ||||
| 						} | ||||
| 						else { | ||||
| 							b - 1 | ||||
|  | @ -408,7 +500,8 @@ impl State { | |||
| 						let num_results = x (self, num_args); | ||||
| 						
 | ||||
| 						let popped_frame = self.stack.pop ().unwrap (); | ||||
| 						let offset = popped_frame.register_offset - 1; | ||||
| 						self.register_offset = old_offset; | ||||
| 						let offset = old_offset + usize::from (a); | ||||
| 						
 | ||||
| 						for i in (offset)..(offset + usize::try_from (num_results).unwrap ()) { | ||||
| 							self.registers [i] = self.registers [i + 1].take (); | ||||
|  | @ -501,32 +594,10 @@ impl State { | |||
| 				*self.reg_mut (a + 3) = start.into (); | ||||
| 			}, | ||||
| 			Instruction::GetField (a, b, c) => { | ||||
| 				let t = match self.reg (b) { | ||||
| 					Value::Nil => Err (make_step_error ("R[B] must not be nil"))?, | ||||
| 					Value::Table (t) => t, | ||||
| 					_ => Err (make_step_error ("R[B] must be a table"))?, | ||||
| 				}; | ||||
| 				
 | ||||
| 				let key = match &k [usize::from (c)] { | ||||
| 					Value::String (s) => *s, | ||||
| 					_ => panic! ("K[C] must be a string"), | ||||
| 				}; | ||||
| 				
 | ||||
| 				let val = t.borrow ().get_str (key).clone (); | ||||
| 				
 | ||||
| 				*self.reg_mut (a) = val; | ||||
| 				self.op_get_field (a, b, c); | ||||
| 			}, | ||||
| 			Instruction::GetTable (a, b, c) => { | ||||
| 				let t = match self.reg (b) { | ||||
| 					Value::Table (t) => t, | ||||
| 					_ => panic! ("R[B] must be a table"), | ||||
| 				}; | ||||
| 				
 | ||||
| 				let key = self.reg (c); | ||||
| 				
 | ||||
| 				let val = t.borrow ().get (key.clone ()); | ||||
| 				
 | ||||
| 				*self.reg_mut (a) = val; | ||||
| 				self.op_get_table (a, b, c); | ||||
| 			}, | ||||
| 			Instruction::GetTabUp (a, b, c) => { | ||||
| 				let b = usize::try_from (b).unwrap (); | ||||
|  | @ -563,7 +634,7 @@ impl State { | |||
| 				
 | ||||
| 				let value = { | ||||
| 					let table = self.reg (b).as_table ().expect ("GetI only works on tables").borrow (); | ||||
| 					table.get_int (key) | ||||
| 					table.get_int (key).clone () | ||||
| 				}; | ||||
| 				
 | ||||
| 				*self.reg_mut (a) = value; | ||||
|  | @ -621,16 +692,8 @@ impl State { | |||
| 			Instruction::LoadTrue (a) => { | ||||
| 				*self.reg_mut (a) = true.into (); | ||||
| 			}, | ||||
| 			Instruction::MmBin (a, b, _c) => { | ||||
| 				let a = self.reg (a); | ||||
| 				let b = self.reg (b); | ||||
| 				
 | ||||
| 				if a.as_float().is_some() && b.as_float().is_some () { | ||||
| 					// No need for metamethods
 | ||||
| 				} | ||||
| 				else { | ||||
| 					panic! ("Not sure how to implememtn OP_MMBIN for these 2 values {a:?}, {b:?}"); | ||||
| 				} | ||||
| 			Instruction::MmBin (a, b, c) => { | ||||
| 				self.op_mmbin (a, b, c); | ||||
| 			}, | ||||
| 			Instruction::MmBinI (_a, _s_b, _c, _k) => { | ||||
| 				// Ignore
 | ||||
|  | @ -651,20 +714,12 @@ impl State { | |||
| 				*self.reg_mut (a) = self.reg (b).clone (); | ||||
| 			}, | ||||
| 			Instruction::Mul (a, b, c) => { | ||||
| 				let v_b = self.reg (b); | ||||
| 				let v_c = self.reg (c); | ||||
| 				// If we handled the mul as a regular int or float,
 | ||||
| 				// skip the OP_MMBIN that probably comes after this
 | ||||
| 				
 | ||||
| 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | ||||
| 				{ | ||||
| 					Value::from (v_b * v_c) | ||||
| 				if self.op_mul (a, b, c) { | ||||
| 					next_pc += 1; | ||||
| 				} | ||||
| 				else { | ||||
| 					let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}")); | ||||
| 					let v_c = v_c.as_float ().unwrap_or_else (|| panic! ("{v_c}")); | ||||
| 					Value::from (v_b * v_c) | ||||
| 				}; | ||||
| 				
 | ||||
| 				*self.reg_mut (a) = x; | ||||
| 			}, | ||||
| 			Instruction::MulK (a, b, c) => { | ||||
| 				let v_b = self.reg (b); | ||||
|  | @ -776,25 +831,7 @@ impl State { | |||
| 				self.top = popped_frame.register_offset - 1 + 1; | ||||
| 			}, | ||||
| 			Instruction::SetField (a, b, c, k_flag) => { | ||||
| 				let value = if k_flag { | ||||
| 					&k [usize::from (c)] | ||||
| 				} | ||||
| 				else { | ||||
| 					self.reg (c) | ||||
| 				} | ||||
| 				.clone (); | ||||
| 				
 | ||||
| 				let b = usize::try_from (b).unwrap (); | ||||
| 				
 | ||||
| 				let key = match k.get (b).unwrap () { | ||||
| 					Value::String (s) => *s, | ||||
| 					_ => panic! ("SetField only supports string keys"), | ||||
| 				}; | ||||
| 				
 | ||||
| 				let mut dst = self.reg (a).as_table () | ||||
| 				.expect ("SetField only works on tables").borrow_mut (); | ||||
| 				
 | ||||
| 				dst.insert_str (key, value); | ||||
| 				self.op_set_field (a, b, c, k_flag); | ||||
| 			}, | ||||
| 			Instruction::SetI (a, b, c, k_flag) => { | ||||
| 				let value = if k_flag { | ||||
|  |  | |||
|  | @ -23,17 +23,18 @@ fn calculate_hash<T: Hash>(t: &T) -> u64 { | |||
| /// Takes arguments and a parsed Lua chunk, runs its,
 | ||||
| /// and returns the output
 | ||||
| 
 | ||||
| fn run_chunk (args: &[&str], chunk: Chunk) -> Vec <Value> { | ||||
| 	let mut vm = State::new_with_args (chunk, args.iter ()); | ||||
| fn run_chunk (vm: &mut State, args: &[&str], chunk: Chunk) -> Vec <Value> { | ||||
| 	vm.upvalues = State::upvalues_from_args(&mut vm.si, args.into_iter ().map (|s| s.to_string ())); | ||||
| 	vm.set_chunk (chunk); | ||||
| 	vm.execute ().unwrap () | ||||
| } | ||||
| 
 | ||||
| /// Takes arguments and Lua bytecode, loads it, runs it,
 | ||||
| /// and return the output
 | ||||
| 
 | ||||
| fn run_bytecode (args: &[&str], bc: &[u8], si: &mut Interner) -> Vec <Value> { | ||||
| 	let chunk = loader::parse_chunk (&bc, si).unwrap (); | ||||
| 	run_chunk (args, chunk) | ||||
| fn run_bytecode (vm: &mut State, args: &[&str], bc: &[u8]) -> Vec <Value> { | ||||
| 	let chunk = loader::parse_chunk (&bc, &mut vm.si).unwrap (); | ||||
| 	run_chunk (vm, args, chunk) | ||||
| } | ||||
| 
 | ||||
| /// Takes arguments and Lua source code, 
 | ||||
|  | @ -41,9 +42,9 @@ fn run_bytecode (args: &[&str], bc: &[u8], si: &mut Interner) -> Vec <Value> { | |||
| /// runs it, 
 | ||||
| /// and returns the output
 | ||||
| 
 | ||||
| fn run_source (args: &[&str], s: &str, si: &mut Interner) -> Vec <Value> { | ||||
| fn run_source (vm: &mut State, args: &[&str], s: &str) -> Vec <Value> { | ||||
| 	let bc = loader::compile_bytecode (s.as_bytes ().to_vec ()).unwrap (); | ||||
| 	run_bytecode (args, &bc, si) | ||||
| 	run_bytecode (vm, args, &bc) | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
|  | @ -114,15 +115,15 @@ fn bools () { | |||
| 		], | ||||
| 	}; | ||||
| 	
 | ||||
| 	let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter()); | ||||
| 	
 | ||||
| 	for (arg, expected) in [ | ||||
| 		(vec! ["_exe_name"], vec! [98.into ()]), | ||||
| 		(vec! ["_exe_name", "asdf"], vec! [99.into ()]), | ||||
| 	] { | ||||
| 		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 ().unwrap (); | ||||
| 		let actual = run_chunk (&mut vm, &arg, chunk.clone ()); | ||||
| 		assert_eq! (actual, expected); | ||||
| 	} | ||||
| } | ||||
|  | @ -131,9 +132,11 @@ fn bools () { | |||
| fn closure () { | ||||
| 	let source = include_bytes! ("../test_vectors/closure.lua"); | ||||
| 	let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap (); | ||||
| 	let chunk = crate::loader::parse_chunk (bytecode, &mut Interner::default ()).unwrap (); | ||||
| 	let mut si = Interner::default (); | ||||
| 	let chunk = crate::loader::parse_chunk (bytecode, &mut si).unwrap (); | ||||
| 	
 | ||||
| 	assert_eq! (run_chunk (&["_exe_name"], chunk), vec! [Value::from (23i64)]); | ||||
| 	let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter()); | ||||
| 	assert_eq! (run_chunk (&mut vm, &["_exe_name"], chunk), vec! [Value::from (23i64)]); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
|  | @ -173,14 +176,14 @@ fn floats () { | |||
| 		blocks: vec! [block], | ||||
| 	}; | ||||
| 	
 | ||||
| 	let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter()); | ||||
| 	
 | ||||
| 	for (arg, expected) in [ | ||||
| 		(vec! ["_exe_name"], vec! [3.5.into ()]), | ||||
| 		(vec! ["_exe_name", " "], vec! [3.5.into ()]), | ||||
| 	] { | ||||
| 		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 ().unwrap (); | ||||
| 		let actual = run_chunk (&mut vm, &arg, chunk.clone ()); | ||||
| 		
 | ||||
| 		assert_eq! (actual, expected); | ||||
| 	} | ||||
|  | @ -189,15 +192,15 @@ fn floats () { | |||
| #[test] | ||||
| fn fma () { | ||||
| 	let source = include_bytes! ("../test_vectors/fma.lua"); | ||||
| 	let mut si = Interner::default (); | ||||
| 	let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap (); | ||||
| 	let chunk = crate::loader::parse_chunk (bytecode, &mut Interner::default ()).unwrap (); | ||||
| 	let chunk = crate::loader::parse_chunk (bytecode, &mut si).unwrap (); | ||||
| 	assert_eq! (chunk.blocks.len (), 5); | ||||
| 	
 | ||||
| 	assert_eq! (chunk.blocks [3].upvalues.len (), 2); | ||||
| 	
 | ||||
| 	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 mut vm = crate::State::new_with_args (chunk, si, vec! ["_exe_name".to_string ()].into_iter ()); | ||||
| 	let actual = vm.execute ().unwrap (); | ||||
| 	let expected = vec! [Value::from (122)]; | ||||
| 	
 | ||||
|  | @ -206,9 +209,8 @@ fn fma () { | |||
| 
 | ||||
| #[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); | ||||
| 	let si = Interner::default (); | ||||
| 	let mut vm = crate::State::new_with_args (crate::Chunk::default (), si, vec! ["_exe_name".to_string ()].into_iter ()); | ||||
| 	
 | ||||
| 	vm.eval ("print (x ())").ok (); | ||||
| 	vm.eval ("x = function () return 5 end").ok (); | ||||
|  | @ -220,9 +222,8 @@ fn function_calls () { | |||
| 
 | ||||
| #[test] | ||||
| fn function_returns () { | ||||
| 	let upvalues = crate::State::upvalues_from_args (vec! ["_exe_name".to_string ()].into_iter ()); | ||||
| 	
 | ||||
| 	let mut vm = crate::State::new (crate::Chunk::default (), upvalues); | ||||
| 	let si = Interner::default (); | ||||
| 	let mut vm = crate::State::new_with_args (crate::Chunk::default (), si, vec! ["_exe_name".to_string ()].into_iter ()); | ||||
| 	
 | ||||
| 	assert_eq! ( | ||||
| 		vm.eval ("return ((function () return 5 end)())").unwrap (), 
 | ||||
|  | @ -316,15 +317,17 @@ fn is_93 () { | |||
| 	"#;
 | ||||
| 	
 | ||||
| 	let bc = loader::compile_bytecode (src.to_vec ()).unwrap (); | ||||
| 	let chunk = loader::parse_chunk (&bc, &mut Interner::default ()).unwrap (); | ||||
| 	let chunk = loader::parse_chunk (&bc, &mut si).unwrap (); | ||||
| 	
 | ||||
| 	assert_eq! (chunk.blocks [0].instructions [3], Inst::EqK (0, 1, false)); | ||||
| 	
 | ||||
| 	let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter()); | ||||
| 	
 | ||||
| 	let run = run_chunk; | ||||
| 	
 | ||||
| 	assert_eq! (run (&[""], chunk.clone ()), vec! [Value::from (1)]); | ||||
| 	assert_eq! (run (&["", "93"], chunk.clone ()), vec! [Value::from (0)]); | ||||
| 	assert_eq! (run (&["", "94"], chunk.clone ()), vec! [Value::from (1)]); | ||||
| 	assert_eq! (run (&mut vm, &[""], chunk.clone ()), vec! [Value::from (1)]); | ||||
| 	assert_eq! (run (&mut vm, &["", "93"], chunk.clone ()), vec! [Value::from (0)]); | ||||
| 	assert_eq! (run (&mut vm, &["", "94"], chunk.clone ()), vec! [Value::from (1)]); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
|  | @ -377,7 +380,9 @@ fn tables_1 () { | |||
| 	print (t ["t"][1]) | ||||
| 	"#;
 | ||||
| 	
 | ||||
| 	run_source (&[], src, &mut Interner::default ()); | ||||
| 	let si = Interner::default (); | ||||
| 	let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter()); | ||||
| 	run_source (&mut vm, &[], src); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
|  | @ -392,23 +397,29 @@ fn tables_2 () { | |||
| 	print (a [2]) | ||||
| 	"#;
 | ||||
| 	
 | ||||
| 	run_source (&[], src, &mut Interner::default ()); | ||||
| 	let si = Interner::default (); | ||||
| 	let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter()); | ||||
| 	run_source (&mut vm, &[], src); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn tailcall () { | ||||
| 	use crate::instruction::Instruction; | ||||
| 	
 | ||||
| 	let mut si = Interner::default (); | ||||
| 	
 | ||||
| 	let src = br#" | ||||
| 	return tonumber ("5") | ||||
| 	"#;
 | ||||
| 	
 | ||||
| 	let bc = loader::compile_bytecode (src.to_vec ()).unwrap (); | ||||
| 	let chunk = loader::parse_chunk (&bc, &mut Interner::default ()).unwrap (); | ||||
| 	let chunk = loader::parse_chunk (&bc, &mut si).unwrap (); | ||||
| 	
 | ||||
| 	assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false)); | ||||
| 	
 | ||||
| 	let actual = run_chunk (&[], chunk); | ||||
| 	let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter()); | ||||
| 	
 | ||||
| 	let actual = run_chunk (&mut vm, &[], chunk); | ||||
| 	let expected = vec! [Value::from (5)]; | ||||
| 	
 | ||||
| 	assert_eq! (actual, expected); | ||||
|  |  | |||
|  | @ -259,20 +259,21 @@ impl fmt::Debug for Table { | |||
| } | ||||
| 
 | ||||
| impl Table { | ||||
| 	fn get_inner (&self, key: &Value) -> Value { | ||||
| 	fn get_inner (&self, key: &Value) -> &Value { | ||||
| 		match key { | ||||
| 			Value::Nil => Value::Nil, | ||||
| 			Value::String (x) => self.map.get (x).cloned ().unwrap_or_default (), | ||||
| 			x => self.hash.get (x).cloned ().unwrap_or_default (), | ||||
| 			Value::Nil => &NIL, | ||||
| 			Value::String (x) => self.map.get (x).unwrap_or (&NIL), | ||||
| 			Value::Integer (x) => self.array.get (usize::try_from (*x).unwrap ()).unwrap_or (&NIL), | ||||
| 			x => self.hash.get (x).unwrap_or (&NIL), | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn get <A: Into <Value>> (&self, key: A) -> Value { | ||||
| 	pub fn get <A: Into <Value>> (&self, key: A) -> &Value { | ||||
| 		self.get_inner (&(key.into ())) | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn get_int (&self, key: i64) -> Value { | ||||
| 		self.get_inner (&(key.into ())) | ||||
| 	pub fn get_int (&self, key: i64) -> &Value { | ||||
| 		self.array.get (usize::try_from (key).unwrap ()).unwrap_or (&NIL) | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn get_str (&self, key: InternedString) -> &Value { | ||||
|  | @ -290,6 +291,7 @@ impl Table { | |||
| 		b: B, | ||||
| 	) { | ||||
| 		match a.into () { | ||||
| 			Value::Integer (x) => self.insert_int (x, b), | ||||
| 			Value::Nil => (), | ||||
| 			Value::String (x) => { | ||||
| 				self.map.insert (x, b.into ()); | ||||
|  | @ -304,7 +306,9 @@ impl Table { | |||
| 	
 | ||||
| 	pub fn insert_int <A: Into <Value>> (&mut self, k: i64, v: A) 
 | ||||
| 	{ | ||||
| 		self.insert (Value::from (k), v.into ()) | ||||
| 		let k = usize::try_from (k).unwrap (); | ||||
| 		self.array.resize (k + 1, NIL); | ||||
| 		self.array [k] = v.into (); | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn insert_str (&mut self, key: InternedString, v: Value) { | ||||
|  | @ -313,7 +317,7 @@ impl Table { | |||
| 	
 | ||||
| 	pub fn length (&self) -> i64 { | ||||
| 		for i in 1..i64::MAX { | ||||
| 			if self.get (i) == Value::Nil { | ||||
| 			if self.get (i) == &NIL { | ||||
| 				return i - 1; | ||||
| 			} | ||||
| 		} | ||||
|  | @ -359,16 +363,16 @@ mod tests { | |||
| 		
 | ||||
| 		let mut t = Table::default (); | ||||
| 		
 | ||||
| 		assert_eq! (t.get (Value::from_str (&mut si, "a")), nil); | ||||
| 		assert_eq! (t.get (Value::from_str (&mut si, "b")), nil); | ||||
| 		assert_eq! (t.get (si.intern ("a")), &nil); | ||||
| 		assert_eq! (t.get (si.intern ("b")), &nil); | ||||
| 		
 | ||||
| 		t.insert (si.to_value ("a"), 1993); | ||||
| 		t.insert (si.to_value ("b"), 2007); | ||||
| 		
 | ||||
| 		assert_eq! (t.get (Value::from_str (&mut si, "a")), 1993); | ||||
| 		assert_eq! (t.get (Value::from_str (&mut si, "b")), 2007); | ||||
| 		assert_eq! (t.get (si.intern ("a")), &Value::from (1993)); | ||||
| 		assert_eq! (t.get (si.intern ("b")), &Value::from (2007)); | ||||
| 		
 | ||||
| 		t.insert (19, 93); | ||||
| 		assert_eq! (t.get (19), 93); | ||||
| 		assert_eq! (t.get (19), &Value::from (93)); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -19,16 +19,18 @@ fn embedding () { | |||
| 		1 | ||||
| 	} | ||||
| 	
 | ||||
| 	let mut si = lwvm::Interner::default (); | ||||
| 	
 | ||||
| 	let bc = lwvm::compile_bytecode (src.to_vec ()).unwrap (); | ||||
| 	let chunk = lwvm::parse_chunk (&bc).unwrap (); | ||||
| 	let chunk = lwvm::parse_chunk (&bc, &mut si).unwrap (); | ||||
| 	
 | ||||
| 	let host_lib = [ | ||||
| 		("add", Value::RsFunc (host_add)), | ||||
| 	].into_iter ().map (|(k, v)| (k.to_string (), v)); | ||||
| 	].into_iter ().map (|(k, v)| (si.intern (k), v)); | ||||
| 	
 | ||||
| 	let env = [ | ||||
| 		("host_lib", Value::from_iter (host_lib.into_iter ())), | ||||
| 	].into_iter ().map (|(k, v)| (k.to_string (), v)); | ||||
| 	].into_iter ().map (|(k, v)| (si.intern (k), v)); | ||||
| 	
 | ||||
| 	let upvalues = vec! [ | ||||
| 		Value::from_iter (env.into_iter ()), | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 _
						_