lunar_wave/lunar_wave_vm/src/value.rs

375 lines
8.1 KiB
Rust

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 <Value>,
}
#[derive (Clone, PartialEq)]
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 (Rc <RefCell <Table>>),
// These are all bogus, I haven't figured out how to implement
// closures yet
BogusClosure (Rc <RefCell <BogusClosure>>),
}
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, "{:?}", t.borrow ()),
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: {:?}", std::rc::Rc::as_ptr (t)),
Value::BogusClosure (x) => write! (f, "BogusClosure: {:?}", std::rc::Rc::as_ptr (x)),
}
}
}
impl From <BogusClosure> for Value {
fn from (x: BogusClosure) -> Self {
Self::BogusClosure (Rc::new (RefCell::new (x)))
}
}
impl From <bool> for Value {
fn from (x: bool) -> Self {
Self::Boolean (x)
}
}
impl From <i32> for Value {
fn from (x: i32) -> Self {
Self::Integer (i64::from (x))
}
}
impl From <i64> for Value {
fn from (x: i64) -> Self {
Self::Integer (x)
}
}
impl From <f64> for Value {
fn from (x: f64) -> Self {
Self::Float (x)
}
}
impl From <usize> for Value {
fn from (x: usize) -> Self {
Self::Integer (i64::try_from (x).unwrap ())
}
}
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)))
}
}
impl FromIterator <(Value, Value)> for Value {
fn from_iter <I: IntoIterator <Item=(Value, Value)>> (i: I) -> Self {
let table = Table::from_iter (i);
Self::from (table)
}
}
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 <Value> for Value {
fn from_iter <I: IntoIterator <Item=Value>> (i: I) -> Self {
Self::from_iter ((1..).zip (i.into_iter ()).map (|(i, v)| (Value::from (i), v)))
}
}
impl Eq for Value {}
impl std::hash::Hash for Value {
fn hash <H: std::hash::Hasher> (&self, state: &mut H) {
// Per https://doc.rust-lang.org/std/hash/trait.Hash.html#prefix-collisions
[0xff].hash (state);
match self {
// TODO: Weaken to a Lua error
Self::Nil => panic! ("can't hash a nil value"),
Self::Boolean (x) => x.hash (state),
Self::Float (x) => x.to_ne_bytes ().hash (state),
Self::Integer (x) => x.hash (state),
Self::RsFunc (x) => x.hash (state),
// TODO: Implement string interning so we don't hash the whole string here
Self::String (x) => x.hash (state),
Self::Table (x) => Rc::as_ptr (&x).hash (state),
Self::BogusClosure (_) => panic! ("can't hash Bogus values"),
}
}
}
impl PartialEq <i64> for Value {
fn eq (&self, rhs: &i64) -> bool {
*self == Value::from (*rhs)
}
}
impl Value {
pub fn as_closure (&self) -> Option <&Rc <RefCell <BogusClosure>>> {
match self {
Self::BogusClosure (x) => Some (x),
_ => None,
}
}
/// Coerces ints to float
pub fn as_float (&self) -> Option <f64> {
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 <i64> {
match self {
Self::Integer (x) => Some (*x),
_ => None,
}
}
pub fn as_str (&self) -> Option <InternedString> {
match self {
Self::String (x) => Some (*x),
_ => None,
}
}
pub fn as_table (&self) -> Option <&Rc <RefCell <Table>>> {
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 <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)?;
Ok (())
}
}
impl Table {
fn get_inner (&self, key: &Value) -> Value {
match key {
Value::Nil => Value::Nil,
Value::String (x) => self.map.get (x).cloned ().unwrap_or_default (),
x => self.hash.get (x).cloned ().unwrap_or_default (),
}
}
pub fn get <A: Into <Value>> (&self, key: A) -> Value {
self.get_inner (&(key.into ()))
}
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,
}
}
/// Insert value at arbitrary key
pub fn insert <A: Into <Value>, B: Into <Value>> (
&mut self,
a: A,
b: B,
) {
match a.into () {
Value::Nil => (),
Value::String (x) => {
self.map.insert (x, b.into ());
},
x => {
self.hash.insert (x, b.into ());
},
}
}
/// Insert value at integer key
pub fn insert_int <A: Into <Value>> (&mut self, k: i64, v: A)
{
self.insert (Value::from (k), v.into ())
}
pub fn insert_str (&mut self, key: InternedString, v: Value) {
self.map.insert (key, v);
}
pub fn length (&self) -> i64 {
for i in 1..i64::MAX {
if self.get (i) == Value::Nil {
return i - 1;
}
}
0
}
}
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);
}
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 (Value::from_str (&mut si, "a")), nil);
assert_eq! (t.get (Value::from_str (&mut si, "b")), nil);
t.insert (si.to_value ("a"), 1993);
t.insert (si.to_value ("b"), 2007);
assert_eq! (t.get (Value::from_str (&mut si, "a")), 1993);
assert_eq! (t.get (Value::from_str (&mut si, "b")), 2007);
t.insert (19, 93);
assert_eq! (t.get (19), 93);
}
}