Compare commits
	
		
			No commits in common. "130330b688acb309216759962de51c4df22fccca" and "bba98043c75be16d1d1e4c05ded79e07c90f7779" have entirely different histories. 
		
	
	
		
			130330b688
			...
			bba98043c7
		
	
		|  | @ -22,8 +22,6 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError | |||
| 	let mut arg_iter = args.iter (); | ||||
| 	let _exe_name = arg_iter.next ().unwrap (); | ||||
| 	
 | ||||
| 	let mut si = lwvm::Interner::default (); | ||||
| 	
 | ||||
| 	while let Some (arg) = arg_iter.next () { | ||||
| 		match arg.as_str () { | ||||
| 			"--break" => { | ||||
|  | @ -42,7 +40,7 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError | |||
| 				let mut buf = vec! []; | ||||
| 				std::io::stdin ().read_to_end (&mut buf).unwrap (); | ||||
| 				let bc = lwvm::ensure_bytecode (buf).unwrap (); | ||||
| 				chunk = Some (lwvm::parse_chunk (&bc, &mut si).unwrap ()); | ||||
| 				chunk = Some (lwvm::parse_chunk (&bc).unwrap ()); | ||||
| 				
 | ||||
| 				lua_args = vec! ["-".to_string ()]; | ||||
| 			}, | ||||
|  | @ -53,7 +51,7 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError | |||
| 				} | ||||
| 				else if chunk.is_none () { | ||||
| 					let bc = lwvm::compile_bytecode_from_file (x); | ||||
| 					chunk = Some (lwvm::parse_chunk (&bc, &mut si).unwrap ()); | ||||
| 					chunk = Some (lwvm::parse_chunk (&bc).unwrap ()); | ||||
| 					
 | ||||
| 					lua_args = vec! [x.to_string ()]; | ||||
| 				} | ||||
|  | @ -70,12 +68,10 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError | |||
| 			chunk, | ||||
| 			list_bytecode, | ||||
| 			lua_args, | ||||
| 			si, | ||||
| 		}), | ||||
| 		None => repl (ReplParams { | ||||
| 			list_bytecode, | ||||
| 			lua_args, | ||||
| 			si, | ||||
| 		}), | ||||
| 	} | ||||
| } | ||||
|  | @ -85,13 +81,11 @@ struct DebuggerParams { | |||
| 	chunk: lwvm::Chunk, | ||||
| 	list_bytecode: bool, | ||||
| 	lua_args: Vec <String>, | ||||
| 	si: lwvm::Interner, | ||||
| } | ||||
| 
 | ||||
| struct ReplParams { | ||||
| 	list_bytecode: bool, | ||||
| 	lua_args: Vec <String>, | ||||
| 	si: lwvm::Interner, | ||||
| } | ||||
| 
 | ||||
| /// The interpreter mode, which has optional debugging abilities
 | ||||
|  | @ -103,7 +97,9 @@ fn debugger (params: DebuggerParams) -> Result <Vec <lwvm::Value>, lwvm::StepErr | |||
| 		dbg! (¶ms.chunk); | ||||
| 	} | ||||
| 	
 | ||||
| 	let mut vm = lwvm::State::new_with_args (params.chunk, params.si, params.lua_args.into_iter ()); | ||||
| 	let upvalues = lwvm::State::upvalues_from_args (params.lua_args.into_iter ()); | ||||
| 	
 | ||||
| 	let mut vm = lwvm::State::new (params.chunk, upvalues); | ||||
| 	if std::env::var("LWVM_DEBUG").is_ok() { | ||||
| 		vm.debug_print = true; | ||||
| 	} | ||||
|  | @ -164,7 +160,9 @@ fn debugger (params: DebuggerParams) -> Result <Vec <lwvm::Value>, lwvm::StepErr | |||
| 
 | ||||
| fn repl (params: ReplParams) -> Result <Vec <lwvm::Value>, lwvm::StepError> 
 | ||||
| { | ||||
| 	let mut vm = lwvm::State::new_with_args (lwvm::Chunk::default (), params.si, params.lua_args.into_iter ()); | ||||
| 	let upvalues = lwvm::State::upvalues_from_args (params.lua_args.into_iter ()); | ||||
| 	
 | ||||
| 	let mut vm = lwvm::State::new (lwvm::Chunk::default (), upvalues); | ||||
| 	
 | ||||
| 	println! ("Lunar Wave 0.1.0-modified  Copyright (C) 2023 ReactorScram (implements Lua 5.4  Copyright (C) 1994-2022 Lua.org, PUC-Rio"); | ||||
| 	
 | ||||
|  | @ -190,7 +188,7 @@ fn repl (params: ReplParams) -> Result <Vec <lwvm::Value>, lwvm::StepError> | |||
| 				continue; | ||||
| 			}, | ||||
| 		}; | ||||
| 		let chunk = lwvm::parse_chunk (&bytecode, &mut vm.si).unwrap (); | ||||
| 		let chunk = lwvm::parse_chunk (&bytecode).unwrap (); | ||||
| 		
 | ||||
