♻️ refactor
							parent
							
								
									b07de4810d
								
							
						
					
					
						commit
						8c70469276
					
				
							
								
								
									
										213
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										213
									
								
								src/main.rs
								
								
								
								
							|  | @ -1,3 +1,5 @@ | |||
| use std::collections::BTreeMap; | ||||
| 
 | ||||
| enum Instruction { | ||||
| 	VarArgPrep (i32), | ||||
| 	GetTabUp (u8, u8, u8), | ||||
|  | @ -21,7 +23,8 @@ enum Value { | |||
| 	// These are all bogus, I haven't figured out how to implement
 | ||||
| 	// tables and function pointers yet
 | ||||
| 	
 | ||||
| 	BogusArg, | ||||
| 	BogusArg (Vec <String>), | ||||
| 	BogusEnv (BTreeMap <String, Value>), | ||||
| 	BogusPrint, | ||||
| } | ||||
| 
 | ||||
|  | @ -48,6 +51,115 @@ struct Chunk { | |||
| 	constants: Vec <Value>, | ||||
| } | ||||
| 
 | ||||
| struct VirtualMachine { | ||||
| 	registers: Vec <Value>, | ||||
| 	
 | ||||
| 	// i32 makes it a little easier to implement jumps
 | ||||
| 	program_counter: i32, | ||||
| } | ||||
| 
 | ||||
| impl Default for VirtualMachine { | ||||
| 	fn default () -> Self { | ||||
| 		Self { | ||||
| 			registers: vec! [Value::Nil; 256], | ||||
| 			program_counter: 0, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl VirtualMachine { | ||||
| 	fn execute_chunk (&mut self, chunk: &Chunk, upvalues: &Vec <Value>) { | ||||
| 		let max_iters = 2000; | ||||
| 		
 | ||||
| 		for _ in 0..max_iters { | ||||
| 			let instruction = chunk.instructions.get (usize::try_from (self.program_counter).unwrap ()).unwrap (); | ||||
| 			
 | ||||
| 			let r = &mut self.registers; | ||||
| 			let k = &chunk.constants; | ||||
| 			
 | ||||
| 			match instruction { | ||||
| 				Instruction::Call (a, b, c) => { | ||||
| 					// Take arguments from registers [a + 1, a + b)
 | ||||
| 					// Call the function in register [a]
 | ||||
| 					// Return values in registers [a, a + c - 1)
 | ||||
| 					// 
 | ||||
| 					// That is, call a with b - 1 arguments and expect c returns
 | ||||
| 					// 
 | ||||
| 					// e.g. CALL 0 2 1 mean "Call 0 with 1 argument, return 1 value", like for printing a constant
 | ||||
| 					
 | ||||
| 					// TODO: Only implement printing constants for now
 | ||||
| 					
 | ||||
| 					let a = usize::try_from (*a).unwrap (); | ||||
| 					
 | ||||
| 					assert_eq! (*b, 2); | ||||
| 					assert_eq! (*c, 1); | ||||
| 					
 | ||||
| 					println! ("{:?}", r [a + 1]); | ||||
| 				}, | ||||
| 				Instruction::EqK (a, b, c_k) => { | ||||
| 					let a = usize::try_from (*a).unwrap (); | ||||
| 					let b = usize::try_from (*b).unwrap (); | ||||
| 					
 | ||||
| 					let equal = r [a] == k [b]; | ||||
| 					
 | ||||
| 					match (equal, c_k) { | ||||
| 						(true, 0) => self.program_counter += 1, | ||||
| 						(false, 1) => self.program_counter += 1, | ||||
| 						_ => (), | ||||
| 					} | ||||
| 				}, | ||||
| 				Instruction::GetTabUp (a, b, c) => { | ||||
| 					let a = usize::try_from (*a).unwrap (); | ||||
| 					let b = usize::try_from (*b).unwrap (); | ||||
| 					let c = usize::try_from (*c).unwrap (); | ||||
| 					
 | ||||
| 					let env = match upvalues.get (b).unwrap () { | ||||
| 						Value::BogusEnv (x) => x, | ||||
| 						_ => panic! ("Only allowed upvalue is BogusEnv"), | ||||
| 					}; | ||||
| 					
 | ||||
| 					let key = match k.get (c).unwrap () { | ||||
| 						Value::String (s) => s, | ||||
| 						_ => panic! ("GetTabUp only supports string keys"), | ||||
| 					}; | ||||
| 					
 | ||||
| 					
 | ||||
| 					let value = env.get (key).unwrap (); | ||||
| 					
 | ||||
| 					r [a] = value.clone (); | ||||
| 				}, | ||||
| 				Instruction::GetI (a, b, c) => { | ||||
| 					let a = usize::try_from (*a).unwrap (); | ||||
| 					let b = usize::try_from (*b).unwrap (); | ||||
| 					let c = usize::try_from (*c).unwrap (); | ||||
| 					
 | ||||
| 					let table = r.get (b).unwrap (); | ||||
| 					let value = match table { | ||||
| 						Value::BogusArg (arg) => arg.get (c).unwrap ().as_str().into (), | ||||
| 						_ => unimplemented!(), | ||||
| 					}; | ||||
| 					
 | ||||
| 					r [a] = value; | ||||
| 				}, | ||||
| 				Instruction::Jmp (sJ) => self.program_counter += sJ, | ||||
| 				Instruction::LoadK (a, bx) => { | ||||
| 					let a  = usize::try_from  (*a).unwrap (); | ||||
| 					let bx = usize::try_from (*bx).unwrap (); | ||||
| 					
 | ||||
| 					r [a] = k [bx].clone (); | ||||
| 				}, | ||||
| 				Instruction::Return (_a, _b, _c) => { | ||||
| 					break; | ||||
| 				}, | ||||
| 				Instruction::VarArgPrep (_) => (), | ||||
| 				_ => (), | ||||
| 			} | ||||
| 			
 | ||||
| 			self.program_counter += 1; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	let arg: Vec <_> = std::env::args ().collect (); | ||||
| 	
 | ||||
|  | @ -76,96 +188,15 @@ fn main() { | |||
| 		].into_iter ().map (|s| Value::from (s)).collect (), | ||||
| 	}; | ||||
| 	
 | ||||
| 	let mut registers = vec! [Value::default (); 256]; | ||||
| 	let env = BTreeMap::from_iter ([ | ||||
| 		("arg", Value::BogusArg (arg.clone ())), | ||||
| 		("print", Value::BogusPrint), | ||||
| 	].map (|(k, v)| (k.to_string (), v)).into_iter ()); | ||||
| 	
 | ||||
| 	let upvalues = vec! [ | ||||
| 		Value::BogusEnv (env), | ||||
| 	]; | ||||
| 	
 | ||||
| 	let mut program_counter = 0i32; | ||||
| 	let max_iters = 2000; | ||||
| 	
 | ||||
| 	for _ in 0..max_iters { | ||||
| 		let instruction = chunk.instructions.get (usize::try_from (program_counter).unwrap ()).unwrap (); | ||||
| 		
 | ||||
| 		let r = &mut registers; | ||||
| 		let k = &chunk.constants; | ||||
| 		
 | ||||
| 		match instruction { | ||||
| 			Instruction::Call (a, b, c) => { | ||||
| 				// Take arguments from registers [a + 1, a + b)
 | ||||
| 				// Call the function in register [a]
 | ||||
| 				// Return values in registers [a, a + c - 1)
 | ||||
| 				// 
 | ||||
| 				// That is, call a with b - 1 arguments and expect c returns
 | ||||
| 				// 
 | ||||
| 				// e.g. CALL 0 2 1 mean "Call 0 with 1 argument, return 1 value", like for printing a constant
 | ||||
| 				
 | ||||
| 				// TODO: Only implement printing constants for now
 | ||||
| 				
 | ||||
| 				let a = usize::try_from (*a).unwrap (); | ||||
| 				
 | ||||
| 				assert_eq! (*b, 2); | ||||
| 				assert_eq! (*c, 1); | ||||
| 				
 | ||||
| 				println! ("{:?}", r [a + 1]); | ||||
| 			}, | ||||
| 			Instruction::EqK (a, b, c_k) => { | ||||
| 				let a = usize::try_from (*a).unwrap (); | ||||
| 				let b = usize::try_from (*b).unwrap (); | ||||
| 				
 | ||||
| 				let equal = r [a] == k [b]; | ||||
| 				
 | ||||
| 				match (equal, c_k) { | ||||
| 					(true, 0) => program_counter += 1, | ||||
| 					(false, 1) => program_counter += 1, | ||||
| 					_ => (), | ||||
| 				} | ||||
| 			}, | ||||
| 			Instruction::GetTabUp (a, b, c) => { | ||||
| 				let a = usize::try_from (*a).unwrap (); | ||||
| 				let b = usize::try_from (*b).unwrap (); | ||||
| 				let c = usize::try_from (*c).unwrap (); | ||||
| 				
 | ||||
| 				// Only supported upvalue is `_ENV`
 | ||||
| 				assert_eq! (b, 0); | ||||
| 				
 | ||||
| 				let key = k.get (c).unwrap (); | ||||
| 				let value = match key { | ||||
| 					Value::String (s) => match s.as_str() { | ||||
| 						"arg" => Value::BogusArg, | ||||
| 						"print" => Value::BogusPrint, | ||||
| 						_ => panic! ("key not in _ENV upvalue"), | ||||
| 					}, | ||||
| 					_ => unimplemented!(), | ||||
| 				}; | ||||
| 				
 | ||||
| 				r [a] = value; | ||||
| 			}, | ||||
| 			Instruction::GetI (a, b, c) => { | ||||
| 				let a = usize::try_from (*a).unwrap (); | ||||
| 				let b = usize::try_from (*b).unwrap (); | ||||
| 				let c = usize::try_from (*c).unwrap (); | ||||
| 				
 | ||||
| 				let table = r.get (b).unwrap (); | ||||
| 				let value = match table { | ||||
| 					Value::BogusArg => arg.get (c).unwrap ().as_str().into (), | ||||
| 					_ => unimplemented!(), | ||||
| 				}; | ||||
| 				
 | ||||
| 				r [a] = value; | ||||
| 			}, | ||||
| 			Instruction::Jmp (sJ) => program_counter += sJ, | ||||
| 			Instruction::LoadK (a, bx) => { | ||||
| 				let a  = usize::try_from  (*a).unwrap (); | ||||
| 				let bx = usize::try_from (*bx).unwrap (); | ||||
| 				
 | ||||
| 				r [a] = k [bx].clone (); | ||||
| 			}, | ||||
| 			Instruction::Return (_a, _b, _c) => { | ||||
| 				break; | ||||
| 			}, | ||||
| 			Instruction::VarArgPrep (_) => (), | ||||
| 			_ => (), | ||||
| 		} | ||||
| 		
 | ||||
| 		program_counter += 1; | ||||
| 	} | ||||
| 	let mut vm = VirtualMachine::default (); | ||||
| 	vm.execute_chunk (&chunk, &upvalues); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 _
						_