use std::{ cell::RefCell, cmp::{ Eq, PartialEq, }, collections::{BTreeMap, 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? pub idx: usize, pub upvalues: Vec , } #[derive (Clone, PartialEq)] #[repr (u8)] pub enum Value { Nil, Boolean (bool), // Rust is very strict about float equality, so some of my code // here is probably wrong in subtle ways. Float (f64), Integer (i64), RsFunc (fn (&mut crate::state::State, usize) -> usize), String (InternedString), Table (usize), // These are all bogus, I haven't figured out how to implement // closures yet BogusClosure (Rc >), } const NIL: Value = Value::Nil; impl fmt::Debug for Value { fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Value::Nil => write! (f, "nil"), Value::Boolean (false) => write! (f, "false"), Value::Boolean (true) => write! (f, "true"), 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::Table (t) => write! (f, "table"), Value::BogusClosure (x) => write! (f, "{:?}", x.borrow ()), } } } impl Default for Value { fn default () -> Self { Self::Nil } } impl fmt::Display for Value { fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Value::Nil => write! (f, "nil"), Value::Boolean (false) => write! (f, "false"), Value::Boolean (true) => write! (f, "true"), 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::Table (t) => write! (f, "table"), Value::BogusClosure (x) => write! (f, "BogusClosure: {:?}", std::rc::Rc::as_ptr (x)), } } } impl From for Value { fn from (x: BogusClosure) -> Self { Self::BogusClosure (Rc::new (RefCell::new (x))) } } impl From for Value { fn from (x: bool) -> Self { Self::Boolean (x) } } impl From for Value { fn from (x: i32) -> Self { Self::Integer (i64::from (x)) } } impl From for Value { fn from (x: i64) -> Self { Self::Integer (x) } } impl From for Value { fn from (x: f64) -> Self { Self::Float (x) } } impl From for Value { fn from (x: usize) -> Self { Self::Integer (i64::try_from (x).unwrap ()) } } impl From for Value { fn from (x: InternedString) -> Self { Self::String (x) } } impl Eq for Value {} impl std::hash::Hash for Value { fn hash (&self, state: &mut H) { // Per https://doc.rust-lang.org/std/hash/trait.Hash.html#prefix-collisions [0xff].hash (state); match self { // TODO: Weaken to a Lua error Self::Nil => panic! ("can't hash a nil value"), Self::Boolean (x) => { [0x01].hash (state); x.hash (state) }, Self::Float (x) => { [0x02].hash (state); x.to_ne_bytes ().hash (state) }, Self::Integer (x) => { [0x03].hash (state); x.hash (state) }, Self::RsFunc (x) => { [0x04].hash (state); x.hash (state) }, // TODO: Implement string interning so we don't hash the whole string here Self::String (x) => { [0x05].hash (state); x.hash (state) }, Self::Table (x) => { [0x06].hash (state); x.hash (state) }, Self::BogusClosure (_) => panic! ("can't hash Bogus values"), } } } impl PartialEq for Value { fn eq (&self, rhs: &i64) -> bool { *self == Value::from (*rhs) } } impl Value { pub fn as_closure (&self) -> Option <&Rc >> { match self { Self::BogusClosure (x) => Some (x), _ => None, } } /// Coerces ints to float pub fn as_float (&self) -> Option { match self { Self::Float (x) => Some (*x), // FloatToInt isn't stable yet, so only ints in i32 space can practically be used for now Self::Integer (x) => f64::try_from (i32::try_from (*x).ok ()?).ok (), _ => None, } } /// Does not coerce floats pub fn as_int (&self) -> Option { match self { Self::Integer (x) => Some (*x), _ => None, } } pub fn as_str (&self) -> Option { match self { Self::String (x) => Some (*x), _ => None, } } pub fn as_table (&self) -> Option { match self { Self::Table (t) => Some (*t), _ => None, } } 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. match self { Value::Nil => false, Value::Boolean (false) => false, _ => true, } } pub fn take (&mut self) -> Self { let mut x = Value::Nil; std::mem::swap (self, &mut x); x } } #[derive (Default, Eq, PartialEq)] pub struct Table { array: Vec , hash: HashMap , strings: Vec <(InternedString, Value)>, map: BTreeMap , } impl fmt::Debug for Table { fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result { write! (f, "Table {:#?}", self.map)?; Ok (()) } } impl Table { fn get_inner (&self, key: &Value) -> &Value { match key { Value::Nil => &NIL, Value::String (x) => self.get_str (*x), Value::Integer (x) => self.array.get (usize::try_from (*x).unwrap ()).unwrap_or (&NIL), x => self.hash.get (x).unwrap_or (&NIL), } } pub fn get > (&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_str (&self, key: InternedString) -> &Value { self.strings.iter ().find (|(hay, _)| hay == &key).map (|(_, v)| v).unwrap_or (&NIL) } /// Insert value at arbitrary key pub fn insert , B: Into > ( &mut self, a: A, b: B, ) { match a.into () { Value::Integer (x) => self.insert_int (x, b), Value::Nil => (), Value::String (x) => self.insert_str (x, b.into ()), x => { self.hash.insert (x, b.into ()); }, } } /// Insert value at integer key pub fn insert_int > (&mut self, k: i64, v: A) { let k = usize::try_from (k).unwrap (); self.array.resize (k + 1, NIL); self.array [k] = v.into (); } pub fn insert_str (&mut self, key: InternedString, v: Value) { match self.strings.iter_mut ().find (|(hay, _)| hay == &key).map (|(_, v)| v) { None => self.strings.push ((key, v)), Some (x) => *x = v, } } pub fn length (&self) -> i64 { for i in 1..i64::MAX { if self.get (i) == &NIL { return i - 1; } } 0 } } impl FromIterator <(Value, Value)> for Table { fn from_iter> (i: I) -> Self { let mut that = Self::default (); for (k, v) in i.into_iter () { that.insert (k, v); } that } } #[cfg (test)] mod tests { use std::collections::HashMap; use super::*; #[test] fn smoke () { let v_a = Value::from (5.0); let v_b = Value::from (5); assert_eq! (v_a.as_float (), Some (5.0)); assert_eq! (v_b.as_float (), Some (5.0)); } #[test] fn tables () { let nil = Value::Nil; 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))); let mut t = Table::default (); assert_eq! (t.get (si.intern ("a")), &nil); assert_eq! (t.get (si.intern ("b")), &nil); t.insert (si.to_value ("a"), 1993); t.insert (si.to_value ("b"), 2007); assert_eq! (t.get (si.intern ("a")), &Value::from (1993)); assert_eq! (t.get (si.intern ("b")), &Value::from (2007)); t.insert (19, 93); assert_eq! (t.get (19), &Value::from (93)); } }