use crate::frontend::ast::Expr;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct DataFrameColumn {
pub name: String,
pub values: Vec<Value>,
}
#[derive(Clone, Debug)]
pub enum Value {
Integer(i64),
Float(f64),
Bool(bool),
Byte(u8),
Nil,
Atom(String),
String(Arc<str>),
Array(Arc<[Value]>),
Tuple(Arc<[Value]>),
Closure {
params: Vec<(String, Option<Arc<Expr>>)>, body: Arc<Expr>,
env: Rc<RefCell<HashMap<String, Value>>>, },
DataFrame { columns: Vec<DataFrameColumn> },
Object(Arc<HashMap<String, Value>>),
ObjectMut(Arc<std::sync::Mutex<HashMap<String, Value>>>),
Range {
start: Box<Value>,
end: Box<Value>,
inclusive: bool,
},
EnumVariant {
enum_name: String, variant_name: String, data: Option<Vec<Value>>,
},
BuiltinFunction(String),
Struct {
name: String,
fields: Arc<HashMap<String, Value>>,
},
Class {
class_name: String,
fields: Arc<std::sync::RwLock<HashMap<String, Value>>>,
methods: Arc<HashMap<String, Value>>, },
#[cfg(not(target_arch = "wasm32"))]
HtmlDocument(crate::stdlib::html::HtmlDocument),
#[cfg(not(target_arch = "wasm32"))]
HtmlElement(crate::stdlib::html::HtmlElement),
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Integer(a), Value::Integer(b)) => a == b,
(Value::Float(a), Value::Float(b)) => a == b,
(Value::String(a), Value::String(b)) => a == b,
(Value::Atom(a), Value::Atom(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Array(a), Value::Array(b)) => a == b,
(Value::Tuple(a), Value::Tuple(b)) => a == b,
(Value::Object(a), Value::Object(b)) => Arc::ptr_eq(a, b) || **a == **b,
(Value::ObjectMut(a), Value::ObjectMut(b)) => Arc::ptr_eq(a, b), (
Value::Struct {
name: n1,
fields: f1,
},
Value::Struct {
name: n2,
fields: f2,
},
) => {
n1 == n2 && **f1 == **f2 }
(Value::Class { fields: f1, .. }, Value::Class { fields: f2, .. }) => {
Arc::ptr_eq(f1, f2) }
(Value::Nil, Value::Nil) => true,
(Value::Byte(a), Value::Byte(b)) => a == b,
#[cfg(not(target_arch = "wasm32"))]
(Value::HtmlDocument(_), Value::HtmlDocument(_)) => false, #[cfg(not(target_arch = "wasm32"))]
(Value::HtmlElement(_), Value::HtmlElement(_)) => false, _ => false, }
}
}
impl Value {
pub fn type_id(&self) -> std::any::TypeId {
use std::any::TypeId;
match self {
Value::Integer(_) => TypeId::of::<i64>(),
Value::Float(_) => TypeId::of::<f64>(),
Value::Bool(_) => TypeId::of::<bool>(),
Value::Byte(_) => TypeId::of::<u8>(),
Value::String(_) => TypeId::of::<String>(),
Value::Atom(_) => TypeId::of::<crate::frontend::lexer::Token>(), Value::Nil => TypeId::of::<()>(),
Value::Array(_) => TypeId::of::<Vec<Value>>(),
Value::Tuple(_) => TypeId::of::<(Value,)>(), Value::Closure { .. } => TypeId::of::<fn()>(), Value::DataFrame { .. } => TypeId::of::<DataFrameColumn>(),
Value::Object(_) => TypeId::of::<HashMap<String, Value>>(),
Value::ObjectMut(_) => TypeId::of::<HashMap<String, Value>>(),
Value::Range { .. } => TypeId::of::<std::ops::Range<i64>>(),
Value::EnumVariant { .. } => TypeId::of::<(String, Option<Vec<Value>>)>(),
Value::BuiltinFunction(_) => TypeId::of::<fn()>(),
Value::Struct { .. } => TypeId::of::<HashMap<String, Value>>(),
Value::Class { .. } => TypeId::of::<HashMap<String, Value>>(),
#[cfg(not(target_arch = "wasm32"))]
Value::HtmlDocument(_) => TypeId::of::<crate::stdlib::html::HtmlDocument>(),
#[cfg(not(target_arch = "wasm32"))]
Value::HtmlElement(_) => TypeId::of::<crate::stdlib::html::HtmlElement>(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_integer_equality() {
assert_eq!(Value::Integer(42), Value::Integer(42));
assert_ne!(Value::Integer(42), Value::Integer(43));
}
#[test]
fn test_value_float_equality() {
assert_eq!(Value::Float(3.14), Value::Float(3.14));
assert_ne!(Value::Float(3.14), Value::Float(2.71));
}
#[test]
fn test_value_bool_equality() {
assert_eq!(Value::Bool(true), Value::Bool(true));
assert_eq!(Value::Bool(false), Value::Bool(false));
assert_ne!(Value::Bool(true), Value::Bool(false));
}
#[test]
fn test_value_nil_equality() {
assert_eq!(Value::Nil, Value::Nil);
}
#[test]
fn test_value_byte_equality() {
assert_eq!(Value::Byte(255), Value::Byte(255));
assert_ne!(Value::Byte(0), Value::Byte(1));
}
#[test]
fn test_value_string_equality() {
let s1 = Value::String(Arc::from("hello"));
let s2 = Value::String(Arc::from("hello"));
let s3 = Value::String(Arc::from("world"));
assert_eq!(s1, s2);
assert_ne!(s1, s3);
}
#[test]
fn test_value_atom_equality() {
assert_eq!(
Value::Atom("foo".to_string()),
Value::Atom("foo".to_string())
);
assert_ne!(
Value::Atom("foo".to_string()),
Value::Atom("bar".to_string())
);
}
#[test]
fn test_value_array_equality() {
let a1 = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let a2 = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let a3 = Value::Array(Arc::from(vec![Value::Integer(3)]));
assert_eq!(a1, a2);
assert_ne!(a1, a3);
}
#[test]
fn test_value_tuple_equality() {
let t1 = Value::Tuple(Arc::from(vec![Value::Integer(1), Value::Bool(true)]));
let t2 = Value::Tuple(Arc::from(vec![Value::Integer(1), Value::Bool(true)]));
assert_eq!(t1, t2);
}
#[test]
fn test_value_object_equality() {
let mut map1 = HashMap::new();
map1.insert("a".to_string(), Value::Integer(1));
let o1 = Value::Object(Arc::new(map1.clone()));
let o2 = Value::Object(Arc::new(map1));
assert_eq!(o1, o2);
}
#[test]
fn test_value_object_mut_identity() {
let map = HashMap::new();
let o1 = Value::ObjectMut(Arc::new(std::sync::Mutex::new(map.clone())));
let o2 = Value::ObjectMut(Arc::new(std::sync::Mutex::new(map)));
assert_ne!(o1, o2);
}
#[test]
fn test_value_struct_equality() {
let mut fields = HashMap::new();
fields.insert("x".to_string(), Value::Integer(10));
let s1 = Value::Struct {
name: "Point".to_string(),
fields: Arc::new(fields.clone()),
};
let s2 = Value::Struct {
name: "Point".to_string(),
fields: Arc::new(fields),
};
assert_eq!(s1, s2);
}
#[test]
fn test_value_class_identity() {
let fields1 = Arc::new(std::sync::RwLock::new(HashMap::new()));
let fields2 = Arc::new(std::sync::RwLock::new(HashMap::new()));
let methods = Arc::new(HashMap::new());
let c1 = Value::Class {
class_name: "MyClass".to_string(),
fields: fields1.clone(),
methods: methods.clone(),
};
let c2 = Value::Class {
class_name: "MyClass".to_string(),
fields: fields2,
methods: methods.clone(),
};
let c3 = Value::Class {
class_name: "MyClass".to_string(),
fields: fields1,
methods: methods,
};
assert_ne!(c1, c2);
assert_eq!(c1, c3);
}
#[test]
fn test_value_different_variants_not_equal() {
assert_ne!(Value::Integer(42), Value::Float(42.0));
assert_ne!(Value::Bool(true), Value::Integer(1));
assert_ne!(Value::Nil, Value::Integer(0));
}
#[test]
fn test_value_type_id() {
let int_val = Value::Integer(42);
let float_val = Value::Float(3.14);
let bool_val = Value::Bool(true);
assert_ne!(int_val.type_id(), float_val.type_id());
assert_ne!(float_val.type_id(), bool_val.type_id());
assert_eq!(int_val.type_id(), Value::Integer(0).type_id());
}
#[test]
fn test_dataframe_column_clone() {
let col = DataFrameColumn {
name: "test".to_string(),
values: vec![Value::Integer(1), Value::Integer(2)],
};
let cloned = col.clone();
assert_eq!(cloned.name, "test");
assert_eq!(cloned.values.len(), 2);
}
}