lunar_wave/src/value.rs

323 lines
6.8 KiB
Rust
Raw Normal View History

use std::{
cell::RefCell,
2023-09-26 20:49:12 +00:00
cmp::{
Eq,
PartialEq,
},
collections::HashMap,
fmt,
rc::Rc,
};
2023-09-26 20:49:12 +00:00
#[derive (Debug, Eq, PartialEq)]
pub struct BogusClosure {
// I'm pretty sure this should be absolute?
pub idx: usize,
pub upvalues: Vec <Value>,
}
2023-09-29 22:17:53 +00:00
#[derive (Clone, PartialEq)]
pub enum Value {
Nil,
Boolean (bool),
2023-09-26 20:49:12 +00:00
// Rust is very strict about float equality, so some of my code
// here is probably wrong in subtle ways.
Float (f64),
2023-09-26 20:49:12 +00:00
Integer (i64),
RsFunc (fn (&mut crate::state::State) -> i32),
String (Rc <String>),
Table (Rc <RefCell <Table>>),
// These are all bogus, I haven't figured out how to implement
// closures yet
BogusClosure (Rc <RefCell <BogusClosure>>),
}
2023-09-29 22:17:53 +00:00
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, "\"{}\"", s),
2023-09-29 22:17:53 +00:00
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, "{}", 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)),
}
}
}
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 <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 {
2023-09-26 20:49:12 +00:00
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 ())
}
}
2023-09-26 20:49:12 +00:00
impl From <Table> for Value {
fn from (x: Table) -> Self {
Self::Table (Rc::new (RefCell::new (x)))
2023-09-26 20:49:12 +00:00
}
}
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 <(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)))
}
}
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)))
}
}
2023-09-26 20:49:12 +00:00
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),
2023-09-26 20:49:12 +00:00
Self::RsFunc (x) => x.hash (state),
2023-09-26 20:49:12 +00:00
// 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"),
}
}
}
2023-09-26 20:49:12 +00:00
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_table (&self) -> Option <&Rc <RefCell <Table>>> {
match self {
Self::Table (t) => Some (t),
_ => None,
}
}
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
}
}
2023-09-26 20:49:12 +00:00
2023-09-29 22:17:53 +00:00
#[derive (Default, Eq, PartialEq)]
2023-09-26 20:49:12 +00:00
pub struct Table {
array: Vec <Value>,
hash: HashMap <Value, Value>,
}
2023-09-29 22:17:53 +00:00
impl fmt::Debug for Table {
fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
write! (f, "Table {:#?}", self.hash)?;
Ok (())
}
}
2023-09-26 20:49:12 +00:00
impl Table {
fn get_inner (&self, key: &Value) -> Value {
self.hash.get (key).cloned ().unwrap_or_default ()
}
pub fn get <A: Into <Value>> (&self, key: A) -> Value {
self.get_inner (&(key.into ()))
}
fn insert_inner (&mut self, a: Value, b: Value) {
self.hash.insert (a, b);
}
pub fn insert <A: Into <Value>, B: Into <Value>> (
&mut self,
a: A,
b: B,
) {
self.insert_inner (a.into (), b.into ())
}
}
impl FromIterator <(Value, Value)> for Table {
fn from_iter<I: IntoIterator<Item = (Value, Value)>> (i: I) -> Self
{
let hash = i.into_iter ().collect ();
Self {
array: Default::default (),
hash,
}
}
}
2023-09-26 20:49:12 +00:00
#[cfg (test)]
mod tests {
use std::collections::HashMap;
use super::{
Table,
Value,
};
#[test]
fn smoke () {
let v_a = Value::from (5.0);
let v_b = Value::from (5);
assert_eq! (v_a.as_float (), Some (5.0));
assert_eq! (v_b.as_float (), Some (5.0));
}
#[test]
fn tables () {
let nil = Value::Nil;
assert_ne! (Value::from (18.0), Value::from (19.0));
let mut t = HashMap::new ();
t.insert (Value::from ("x"), Value::from (19.0));
assert_eq! (t.get (&Value::from ("x")), Some (&Value::from (19.0)));
let mut t = Table::default ();
assert_eq! (t.get ("a"), nil);
assert_eq! (t.get ("b"), nil);
t.insert ("a", 1993);
t.insert ("b", 2007);
assert_eq! (t.get ("a"), 1993);
assert_eq! (t.get ("b"), 2007);
t.insert (19, 93);
assert_eq! (t.get (19), 93);
}
}