Skip to main content

hematita/vm/
value.rs

1pub use self::{super::{Chunk, VirtualMachine}, Nillable::{Nil, NonNil}};
2use hashbrown::HashMap;
3use std::{borrow::Borrow, fmt::{Debug, Display, Formatter, Result as FMTResult}, hash::{BuildHasher, Hash, Hasher}, mem::take, ptr::{eq, hash}, sync::{Arc, Mutex}};
4
5macro_rules! value_conversions {
6	(
7		impl<$lf:tt $(; $($param:tt),*)?> for $convert:ident @ $for:ty $code:block
8		$($rest:tt)*
9	) => {
10		impl<$lf $(, $($param),*)?> From<$for> for Value<$lf> {
11			fn from($convert: $for) -> Value<$lf> {
12				$code
13			}
14		}
15
16		value_conversions! {$($rest)*}
17	};
18	() => {}
19}
20
21macro_rules! nillable_conversions {
22	(
23		impl<$lf:tt $(; $($param:tt),*)?> all
24		for $convert:ident @ $for:ty $code:block $($rest:tt)*
25	) => {
26		impl<$lf $(, $($param),*)?> IntoNillable<$lf> for $for {
27			#[inline]
28			fn nillable(self) -> Nillable<$lf> {
29				let $convert = self;
30				$code
31			}
32		}
33
34		impl<$lf $(, $($param),*)?> From<$for> for Nillable<$lf> {
35			#[inline]
36			fn from($convert: $for) -> Self {
37				$code
38			}
39		}
40
41		nillable_conversions! {$($rest)*}
42	};
43	(
44		impl<$lf:tt $(; $($param:tt),*)?>
45		for $convert:ident @ $for:ty $code:block $($rest:tt)*
46	) => {
47		impl<$lf $(, $($param),*)?> IntoNillable<$lf> for $for {
48			#[inline]
49			fn nillable(self) -> Nillable<$lf> {
50				let $convert = self;
51				$code
52			}
53		}
54
55		nillable_conversions! {$($rest)*}
56	};
57	() => {}
58}
59
60#[macro_export]
61macro_rules! lua_value {
62	($raw:literal) => {$crate::vm::value::Value::from($raw)};
63	($($other:tt)*) => {Value::Table(lua_table! {$($other)*}.arc())}
64}
65
66#[macro_export]
67macro_rules! lua_table {
68	($($arm:tt)*) => {{
69		#[allow(unused_assignments, unused_mut, unused_variables, unused_imports)]
70		{
71			use $crate::{vm::value::{Table, Value}, lua_table_inner, lua_value};
72			use hashbrown::HashMap;
73			use std::{default::Default, sync::Mutex};
74
75			let mut table = HashMap::<Value, Value>::new();
76			let mut counter = 1;
77
78			lua_table_inner!(table counter {$($arm)*});
79
80			Table {data: Mutex::new(table), ..Default::default()}
81		}
82	}}
83}
84
85#[macro_export]
86macro_rules! lua_table_inner {
87	($table:ident $counter:ident {[$key:expr] = $value:expr $(, $($rest:tt)*)?}) => {
88		{
89			$table.insert(lua_table_inner!($key), lua_table_inner!($value));
90		}
91
92		lua_table_inner!($table $counter {$($($rest)*)?});
93	};
94	($table:ident $counter:ident {$key:ident = $value:expr $(, $($rest:tt)*)?}) => {
95		{
96			$table.insert(Value::from(stringify!($key)), lua_table_inner!($value));
97		}
98
99		lua_table_inner!($table $counter {$($($rest)*)?});
100	};
101	($table:ident $counter:ident {$value:expr $(, $($rest:tt)*)?}) => {
102		{
103			$table.insert(Value::from($counter), lua_table_inner!($value));
104			$counter += 1;
105		}
106
107		lua_table_inner!($table $counter {$($($rest)*)?});
108	};
109	($table:ident $counter:ident {$($rest:tt)*}) => {};
110
111	($value:literal) => {lua_value!($value)};
112	($value:expr) => {$value}
113}
114
115#[macro_export]
116macro_rules! lua_tuple {
117	($($arm:tt)*) => {{
118		#[allow(unused_assignments, unused_mut, unused_variables, unused_imports)]
119		{
120			use $crate::{
121				vm::value::{IntoNillable, Nillable::NonNil, Table, Value},
122				lua_tuple_inner, lua_value
123			};
124			use hashbrown::HashMap;
125			use std::{default::Default, sync::Mutex};
126
127			let mut table = HashMap::<Value, Value>::new();
128			let mut counter = 0;
129
130			lua_tuple_inner!(table counter {$($arm)*});
131			table.insert(Value::Integer(0), Value::Integer(counter));
132
133			Table {data: Mutex::new(table), ..Default::default()}
134		}
135	}}
136}
137
138#[macro_export]
139macro_rules! lua_tuple_inner {
140	($table:ident $counter:ident {$value:expr $(, $($rest:tt)*)?}) => {
141		{
142			$counter += 1;
143			if let NonNil(value) = IntoNillable::nillable(lua_tuple_inner!($value).clone()) {
144				$table.insert(Value::Integer($counter), value);
145			}
146		}
147
148		lua_tuple_inner!($table $counter {$($($rest)*)?});
149	};
150	($table:ident $counter:ident {}) => {};
151
152	($value:literal) => {lua_value!($value)};
153	($value:expr) => {$value}
154}
155
156pub trait UserData: Send + Sync {
157	fn type_name(&self) -> &'static str;
158}
159
160pub type NativeFunction<'n> = &'n (dyn Fn(Arc<Table<'n>>, &VirtualMachine<'n>)
161	-> Result<Arc<Table<'n>>, String> + Send + Sync);
162
163/// Represents a lua value.
164// TODO: Add floats.
165#[derive(Clone)]
166pub enum Value<'n> {
167	Integer(i64),
168	String(Box<str>),
169	Boolean(bool),
170	Table(Arc<Table<'n>>),
171	UserData {
172		data: &'n dyn UserData,
173		meta: Option<Arc<Table<'n>>>
174	},
175	Function(Arc<Function<'n>>),
176	NativeFunction(NativeFunction<'n>)
177}
178
179impl<'n> Value<'n> {
180	pub fn new_string(string: impl AsRef<str>) -> Self {
181		Self::String(string.as_ref().to_owned().into_boxed_str())
182	}
183
184	pub fn type_name(&self) -> &'static str {
185		match self {
186			Self::Integer(_) => "number",
187			Self::String(_) => "string",
188			Self::Boolean(_) => "boolean",
189			Self::Table(_) => "table",
190			Self::UserData {data, ..} => data.type_name(),
191			Self::Function(_) | Self::NativeFunction(_) => "function"
192		}
193	}
194
195	/// Coerces this value to a bool. The rules are as follows; If the value is
196	/// not a boolean, then true is returned, otherwise the value of the bool
197	/// is returned.
198	pub fn coerce_to_bool(&self) -> bool {
199		match self {
200			Self::Boolean(value) => *value,
201			_ => true
202		}
203	}
204
205	/// Like [coerce_to_bool], but wraps the result in a value.
206	pub fn coerce_to_boolean<'nn>(&self) -> Value<'nn> {
207		Value::Boolean(self.coerce_to_bool())
208	}
209
210	pub fn integer(&self) -> Option<i64> {
211		match self {
212			Self::Integer(integer) => Some(*integer),
213			_ => None
214		}
215	}
216
217	pub fn string(&self) -> Option<&str> {
218		match self {
219			Self::String(string) => Some(string),
220			_ => None
221		}
222	}
223
224	pub fn boolean(&self) -> Option<bool> {
225		match self {
226			Self::Boolean(boolean) => Some(*boolean),
227			_ => None
228		}
229	}
230
231	pub fn table(&self) -> Option<&Arc<Table<'n>>> {
232		match self {
233			Self::Table(table) => Some(table),
234			_ => None
235		}
236	}
237
238	pub fn function(&self) -> Option<&Arc<Function<'n>>> {
239		match self {
240			Self::Function(function) => Some(function),
241			_ => None
242		}
243	}
244}
245
246impl Display for Value<'_> {
247	fn fmt(&self, f: &mut Formatter) -> FMTResult {
248		match self {
249			Self::Integer(integer) => write!(f, "{}", integer),
250			Self::String(string) => write!(f, "{}", string),
251			Self::Boolean(boolean) => write!(f, "{}", boolean),
252			Self::Table(table) => write!(f, "{}", table),
253			Self::UserData {..} => todo!(),
254			Self::Function(function) => write!(f, "{}", function),
255			Self::NativeFunction(function) => write!(f, "function: {:p}", *function)
256		}
257	}
258}
259
260impl Debug for Value<'_> {
261	fn fmt(&self, f: &mut Formatter) -> FMTResult {
262		match self {
263			Self::Integer(integer) => Debug::fmt(integer, f),
264			Self::String(string) => Debug::fmt(string, f),
265			Self::Boolean(boolean) => Debug::fmt(boolean, f),
266			Self::Table(table) => Debug::fmt(table, f),
267			Self::UserData {..} => todo!(),
268			Self::Function(function) => Debug::fmt(function, f),
269			Self::NativeFunction(function) => write!(f, "function: {:p}", function)
270		}
271	}
272}
273
274impl Eq for Value<'_> {}
275
276impl<'l, 'r> PartialEq<Value<'r>> for Value<'l> {
277	fn eq(&self, other: &Value<'r>) -> bool {
278		match (self, other) {
279			(Self::Integer(a), Value::Integer(b)) => *a == *b,
280			(Self::String(a), Value::String(b)) => *a == *b,
281			(Self::Boolean(a), Value::Boolean(b)) => *a == *b,
282			(Self::Function(a), Value::Function(b)) =>
283				eq(Arc::as_ptr(a) as *const u8, Arc::as_ptr(b) as *const u8),
284			(Self::Table(a), Value::Table(b)) =>
285				eq(Arc::as_ptr(a) as *const u8, Arc::as_ptr(b) as *const u8),
286			(Self::NativeFunction(a), Value::NativeFunction(b)) =>
287				eq(*a as *const _ as *const u8, *b as *const _ as *const u8),
288			_ => false
289		}
290	}
291}
292
293impl Hash for Value<'_> {
294	fn hash<H>(&self, state: &mut H)
295			where H: Hasher {
296		match self {
297			Self::Integer(integer) => integer.hash(state),
298			Self::String(string) => string.hash(state),
299			Self::Boolean(boolean) => boolean.hash(state),
300			Self::Table(arc) => Arc::as_ptr(arc).hash(state),
301			Self::UserData {data, ..} => hash(data, state),
302			Self::Function(arc) => Arc::as_ptr(arc).hash(state),
303			Self::NativeFunction(func) => hash(func, state)
304		}
305	}
306}
307
308value_conversions! {
309	impl<'n> for value @ i64 {Value::Integer(value)}
310	impl<'n; 'r> for value @ &'r str {Value::String(value.into())}
311	impl<'n> for value @ Box<str> {Value::String(value)}
312	impl<'n> for value @ String {Value::String(value.into_boxed_str())}
313	impl<'n> for value @ bool {Value::Boolean(value)}
314	impl<'n> for value @ Table<'n> {Value::Table(value.arc())}
315	impl<'n> for value @ Arc<Table<'n>> {Value::Table(value)}
316}
317
318/// Represents a lua value that may be nil. This type has a lot in common with
319/// the [Option] type, but this type has purpose built methods and trait
320/// implementations for handling lua nil values. Unlike option, NillableValue
321/// can only hold [Value]s or references to them.
322#[derive(Clone, Eq, Hash, PartialEq)]
323pub enum Nillable<'n> {
324	/// Variant for when the value is not nil.
325	NonNil(Value<'n>),
326	/// Variant for when the value is nil.
327	Nil
328}
329
330impl<'n> Nillable<'n> {
331	/// Get the human readable name of the type of this value.
332	pub fn type_name(&self) -> &'static str {
333		match self {
334			NonNil(value) => value.borrow().type_name(),
335			Nil => "nil"
336		}
337	}
338
339	/// Convenience method for using [Into::into] or [From::from].
340	pub fn option(self) -> Option<Value<'n>> {
341		self.into()
342	}
343
344	/// Like [Value::coerce_to_bool], but also handles nil cases. The rules are as
345	/// follows; If the value is nil or false, false is returned, otherwise true
346	/// is.
347	pub fn coerce_to_bool(&self) -> bool {
348		match self {
349			NonNil(value) => value.borrow().coerce_to_bool(),
350			Nil => false
351		}
352	}
353
354	/// Like [coerce_to_bool], but wraps the result in a value.
355	pub fn coerce_to_boolean<'nn>(&self) -> Value<'nn> {
356		Value::Boolean(self.coerce_to_bool())
357	}
358
359	pub fn is_nil(&self) -> bool {
360		matches!(self, Nil)
361	}
362
363	pub fn is_non_nil(&self) -> bool {
364		matches!(self, NonNil(_))
365	}
366}
367
368impl Display for Nillable<'_> {
369	fn fmt(&self, f: &mut Formatter) -> FMTResult {
370		match self {
371			Nillable::NonNil(value) => write!(f, "{}", value.borrow()),
372			Nil => write!(f, "nil")
373		}
374	}
375}
376
377impl Debug for Nillable<'_> {
378	fn fmt(&self, f: &mut Formatter) -> FMTResult {
379		match self {
380			Nillable::NonNil(value) => write!(f, "{:?}", value.borrow()),
381			Nil => write!(f, "nil")
382		}
383	}
384}
385
386impl Default for Nillable<'_> {
387	fn default() -> Self {
388		Nil
389	}
390}
391
392pub trait IntoNillable<'n>: Sized {
393	fn nillable(self) -> Nillable<'n>;
394}
395
396nillable_conversions! {
397	// From Option
398
399	impl<'n> all for value @ Option<Value<'n>> {
400		match value {
401			Some(value) => NonNil(value),
402			None => Nil
403		}
404	}
405
406	impl<'n; 'r> for value @ Option<&'r Value<'n>> {
407		match value {
408			Some(value) => NonNil(value.clone()),
409			None => Nil
410		}
411	}
412
413	// From Self or Value
414
415	impl<'n> for value @ Nillable<'n> {value}
416	impl<'n> all for value @ Value<'n> {NonNil(value)}
417
418	// From Into<Value>
419
420	impl<'n> all for value @ i64 {NonNil(value.into())}
421	impl<'n; 'r> all for value @ &'r str {NonNil(value.into())}
422	impl<'n> all for value @ Box<str> {NonNil(value.into())}
423	impl<'n> all for value @ String {NonNil(value.into())}
424	impl<'n> all for value @ bool {NonNil(value.into())}
425	impl<'n> all for value @ Table<'n> {NonNil(value.into())}
426	impl<'n> all for value @ Arc<Table<'n>> {NonNil(value.into())}
427	impl<'n> all for _value @ () {Nil}
428}
429
430impl<'n> From<Nillable<'n>> for Option<Value<'n>> {
431	fn from(nillable: Nillable<'n>) -> Self {
432		match nillable {
433			NonNil(value) => Some(value),
434			Nil => None
435		}
436	}
437}
438
439#[derive(Clone, Debug)]
440pub enum MaybeUpValue<'n> {
441	UpValue(Arc<Mutex<Nillable<'n>>>),
442	Normal(Nillable<'n>)
443}
444
445impl<'n> MaybeUpValue<'n> {
446	pub fn up_value(&mut self) -> &Arc<Mutex<Nillable<'n>>> {
447		match self {
448			Self::UpValue(up_value) => up_value,
449			Self::Normal(normal) => {
450				let normal = Arc::new(Mutex::new(std::mem::replace(normal, Nil)));
451				*self = Self::UpValue(normal);
452				match self {
453					Self::UpValue(up_value) => up_value,
454					_ => unreachable!()
455				}
456			}
457		}
458	}
459}
460
461impl Default for MaybeUpValue<'_> {
462	fn default() -> Self {
463		Self::Normal(Nil)
464	}
465}
466
467#[derive(Default)]
468pub struct Table<'n> {
469	pub data: Mutex<HashMap<Value<'n>, Value<'n>>>,
470	pub metatable: Mutex<Option<Arc<Table<'n>>>>
471}
472
473impl<'n> Table<'n> {
474	/// Inserts a value into this table as if it was an array.
475	#[inline]
476	pub fn array_insert(&self, index: i64, mut value: Nillable<'n>) {
477		let len = self.array_len();
478		let mut data = self.data.lock().unwrap();
479
480		(index..=(len.max(1) + 1))
481			.for_each(|index| match take(&mut value) {
482				NonNil(new) =>
483					value = data.insert(Value::Integer(index), new).nillable(),
484				Nil =>
485					value = data.remove(&Value::Integer(index)).nillable()
486			});
487	}
488
489	#[inline]
490	pub fn array_remove(&self, index: i64) -> Nillable<'n> {
491		let len = self.array_len();
492		let mut data = self.data.lock().unwrap();
493
494		let mut value = Nil;
495		(index..=len).rev()
496			.for_each(|index| match take(&mut value) {
497				NonNil(new) =>
498					value = data.insert(Value::Integer(index as i64), new).nillable(),
499				Nil =>
500					value = data.remove(&Value::Integer(index as i64)).nillable()
501			});
502		value
503	}
504
505	#[inline]
506	pub fn array_push(&self, value: Nillable<'n>) {
507		self.array_insert(self.array_len(), value)
508	}
509
510	pub fn array_len(&self) -> i64 {
511		self.data.lock().unwrap().iter()
512			.filter_map(|(key, _)| key.integer())
513			.fold(0, |result, index| result.max(index))
514	}
515
516	pub fn array_is_empty(&self) -> bool {
517		self.data.lock().unwrap().iter()
518			.any(|(key, _)| key.integer().is_some())
519	}
520
521	/// Inserts a value into this table as if it was a tuple.
522	#[inline]
523	pub fn tuple_insert(&self, index: i64, mut value: Nillable<'n>) {
524		let len = self.tuple_len();
525		let mut data = self.data.lock().unwrap();
526		data.insert(Value::Integer(0), Value::Integer(len + 1));
527
528		(index..=(len.max(1) + 1))
529			.for_each(|index| match take(&mut value) {
530				NonNil(new) =>
531					value = data.insert(Value::Integer(index), new).nillable(),
532				Nil =>
533					value = data.remove(&Value::Integer(index)).nillable()
534			});
535	}
536
537	pub fn tuple_len(&self) -> i64 {
538		self.data.lock().unwrap().get(&Value::Integer(0))
539			.unwrap().integer().unwrap()
540	}
541
542	pub fn index<'qn>(&self, index: &Value<'qn>) -> Nillable<'n> {
543		// std::collections::HashMap::get's signature is overly strict. It requires
544		// that Q lives as long as K does, but it only uses Q as long as the call.
545		// To get around this, we *could* use the nightly feature `hash_raw_entry`,
546		// or we could just require the hash library the standard library uses under
547		// the hood, hashbrown. Obviously the less painful solution was taken.
548		// TODO: Should we drop hashbrown as a dependency when `hash_raw_entry` is
549		// stablized? Or is it better to have something that can be upgraded
550		// according to semver?
551
552		let data = self.data.lock().unwrap();
553		let mut hasher = data.hasher().build_hasher();
554		index.hash(&mut hasher);
555		data.raw_entry().from_hash(hasher.finish(), |check| index == check)
556			.map(|(_, value)| value).cloned().nillable()
557		//todo!()
558	}
559
560	pub fn arc(self) -> Arc<Self> {
561		Arc::new(self)
562	}
563}
564
565impl<'n> PartialEq for Table<'n> {
566	fn eq(&self, other: &Table<'n>) -> bool {
567		eq(self, other)
568	}
569}
570
571impl Display for Table<'_> {
572	fn fmt(&self, f: &mut Formatter) -> FMTResult {
573		write!(f, "table: {:p}", &*self)
574	}
575}
576
577impl Debug for Table<'_> {
578	fn fmt(&self, f: &mut Formatter) -> FMTResult {
579		match self.data.try_lock() {
580			Ok(data) => {
581				let mut first = true;
582				let mut comma = || {
583					if first {first = false; ""}
584					else {", "}
585				};
586
587				write!(f, "{{")?;
588				let mut array = data.iter()
589					.filter_map(|(key, value)| if let Value::Integer(key) = key
590						{Some((key, value))} else {None})
591					.collect::<Vec<_>>();
592				array.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
593				if let Some((highest, _)) = array.last() {
594					(1..=**highest)
595						.map(|index| array.iter().find(|value| *value.0 == index)
596							.map(|(_, value)| *value))
597						.try_for_each(|value| write!(f, "{}{:?}", comma(), value.nillable()))?;
598				}
599
600				data.iter()
601					.try_for_each(|(key, value)| match key {
602						Value::Integer(_) => Ok(()),
603						key => write!(f, "{}[{:?}] = {:?}", comma(), key, value)
604					})?;
605
606				write!(f, "}}")
607			},
608			Err(_) => write!(f, "{{<table is being accessed>}}")
609		}
610	}
611}
612
613#[derive(Debug)]
614pub struct Function<'n> {
615	pub up_values: Box<[Arc<Mutex<Nillable<'n>>>]>,
616	pub chunk: Arc<Chunk>
617}
618
619impl Function<'_> {
620	pub fn arc(self) -> Arc<Self> {
621		Arc::new(self)
622	}
623}
624
625impl<'n> PartialEq for Function<'n> {
626	fn eq(&self, other: &Function<'n>) -> bool {
627		eq(self, other)
628	}
629}
630
631impl Eq for Function<'_> {}
632
633impl Display for Function<'_> {
634	fn fmt(&self, f: &mut Formatter) -> FMTResult {
635		write!(f, "function: {:p}", &self)
636	}
637}
638
639impl From<Chunk> for Function<'_> {
640	fn from(chunk: Chunk) -> Self {
641		Self {chunk: chunk.arc(), up_values: vec![].into_boxed_slice()}
642	}
643}