| 		if params.list_bytecode { | ||||
| 			dbg! (&chunk); | ||||
|  |  | |||
|  | @ -1,636 +0,0 @@ | |||
| use std::{ | ||||
| 	collections::{BTreeMap, HashMap}, | ||||
| 	time::Instant, | ||||
| }; | ||||
| 
 | ||||
| fn main () { | ||||
| 	// PUC Lua is 250 ms
 | ||||
| 	// I'm using AOT Rust here so anything more than that 
 | ||||
| 	// is simply embarrassing
 | ||||
| 	
 | ||||
| 	fn timer <F: Fn ()> (f: F, label: &str) { | ||||
| 		let start = Instant::now (); | ||||
| 		f (); | ||||
| 		let stop = Instant::now (); | ||||
| 		println! ("{}: {} ms", label, (stop - start).as_millis ()); | ||||
| 	} | ||||
| 	
 | ||||
| 	// timer (|| n_body::<BTreeTable> (100000), "BTreeTable");
 | ||||
| 	// timer (|| n_body::<BTreeTable2> (100000), "BTreeTable2");
 | ||||
| 	// timer (|| n_body::<HashTable> (100000), "HashTable");
 | ||||
| 	// timer (|| n_body_2::<HashTable2> (100000), "HashTable2");
 | ||||
| 	
 | ||||
| 	// Fastest on my system, at 83 ms
 | ||||
| 	timer (|| n_body_2::<BTreeTable3> (100000), "BTreeTable3"); | ||||
| 	timer (|| n_body_2::<BTreeTable3> (500000), "BTreeTable3"); | ||||
| 	timer (|| n_body_3::<BTreeTable4> (500000), "BTreeTable4"); | ||||
| } | ||||
| 
 | ||||
| trait Table { | ||||
| 	fn get (&self, key: &str) -> f64; | ||||
| 	fn set (&mut self, key: &'static str, value: f64); | ||||
| } | ||||
| 
 | ||||
| #[derive (Default)] | ||||
| struct BTreeTable (BTreeMap <String, f64>); | ||||
| 
 | ||||
| impl Table for BTreeTable { | ||||
| 	fn get (&self, key: &str) -> f64 { | ||||
| 		*self.0.get (key).unwrap () | ||||
| 	} | ||||
| 	
 | ||||
| 	fn set (&mut self, key: &str, value: f64) { | ||||
| 		self.0.insert (key.to_string (), value); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[derive (Default)] | ||||
| struct BTreeTable2 (BTreeMap <&'static str, f64>); | ||||
| 
 | ||||
| impl Table for BTreeTable2 { | ||||
| 	fn get (&self, key: &str) -> f64 { | ||||
| 		*self.0.get (key).unwrap () | ||||
| 	} | ||||
| 	
 | ||||
| 	fn set (&mut self, key: &'static str, value: f64) { | ||||
| 		self.0.insert (key, value); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[derive (Default)] | ||||
| struct HashTable (HashMap <String, f64>); | ||||
| 
 | ||||
| impl Table for HashTable { | ||||
| 	fn get (&self, key: &str) -> f64 { | ||||
| 		*self.0.get (key).unwrap () | ||||
| 	} | ||||
| 	
 | ||||
| 	fn set (&mut self, key: &str, value: f64) { | ||||
| 		self.0.insert (key.to_string (), value); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| const PI: f64 = 3.141592653589793f64; | ||||
| const SOLAR_MASS: f64 = 4.0 * PI * PI; | ||||
| 
 | ||||
| fn n_body <T: Default + Table> (num_iters: usize) { | ||||
| 	const X: &str = "x"; | ||||
| 	const Y: &str = "y"; | ||||
| 	const Z: &str = "z"; | ||||
| 	const VX: &str = "vx"; | ||||
| 	const VY: &str = "vy"; | ||||
| 	const VZ: &str = "vz"; | ||||
| 	const MASS: &str = "mass"; | ||||
| 	
 | ||||
| 	let days_per_year = 365.24; | ||||
| 	
 | ||||
| 	let mut bodies = vec! []; | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 0.0); | ||||
| 		t.set (Y, 0.0); | ||||
| 		t.set (Z, 0.0); | ||||
| 		t.set (VX, 0.0); | ||||
| 		t.set (VY, 0.0); | ||||
| 		t.set (VZ, 0.0); | ||||
| 		t.set (MASS, SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 4.84143144246472090e+00); | ||||
| 		t.set (Y, -1.16032004402742839e+00); | ||||
| 		t.set (Z, -1.03622044471123109e-01); | ||||
| 		t.set (VX, 1.66007664274403694e-03 * days_per_year); | ||||
| 		t.set (VY, 7.69901118419740425e-03 * days_per_year); | ||||
| 		t.set (VZ, -6.90460016972063023e-05 * days_per_year); | ||||
| 		t.set (MASS, 9.54791938424326609e-04 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 8.34336671824457987e+00); | ||||
| 		t.set (Y, 4.12479856412430479e+00); | ||||
| 		t.set (Z, -4.03523417114321381e-01); | ||||
| 		t.set (VX, -2.76742510726862411e-03 * days_per_year); | ||||
| 		t.set (VY, 4.99852801234917238e-03 * days_per_year); | ||||
| 		t.set (VZ, 2.30417297573763929e-05 * days_per_year); | ||||
| 		t.set (MASS, 2.85885980666130812e-04 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 1.28943695621391310e+01); | ||||
| 		t.set (Y, -1.51111514016986312e+01); | ||||
| 		t.set (Z, -2.23307578892655734e-01); | ||||
| 		t.set (VX, 2.96460137564761618e-03 * days_per_year); | ||||
| 		t.set (VY, 2.37847173959480950e-03 * days_per_year); | ||||
| 		t.set (VZ, -2.96589568540237556e-05 * days_per_year); | ||||
| 		t.set (MASS, 4.36624404335156298e-05 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 1.53796971148509165e+01); | ||||
| 		t.set (Y, -2.59193146099879641e+01); | ||||
| 		t.set (Z, 1.79258772950371181e-01); | ||||
| 		t.set (VX, 2.68067772490389322e-03 * days_per_year); | ||||
| 		t.set (VY, 1.62824170038242295e-03 * days_per_year); | ||||
| 		t.set (VZ, -9.51592254519715870e-05 * days_per_year); | ||||
| 		t.set (MASS, 5.15138902046611451e-05 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	
 | ||||
| 	fn advance <T: Table> (bodies: &mut [T], dt: f64) { | ||||
| 		let nbody = bodies.len (); | ||||
| 		
 | ||||
| 		for i in 0..nbody { | ||||
| 			let bix; | ||||
| 			let biy; | ||||
| 			let biz; | ||||
| 			let bimass; | ||||
| 			let mut bivx; | ||||
| 			let mut bivy; | ||||
| 			let mut bivz; | ||||
| 			{ | ||||
| 				let bi = &mut bodies [i]; | ||||
| 				bix = bi.get (X); | ||||
| 				biy = bi.get (Y); | ||||
| 				biz = bi.get (Z); | ||||
| 				bimass = bi.get (MASS); | ||||
| 				bivx = bi.get (VX); | ||||
| 				bivy = bi.get (VY); | ||||
| 				bivz = bi.get (VZ); | ||||
| 			} | ||||
| 			
 | ||||
| 			for j in i + 1..nbody { | ||||
| 				let bj = &mut bodies [j]; | ||||
| 				let dx = bix - bj.get (X); | ||||
| 				let dy = biy - bj.get (Y); | ||||
| 				let dz = biz - bj.get (Z); | ||||
| 				let mut mag = (dx * dx + dy * dy + dz * dz).sqrt (); | ||||
| 				mag = dt / (mag * mag * mag); | ||||
| 				let mut bm = bj.get (MASS) * mag; | ||||
| 				bivx -= dx * bm; | ||||
| 				bivy -= dy * bm; | ||||
| 				bivz -= dz * bm; | ||||
| 				bm = bimass * mag; | ||||
| 				bj.set (VX, bj.get (VX) + dx * bm); | ||||
| 				bj.set (VY, bj.get (VY) + dy * bm); | ||||
| 				bj.set (VZ, bj.get (VZ) + dz * bm); | ||||
| 			} | ||||
| 			
 | ||||
| 			let bi = &mut bodies [i]; | ||||
| 			bi.set (VX, bivx); | ||||
| 			bi.set (VY, bivy); | ||||
| 			bi.set (VZ, bivz); | ||||
| 			bi.set (X, bix + dt * bivx); | ||||
| 			bi.set (Y, biy + dt * bivy); | ||||
| 			bi.set (Z, biz + dt * bivz); | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	fn energy <T: Table> (bodies: &[T]) -> f64 { | ||||
| 		let mut e = 0.0; | ||||
| 		for (i, bi) in bodies.iter ().enumerate () { | ||||
| 			let vx = bi.get (VX); | ||||
| 			let vy = bi.get (VY); | ||||
| 			let vz = bi.get (VZ); | ||||
| 			let bim = bi.get (MASS); | ||||
| 			
 | ||||
| 			e += 0.5 * bim * (vx * vx + vy * vy + vz * vz); | ||||
| 			
 | ||||
| 			for j in i + 1..bodies.len () { | ||||
| 				let bj = &bodies [j]; | ||||
| 				let dx = bi.get (X) - bj.get (X); | ||||
| 				let dy = bi.get (Y) - bj.get (Y); | ||||
| 				let dz = bi.get (Z) - bj.get (Z); | ||||
| 				let distance = (dx * dx + dy * dy + dz * dz).sqrt (); | ||||
| 				e -= (bim * bj.get (MASS)) / distance; | ||||
| 			} | ||||
| 		} | ||||
| 		
 | ||||
| 		e | ||||
| 	} | ||||
| 	
 | ||||
| 	fn offset_momentum <T: Table> (b: &mut [T]) { | ||||
| 		let mut px = 0.0; | ||||
| 		let mut py = 0.0; | ||||
| 		let mut pz = 0.0; | ||||
| 		
 | ||||
| 		for bi in b.iter () { | ||||
| 			let bim = bi.get (MASS); | ||||
| 			px += bi.get (VX) * bim; | ||||
| 			py += bi.get (VY) * bim; | ||||
| 			pz += bi.get (VZ) * bim; | ||||
| 		} | ||||
| 		
 | ||||
| 		let bimass = b [0].get (MASS); | ||||
| 		b [0].set (VX, -px / bimass); | ||||
| 		b [0].set (VY, -py / bimass); | ||||
| 		b [0].set (VZ, -pz / bimass); | ||||
| 	} | ||||
| 	
 | ||||
| 	offset_momentum (&mut bodies); | ||||
| 	println! ("{:0.9}", energy (&bodies)); | ||||
| 	for _ in 0..num_iters { | ||||
| 		advance (&mut bodies, 0.01); | ||||
| 	} | ||||
| 	println! ("{:0.9}", energy (&bodies)); | ||||
| } | ||||
| 
 | ||||
| trait Table2 { | ||||
| 	fn get (&self, key: i64) -> f64; | ||||
| 	fn set (&mut self, key: i64, value: f64); | ||||
| } | ||||
| 
 | ||||
| #[derive (Default)] | ||||
| struct BTreeTable3 (BTreeMap <i64, f64>); | ||||
| 
 | ||||
| impl Table2 for BTreeTable3 { | ||||
| 	fn get (&self, key: i64) -> f64 { | ||||
| 		*self.0.get (&key).unwrap () | ||||
| 	} | ||||
| 	
 | ||||
| 	fn set (&mut self, key: i64, value: f64) { | ||||
| 		self.0.insert (key, value); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[derive (Default)] | ||||
| struct HashTable2 (HashMap <i64, f64>); | ||||
| 
 | ||||
| impl Table2 for HashTable2 { | ||||
| 	fn get (&self, key: i64) -> f64 { | ||||
| 		*self.0.get (&key).unwrap () | ||||
| 	} | ||||
| 	
 | ||||
| 	fn set (&mut self, key: i64, value: f64) { | ||||
| 		self.0.insert (key, value); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn n_body_2 <T: Default + Table2> (num_iters: usize) { | ||||
| 	const X: i64 = 1; | ||||
| 	const Y: i64 = 2; | ||||
| 	const Z: i64 = 3; | ||||
| 	const VX: i64 = 4; | ||||
| 	const VY: i64 = 5; | ||||
| 	const VZ: i64 = 6; | ||||
| 	const MASS: i64 = 7; | ||||
| 	
 | ||||
| 	let days_per_year = 365.24; | ||||
| 	
 | ||||
| 	let mut bodies = vec! []; | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 0.0); | ||||
| 		t.set (Y, 0.0); | ||||
| 		t.set (Z, 0.0); | ||||
| 		t.set (VX, 0.0); | ||||
| 		t.set (VY, 0.0); | ||||
| 		t.set (VZ, 0.0); | ||||
| 		t.set (MASS, SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 4.84143144246472090e+00); | ||||
| 		t.set (Y, -1.16032004402742839e+00); | ||||
| 		t.set (Z, -1.03622044471123109e-01); | ||||
| 		t.set (VX, 1.66007664274403694e-03 * days_per_year); | ||||
| 		t.set (VY, 7.69901118419740425e-03 * days_per_year); | ||||
| 		t.set (VZ, -6.90460016972063023e-05 * days_per_year); | ||||
| 		t.set (MASS, 9.54791938424326609e-04 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 8.34336671824457987e+00); | ||||
| 		t.set (Y, 4.12479856412430479e+00); | ||||
| 		t.set (Z, -4.03523417114321381e-01); | ||||
| 		t.set (VX, -2.76742510726862411e-03 * days_per_year); | ||||
| 		t.set (VY, 4.99852801234917238e-03 * days_per_year); | ||||
| 		t.set (VZ, 2.30417297573763929e-05 * days_per_year); | ||||
| 		t.set (MASS, 2.85885980666130812e-04 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 1.28943695621391310e+01); | ||||
| 		t.set (Y, -1.51111514016986312e+01); | ||||
| 		t.set (Z, -2.23307578892655734e-01); | ||||
| 		t.set (VX, 2.96460137564761618e-03 * days_per_year); | ||||
| 		t.set (VY, 2.37847173959480950e-03 * days_per_year); | ||||
| 		t.set (VZ, -2.96589568540237556e-05 * days_per_year); | ||||
| 		t.set (MASS, 4.36624404335156298e-05 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 1.53796971148509165e+01); | ||||
| 		t.set (Y, -2.59193146099879641e+01); | ||||
| 		t.set (Z, 1.79258772950371181e-01); | ||||
| 		t.set (VX, 2.68067772490389322e-03 * days_per_year); | ||||
| 		t.set (VY, 1.62824170038242295e-03 * days_per_year); | ||||
| 		t.set (VZ, -9.51592254519715870e-05 * days_per_year); | ||||
| 		t.set (MASS, 5.15138902046611451e-05 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	
 | ||||
| 	fn advance <T: Table2> (bodies: &mut [T], dt: f64) { | ||||
| 		let nbody = bodies.len (); | ||||
| 		
 | ||||
| 		for i in 0..nbody { | ||||
| 			let bix; | ||||
| 			let biy; | ||||
| 			let biz; | ||||
| 			let bimass; | ||||
| 			let mut bivx; | ||||
| 			let mut bivy; | ||||
| 			let mut bivz; | ||||
| 			{ | ||||
| 				let bi = &mut bodies [i]; | ||||
| 				bix = bi.get (X); | ||||
| 				biy = bi.get (Y); | ||||
| 				biz = bi.get (Z); | ||||
| 				bimass = bi.get (MASS); | ||||
| 				bivx = bi.get (VX); | ||||
| 				bivy = bi.get (VY); | ||||
| 				bivz = bi.get (VZ); | ||||
| 			} | ||||
| 			
 | ||||
| 			for j in i + 1..nbody { | ||||
| 				let bj = &mut bodies [j]; | ||||
| 				let dx = bix - bj.get (X); | ||||
| 				let dy = biy - bj.get (Y); | ||||
| 				let dz = biz - bj.get (Z); | ||||
| 				let mut mag = (dx * dx + dy * dy + dz * dz).sqrt (); | ||||
| 				mag = dt / (mag * mag * mag); | ||||
| 				let mut bm = bj.get (MASS) * mag; | ||||
| 				bivx -= dx * bm; | ||||
| 				bivy -= dy * bm; | ||||
| 				bivz -= dz * bm; | ||||
| 				bm = bimass * mag; | ||||
| 				bj.set (VX, bj.get (VX) + dx * bm); | ||||
| 				bj.set (VY, bj.get (VY) + dy * bm); | ||||
| 				bj.set (VZ, bj.get (VZ) + dz * bm); | ||||
| 			} | ||||
| 			
 | ||||
| 			let bi = &mut bodies [i]; | ||||
| 			bi.set (VX, bivx); | ||||
| 			bi.set (VY, bivy); | ||||
| 			bi.set (VZ, bivz); | ||||
| 			bi.set (X, bix + dt * bivx); | ||||
| 			bi.set (Y, biy + dt * bivy); | ||||
| 			bi.set (Z, biz + dt * bivz); | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	fn energy <T: Table2> (bodies: &[T]) -> f64 { | ||||
| 		let mut e = 0.0; | ||||
| 		for (i, bi) in bodies.iter ().enumerate () { | ||||
| 			let vx = bi.get (VX); | ||||
| 			let vy = bi.get (VY); | ||||
| 			let vz = bi.get (VZ); | ||||
| 			let bim = bi.get (MASS); | ||||
| 			
 | ||||
| 			e += 0.5 * bim * (vx * vx + vy * vy + vz * vz); | ||||
| 			
 | ||||
| 			for j in i + 1..bodies.len () { | ||||
| 				let bj = &bodies [j]; | ||||
| 				let dx = bi.get (X) - bj.get (X); | ||||
| 				let dy = bi.get (Y) - bj.get (Y); | ||||
| 				let dz = bi.get (Z) - bj.get (Z); | ||||
| 				let distance = (dx * dx + dy * dy + dz * dz).sqrt (); | ||||
| 				e -= (bim * bj.get (MASS)) / distance; | ||||
| 			} | ||||
| 		} | ||||
| 		
 | ||||
| 		e | ||||
| 	} | ||||
| 	
 | ||||
| 	fn offset_momentum <T: Table2> (b: &mut [T]) { | ||||
| 		let mut px = 0.0; | ||||
| 		let mut py = 0.0; | ||||
| 		let mut pz = 0.0; | ||||
| 		
 | ||||
| 		for bi in b.iter () { | ||||
| 			let bim = bi.get (MASS); | ||||
| 			px += bi.get (VX) * bim; | ||||
| 			py += bi.get (VY) * bim; | ||||
| 			pz += bi.get (VZ) * bim; | ||||
| 		} | ||||
| 		
 | ||||
| 		let bimass = b [0].get (MASS); | ||||
| 		b [0].set (VX, -px / bimass); | ||||
| 		b [0].set (VY, -py / bimass); | ||||
| 		b [0].set (VZ, -pz / bimass); | ||||
| 	} | ||||
| 	
 | ||||
| 	offset_momentum (&mut bodies); | ||||
| 	println! ("{:0.9}", energy (&bodies)); | ||||
| 	for _ in 0..num_iters { | ||||
| 		advance (&mut bodies, 0.01); | ||||
| 	} | ||||
| 	println! ("{:0.9}", energy (&bodies)); | ||||
| } | ||||
| 
 | ||||
| trait Table3 { | ||||
| 	fn get (&self, key: u64) -> f64; | ||||
| 	fn set (&mut self, key: u64, value: f64); | ||||
| } | ||||
| 
 | ||||
| #[derive (Default)] | ||||
| struct BTreeTable4 (BTreeMap <u64, f64>); | ||||
| 
 | ||||
| impl Table3 for BTreeTable4 { | ||||
| 	fn get (&self, key: u64) -> f64 { | ||||
| 		*self.0.get (&key).unwrap () | ||||
| 	} | ||||
| 	
 | ||||
| 	fn set (&mut self, key: u64, value: f64) { | ||||
| 		self.0.insert (key, value); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn n_body_3 <T: Default + Table3> (num_iters: usize) { | ||||
| 	const X: u64 = 1; | ||||
| 	const Y: u64 = 2; | ||||
| 	const Z: u64 = 3; | ||||
| 	const VX: u64 = 4; | ||||
| 	const VY: u64 = 5; | ||||
| 	const VZ: u64 = 6; | ||||
| 	const MASS: u64 = 7; | ||||
| 	
 | ||||
| 	let days_per_year = 365.24; | ||||
| 	
 | ||||
| 	let mut bodies = vec! []; | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 0.0); | ||||
| 		t.set (Y, 0.0); | ||||
| 		t.set (Z, 0.0); | ||||
| 		t.set (VX, 0.0); | ||||
| 		t.set (VY, 0.0); | ||||
| 		t.set (VZ, 0.0); | ||||
| 		t.set (MASS, SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 4.84143144246472090e+00); | ||||
| 		t.set (Y, -1.16032004402742839e+00); | ||||
| 		t.set (Z, -1.03622044471123109e-01); | ||||
| 		t.set (VX, 1.66007664274403694e-03 * days_per_year); | ||||
| 		t.set (VY, 7.69901118419740425e-03 * days_per_year); | ||||
| 		t.set (VZ, -6.90460016972063023e-05 * days_per_year); | ||||
| 		t.set (MASS, 9.54791938424326609e-04 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 8.34336671824457987e+00); | ||||
| 		t.set (Y, 4.12479856412430479e+00); | ||||
| 		t.set (Z, -4.03523417114321381e-01); | ||||
| 		t.set (VX, -2.76742510726862411e-03 * days_per_year); | ||||
| 		t.set (VY, 4.99852801234917238e-03 * days_per_year); | ||||
| 		t.set (VZ, 2.30417297573763929e-05 * days_per_year); | ||||
| 		t.set (MASS, 2.85885980666130812e-04 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 1.28943695621391310e+01); | ||||
| 		t.set (Y, -1.51111514016986312e+01); | ||||
| 		t.set (Z, -2.23307578892655734e-01); | ||||
| 		t.set (VX, 2.96460137564761618e-03 * days_per_year); | ||||
| 		t.set (VY, 2.37847173959480950e-03 * days_per_year); | ||||
| 		t.set (VZ, -2.96589568540237556e-05 * days_per_year); | ||||
| 		t.set (MASS, 4.36624404335156298e-05 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	{ | ||||
| 		let mut t = T::default (); | ||||
| 		t.set (X, 1.53796971148509165e+01); | ||||
| 		t.set (Y, -2.59193146099879641e+01); | ||||
| 		t.set (Z, 1.79258772950371181e-01); | ||||
| 		t.set (VX, 2.68067772490389322e-03 * days_per_year); | ||||
| 		t.set (VY, 1.62824170038242295e-03 * days_per_year); | ||||
| 		t.set (VZ, -9.51592254519715870e-05 * days_per_year); | ||||
| 		t.set (MASS, 5.15138902046611451e-05 * SOLAR_MASS); | ||||
| 		
 | ||||
| 		bodies.push (t); | ||||
| 	} | ||||
| 	
 | ||||
| 	fn advance <T: Table3> (bodies: &mut [T], dt: f64) { | ||||
| 		let nbody = bodies.len (); | ||||
| 		
 | ||||
| 		for i in 0..nbody { | ||||
| 			let bix; | ||||
| 			let biy; | ||||
| 			let biz; | ||||
| 			let bimass; | ||||
| 			let mut bivx; | ||||
| 			let mut bivy; | ||||
| 			let mut bivz; | ||||
| 			{ | ||||
| 				let bi = &mut bodies [i]; | ||||
| 				bix = bi.get (X); | ||||
| 				biy = bi.get (Y); | ||||
| 				biz = bi.get (Z); | ||||
| 				bimass = bi.get (MASS); | ||||
| 				bivx = bi.get (VX); | ||||
| 				bivy = bi.get (VY); | ||||
| 				bivz = bi.get (VZ); | ||||
| 			} | ||||
| 			
 | ||||
| 			for j in i + 1..nbody { | ||||
| 				let bj = &mut bodies [j]; | ||||
| 				let dx = bix - bj.get (X); | ||||
| 				let dy = biy - bj.get (Y); | ||||
| 				let dz = biz - bj.get (Z); | ||||
| 				let mut mag = (dx * dx + dy * dy + dz * dz).sqrt (); | ||||
| 				mag = dt / (mag * mag * mag); | ||||
| 				let mut bm = bj.get (MASS) * mag; | ||||
| 				bivx -= dx * bm; | ||||
| 				bivy -= dy * bm; | ||||
| 				bivz -= dz * bm; | ||||
| 				bm = bimass * mag; | ||||
| 				bj.set (VX, bj.get (VX) + dx * bm); | ||||
| 				bj.set (VY, bj.get (VY) + dy * bm); | ||||
| 				bj.set (VZ, bj.get (VZ) + dz * bm); | ||||
| 			} | ||||
| 			
 | ||||
| 			let bi = &mut bodies [i]; | ||||
| 			bi.set (VX, bivx); | ||||
| 			bi.set (VY, bivy); | ||||
| 			bi.set (VZ, bivz); | ||||
| 			bi.set (X, bix + dt * bivx); | ||||
| 			bi.set (Y, biy + dt * bivy); | ||||
| 			bi.set (Z, biz + dt * bivz); | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	fn energy <T: Table3> (bodies: &[T]) -> f64 { | ||||
| 		let mut e = 0.0; | ||||
| 		for (i, bi) in bodies.iter ().enumerate () { | ||||
| 			let vx = bi.get (VX); | ||||
| 			let vy = bi.get (VY); | ||||
| 			let vz = bi.get (VZ); | ||||
| 			let bim = bi.get (MASS); | ||||
| 			
 | ||||
| 			e += 0.5 * bim * (vx * vx + vy * vy + vz * vz); | ||||
| 			
 | ||||
| 			for j in i + 1..bodies.len () { | ||||
| 				let bj = &bodies [j]; | ||||
| 				let dx = bi.get (X) - bj.get (X); | ||||
| 				let dy = bi.get (Y) - bj.get (Y); | ||||
| 				let dz = bi.get (Z) - bj.get (Z); | ||||
| 				let distance = (dx * dx + dy * dy + dz * dz).sqrt (); | ||||
| 				e -= (bim * bj.get (MASS)) / distance; | ||||
| 			} | ||||
| 		} | ||||
| 		
 | ||||
| 		e | ||||
| 	} | ||||
| 	
 | ||||
| 	fn offset_momentum <T: Table3> (b: &mut [T]) { | ||||
| 		let mut px = 0.0; | ||||
| 		let mut py = 0.0; | ||||
| 		let mut pz = 0.0; | ||||
| 		
 | ||||
| 		for bi in b.iter () { | ||||
| 			let bim = bi.get (MASS); | ||||
| 			px += bi.get (VX) * bim; | ||||
| 			py += bi.get (VY) * bim; | ||||
| 			pz += bi.get (VZ) * bim; | ||||
| 		} | ||||
| 		
 | ||||
| 		let bimass = b [0].get (MASS); | ||||
| 		b [0].set (VX, -px / bimass); | ||||
| 		b [0].set (VY, -py / bimass); | ||||
| 		b [0].set (VZ, -pz / bimass); | ||||
| 	} | ||||
| 	
 | ||||
| 	offset_momentum (&mut bodies); | ||||
| 	println! ("{:0.9}", energy (&bodies)); | ||||
| 	for _ in 0..num_iters { | ||||
| 		advance (&mut bodies, 0.01); | ||||
| 	} | ||||
| 	println! ("{:0.9}", energy (&bodies)); | ||||
| } | ||||
|  | @ -2,7 +2,6 @@ mod error; | |||
| mod instruction; | ||||
| mod loader; | ||||
| mod state; | ||||
| mod string_interner; | ||||
| mod value; | ||||
| 
 | ||||
| pub use error::Error as Error; | ||||
|  | @ -16,7 +15,6 @@ pub use state::Chunk as Chunk; | |||
| pub use state::State as State; | ||||
| pub use state::StepError as StepError; | ||||
| pub use state::StepOutput as StepOutput; | ||||
| pub use string_interner::Interner as Interner; | ||||
| pub use value::Value as Value; | ||||
| 
 | ||||
| #[cfg (test)] | ||||
|  |  | |||
|  | @ -5,8 +5,7 @@ use crate::{ | |||
| 	state::{ | ||||
| 		Block, | ||||
| 		Chunk, | ||||
| 	}, | ||||
| 	string_interner::Interner, | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| pub fn compile_bytecode_from_file (path: &str) -> Vec <u8> { | ||||
|  | @ -243,7 +242,7 @@ fn parse_i64 <R: Read> (rdr: &mut R) -> Option <i64> { | |||
| // code, but I don't like recursion in general, and I don't know
 | ||||
| // why PUC wrote it that way.
 | ||||
| 
 | ||||
| pub fn parse_block <R: Read> (rdr: &mut R, si: &mut Interner, blocks: &mut Vec <Block>) 
 | ||||
| pub fn parse_block <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>) 
 | ||||
| -> Option <()> 
 | ||||
| { | ||||
| 	// Ignore things I haven't implemented yet
 | ||||
|  | @ -274,12 +273,12 @@ pub fn parse_block <R: Read> (rdr: &mut R, si: &mut Interner, blocks: &mut Vec < | |||
| 		
 | ||||
| 		let val = match const_type { | ||||
| 			 3 => parse_i64 (rdr).unwrap ().into (), | ||||
| 			 4 => si.to_value (parse_string (rdr).unwrap ().as_str ()), | ||||
| 			 4 => parse_string (rdr).unwrap ().into (), | ||||
| 			 
 | ||||
| 			// For LUA_TNUMBER, PUC Lua uses a macro that adds 16 to signify a float
 | ||||
| 			19 => parse_float (rdr).unwrap ().into (), | ||||
| 			// 0x10 + 4 = long string
 | ||||
| 			20 => si.to_value (parse_string (rdr).unwrap ().as_str ()), | ||||
| 			20 => parse_string (rdr).unwrap ().into (), | ||||
| 			x => panic! ("Constant {} has type {}", i, x), | ||||
| 		}; | ||||
| 		
 | ||||
|  | @ -313,7 +312,7 @@ pub fn parse_block <R: Read> (rdr: &mut R, si: &mut Interner, blocks: &mut Vec < | |||
| 	// Subfunctions. PUC calls them protos.
 | ||||
| 	let protos_count = parse_int (rdr).unwrap (); | ||||
| 	for _ in 0..protos_count { | ||||
| 		parse_block (rdr, si, blocks).unwrap (); | ||||
| 		parse_block (rdr, blocks).unwrap (); | ||||
| 	} | ||||
| 	
 | ||||
| 	// Skip over debug stuff
 | ||||
|  | @ -349,12 +348,12 @@ pub fn parse_block <R: Read> (rdr: &mut R, si: &mut Interner, blocks: &mut Vec < | |||
| 	Some (()) | ||||
| } | ||||
| 
 | ||||
| pub fn parse_chunk (buf: &[u8], si: &mut Interner) -> Option <Chunk> { | ||||
| pub fn parse_chunk (buf: &[u8]) -> Option <Chunk> { | ||||
| 	let mut rdr = std::io::Cursor::new (buf); | ||||
| 	parse_chunk_from_reader (&mut rdr, si) | ||||
| 	parse_chunk_from_reader (&mut rdr) | ||||
| } | ||||
| 
 | ||||
| pub fn parse_chunk_from_reader <R: Read> (rdr: &mut R, si: &mut Interner) -> Option <Chunk> { | ||||
| pub fn parse_chunk_from_reader <R: Read> (rdr: &mut R) -> Option <Chunk> { | ||||
| 	// Discard 32 bytes from the start of the file.
 | ||||
| 	// This is magic number, version number, etc.
 | ||||
| 	
 | ||||
|  | @ -365,7 +364,7 @@ pub fn parse_chunk_from_reader <R: Read> (rdr: &mut R, si: &mut Interner) -> Opt | |||
| 	
 | ||||
| 	let mut blocks = vec![]; | ||||
| 	
 | ||||
| 	parse_block (rdr, si, &mut blocks).unwrap (); | ||||
| 	parse_block (rdr, &mut blocks).unwrap (); | ||||
| 	
 | ||||
| 	Some (Chunk { | ||||
| 		blocks, | ||||
|  | @ -374,8 +373,6 @@ pub fn parse_chunk_from_reader <R: Read> (rdr: &mut R, si: &mut Interner) -> Opt | |||
| 
 | ||||
| #[cfg (test)] | ||||
| mod tests { | ||||
| 	use super::*; | ||||
| 	
 | ||||
| 	#[test] | ||||
| 	fn load_size () { | ||||
| 		let f = |input: &[u8]| { | ||||
|  | @ -444,8 +441,6 @@ mod tests { | |||
| 	fn parse_nested_functions () { | ||||
| 		use std::io::Read; | ||||
| 		
 | ||||
| 		let mut si = Interner::default (); | ||||
| 		
 | ||||
| 		let bytecode = include_bytes! ("../test_vectors/functions.luac"); | ||||
| 		
 | ||||
| 		{ | ||||
|  | @ -456,7 +451,7 @@ mod tests { | |||
| 			
 | ||||
| 			let mut blocks = vec! []; | ||||
| 			
 | ||||
| 			super::parse_block (&mut rdr, &mut si, &mut blocks).unwrap (); | ||||
| 			super::parse_block (&mut rdr, &mut blocks).unwrap (); | ||||
| 			
 | ||||
| 			assert_eq! (blocks [0].instructions.len (), 15); | ||||
| 			assert_eq! (blocks [1].instructions.len (),  6); | ||||
|  | @ -466,7 +461,7 @@ mod tests { | |||
| 		} | ||||
| 		
 | ||||
| 		if false { | ||||
| 			let file = crate::loader::parse_chunk (bytecode, &mut si).unwrap (); | ||||
| 			let file = crate::loader::parse_chunk (bytecode).unwrap (); | ||||
| 			
 | ||||
| 			assert_eq! (file.blocks.len (), 5); | ||||
| 		} | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| use crate::{ | ||||
| 	instruction::Instruction, | ||||
| 	string_interner::Interner, | ||||
| 	value::{ | ||||
| 		BogusClosure, | ||||
| 		Value, | ||||
|  | @ -55,10 +54,7 @@ pub struct State { | |||
| 	pub debug_print: bool, | ||||
| 	step_count: u32, | ||||
| 	chunk: Chunk, | ||||
| 	pub upvalues: Vec <Value>, | ||||
| 	pub si: Interner, | ||||
| 	register_offset: usize, | ||||
| 	block_idx: usize, | ||||
| 	upvalues: Vec <Value>, | ||||
| } | ||||
| 
 | ||||
| fn lw_io_write (l: &mut State, num_args: usize) -> usize { | ||||
|  | @ -66,7 +62,7 @@ fn lw_io_write (l: &mut State, num_args: usize) -> usize { | |||
| 		match l.reg (i) { | ||||
| 			Value::Float (x) => print! ("{}", x), | ||||
| 			Value::Integer (x) => print! ("{}", x), | ||||
| 			Value::String (x) => print! ("{}", l.si.get (*x)), | ||||
| 			Value::String (x) => print! ("{}", x), | ||||
| 			_ => panic! ("Can't io.write this value"), | ||||
| 		} | ||||
| 	} | ||||
|  | @ -101,12 +97,12 @@ fn lw_string_format (l: &mut State, num_args: usize) -> usize { | |||
| 	assert! (num_args >= 1, "string.format needs at least 1 argument"); | ||||
| 	assert_eq! (l.get_top (), 2, "string.format not fully implemented"); | ||||
| 	let f_string = l.reg (0).as_str ().unwrap (); | ||||
| 	assert_eq! (Value::String (f_string), l.to_string ("%0.9f")); | ||||
| 	assert_eq! (f_string, "%0.9f"); | ||||
| 	let num = l.reg (1).as_float ().unwrap (); | ||||
| 	
 | ||||
| 	let output = format! ("{:0.9}", num); | ||||
| 	
 | ||||
| 	*l.reg_mut (0) = l.to_string (&output); | ||||
| 	*l.reg_mut (0) = Value::from (output); | ||||
| 	1 | ||||
| } | ||||
| 
 | ||||
|  | @ -121,14 +117,14 @@ fn lw_table_concat (l: &mut State, num_args: usize) -> usize { | |||
| 		
 | ||||
| 		for i in 0..t.length () { | ||||
| 			if i > 0 { | ||||
| 				s.push_str (l.si.get (joiner)); | ||||
| 				s.push_str (joiner); | ||||
| 			} | ||||
| 			let x = t.get_int (i + 1); | ||||
| 			s.push_str (&format! ("{}", x)); | ||||
| 		} | ||||
| 		s | ||||
| 	}; | ||||
| 	*l.reg_mut (0) = l.to_string (&s); | ||||
| 	*l.reg_mut (0) = Value::from (s); | ||||
| 	1 | ||||
| } | ||||
| 
 | ||||
|  | @ -141,15 +137,15 @@ fn lw_table_pack (l: &mut State, num_args: usize) -> usize { | |||
| 	1 | ||||
| } | ||||
| 
 | ||||
| fn tonumber (l: &State, value: &Value) -> Value { | ||||
| fn tonumber (value: &Value) -> Value { | ||||
| 	match value { | ||||
| 		Value::Float (x) => Value::Float (*x), | ||||
| 		Value::Integer (x) => Value::Integer (*x), | ||||
| 		Value::String (x) => { | ||||
| 			if let Ok (x) = str::parse::<i64> (l.si.get (*x)) { | ||||
| 			if let Ok (x) = str::parse::<i64> (x) { | ||||
| 				Value::from (x) | ||||
| 			} | ||||
| 			else if let Ok (x) = str::parse::<f64> (l.si.get (*x)) { | ||||
| 			else if let Ok (x) = str::parse::<f64> (x) { | ||||
| 				Value::from (x) | ||||
| 			} | ||||
| 			else { | ||||
|  | @ -162,7 +158,7 @@ fn tonumber (l: &State, value: &Value) -> Value { | |||
| 
 | ||||
| fn lw_tonumber (l: &mut State, num_args: usize) -> usize { | ||||
| 	assert_eq! (num_args, 1, "tonumber only implemented for 1 argument"); | ||||
| 	let output = tonumber (&l, l.reg (0)); | ||||
| 	let output = tonumber (l.reg (0)); | ||||
| 	*l.reg_mut (0) = output; | ||||
| 	1 | ||||
| } | ||||
|  | @ -195,30 +191,6 @@ impl State { | |||
| 			step_count: 0, | ||||
| 			chunk, | ||||
| 			upvalues, | ||||
| 			si: Default::default (), | ||||
| 			register_offset: 0, | ||||
| 			block_idx: 0, | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn new_with_args <I: Iterator <Item = String>> (chunk: Chunk, mut si: Interner, args: I) -> Self { | ||||
| 		let upvalues = Self::upvalues_from_args (&mut si, args); | ||||
| 		
 | ||||
| 		Self { | ||||
| 			// TODO: Stack is actually supposed to grow to a limit of
 | ||||
| 			// idk 10,000. I thought it was fixed at 256.
 | ||||
| 			registers: vec! [Value::Nil; 256], | ||||
| 			top: 0, | ||||
| 			stack: vec! [ | ||||
| 				StackFrame::default (), | ||||
| 			], | ||||
| 			debug_print: false, | ||||
| 			step_count: 0, | ||||
| 			chunk, | ||||
| 			upvalues, | ||||
| 			si, | ||||
| 			register_offset: 0, | ||||
| 			block_idx: 0, | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
|  | @ -227,27 +199,27 @@ impl State { | |||
| 		frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn upvalues_from_args <I: Iterator <Item = String>> (si: &mut Interner, args: I) -> Vec <Value> | ||||
| 	pub fn upvalues_from_args <I: Iterator <Item = String>> (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)))); | ||||
| 		let arg = args.map (|s| Value::from (s)).enumerate (); | ||||
| 		let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), v))); | ||||
| 		
 | ||||
| 		let io: Vec <_> = [ | ||||
| 		let io = [ | ||||
| 			("write", Value::RsFunc (lw_io_write)), | ||||
| 		].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); | ||||
| 		].into_iter ().map (|(k, v)| (k.to_string (), v)); | ||||
| 		
 | ||||
| 		let math: Vec <_> = [ | ||||
| 		let math = [ | ||||
| 			("sqrt", Value::RsFunc (lw_sqrt)), | ||||
| 		].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); | ||||
| 		].into_iter ().map (|(k, v)| (k.to_string (), v)); | ||||
| 		
 | ||||
| 		let string: Vec <_> = [ | ||||
| 		let string = [ | ||||
| 			("format", Value::RsFunc (lw_string_format)), | ||||
| 		].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); | ||||
| 		].into_iter ().map (|(k, v)| (k.to_string (), v)); | ||||
| 		
 | ||||
| 		let table: Vec <_> = [ | ||||
| 		let table = [ | ||||
| 			("concat", Value::RsFunc (lw_table_concat)), | ||||
| 			("pack", Value::RsFunc (lw_table_pack)), | ||||
| 		].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); | ||||
| 		].into_iter ().map (|(k, v)| (k.to_string (), v)); | ||||
| 		
 | ||||
| 		let env = [ | ||||
| 			("arg", arg), | ||||
|  | @ -257,7 +229,7 @@ impl State { | |||
| 			("string", Value::from_iter (string.into_iter ())), | ||||
| 			("table", Value::from_iter (table.into_iter ())), | ||||
| 			("tonumber", Value::RsFunc (lw_tonumber)), | ||||
| 		].into_iter ().map (|(k, v)| (si.intern (k), v)); | ||||
| 		].into_iter ().map (|(k, v)| (k.to_string (), v)); | ||||
| 		
 | ||||
| 		vec! [ | ||||
| 			Value::from_iter (env.into_iter ()), | ||||
|  | @ -272,11 +244,13 @@ impl State { | |||
| 	/// Short form to get access to a register within our window
 | ||||
| 	
 | ||||
| 	pub fn reg (&self, i: u8) -> &Value { | ||||
| 		&self.registers [self.register_offset + i as usize] | ||||
| 		let frame = self.stack.last ().unwrap (); | ||||
| 		&self.registers [frame.register_offset + i as usize] | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn reg_mut (&mut self, i: u8) -> &mut Value { | ||||
| 		&mut self.registers [self.register_offset + i as usize] | ||||
| 		let frame = self.stack.last ().unwrap (); | ||||
| 		&mut self.registers [frame.register_offset + i as usize] | ||||
| 	} | ||||
| 	
 | ||||
| 	// For native functions to check how many args they got
 | ||||
|  | @ -293,120 +267,18 @@ 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 ().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 frame = self.stack.last_mut ().unwrap ().clone (); | ||||
| 		let block = self.chunk.blocks.get (frame.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"); | ||||
|  | @ -417,14 +289,25 @@ 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 { | ||||
| 		match instruction.clone () { | ||||
| 			Instruction::Add (a, b, c) => { | ||||
| 				if self.op_add (a, b, c) { | ||||
| 					next_pc += 1; | ||||
| 				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) | ||||
| 				} | ||||
| 				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); | ||||
|  | @ -460,12 +343,15 @@ 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: self.register_offset + a as usize + 1, | ||||
| 							register_offset: current_frame.register_offset + a as usize + 1, | ||||
| 						}); | ||||
| 						
 | ||||
| 						if self.debug_print { | ||||
|  | @ -478,19 +364,19 @@ impl State { | |||
| 						return Ok (None); | ||||
| 					}, | ||||
| 					Value::RsFunc (x) => { | ||||
| 						let old_offset = self.register_offset; | ||||
| 						self.register_offset = old_offset + usize::from (a) + 1; | ||||
| 						let current_frame = self.stack.last ().unwrap (); | ||||
| 						let new_offset = current_frame.register_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: self.register_offset, | ||||
| 							register_offset: new_offset, | ||||
| 						}); | ||||
| 						
 | ||||
| 						let num_args = if b == 0 { | ||||
| 							self.top - self.register_offset | ||||
| 							self.top - new_offset | ||||
| 						} | ||||
| 						else { | ||||
| 							b - 1 | ||||
|  | @ -500,8 +386,7 @@ impl State { | |||
| 						let num_results = x (self, num_args); | ||||
| 						
 | ||||
| 						let popped_frame = self.stack.pop ().unwrap (); | ||||
| 						self.register_offset = old_offset; | ||||
| 						let offset = old_offset + usize::from (a); | ||||
| 						let offset = popped_frame.register_offset - 1; | ||||
| 						
 | ||||
| 						for i in (offset)..(offset + usize::try_from (num_results).unwrap ()) { | ||||
| 							self.registers [i] = self.registers [i + 1].take (); | ||||
|  | @ -594,10 +479,32 @@ impl State { | |||
| 				*self.reg_mut (a + 3) = start.into (); | ||||
| 			}, | ||||
| 			Instruction::GetField (a, b, c) => { | ||||
| 				self.op_get_field (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.as_str ()).clone (); | ||||
| 				
 | ||||
| 				*self.reg_mut (a) = val; | ||||
| 			}, | ||||
| 			Instruction::GetTable (a, b, c) => { | ||||
| 				self.op_get_table (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; | ||||
| 			}, | ||||
| 			Instruction::GetTabUp (a, b, c) => { | ||||
| 				let b = usize::try_from (b).unwrap (); | ||||
|  | @ -623,18 +530,18 @@ impl State { | |||
| 				let table = value.as_table ().expect ("GetTabUp only works on tables").borrow (); | ||||
| 				
 | ||||
| 				let key = match k.get (c).unwrap () { | ||||
| 					Value::String (s) => *s, | ||||
| 					Value::String (s) => String::from (s.as_ref()), | ||||
| 					_ => panic! ("GetTabUp only supports string keys"), | ||||
| 				}; | ||||
| 				
 | ||||
| 				*self.reg_mut (a) = table.get_str (key).clone (); | ||||
| 				*self.reg_mut (a) = table.get (key); | ||||
| 			}, | ||||
| 			Instruction::GetI (a, b, c) => { | ||||
| 				let key = i64::try_from (c).unwrap (); | ||||
| 				
 | ||||
| 				let value = { | ||||
| 					let table = self.reg (b).as_table ().expect ("GetI only works on tables").borrow (); | ||||
| 					table.get_int (key).clone () | ||||
| 					table.get_int (key) | ||||
| 				}; | ||||
| 				
 | ||||
| 				*self.reg_mut (a) = value; | ||||
|  | @ -666,7 +573,7 @@ impl State { | |||
| 					Value::Integer (_) => Err (make_step_error ("attempt to get length of a number value"))?, | ||||
| 					Value::Nil => Err (make_step_error ("attempt to get length of a nil value"))?, | ||||
| 					Value::RsFunc (_) => Err (make_step_error ("attempt to get length of a function value"))?, | ||||
| 					Value::String (s) => self.si.get (*s).len ().into (), | ||||
| 					Value::String (s) => s.len ().into (), | ||||
| 					Value::Table (t) => t.borrow ().length ().into (), | ||||
| 				}; | ||||
| 				
 | ||||
|  | @ -692,8 +599,16 @@ impl State { | |||
| 			Instruction::LoadTrue (a) => { | ||||
| 				*self.reg_mut (a) = true.into (); | ||||
| 			}, | ||||
| 			Instruction::MmBin (a, b, c) => { | ||||
| 				self.op_mmbin (a, b, c); | ||||
| 			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::MmBinI (_a, _s_b, _c, _k) => { | ||||
| 				// Ignore
 | ||||
|  | @ -714,12 +629,20 @@ impl State { | |||
| 				*self.reg_mut (a) = self.reg (b).clone (); | ||||
| 			}, | ||||
| 			Instruction::Mul (a, b, c) => { | ||||
| 				// If we handled the mul as a regular int or float,
 | ||||
| 				// skip the OP_MMBIN that probably comes after this
 | ||||
| 				let v_b = self.reg (b); | ||||
| 				let v_c = self.reg (c); | ||||
| 				
 | ||||
| 				if self.op_mul (a, b, c) { | ||||
| 					next_pc += 1; | ||||
| 				let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) 
 | ||||
| 				{ | ||||
| 					Value::from (v_b * v_c) | ||||
| 				} | ||||
| 				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); | ||||
|  | @ -831,7 +754,25 @@ impl State { | |||
| 				self.top = popped_frame.register_offset - 1 + 1; | ||||
| 			}, | ||||
| 			Instruction::SetField (a, b, c, k_flag) => { | ||||
| 				self.op_set_field (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.as_ref (), | ||||
| 					_ => 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.as_str (), value); | ||||
| 			}, | ||||
| 			Instruction::SetI (a, b, c, k_flag) => { | ||||
| 				let value = if k_flag { | ||||
|  | @ -1005,7 +946,7 @@ impl State { | |||
| 	pub fn eval (&mut self, src: &str) -> Result <Vec <Value>, crate::Error> | ||||
| 	{ | ||||
| 		let bytecode = crate::compile_bytecode (src.as_bytes ().to_vec ())?; | ||||
| 		let chunk = crate::parse_chunk (&bytecode, &mut self.si).unwrap (); | ||||
| 		let chunk = crate::parse_chunk (&bytecode).unwrap (); | ||||
| 		
 | ||||
| 		self.set_chunk (chunk); | ||||
| 		Ok (self.execute ()?) | ||||
|  | @ -1030,8 +971,4 @@ impl State { | |||
| 		self.stack = vec! [Default::default ()]; | ||||
| 		self.chunk = chunk; | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn to_string (&mut self, s: &str) -> Value { | ||||
| 		Value::String (self.si.intern (s)) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,56 +0,0 @@ | |||
| use std::collections::BTreeMap; | ||||
| 
 | ||||
| use crate::value::Value; | ||||
| 
 | ||||
| #[derive (Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | ||||
| pub struct InternedString (i64); | ||||
| 
 | ||||
| #[derive (Debug, Default)] | ||||
| pub struct Interner { | ||||
| 	table_fwd: BTreeMap <String, i64>, | ||||
| 	table_rev: BTreeMap <i64, String>, | ||||
| 	counter: i64, | ||||
| } | ||||
| 
 | ||||
| impl Interner { | ||||
| 	pub fn get (&self, s: InternedString) -> &str { | ||||
| 		self.table_rev.get (&s.0).unwrap () | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn intern (&mut self, s: &str) -> InternedString { | ||||
| 		match self.table_fwd.get (s) { | ||||
| 			Some (x) => InternedString (*x), | ||||
| 			None => { | ||||
| 				self.counter += 1; | ||||
| 				if self.counter == i64::MAX { | ||||
| 					panic! ("Out of IDs"); | ||||
| 				} | ||||
| 				self.table_fwd.insert (s.to_string (), self.counter); | ||||
| 				self.table_rev.insert (self.counter, s.to_string ()); | ||||
| 				InternedString (self.counter) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn to_value (&mut self, s: &str) -> Value { | ||||
| 		Value::from (self.intern (s)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[cfg (test)] | ||||
| mod tests { | ||||
| 	use super::*; | ||||
| 	
 | ||||
| 	#[test] | ||||
| 	fn test () { | ||||
| 		let mut interner = Interner::default (); | ||||
| 		
 | ||||
| 		assert_eq! (interner.intern ("x").0, 1); | ||||
| 		assert_eq! (interner.intern ("x").0, 1); | ||||
| 		assert_eq! (interner.intern ("y").0, 2); | ||||
| 		assert_eq! (interner.intern ("z").0, 3); | ||||
| 		assert_eq! (interner.intern ("y").0, 2); | ||||
| 		
 | ||||
| 		assert_eq! (interner.intern ("asdf").0, 4); | ||||
| 	} | ||||
| } | ||||
|  | @ -8,7 +8,6 @@ use crate::{ | |||
| 		Chunk, | ||||
| 		State, | ||||
| 	}, | ||||
| 	string_interner::Interner, | ||||
| 	value::Value, | ||||
| }; | ||||
| 
 | ||||
|  | @ -23,18 +22,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 (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); | ||||
| fn run_chunk (args: &[&str], chunk: Chunk) -> Vec <Value> { | ||||
| 	let upvalues = State::upvalues_from_args (args.into_iter ().map (|s| s.to_string ())); | ||||
| 	let mut vm = State::new (chunk, upvalues); | ||||
| 	vm.execute ().unwrap () | ||||
| } | ||||
| 
 | ||||
| /// Takes arguments and Lua bytecode, loads it, runs it,
 | ||||
| /// and return the output
 | ||||
| 
 | ||||
| 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) | ||||
| fn run_bytecode (args: &[&str], bc: &[u8]) -> Vec <Value> { | ||||
| 	let chunk = loader::parse_chunk (&bc).unwrap (); | ||||
| 	run_chunk (args, chunk) | ||||
| } | ||||
| 
 | ||||
| /// Takes arguments and Lua source code, 
 | ||||
|  | @ -42,9 +41,9 @@ fn run_bytecode (vm: &mut State, args: &[&str], bc: &[u8]) -> Vec <Value> { | |||
| /// runs it, 
 | ||||
| /// and returns the output
 | ||||
| 
 | ||||
| fn run_source (vm: &mut State, args: &[&str], s: &str) -> Vec <Value> { | ||||
| fn run_source (args: &[&str], s: &str) -> Vec <Value> { | ||||
| 	let bc = loader::compile_bytecode (s.as_bytes ().to_vec ()).unwrap (); | ||||
| 	run_bytecode (vm, args, &bc) | ||||
| 	run_bytecode (args, &bc) | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
|  | @ -63,8 +62,6 @@ fn bools () { | |||
| 	return x | ||||
| 	*/ | ||||
| 	
 | ||||
| 	let mut si = Interner::default (); | ||||
| 	
 | ||||
| 	let chunk = Chunk { | ||||
| 		blocks: vec! [ | ||||
| 			Block { | ||||
|  | @ -93,8 +90,8 @@ fn bools () { | |||
| 					Inst::Return (2, 1, 1, false), | ||||
| 				], | ||||
| 				constants: vec! [ | ||||
| 					si.to_value ("arg"), | ||||
| 					si.to_value ("print"), | ||||
| 					"arg".into (), | ||||
| 					"print".into (), | ||||
| 				], | ||||
| 				upvalues: vec! [], | ||||
| 			}, | ||||
|  | @ -115,15 +112,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 actual = run_chunk (&mut vm, &arg, chunk.clone ()); | ||||
| 		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 (); | ||||
| 		assert_eq! (actual, expected); | ||||
| 	} | ||||
| } | ||||
|  | @ -132,11 +129,9 @@ fn bools () { | |||
| fn closure () { | ||||
| 	let source = include_bytes! ("../test_vectors/closure.lua"); | ||||
| 	let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap (); | ||||
| 	let mut si = Interner::default (); | ||||
| 	let chunk = crate::loader::parse_chunk (bytecode, &mut si).unwrap (); | ||||
| 	let chunk = crate::loader::parse_chunk (bytecode).unwrap (); | ||||
| 	
 | ||||
| 	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)]); | ||||
| 	assert_eq! (run_chunk (&["_exe_name"], chunk), vec! [Value::from (23i64)]); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
|  | @ -151,8 +146,6 @@ fn floats () { | |||
| 	return x | ||||
| 	*/ | ||||
| 	
 | ||||
| 	let mut si = Interner::default (); | ||||
| 	
 | ||||
| 	let block = Block { | ||||
| 		instructions: vec! [ | ||||
| 			Inst::VarArgPrep (0), | ||||
|  | @ -168,7 +161,7 @@ fn floats () { | |||
| 		], | ||||
| 		constants: vec! [ | ||||
| 			0.5.into (), | ||||
| 			si.to_value ("print"), | ||||
| 			"print".into (), | ||||
| 		], | ||||
| 		upvalues: vec! [], | ||||
| 	}; | ||||
|  | @ -176,14 +169,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 actual = run_chunk (&mut vm, &arg, chunk.clone ()); | ||||
| 		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 (); | ||||
| 		
 | ||||
| 		assert_eq! (actual, expected); | ||||
| 	} | ||||
|  | @ -192,15 +185,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 si).unwrap (); | ||||
| 	let chunk = crate::loader::parse_chunk (bytecode).unwrap (); | ||||
| 	assert_eq! (chunk.blocks.len (), 5); | ||||
| 	
 | ||||
| 	assert_eq! (chunk.blocks [3].upvalues.len (), 2); | ||||
| 	
 | ||||
| 	
 | ||||
| 	let mut vm = crate::State::new_with_args (chunk, si, vec! ["_exe_name".to_string ()].into_iter ()); | ||||
| 	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 actual = vm.execute ().unwrap (); | ||||
| 	let expected = vec! [Value::from (122)]; | ||||
| 	
 | ||||
|  | @ -209,8 +202,9 @@ fn fma () { | |||
| 
 | ||||
| #[test] | ||||
| fn function_calls () { | ||||
| 	let si = Interner::default (); | ||||
| 	let mut vm = crate::State::new_with_args (crate::Chunk::default (), si, vec! ["_exe_name".to_string ()].into_iter ()); | ||||
| 	let upvalues = crate::State::upvalues_from_args (vec! ["_exe_name".to_string ()].into_iter ()); | ||||
| 	
 | ||||
| 	let mut vm = crate::State::new (crate::Chunk::default (), upvalues); | ||||
| 	
 | ||||
| 	vm.eval ("print (x ())").ok (); | ||||
| 	vm.eval ("x = function () return 5 end").ok (); | ||||
|  | @ -222,8 +216,9 @@ fn function_calls () { | |||
| 
 | ||||
| #[test] | ||||
| fn function_returns () { | ||||
| 	let si = Interner::default (); | ||||
| 	let mut vm = crate::State::new_with_args (crate::Chunk::default (), si, vec! ["_exe_name".to_string ()].into_iter ()); | ||||
| 	let upvalues = crate::State::upvalues_from_args (vec! ["_exe_name".to_string ()].into_iter ()); | ||||
| 	
 | ||||
| 	let mut vm = crate::State::new (crate::Chunk::default (), upvalues); | ||||
| 	
 | ||||
| 	assert_eq! ( | ||||
| 		vm.eval ("return ((function () return 5 end)())").unwrap (), 
 | ||||
|  | @ -246,8 +241,6 @@ fn heap () { | |||
| 	}; | ||||
| 	use crate::value::Table; | ||||
| 	
 | ||||
| 	let mut si = Interner::default (); | ||||
| 	
 | ||||
| 	{ | ||||
| 		let mut allocations = HashMap::new (); | ||||
| 		let mut ctr = 0; | ||||
|  | @ -262,7 +255,7 @@ fn heap () { | |||
| 		
 | ||||
| 		allocations.get_mut (&a).unwrap ().insert (1, c); | ||||
| 		
 | ||||
| 		allocations.get_mut (&c).unwrap ().insert (2, si.to_value ("eee")); | ||||
| 		allocations.get_mut (&c).unwrap ().insert (2, "eee"); | ||||
| 	} | ||||
| 	
 | ||||
| 	if true { | ||||
|  | @ -299,12 +292,10 @@ fn heap () { | |||
| 
 | ||||
| #[test] | ||||
| fn is_93 () { | ||||
| 	let mut si = Interner::default (); | ||||
| 	
 | ||||
| 	assert_eq! (si.to_value ("93"), si.to_value ("93")); | ||||
| 	assert_ne! (si.to_value ("94"), si.to_value ("93")); | ||||
| 	assert_ne! (calculate_hash (&si.to_value ("94")), calculate_hash (&si.to_value ("93"))); | ||||
| 	assert_ne! (Value::Nil, si.to_value ("93")); | ||||
| 	assert_eq! (Value::from ("93"), Value::from ("93")); | ||||
| 	assert_ne! (Value::from ("94"), Value::from ("93")); | ||||
| 	assert_ne! (calculate_hash (&Value::from ("94")), calculate_hash (&Value::from ("93"))); | ||||
| 	assert_ne! (Value::Nil, Value::from ("93")); | ||||
| 	
 | ||||
| 	let src = br#" | ||||
| 	if arg [1] == "93" then | ||||
|  | @ -317,17 +308,15 @@ fn is_93 () { | |||
| 	"#;
 | ||||
| 	
 | ||||
| 	let bc = loader::compile_bytecode (src.to_vec ()).unwrap (); | ||||
| 	let chunk = loader::parse_chunk (&bc, &mut si).unwrap (); | ||||
| 	let chunk = loader::parse_chunk (&bc).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 (&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)]); | ||||
| 	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)]); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
|  | @ -380,9 +369,7 @@ fn tables_1 () { | |||
| 	print (t ["t"][1]) | ||||
| 	"#;
 | ||||
| 	
 | ||||
| 	let si = Interner::default (); | ||||
| 	let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter()); | ||||
| 	run_source (&mut vm, &[], src); | ||||
| 	run_source (&[], src); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
|  | @ -397,29 +384,23 @@ fn tables_2 () { | |||
| 	print (a [2]) | ||||
| 	"#;
 | ||||
| 	
 | ||||
| 	let si = Interner::default (); | ||||
| 	let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter()); | ||||
| 	run_source (&mut vm, &[], src); | ||||
| 	run_source (&[], 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 si).unwrap (); | ||||
| 	let chunk = loader::parse_chunk (&bc).unwrap (); | ||||
| 	
 | ||||
| 	assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false)); | ||||
| 	
 | ||||
| 	let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter()); | ||||
| 	
 | ||||
| 	let actual = run_chunk (&mut vm, &[], chunk); | ||||
| 	let actual = run_chunk (&[], chunk); | ||||
| 	let expected = vec! [Value::from (5)]; | ||||
| 	
 | ||||
| 	assert_eq! (actual, expected); | ||||
|  |  | |||
|  | @ -4,16 +4,11 @@ use std::{ | |||
| 		Eq, | ||||
| 		PartialEq, | ||||
| 	}, | ||||
| 	collections::{BTreeMap, HashMap}, | ||||
| 	collections::HashMap, | ||||
| 	fmt, | ||||
| 	rc::Rc, | ||||
| }; | ||||
| 
 | ||||
| use crate::string_interner::{ | ||||
| 	Interner, | ||||
| 	InternedString, | ||||
| }; | ||||
| 
 | ||||
| #[derive (Debug, Eq, PartialEq)] | ||||
| pub struct BogusClosure { | ||||
| 	// I'm pretty sure this should be absolute?
 | ||||
|  | @ -32,7 +27,7 @@ pub enum Value { | |||
| 	
 | ||||
| 	Integer (i64), | ||||
| 	RsFunc (fn (&mut crate::state::State, usize) -> usize), | ||||
| 	String (InternedString), | ||||
| 	String (Rc <String>), | ||||
| 	Table (Rc <RefCell <Table>>), | ||||
| 	
 | ||||
| 	// These are all bogus, I haven't figured out how to implement
 | ||||
|  | @ -52,7 +47,7 @@ impl fmt::Debug for Value { | |||
| 			Value::Float (x) => write! (f, "{:?}", x), | ||||
| 			Value::Integer (x) => write! (f, "{}", x), | ||||
| 			Value::RsFunc (x) => write! (f, "function: {:?}", x), | ||||
| 			Value::String (s) => write! (f, "unimplemented Debug",), | ||||
| 			Value::String (s) => write! (f, "\"{}\"", s), | ||||
| 			Value::Table (t) => write! (f, "{:?}", t.borrow ()), | ||||
| 			
 | ||||
| 			Value::BogusClosure (x) => write! (f, "{:?}", x.borrow ()), | ||||
|  | @ -75,7 +70,7 @@ impl fmt::Display for Value { | |||
| 			Value::Float (x) => write! (f, "{:?}", x), | ||||
| 			Value::Integer (x) => write! (f, "{}", x), | ||||
| 			Value::RsFunc (x) => write! (f, "function: {:?}", x), | ||||
| 			Value::String (s) => write! (f, "unimplemented Display"), | ||||
| 			Value::String (s) => write! (f, "{}", s), | ||||
| 			Value::Table (t) => write! (f, "table: {:?}", std::rc::Rc::as_ptr (t)), | ||||
| 			
 | ||||
| 			Value::BogusClosure (x) => write! (f, "BogusClosure: {:?}", std::rc::Rc::as_ptr (x)), | ||||
|  | @ -95,6 +90,18 @@ impl From <bool> for Value { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From <String> for Value { | ||||
| 	fn from (x: String) -> Self { | ||||
| 		Self::String (x.into ()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From <&str> for Value { | ||||
| 	fn from (x: &str) -> Self { | ||||
| 		Self::from (String::from (x)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From <i32> for Value { | ||||
| 	fn from (x: i32) -> Self { | ||||
| 		Self::Integer (i64::from (x)) | ||||
|  | @ -119,12 +126,6 @@ impl From <usize> for Value { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From <InternedString> for Value { | ||||
| 	fn from (x: InternedString) -> Self { | ||||
| 		Self::String (x) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From <Table> for Value { | ||||
| 	fn from (x: Table) -> Self { | ||||
| 		Self::Table (Rc::new (RefCell::new (x))) | ||||
|  | @ -138,9 +139,9 @@ impl FromIterator <(Value, Value)> for Value { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl FromIterator <(InternedString, Value)> for Value { | ||||
| 	fn from_iter <I: IntoIterator <Item=(InternedString, Value)>> (i: I) -> Self { | ||||
| 		Self::from_iter (i.into_iter ().map (|(s, v)| (Value::String (s), v))) | ||||
| impl FromIterator <(String, Value)> for Value { | ||||
| 	fn from_iter <I: IntoIterator <Item=(String, Value)>> (i: I) -> Self { | ||||
| 		Self::from_iter (i.into_iter ().map (|(s, v)| (Value::from (s), v))) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -208,9 +209,9 @@ impl Value { | |||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn as_str (&self) -> Option <InternedString> { | ||||
| 	pub fn as_str (&self) -> Option <&str> { | ||||
| 		match self { | ||||
| 			Self::String (x) => Some (*x), | ||||
| 			Self::String (x) => Some (x.as_str ()), | ||||
| 			_ => None, | ||||
| 		} | ||||
| 	} | ||||
|  | @ -222,10 +223,6 @@ impl Value { | |||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn from_str (si: &mut Interner, s: &str) -> Value { | ||||
| 		Value::String (si.intern (s)) | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn is_truthy (&self) -> bool { | ||||
| 		// And this is something Lua does better than JS and Python.
 | ||||
| 		
 | ||||
|  | @ -247,40 +244,55 @@ impl Value { | |||
| pub struct Table { | ||||
| 	array: Vec <Value>, | ||||
| 	hash: HashMap <Value, Value>, | ||||
| 	map: BTreeMap <InternedString, Value>, | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for Table { | ||||
| 	fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		write! (f, "Table {:#?}", self.map)?; | ||||
| 		write! (f, "Table {:#?}", self.hash)?; | ||||
| 		
 | ||||
| 		Ok (()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Table { | ||||
| 	fn get_inner (&self, key: &Value) -> &Value { | ||||
| 		match key { | ||||
| 			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), | ||||
| 	fn get_inner (&self, key: &Value) -> Value { | ||||
| 		// self.hash.get (key).cloned ().unwrap_or_default ()
 | ||||
| 		for (hay, value) in &self.hash { | ||||
| 			if key == hay { | ||||
| 				return value.clone (); | ||||
| 			} | ||||
| 		} | ||||
| 			
 | ||||
| 		Value::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.array.get (usize::try_from (key).unwrap ()).unwrap_or (&NIL) | ||||
| 	pub fn get_int (&self, key: i64) -> Value { | ||||
| 		self.get_inner (&(key.into ())) | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn get_str (&self, key: InternedString) -> &Value { | ||||
| 		match self.map.get (&key) { | ||||
| 			None => &NIL, | ||||
| 			Some (x) => x, | ||||
| 	pub fn get_str (&self, key: &str) -> &Value { | ||||
| 		for (hay, value) in &self.hash { | ||||
| 			if Some (key) == hay.as_str () { | ||||
| 				return value; | ||||
| 			} | ||||
| 		} | ||||
| 			
 | ||||
| 		&NIL | ||||
| 	} | ||||
| 	
 | ||||
| 	fn insert_inner (&mut self, a: Value, b: Value) { | ||||
| 		for (hay, value) in &mut self.hash { | ||||
| 			if &a == hay { | ||||
| 				*value = b; | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 		
 | ||||
| 		self.hash.insert (a, b); | ||||
| 	} | ||||
| 	
 | ||||
| 	/// Insert value at arbitrary key
 | ||||
|  | @ -290,34 +302,30 @@ impl Table { | |||
| 		a: A, | ||||
| 		b: B, | ||||
| 	) { | ||||
| 		match a.into () { | ||||
| 			Value::Integer (x) => self.insert_int (x, b), | ||||
| 			Value::Nil => (), | ||||
| 			Value::String (x) => { | ||||
| 				self.map.insert (x, b.into ()); | ||||
| 			}, | ||||
| 			x => { | ||||
| 				self.hash.insert (x, b.into ()); | ||||
| 			}, | ||||
| 		} | ||||
| 		self.insert_inner (a.into (), b.into ()) | ||||
| 	} | ||||
| 	
 | ||||
| 	/// Insert value at integer key
 | ||||
| 	
 | ||||
| 	pub fn insert_int <A: Into <Value>> (&mut self, k: i64, v: A) 
 | ||||
| 	{ | ||||
| 		let k = usize::try_from (k).unwrap (); | ||||
| 		self.array.resize (k + 1, NIL); | ||||
| 		self.array [k] = v.into (); | ||||
| 		self.insert_inner (k.into (), v.into ()) | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn insert_str (&mut self, key: InternedString, v: Value) { | ||||
| 		self.map.insert (key, v); | ||||
| 	pub fn insert_str (&mut self, key: &str, v: Value) { | ||||
| 		for (hay, value) in &mut self.hash { | ||||
| 			if Some (key) == hay.as_str () { | ||||
| 				*value = v; | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 			
 | ||||
| 		self.hash.insert (key.into (), v); | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn length (&self) -> i64 { | ||||
| 		for i in 1..i64::MAX { | ||||
| 			if self.get (i) == &NIL { | ||||
| 			if self.get (i) == Value::Nil { | ||||
| 				return i - 1; | ||||
| 			} | ||||
| 		} | ||||
|  | @ -328,18 +336,21 @@ impl Table { | |||
| impl FromIterator <(Value, Value)> for Table { | ||||
| 	fn from_iter<I: IntoIterator<Item = (Value, Value)>> (i: I) -> Self 
 | ||||
| 	{ | ||||
| 		let mut that = Self::default (); | ||||
| 		for (k, v) in i.into_iter () { | ||||
| 			that.insert (k, v); | ||||
| 		let hash = i.into_iter ().collect (); | ||||
| 		Self { | ||||
| 			array: Default::default (), | ||||
| 			hash, | ||||
| 		} | ||||
| 		that | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[cfg (test)] | ||||
| mod tests { | ||||
| 	use std::collections::HashMap; | ||||
| 	use super::*; | ||||
| 	use super::{ | ||||
| 		Table, | ||||
| 		Value, | ||||
| 	}; | ||||
| 	
 | ||||
| 	#[test] | ||||
| 	fn smoke () { | ||||
|  | @ -353,26 +364,25 @@ mod tests { | |||
| 	#[test] | ||||
| 	fn tables () { | ||||
| 		let nil = Value::Nil; | ||||
| 		let mut si = Interner::default (); | ||||
| 		
 | ||||
| 		assert_ne! (Value::from (18.0), Value::from (19.0)); | ||||
| 		
 | ||||
| 		let mut t = HashMap::new (); | ||||
| 		t.insert (Value::from_str (&mut si, "x"), Value::from (19.0)); | ||||
| 		assert_eq! (t.get (&Value::from_str (&mut si, "x")), Some (&Value::from (19.0))); | ||||
| 		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 (si.intern ("a")), &nil); | ||||
| 		assert_eq! (t.get (si.intern ("b")), &nil); | ||||
| 		assert_eq! (t.get ("a"), nil); | ||||
| 		assert_eq! (t.get ("b"), nil); | ||||
| 		
 | ||||
| 		t.insert (si.to_value ("a"), 1993); | ||||
| 		t.insert (si.to_value ("b"), 2007); | ||||
| 		t.insert ("a", 1993); | ||||
| 		t.insert ("b", 2007); | ||||
| 		
 | ||||
| 		assert_eq! (t.get (si.intern ("a")), &Value::from (1993)); | ||||
| 		assert_eq! (t.get (si.intern ("b")), &Value::from (2007)); | ||||
| 		assert_eq! (t.get ("a"), 1993); | ||||
| 		assert_eq! (t.get ("b"), 2007); | ||||
| 		
 | ||||
| 		t.insert (19, 93); | ||||
| 		assert_eq! (t.get (19), &Value::from (93)); | ||||
| 		assert_eq! (t.get (19), 93); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -19,18 +19,16 @@ fn embedding () { | |||
| 		1 | ||||
| 	} | ||||
| 	
 | ||||
| 	let mut si = lwvm::Interner::default (); | ||||
| 	
 | ||||
| 	let bc = lwvm::compile_bytecode (src.to_vec ()).unwrap (); | ||||
| 	let chunk = lwvm::parse_chunk (&bc, &mut si).unwrap (); | ||||
| 	let chunk = lwvm::parse_chunk (&bc).unwrap (); | ||||
| 	
 | ||||
| 	let host_lib = [ | ||||
| 		("add", Value::RsFunc (host_add)), | ||||
| 	].into_iter ().map (|(k, v)| (si.intern (k), v)); | ||||
| 	].into_iter ().map (|(k, v)| (k.to_string (), v)); | ||||
| 	
 | ||||
| 	let env = [ | ||||
| 		("host_lib", Value::from_iter (host_lib.into_iter ())), | ||||
| 	].into_iter ().map (|(k, v)| (si.intern (k), v)); | ||||
| 	].into_iter ().map (|(k, v)| (k.to_string (), v)); | ||||
| 	
 | ||||
| 	let upvalues = vec! [ | ||||
| 		Value::from_iter (env.into_iter ()), | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue