⭐ can create an empty table
							parent
							
								
									e12d749c7c
								
							
						
					
					
						commit
						72b2d6e656
					
				|  | @ -45,6 +45,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst> | |||
| 	
 | ||||
| 	let a = (buf [0] >> 7) | ((buf [1] & 0x7f) << 1); | ||||
| 	let b = buf [2]; | ||||
| 	let ax = a as u32 + ((b as u32) << 8); | ||||
| 	let c = buf [3]; | ||||
| 	let bx = 
 | ||||
| 		(((buf [1] >> 7) as u32) << 0) | 
 | ||||
|  | @ -67,6 +68,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst> | |||
| 		0x0b => Inst::GetTabUp (a, b, c), | ||||
| 		0x0d => Inst::GetI (a, b, c), | ||||
| 		0x0f => Inst::SetTabUp (a, b, c), | ||||
| 		0x13 => Inst::NewTable (a), | ||||
| 		0x22 => Inst::Add (a, b, c), | ||||
| 		0x24 => Inst::Mul (a, b, c), | ||||
| 		0x2e => Inst::MmBin (a, b, c), | ||||
|  | @ -81,6 +83,7 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst> | |||
| 		0x48 => Inst::Return1 (a), | ||||
| 		0x4f => Inst::Closure (a, bx), | ||||
| 		0x51 => Inst::VarArgPrep (a.into ()), | ||||
| 		0x52 => Inst::ExtraArg (ax), | ||||
| 		_ => return None, | ||||
| 	}) | ||||
| } | ||||
|  | @ -264,6 +267,7 @@ mod tests { | |||
| 			([0xbc, 0x00, 0x01, 0x00], Inst::EqK (1, 1, 0)), | ||||
| 			([0xb8, 0x02, 0x00, 0x80], Inst::Jmp (6)), | ||||
| 			([0x38, 0x02, 0x00, 0x80], Inst::Jmp (5)), | ||||
| 			([0x52, 0x00, 0x00, 0x00], Inst::ExtraArg (0)), | ||||
| 		] { | ||||
| 			let actual = super::parse_inst (input).unwrap (); | ||||
| 			assert_eq!(actual, expected); | ||||
|  |  | |||
							
								
								
									
										25
									
								
								src/state.rs
								
								
								
								
							
							
						
						
									
										25
									
								
								src/state.rs
								
								
								
								
							|  | @ -1,7 +1,8 @@ | |||
| use std::collections::BTreeMap; | ||||
| use std::collections::HashMap; | ||||
| 
 | ||||
| use crate::value::{ | ||||
| 	BogusClosure, | ||||
| 	Table, | ||||
| 	Value, | ||||
| }; | ||||
| 
 | ||||
|  | @ -15,6 +16,8 @@ pub enum Instruction { | |||
| 	// Equals Constant?
 | ||||
| 	EqK (u8, u8, u8), | ||||
| 	
 | ||||
| 	ExtraArg (u32), | ||||
| 	
 | ||||
| 	// Get Immediate?
 | ||||
| 	GetI (u8, u8, u8), | ||||
| 	
 | ||||
|  | @ -48,6 +51,8 @@ pub enum Instruction { | |||
| 	
 | ||||
| 	Mul (u8, u8, u8), | ||||
| 	
 | ||||
| 	NewTable (u8), | ||||
| 	
 | ||||
| 	Not (u8, u8), | ||||
| 	
 | ||||
| 	// (A, B, _C, k) Return B - 1 registers starting with  A
 | ||||
|  | @ -129,7 +134,7 @@ impl State { | |||
| 	{ | ||||
| 		let arg: Vec <_> = args.map (|s| s.to_string ()).collect (); | ||||
| 		
 | ||||
| 		let env = BTreeMap::from_iter ([ | ||||
| 		let env = HashMap::from_iter ([ | ||||
| 			("arg", Value::BogusArg (arg.into ())), | ||||
| 			("print", Value::BogusPrint), | ||||
| 		].map (|(k, v)| (k.to_string (), v))); | ||||
|  | @ -247,6 +252,7 @@ impl State { | |||
| 								Value::Float (x) => println! ("{:?}", x), | ||||
| 								Value::Integer (x) => println! ("{}", x), | ||||
| 								Value::String (s) => println! ("{}", s), | ||||
| 								Value::Table (t) => println! ("table: {:?}", std::rc::Rc::as_ptr (t)), | ||||
| 								_ => unimplemented! (), | ||||
| 							}; | ||||
| 							
 | ||||
|  | @ -285,6 +291,12 @@ impl State { | |||
| 						_ => (), | ||||
| 					} | ||||
| 				}, | ||||
| 				Instruction::ExtraArg (ax) => { | ||||
| 					// This is used for NewTable. Maybe it's for reserving
 | ||||
| 					// capacity in the array or something?
 | ||||
| 					
 | ||||
| 					assert_eq! (*ax, 0, "implemented only for ax == 0"); | ||||
| 				}, | ||||
| 				Instruction::GetTabUp (a, b, c) => { | ||||
| 					let a = usize::try_from (*a).unwrap (); | ||||
| 					let b = usize::try_from (*b).unwrap (); | ||||
|  | @ -383,6 +395,11 @@ impl State { | |||
| 					let r = self.register_window_mut(); | ||||
| 					r [a] = r [b].clone (); | ||||
| 				}, | ||||
| 				Instruction::Mul (_a, _b, _c) => unimplemented!(), | ||||
| 				Instruction::NewTable (a) => { | ||||
| 					let a = usize::try_from (*a).unwrap (); | ||||
| 					self.register_window_mut ()[a] = Value::Table (Default::default ()); | ||||
| 				}, | ||||
| 				Instruction::Not (a, b) => { | ||||
| 					let a = usize::try_from (*a).unwrap (); | ||||
| 					let b = usize::try_from (*b).unwrap (); | ||||
|  | @ -444,6 +461,7 @@ impl State { | |||
| 						return self.registers [a..(a + b - 1)].to_vec(); | ||||
| 					} | ||||
| 				}, | ||||
| 				Instruction::Return0 => unimplemented! (), | ||||
| 				Instruction::Return1 (a) => { | ||||
| 					let a = usize::try_from (*a).unwrap (); | ||||
| 					let popped_frame = self.stack.pop ().unwrap (); | ||||
|  | @ -467,6 +485,8 @@ impl State { | |||
| 					let offset = popped_frame.register_offset; | ||||
| 					self.registers [offset - 1] = self.registers [offset + a].take (); | ||||
| 				}, | ||||
| 				Instruction::SetTabUp (_a, _b, _c) => unimplemented! (), | ||||
| 				Instruction::TailCall (_a, _b, _c, _k) => unimplemented! (), | ||||
| 				Instruction::Test (a, _k) => { | ||||
| 					let a = usize::try_from (*a).unwrap (); | ||||
| 					
 | ||||
|  | @ -481,7 +501,6 @@ impl State { | |||
| 					} | ||||
| 				}, | ||||
| 				Instruction::VarArgPrep (_) => (), | ||||
| 				x => panic! ("Unimplemented instruction {x:?}"), | ||||
| 			} | ||||
| 			
 | ||||
| 			next_pc += 1; | ||||
|  |  | |||
							
								
								
									
										35
									
								
								src/tests.rs
								
								
								
								
							
							
						
						
									
										35
									
								
								src/tests.rs
								
								
								
								
							|  | @ -1,8 +1,11 @@ | |||
| use crate::state::{ | ||||
| 	Block, | ||||
| 	Chunk, | ||||
| 	Instruction as Inst, | ||||
| 	State, | ||||
| use crate::{ | ||||
| 	state::{ | ||||
| 		Block, | ||||
| 		Chunk, | ||||
| 		Instruction as Inst, | ||||
| 		State, | ||||
| 	}, | ||||
| 	value::Value, | ||||
| }; | ||||
| 
 | ||||
| #[test] | ||||
|  | @ -75,6 +78,8 @@ fn bools () { | |||
| 		(vec! ["_exe_name"], vec! [98.into ()]), | ||||
| 		(vec! ["_exe_name", "asdf"], vec! [99.into ()]), | ||||
| 	] { | ||||
| 		let expected: Vec <Value> = expected; | ||||
| 		
 | ||||
| 		let mut vm = State::default (); | ||||
| 		let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); | ||||
| 		
 | ||||
|  | @ -94,6 +99,7 @@ fn closure () { | |||
| 		(vec! ["_exe_name"], vec! [23.0.into ()]), | ||||
| 		(vec! ["_exe_name"], vec! [23.0.into ()]), | ||||
| 	] { | ||||
| 		let expected: Vec <Value> = expected; | ||||
| 		let mut vm = State::default (); | ||||
| 		let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); | ||||
| 		let actual = vm.execute_chunk (&file, &upvalues); | ||||
|  | @ -141,6 +147,7 @@ fn floats () { | |||
| 		(vec! ["_exe_name"], vec! [3.5.into ()]), | ||||
| 		(vec! ["_exe_name", " "], vec! [3.5.into ()]), | ||||
| 	] { | ||||
| 		let expected: Vec <Value> = expected; | ||||
| 		let mut vm = State::default (); | ||||
| 		let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); | ||||
| 		let actual = vm.execute_chunk (&chunk, &upvalues); | ||||
|  | @ -160,6 +167,7 @@ fn fma () { | |||
| 		(vec! ["_exe_name"], vec! [122.into ()]), | ||||
| 		(vec! ["_exe_name"], vec! [122.into ()]), | ||||
| 	] { | ||||
| 		let expected: Vec <Value> = expected; | ||||
| 		let mut vm = State::default (); | ||||
| 		let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); | ||||
| 		let actual = vm.execute_chunk (&file, &upvalues); | ||||
|  | @ -184,6 +192,7 @@ fn is_93 () { | |||
| 		(vec! ["_exe_name", "93"], vec! [0.into ()]), | ||||
| 		(vec! ["_exe_name", "94"], vec! [1.into ()]), | ||||
| 	] { | ||||
| 		let expected: Vec <Value> = expected; | ||||
| 		let mut vm = State::default (); | ||||
| 		let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); | ||||
| 		let actual = vm.execute_chunk (&file, &upvalues); | ||||
|  | @ -200,28 +209,16 @@ fn value_size () { | |||
| 	// Lua's tagged union values are 12-16 bytes on a 32-bit system
 | ||||
| 	// with 64-bit floats
 | ||||
| 	// 
 | ||||
| 	// It is very nice if LunarWaveVM is the same or better.
 | ||||
| 	// There is some exploratory things in this test, too
 | ||||
| 	// It would be nice if LunarWaveVM is the same or better.
 | ||||
| 	// There are some exploratory things in this test, too
 | ||||
| 	
 | ||||
| 	use std::{ | ||||
| 		mem::size_of, | ||||
| 		rc::Rc, | ||||
| 	}; | ||||
| 	
 | ||||
| 	assert! (size_of::<Box <()>> () <= 8); | ||||
| 	assert! (size_of::<std::rc::Rc <()>> () <= 8); | ||||
| 	
 | ||||
| 	enum Value { | ||||
| 		Nil, | ||||
| 		Boolean (bool), | ||||
| 		Float (f64), | ||||
| 		String (Rc <String>), | ||||
| 	} | ||||
| 	
 | ||||
| 	let sz = size_of::<Value> (); | ||||
| 	let expected = 16; | ||||
| 	assert! (sz <= expected, "{sz} > {expected}"); | ||||
| 	
 | ||||
| 	let sz = size_of::<crate::value::Value> (); | ||||
| 	let expected = 16; | ||||
| 	assert! (sz <= expected, "{sz} > {expected}"); | ||||
|  |  | |||
							
								
								
									
										114
									
								
								src/value.rs
								
								
								
								
							
							
						
						
									
										114
									
								
								src/value.rs
								
								
								
								
							|  | @ -1,9 +1,13 @@ | |||
| use std::{ | ||||
| 	collections::BTreeMap, | ||||
| 	cmp::{ | ||||
| 		Eq, | ||||
| 		PartialEq, | ||||
| 	}, | ||||
| 	collections::HashMap, | ||||
| 	rc::Rc, | ||||
| }; | ||||
| 
 | ||||
| #[derive (Clone, Debug, Hash, PartialEq)] | ||||
| #[derive (Debug, Eq, PartialEq)] | ||||
| pub struct BogusClosure { | ||||
| 	pub idx: usize, | ||||
| 	pub upvalues: Vec <Value>, | ||||
|  | @ -13,16 +17,21 @@ pub struct BogusClosure { | |||
| pub enum Value { | ||||
| 	Nil, | ||||
| 	Boolean (bool), | ||||
| 	
 | ||||
| 	// Rust is very strict about float equality, so some of my code
 | ||||
| 	// here is probably wrong in subtle ways.
 | ||||
| 	Float (f64), | ||||
| 	
 | ||||
| 	Integer (i64), | ||||
| 	String (Rc <String>), | ||||
| 	Table (Rc <Table>), | ||||
| 	
 | ||||
| 	// These are all bogus, I haven't figured out how to implement
 | ||||
| 	// tables and function pointers yet
 | ||||
| 	
 | ||||
| 	BogusArg (Rc <Vec <String>>), | ||||
| 	BogusClosure (Rc <BogusClosure>), | ||||
| 	BogusEnv (Rc <BTreeMap <String, Value>>), | ||||
| 	BogusEnv (Rc <HashMap <String, Value>>), | ||||
| 	BogusPrint, | ||||
| } | ||||
| 
 | ||||
|  | @ -46,7 +55,13 @@ impl From <&str> for Value { | |||
| 
 | ||||
| impl From <i32> for Value { | ||||
| 	fn from (x: i32) -> Self { | ||||
| 		Self::Integer (i64::try_from (x).unwrap ()) | ||||
| 		Self::Integer (i64::from (x)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From <i64> for Value { | ||||
| 	fn from (x: i64) -> Self { | ||||
| 		Self::Integer (x) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -56,6 +71,14 @@ impl From <f64> for Value { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From <Table> for Value { | ||||
| 	fn from (x: Table) -> Self { | ||||
| 		Self::Table (x.into ()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Eq for Value {} | ||||
| 
 | ||||
| impl std::hash::Hash for Value { | ||||
| 	fn hash <H: std::hash::Hasher> (&self, state: &mut H) { | ||||
| 		// Per https://doc.rust-lang.org/std/hash/trait.Hash.html#prefix-collisions
 | ||||
|  | @ -67,7 +90,11 @@ impl std::hash::Hash for Value { | |||
| 			Self::Boolean (x) => x.hash (state), | ||||
| 			Self::Float (x) => x.to_ne_bytes ().hash (state), | ||||
| 			Self::Integer (x) => x.hash (state), | ||||
| 			Self::String (x) => x.as_ptr ().hash (state), | ||||
| 			
 | ||||
| 			// TODO: Implement string interning so we don't hash the whole string here
 | ||||
| 			Self::String (x) => x.hash (state), | ||||
| 			Self::Table (x) => Rc::as_ptr (&x).hash (state), | ||||
| 			
 | ||||
| 			Self::BogusArg (_) => panic! ("can't hash Bogus values"), | ||||
| 			Self::BogusClosure (_) => panic! ("can't hash Bogus values"), | ||||
| 			Self::BogusEnv (_) => panic! ("can't hash Bogus values"), | ||||
|  | @ -76,6 +103,12 @@ impl std::hash::Hash for Value { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl PartialEq <i64> for Value { | ||||
| 	fn eq (&self, rhs: &i64) -> bool { | ||||
| 		*self == Value::from (*rhs) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Value { | ||||
| 	pub fn as_float (&self) -> Option <f64> { | ||||
| 		match self { | ||||
|  | @ -102,3 +135,74 @@ impl Value { | |||
| 		x | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[derive (Debug, Default, Eq, PartialEq)] | ||||
| pub struct Table { | ||||
| 	array: Vec <Value>, | ||||
| 	hash: HashMap <Value, Value>, | ||||
| } | ||||
| 
 | ||||
| impl Table { | ||||
| 	fn get_inner (&self, key: &Value) -> Value { | ||||
| 		self.hash.get (key).cloned ().unwrap_or_default () | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn get <A: Into <Value>> (&self, key: A) -> Value { | ||||
| 		self.get_inner (&(key.into ())) | ||||
| 	} | ||||
| 	
 | ||||
| 	fn insert_inner (&mut self, a: Value, b: Value) { | ||||
| 		self.hash.insert (a, b); | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn insert <A: Into <Value>, B: Into <Value>> ( | ||||
| 		&mut self, | ||||
| 		a: A, | ||||
| 		b: B, | ||||
| 	) { | ||||
| 		self.insert_inner (a.into (), b.into ()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[cfg (test)] | ||||
| mod tests { | ||||
| 	use std::collections::HashMap; | ||||
| 	use super::{ | ||||
| 		Table, | ||||
| 		Value, | ||||
| 	}; | ||||
| 	
 | ||||
| 	#[test] | ||||
| 	fn smoke () { | ||||
| 		let v_a = Value::from (5.0); | ||||
| 		let v_b = Value::from (5); | ||||
| 		
 | ||||
| 		assert_eq! (v_a.as_float (), Some (5.0)); | ||||
| 		assert_eq! (v_b.as_float (), Some (5.0)); | ||||
| 	} | ||||
| 	
 | ||||
| 	#[test] | ||||
| 	fn tables () { | ||||
| 		let nil = Value::Nil; | ||||
| 		
 | ||||
| 		assert_ne! (Value::from (18.0), Value::from (19.0)); | ||||
| 		
 | ||||
| 		let mut t = HashMap::new (); | ||||
| 		t.insert (Value::from ("x"), Value::from (19.0)); | ||||
| 		assert_eq! (t.get (&Value::from ("x")), Some (&Value::from (19.0))); | ||||
| 		
 | ||||
| 		let mut t = Table::default (); | ||||
| 		
 | ||||
| 		assert_eq! (t.get ("a"), nil); | ||||
| 		assert_eq! (t.get ("b"), nil); | ||||
| 		
 | ||||
| 		t.insert ("a", 1993); | ||||
| 		t.insert ("b", 2007); | ||||
| 		
 | ||||
| 		assert_eq! (t.get ("a"), 1993); | ||||
| 		assert_eq! (t.get ("b"), 2007); | ||||
| 		
 | ||||
| 		t.insert (19, 93); | ||||
| 		assert_eq! (t.get (19), 93); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -4,3 +4,6 @@ print (true) | |||
| print (1993) | ||||
| print (1993.00) | ||||
| print "Hello." | ||||
| 
 | ||||
| local t = {} | ||||
| print (t) | ||||
|  |  | |||
|  | @ -0,0 +1,35 @@ | |||
| 
 | ||||
| main <test_vectors/hello.lua:0,0> (25 instructions at 0x55938922ecd0) | ||||
| 0+ params, 3 slots, 1 upvalue, 1 local, 2 constants, 0 functions | ||||
| 	1	[1]	VARARGPREP	0 | ||||
| 	2	[1]	GETTABUP 	0 0 0	; _ENV "print" | ||||
| 	3	[1]	LOADNIL  	1 0	; 1 out | ||||
| 	4	[1]	CALL     	0 2 1	; 1 in 0 out | ||||
| 	5	[2]	GETTABUP 	0 0 0	; _ENV "print" | ||||
| 	6	[2]	LOADFALSE	1 | ||||
| 	7	[2]	CALL     	0 2 1	; 1 in 0 out | ||||
| 	8	[3]	GETTABUP 	0 0 0	; _ENV "print" | ||||
| 	9	[3]	LOADTRUE 	1 | ||||
| 	10	[3]	CALL     	0 2 1	; 1 in 0 out | ||||
| 	11	[4]	GETTABUP 	0 0 0	; _ENV "print" | ||||
| 	12	[4]	LOADI    	1 1993 | ||||
| 	13	[4]	CALL     	0 2 1	; 1 in 0 out | ||||
| 	14	[5]	GETTABUP 	0 0 0	; _ENV "print" | ||||
| 	15	[5]	LOADF    	1 1993 | ||||
| 	16	[5]	CALL     	0 2 1	; 1 in 0 out | ||||
| 	17	[6]	GETTABUP 	0 0 0	; _ENV "print" | ||||
| 	18	[6]	LOADK    	1 1	; "Hello." | ||||
| 	19	[6]	CALL     	0 2 1	; 1 in 0 out | ||||
| 	20	[8]	NEWTABLE 	0 0 0	; 0 | ||||
| 	21	[8]	EXTRAARG 	0 | ||||
| 	22	[9]	GETTABUP 	1 0 0	; _ENV "print" | ||||
| 	23	[9]	MOVE     	2 0 | ||||
| 	24	[9]	CALL     	1 2 1	; 1 in 0 out | ||||
| 	25	[9]	RETURN   	1 1 1	; 0 out | ||||
| constants (2) for 0x55938922ecd0: | ||||
| 	0	S	"print" | ||||
| 	1	S	"Hello." | ||||
| locals (1) for 0x55938922ecd0: | ||||
| 	0	t	22	26 | ||||
| upvalues (1) for 0x55938922ecd0: | ||||
| 	0	_ENV	1	0 | ||||
		Loading…
	
		Reference in New Issue
	
	 _
						_