use std::collections::HashSet;
use sandpit::{Gc, Mutator};
use crate::symbol_map::{SymID, SymbolMap, BOOL_SYM, FLOAT_SYM, FN_SYM, INT_SYM, LIST_SYM, MAP_SYM, NULL_SYM, STR_SYM, SYM_SYM};
use super::func::Func;
use super::hash_map::GcHashMap;
use super::list::List;
use super::native_func::NativeFunc;
use super::string::VMString;
use super::tagged_value::TaggedValue;
pub enum Value<'gc> {
Null,
Bool(bool),
SymId(u32),
Int(i64),
Float(f64),
List(Gc<'gc, List<'gc>>),
String(Gc<'gc, VMString<'gc>>),
Map(Gc<'gc, GcHashMap<'gc>>),
Func(Gc<'gc, Func<'gc>>),
NativeFunc(Gc<'gc, NativeFunc>),
}
impl<'gc> Value<'gc> {
pub fn to_string(self, syms: &mut SymbolMap, top_level: bool) -> String {
let mut visited = HashSet::new();
self.to_string_internal(syms, top_level, &mut visited)
}
pub(crate) fn to_string_internal(self, syms: &mut SymbolMap, top_level: bool, visited: &mut HashSet<usize>) -> String {
match self {
Value::Null => "null".to_string(),
Value::Int(i) => format!("{i}"),
Value::Float(v) => format!("{v}"),
Value::SymId(id) => format!("${}", syms.get_str(id)),
Value::Bool(b) => format!("{b}"),
Value::Map(map) => {
let ptr = &*map as *const _ as usize;
if visited.contains(&ptr) {
return "{...}".to_string();
}
visited.insert(ptr);
let result = map.to_string_internal(syms, visited);
visited.remove(&ptr);
result
}
Value::List(list) => {
let ptr = &*list as *const _ as usize;
if visited.contains(&ptr) {
return "[...]".to_string();
}
visited.insert(ptr);
let mut s = String::new();
s.push('[');
for i in 0..list.len() {
let item = list.at(i);
s.push_str(item.to_string_internal(syms, false, visited).as_str());
if i != list.len() - 1 {
s.push_str(", ");
};
}
s.push(']');
visited.remove(&ptr);
s
}
Value::String(vm_str) => {
let s = vm_str.as_string();
if top_level {
s
} else {
format!("\"{s}\"")
}
}
Value::Func(func) => format!("Func(id: {}, args: {})", func.get_id(), func.arity()),
Value::NativeFunc(_) => "<native fn>".to_string(),
}
}
pub fn as_tagged(self, mu: &'gc Mutator) -> TaggedValue<'gc> {
TaggedValue::from_value(self, mu)
}
pub fn is_truthy(&self) -> bool {
!matches!(self, Value::Null | Value::Bool(false) | Value::Int(0) | Value::Float(0.0))
}
pub fn is_equal_to(&self, other: &Value<'gc>) -> bool {
match (self, other) {
(Value::Null, Value::Null) => true,
(Value::Float(lhs), Value::Float(rhs)) => lhs == rhs,
(Value::Int(lhs), Value::Int(rhs)) => lhs == rhs,
(Value::SymId(lhs), Value::SymId(rhs)) => lhs == rhs,
(Value::Bool(lhs), Value::Bool(rhs)) => lhs == rhs,
(Value::Float(f), Value::Int(i)) | (Value::Int(i), Value::Float(f)) => *f == *i as f64,
(Value::String(lhs), Value::String(rhs)) => {
if lhs.len() != rhs.len() {
return false;
}
for i in 0..lhs.len() {
if lhs.at(i) != rhs.at(i) {
return false;
}
}
true
}
(Value::List(lhs), Value::List(rhs)) => {
if lhs.len() != rhs.len() {
return false;
}
for i in 0..lhs.len() {
if !lhs.at(i).is_equal_to(&rhs.at(i)) {
return false;
}
}
true
}
(Value::Map(lhs), Value::Map(rhs)) => {
lhs.is_structurally_equal_to(rhs)
}
(Value::Func(lhs), Value::Func(rhs)) => {
core::ptr::eq(&**lhs as *const _, &**rhs as *const _)
}
(Value::NativeFunc(lhs), Value::NativeFunc(rhs)) => {
core::ptr::eq(&**lhs as *const _, &**rhs as *const _)
}
_ => false,
}
}
pub fn get_type_id(&self) -> SymID {
match self {
Value::Null => NULL_SYM,
Value::Bool(_) => BOOL_SYM,
Value::SymId(_) => SYM_SYM,
Value::Float(_) => FLOAT_SYM,
Value::Int(_) => INT_SYM,
Value::String(_) => STR_SYM,
Value::List(_) => LIST_SYM,
Value::Map(_) => MAP_SYM,
Value::Func(_) => FN_SYM,
Value::NativeFunc(_) => FN_SYM,
}
}
pub fn type_str(&self) -> &str {
match self {
Value::Float(_) => "Float",
Value::Int(_) => "Int",
Value::List(_) => "List",
Value::String(_) => "String",
Value::Func(_) => "Func",
Value::NativeFunc(_) => "NativeFunc",
Value::Map(_) => "Map",
Value::SymId(_) => "Symbol",
Value::Null => "Null",
Value::Bool(_) => "Bool",
}
}
pub(crate) fn hash_bytes(&self) -> Vec<u8> {
match self {
Value::Null => vec![0],
Value::Bool(b) => vec![1, *b as u8],
Value::SymId(id) => {
let mut buffer = vec![2];
buffer.extend_from_slice(&id.to_ne_bytes());
buffer
}
Value::Int(i) => {
let mut buffer = vec![3];
buffer.extend_from_slice(&i.to_ne_bytes());
buffer
}
Value::Float(f) => {
let mut buffer = vec![4];
buffer.extend_from_slice(&f.to_ne_bytes());
buffer
}
Value::String(vm_str) => {
let mut buffer = vec![7];
for i in 0..vm_str.len() {
let b = vm_str.at(i).unwrap() as u8;
buffer.push(b);
}
buffer
}
Value::Func(f) => {
let mut buffer = vec![6];
let ptr = &**f as *const _ as usize;
buffer.extend_from_slice(&ptr.to_ne_bytes());
buffer
}
Value::NativeFunc(nf) => {
let mut buffer = vec![8];
let ptr = &**nf as *const _ as usize;
buffer.extend_from_slice(&ptr.to_ne_bytes());
buffer
}
Value::List(_) | Value::Map(_) => {
panic!("List and Map hashing should be handled by hash_value function")
}
}
}
}