/*! * NyashValue - Revolutionary unified value representation system * * Replaces Arc> overuse with direct value storage for primitives * and smart synchronization only where needed. * * Inspired by Lua's TValue system for performance-critical language implementations. */ use std::sync::{Arc, Mutex, Weak}; use std::collections::HashMap; use std::fmt::{self, Display, Debug}; use crate::box_trait::NyashBox; /// Revolutionary unified value type - replaces individual Box allocations #[derive(Clone)] pub enum NyashValue { // Direct primitive values - no Arc overhead Integer(i64), Float(f64), Bool(bool), String(String), // Collections - need synchronization Array(Arc>>), Map(Arc>>), // Legacy Box compatibility and custom types Box(Arc>), // 🔗 Weak reference system - prevents circular reference memory leaks WeakBox(Weak>), // Special values Null, Void, } impl NyashValue { /// Create a new NyashValue from a primitive value pub fn new_integer(value: i64) -> Self { NyashValue::Integer(value) } pub fn new_float(value: f64) -> Self { NyashValue::Float(value) } pub fn new_bool(value: bool) -> Self { NyashValue::Bool(value) } pub fn new_string(value: String) -> Self { NyashValue::String(value) } pub fn new_array() -> Self { NyashValue::Array(Arc::new(Mutex::new(Vec::new()))) } pub fn new_map() -> Self { NyashValue::Map(Arc::new(Mutex::new(HashMap::new()))) } pub fn new_null() -> Self { NyashValue::Null } pub fn new_void() -> Self { NyashValue::Void } /// Convert to string representation pub fn to_string(&self) -> String { match self { NyashValue::Integer(n) => n.to_string(), NyashValue::Float(f) => f.to_string(), NyashValue::Bool(b) => b.to_string(), NyashValue::String(s) => s.clone(), NyashValue::Array(arr) => { if let Ok(guard) = arr.try_lock() { let elements: Vec = guard.iter() .map(|v| v.to_string()) .collect(); format!("[{}]", elements.join(", ")) } else { "[Array (locked)]".to_string() } }, NyashValue::Map(map) => { if let Ok(guard) = map.try_lock() { let pairs: Vec = guard.iter() .map(|(k, v)| format!("{}: {}", k, v.to_string())) .collect(); format!("{{{}}}", pairs.join(", ")) } else { "{Map (locked)}".to_string() } }, NyashValue::Box(b) => { if let Ok(guard) = b.try_lock() { guard.to_string_box().value } else { "Box (locked)".to_string() } }, NyashValue::WeakBox(weak_ref) => { if let Some(arc) = weak_ref.upgrade() { if let Ok(guard) = arc.try_lock() { format!("WeakRef({})", guard.to_string_box().value) } else { "WeakRef(locked)".to_string() } } else { "WeakRef(null)".to_string() } }, NyashValue::Null => "null".to_string(), NyashValue::Void => "void".to_string(), } } /// Convert to integer (with type coercion) pub fn to_integer(&self) -> Result { match self { NyashValue::Integer(n) => Ok(*n), NyashValue::Float(f) => Ok(*f as i64), NyashValue::Bool(b) => Ok(if *b { 1 } else { 0 }), NyashValue::String(s) => { s.parse::() .map_err(|_| format!("Cannot convert '{}' to integer", s)) }, _ => Err(format!("Cannot convert {:?} to integer", self.type_name())), } } /// Convert to float (with type coercion) pub fn to_float(&self) -> Result { match self { NyashValue::Integer(n) => Ok(*n as f64), NyashValue::Float(f) => Ok(*f), NyashValue::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }), NyashValue::String(s) => { s.parse::() .map_err(|_| format!("Cannot convert '{}' to float", s)) }, _ => Err(format!("Cannot convert {:?} to float", self.type_name())), } } /// Convert to boolean (with type coercion) pub fn to_bool(&self) -> Result { match self { NyashValue::Bool(b) => Ok(*b), NyashValue::Integer(n) => Ok(*n != 0), NyashValue::Float(f) => Ok(*f != 0.0), NyashValue::String(s) => Ok(!s.is_empty()), NyashValue::Null => Ok(false), NyashValue::Void => Ok(false), NyashValue::WeakBox(weak_ref) => { // WeakBox is truthy if it can be upgraded (still alive) Ok(weak_ref.upgrade().is_some()) }, _ => Ok(true), // Arrays, Maps, Boxes are truthy } } /// Get the type name for error messages pub fn type_name(&self) -> &'static str { match self { NyashValue::Integer(_) => "Integer", NyashValue::Float(_) => "Float", NyashValue::Bool(_) => "Bool", NyashValue::String(_) => "String", NyashValue::Array(_) => "Array", NyashValue::Map(_) => "Map", NyashValue::Box(_) => "Box", NyashValue::WeakBox(_) => "WeakBox", NyashValue::Null => "Null", NyashValue::Void => "Void", } } /// Check if this value is numeric (Integer or Float) pub fn is_numeric(&self) -> bool { matches!(self, NyashValue::Integer(_) | NyashValue::Float(_)) } /// Check if this value is falsy pub fn is_falsy(&self) -> bool { matches!(self, NyashValue::Null | NyashValue::Void) || self.to_bool().unwrap_or(false) == false } /// 🔗 Weak Reference System - Core functionality /// Upgrade a weak reference to a strong reference /// Returns None if the referenced object has been dropped pub fn upgrade_weak(&self) -> Option { match self { NyashValue::WeakBox(weak_ref) => { weak_ref.upgrade().map(|arc| NyashValue::Box(arc)) }, _ => Some(self.clone()), // Non-weak values return themselves } } /// Downgrade a strong reference to a weak reference /// Only works on Box values, others return None pub fn downgrade_to_weak(&self) -> Option { match self { NyashValue::Box(arc) => { Some(NyashValue::WeakBox(Arc::downgrade(arc))) }, _ => None, // Can only create weak refs from strong Box refs } } /// Check if this is a weak reference pub fn is_weak_reference(&self) -> bool { matches!(self, NyashValue::WeakBox(_)) } /// Check if a weak reference is still valid (not dropped) pub fn is_weak_alive(&self) -> bool { match self { NyashValue::WeakBox(weak_ref) => weak_ref.strong_count() > 0, _ => true, // Non-weak values are always "alive" } } } impl PartialEq for NyashValue { fn eq(&self, other: &Self) -> bool { match (self, other) { // Exact type matches (NyashValue::Integer(a), NyashValue::Integer(b)) => a == b, (NyashValue::Float(a), NyashValue::Float(b)) => (a - b).abs() < f64::EPSILON, (NyashValue::Bool(a), NyashValue::Bool(b)) => a == b, (NyashValue::String(a), NyashValue::String(b)) => a == b, (NyashValue::Null, NyashValue::Null) => true, (NyashValue::Void, NyashValue::Void) => true, // Cross-type numeric equality (42 == 42.0) (NyashValue::Integer(a), NyashValue::Float(b)) => (*a as f64 - b).abs() < f64::EPSILON, (NyashValue::Float(a), NyashValue::Integer(b)) => (a - *b as f64).abs() < f64::EPSILON, // Arrays and Maps require deep comparison (simplified for now) (NyashValue::Array(a), NyashValue::Array(b)) => Arc::ptr_eq(a, b), (NyashValue::Map(a), NyashValue::Map(b)) => Arc::ptr_eq(a, b), (NyashValue::Box(a), NyashValue::Box(b)) => Arc::ptr_eq(a, b), // Weak reference equality (NyashValue::WeakBox(a), NyashValue::WeakBox(b)) => { // Compare if they point to the same object (if both still alive) match (a.upgrade(), b.upgrade()) { (Some(arc_a), Some(arc_b)) => Arc::ptr_eq(&arc_a, &arc_b), (None, None) => true, // Both dropped, consider equal _ => false, // One dropped, one alive } }, // WeakBox vs Box comparison (upgrade weak, then compare) (NyashValue::WeakBox(weak), NyashValue::Box(strong)) => { if let Some(upgraded) = weak.upgrade() { Arc::ptr_eq(&upgraded, strong) } else { false // Dropped weak ref != any strong ref } }, (NyashValue::Box(strong), NyashValue::WeakBox(weak)) => { if let Some(upgraded) = weak.upgrade() { Arc::ptr_eq(strong, &upgraded) } else { false // Dropped weak ref != any strong ref } }, // Everything else is not equal _ => false, } } } impl Display for NyashValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.to_string()) } } impl Debug for NyashValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { NyashValue::Integer(n) => write!(f, "Integer({})", n), NyashValue::Float(fl) => write!(f, "Float({})", fl), NyashValue::Bool(b) => write!(f, "Bool({})", b), NyashValue::String(s) => write!(f, "String(\"{}\")", s), NyashValue::Array(_) => write!(f, "Array([...])"), NyashValue::Map(_) => write!(f, "Map({{...}})"), NyashValue::Box(_) => write!(f, "Box(...)"), NyashValue::WeakBox(weak_ref) => { if weak_ref.upgrade().is_some() { write!(f, "WeakBox(alive)") } else { write!(f, "WeakBox(dropped)") } }, NyashValue::Null => write!(f, "Null"), NyashValue::Void => write!(f, "Void"), } } } /// Legacy Box compatibility - convert from existing Box types impl NyashValue { /// Convert from a legacy NyashBox to NyashValue pub fn from_box(nyash_box: Arc>) -> Self { // Try to extract primitive values for better performance if let Ok(guard) = nyash_box.try_lock() { let type_name = guard.type_name(); let string_rep = guard.to_string_box().value; // Convert common types to direct values match type_name { "IntegerBox" => { if let Ok(value) = string_rep.parse::() { return NyashValue::Integer(value); } }, "FloatBox" => { if let Ok(value) = string_rep.parse::() { return NyashValue::Float(value); } }, "BoolBox" => { if let Ok(value) = string_rep.parse::() { return NyashValue::Bool(value); } }, "StringBox" => { return NyashValue::String(string_rep); }, "NullBox" => { return NyashValue::Null; }, "VoidBox" => { return NyashValue::Void; }, _ => {} } } // Fallback to Box wrapper NyashValue::Box(nyash_box) } /// Convert back to a legacy NyashBox for compatibility pub fn to_box(&self) -> Result>, String> { use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox}; use crate::boxes::null_box::NullBox; match self { NyashValue::Integer(n) => { Ok(Arc::new(Mutex::new(IntegerBox::new(*n)))) }, NyashValue::Float(f) => { // Note: Need FloatBox implementation - for now convert to string Ok(Arc::new(Mutex::new(StringBox::new(f.to_string())))) }, NyashValue::Bool(b) => { Ok(Arc::new(Mutex::new(BoolBox::new(*b)))) }, NyashValue::String(s) => { Ok(Arc::new(Mutex::new(StringBox::new(s.clone())))) }, NyashValue::Null => { Ok(Arc::new(Mutex::new(NullBox::new()))) }, NyashValue::Void => { Ok(Arc::new(Mutex::new(VoidBox::new()))) }, NyashValue::Box(b) => { Ok(b.clone()) }, NyashValue::WeakBox(weak_ref) => { // Try to upgrade weak reference match weak_ref.upgrade() { Some(arc) => Ok(arc), None => Err("Cannot convert dropped weak reference to Box".to_string()), } }, _ => Err(format!("Cannot convert {} to legacy Box", self.type_name())), } } } /// Unified object creation system impl NyashValue { /// Create objects of different types with unified interface pub fn create_object(type_name: &str, args: Vec) -> Result { match type_name { "StringBox" => { let value = match args.get(0) { Some(arg) => arg.to_string(), None => String::new(), }; Ok(NyashValue::String(value)) }, "IntegerBox" => { let value = match args.get(0) { Some(arg) => arg.to_integer()?, None => 0, }; Ok(NyashValue::Integer(value)) }, "FloatBox" => { let value = match args.get(0) { Some(arg) => arg.to_float()?, None => 0.0, }; Ok(NyashValue::Float(value)) }, "BoolBox" => { let value = match args.get(0) { Some(arg) => arg.to_bool()?, None => false, }; Ok(NyashValue::Bool(value)) }, "ArrayBox" => { Ok(NyashValue::Array(Arc::new(Mutex::new(Vec::new())))) }, "MapBox" => { Ok(NyashValue::Map(Arc::new(Mutex::new(HashMap::new())))) }, _ => { Err(format!("Unknown object type: {}", type_name)) } } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_basic_creation() { let int_val = NyashValue::new_integer(42); let float_val = NyashValue::new_float(3.14); let bool_val = NyashValue::new_bool(true); let string_val = NyashValue::new_string("hello".to_string()); assert_eq!(int_val.to_string(), "42"); assert_eq!(float_val.to_string(), "3.14"); assert_eq!(bool_val.to_string(), "true"); assert_eq!(string_val.to_string(), "hello"); } #[test] fn test_type_conversion() { let int_val = NyashValue::new_integer(42); assert_eq!(int_val.to_float().unwrap(), 42.0); assert_eq!(int_val.to_bool().unwrap(), true); let float_val = NyashValue::new_float(3.14); assert_eq!(float_val.to_integer().unwrap(), 3); let zero_val = NyashValue::new_integer(0); assert_eq!(zero_val.to_bool().unwrap(), false); } #[test] fn test_cross_type_equality() { let int_val = NyashValue::new_integer(42); let float_val = NyashValue::new_float(42.0); assert_eq!(int_val, float_val); assert_eq!(float_val, int_val); } #[test] fn test_object_creation() { let string_obj = NyashValue::create_object("StringBox", vec![ NyashValue::new_string("test".to_string()) ]).unwrap(); assert_eq!(string_obj.to_string(), "test"); let int_obj = NyashValue::create_object("IntegerBox", vec![ NyashValue::new_integer(100) ]).unwrap(); assert_eq!(int_obj.to_integer().unwrap(), 100); } #[test] fn test_type_names() { assert_eq!(NyashValue::new_integer(1).type_name(), "Integer"); assert_eq!(NyashValue::new_float(1.0).type_name(), "Float"); assert_eq!(NyashValue::new_bool(true).type_name(), "Bool"); assert_eq!(NyashValue::new_string("".to_string()).type_name(), "String"); assert_eq!(NyashValue::new_null().type_name(), "Null"); assert_eq!(NyashValue::new_void().type_name(), "Void"); } #[test] fn test_weak_reference_basic() { use crate::box_trait::StringBox; // Create a strong reference let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("test".to_string())))); // Create weak reference let weak_ref = strong_ref.downgrade_to_weak().unwrap(); assert!(weak_ref.is_weak_reference()); assert_eq!(weak_ref.type_name(), "WeakBox"); // Upgrade should work let upgraded = weak_ref.upgrade_weak().unwrap(); assert_eq!(upgraded, strong_ref); // Both should be alive initially assert!(weak_ref.is_weak_alive()); } #[test] fn test_weak_reference_drop() { use crate::box_trait::StringBox; let weak_ref = { let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("test".to_string())))); strong_ref.downgrade_to_weak().unwrap() }; // strong_ref goes out of scope and is dropped // Weak reference should now be invalid assert!(!weak_ref.is_weak_alive()); assert!(weak_ref.upgrade_weak().is_none()); // to_bool should return false for dropped weak ref assert_eq!(weak_ref.to_bool().unwrap(), false); } #[test] fn test_weak_reference_equality() { use crate::box_trait::StringBox; let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("test".to_string())))); let weak_ref1 = strong_ref.downgrade_to_weak().unwrap(); let weak_ref2 = strong_ref.downgrade_to_weak().unwrap(); // Weak refs to same object should be equal assert_eq!(weak_ref1, weak_ref2); // Weak ref should equal its strong ref assert_eq!(weak_ref1, strong_ref); assert_eq!(strong_ref, weak_ref1); } #[test] fn test_weak_reference_string_representation() { use crate::box_trait::StringBox; let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("hello".to_string())))); let weak_ref = strong_ref.downgrade_to_weak().unwrap(); // Should show weak reference to the content assert!(weak_ref.to_string().contains("WeakRef")); assert!(weak_ref.to_string().contains("hello")); // After dropping strong ref drop(strong_ref); assert_eq!(weak_ref.to_string(), "WeakRef(null)"); } }