use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Debug, Clone)]
pub enum Value {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Rc<RefCell<Vec<Value>>>),
Object(Rc<RefCell<HashMap<String, Value>>>),
Function {
params: Vec<String>,
body: Vec<crate::iris::parser::Stmt>,
closure: Rc<RefCell<HashMap<String, Value>>>,
},
Builtin(fn(&[Value]) -> Result<Value, String>),
Return(Box<Value>),
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Null, Value::Null) => true,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Number(a), Value::Number(b)) => a == b,
(Value::String(a), Value::String(b)) => a == b,
(Value::Array(a), Value::Array(b)) => a == b,
(Value::Object(a), Value::Object(b)) => a == b,
(
Value::Function { params: ap, body: ab, closure: ac },
Value::Function { params: bp, body: bb, closure: bc },
) => ap == bp && ab == bb && ac == bc,
(Value::Builtin(a), Value::Builtin(b)) => std::ptr::fn_addr_eq(*a, *b),
(Value::Return(a), Value::Return(b)) => a == b,
_ => false,
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Null => write!(f, "null"),
Value::Bool(b) => write!(f, "{}", b),
Value::Number(n) => {
if n.fract() == 0.0 {
write!(f, "{:.0}", n)
} else {
write!(f, "{}", n)
}
}
Value::String(s) => write!(f, "{}", s),
Value::Array(arr) => {
let arr = arr.borrow();
let items: Vec<String> = arr.iter().map(|v| v.to_string()).collect();
write!(f, "[{}]", items.join(", "))
}
Value::Object(obj) => {
let obj = obj.borrow();
let items: Vec<String> = obj
.iter()
.map(|(k, v)| format!("{}: {}", k, v))
.collect();
write!(f, "{{{}}}", items.join(", "))
}
Value::Function { .. } => write!(f, "<function>"),
Value::Builtin(_) => write!(f, "<builtin function>"),
Value::Return(v) => write!(f, "{}", v),
}
}
}
impl Value {
pub fn is_truthy(&self) -> bool {
match self {
Value::Null => false,
Value::Bool(b) => *b,
Value::Number(n) => *n != 0.0,
Value::String(s) => !s.is_empty(),
_ => true,
}
}
pub fn type_name(&self) -> &'static str {
match self {
Value::Null => "null",
Value::Bool(_) => "bool",
Value::Number(_) => "number",
Value::String(_) => "string",
Value::Array(_) => "array",
Value::Object(_) => "object",
Value::Function { .. } | Value::Builtin(_) => "function",
Value::Return(_) => "return",
}
}
}