use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone)]
pub enum Value {
Int(i64),
Float(f64),
Bool(bool),
String(String),
Char(char),
Vec(Vec<Value>),
Struct {
type_name: String,
fields: HashMap<String, Value>,
},
Enum {
type_name: String,
variant: String,
data: EnumData,
},
Function(FunctionValue),
Nil,
Unit,
Map(HashMap<String, Value>),
Tuple(Vec<Value>),
}
#[derive(Debug, Clone)]
pub enum EnumData {
Unit,
Tuple(Vec<Value>),
Struct(HashMap<String, Value>),
}
#[derive(Debug, Clone)]
pub struct FunctionValue {
pub name: String,
pub params: Vec<String>,
pub body_id: usize,
}
impl Value {
pub fn is_truthy(&self) -> bool {
match self {
Value::Bool(b) => *b,
Value::Int(n) => *n != 0,
Value::Float(f) => *f != 0.0,
Value::String(s) => !s.is_empty(),
Value::Nil => false,
Value::Unit => false,
_ => true,
}
}
pub fn type_name(&self) -> &str {
match self {
Value::Int(_) => "int",
Value::Float(_) => "float",
Value::Bool(_) => "bool",
Value::String(_) => "string",
Value::Char(_) => "char",
Value::Vec(_) => "Vec",
Value::Struct { type_name, .. } => type_name,
Value::Enum { type_name, .. } => type_name,
Value::Function(_) => "function",
Value::Nil => "nil",
Value::Unit => "()",
Value::Map(_) => "HashMap",
Value::Tuple(_) => "tuple",
}
}
pub fn as_int(&self) -> Option<i64> {
match self {
Value::Int(n) => Some(*n),
Value::Float(f) => Some(*f as i64),
Value::Bool(b) => Some(if *b { 1 } else { 0 }),
_ => None,
}
}
pub fn as_float(&self) -> Option<f64> {
match self {
Value::Float(f) => Some(*f),
Value::Int(n) => Some(*n as f64),
_ => None,
}
}
pub fn to_display_string(&self) -> String {
match self {
Value::Int(n) => n.to_string(),
Value::Float(f) => {
let s = f.to_string();
if s.contains('.') {
s
} else {
format!("{}.0", s)
}
}
Value::Bool(b) => b.to_string(),
Value::String(s) => s.clone(),
Value::Char(c) => c.to_string(),
Value::Vec(items) => {
let strs: Vec<String> = items.iter().map(|v| v.to_display_string()).collect();
format!("[{}]", strs.join(", "))
}
Value::Struct { type_name, fields } => {
let field_strs: Vec<String> = fields
.iter()
.map(|(k, v)| format!("{}: {}", k, v.to_display_string()))
.collect();
format!("{} {{ {} }}", type_name, field_strs.join(", "))
}
Value::Enum { variant, data, .. } => match data {
EnumData::Unit => variant.clone(),
EnumData::Tuple(vals) => {
let strs: Vec<String> = vals.iter().map(|v| v.to_display_string()).collect();
format!("{}({})", variant, strs.join(", "))
}
EnumData::Struct(fields) => {
let strs: Vec<String> = fields
.iter()
.map(|(k, v)| format!("{}: {}", k, v.to_display_string()))
.collect();
format!("{} {{ {} }}", variant, strs.join(", "))
}
},
Value::Function(f) => format!("<fn {}>", f.name),
Value::Nil => "None".to_string(),
Value::Unit => "()".to_string(),
Value::Map(m) => {
let strs: Vec<String> = m
.iter()
.map(|(k, v)| format!("{}: {}", k, v.to_display_string()))
.collect();
format!("{{{}}}", strs.join(", "))
}
Value::Tuple(items) => {
let strs: Vec<String> = items.iter().map(|v| v.to_display_string()).collect();
format!("({})", strs.join(", "))
}
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_display_string())
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Int(a), Value::Int(b)) => a == b,
(Value::Float(a), Value::Float(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::String(a), Value::String(b)) => a == b,
(Value::Char(a), Value::Char(b)) => a == b,
(Value::Nil, Value::Nil) => true,
(Value::Unit, Value::Unit) => true,
_ => false,
}
}
}