use crate::program::BodyHash;
use indexmap::IndexMap;
use std::collections::{BTreeMap, BTreeSet, VecDeque};
#[derive(Debug, Clone)]
pub enum Value {
Int(i64),
Float(f64),
Bool(bool),
Str(String),
Bytes(Vec<u8>),
Unit,
List(Vec<Value>),
Tuple(Vec<Value>),
Record(IndexMap<String, Value>),
Variant { name: String, args: Vec<Value> },
Closure { fn_id: u32, body_hash: BodyHash, captures: Vec<Value> },
F64Array { rows: u32, cols: u32, data: Vec<f64> },
Map(BTreeMap<MapKey, Value>),
Set(BTreeSet<MapKey>),
Deque(VecDeque<Value>),
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
(Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(Bool(a), Bool(b)) => a == b,
(Str(a), Str(b)) => a == b,
(Bytes(a), Bytes(b)) => a == b,
(Unit, Unit) => true,
(List(a), List(b)) => a == b,
(Tuple(a), Tuple(b)) => a == b,
(Record(a), Record(b)) => a == b,
(Variant { name: an, args: aa }, Variant { name: bn, args: ba }) =>
an == bn && aa == ba,
(Closure { body_hash: ah, captures: ac, .. },
Closure { body_hash: bh, captures: bc, .. }) =>
ah == bh && ac == bc,
(F64Array { rows: ar, cols: ac, data: ad },
F64Array { rows: br, cols: bc, data: bd }) =>
ar == br && ac == bc && ad == bd,
(Map(a), Map(b)) => a == b,
(Set(a), Set(b)) => a == b,
(Deque(a), Deque(b)) => a == b,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MapKey {
Str(String),
Int(i64),
}
impl MapKey {
pub fn from_value(v: &Value) -> Result<Self, String> {
match v {
Value::Str(s) => Ok(MapKey::Str(s.clone())),
Value::Int(n) => Ok(MapKey::Int(*n)),
other => Err(format!(
"map/set key must be Str or Int, got {other:?}")),
}
}
pub fn into_value(self) -> Value {
match self {
MapKey::Str(s) => Value::Str(s),
MapKey::Int(n) => Value::Int(n),
}
}
pub fn as_value(&self) -> Value {
match self {
MapKey::Str(s) => Value::Str(s.clone()),
MapKey::Int(n) => Value::Int(*n),
}
}
}
impl Value {
pub fn as_int(&self) -> i64 {
match self { Value::Int(n) => *n, other => panic!("expected Int, got {other:?}") }
}
pub fn as_float(&self) -> f64 {
match self { Value::Float(n) => *n, other => panic!("expected Float, got {other:?}") }
}
pub fn as_bool(&self) -> bool {
match self { Value::Bool(b) => *b, other => panic!("expected Bool, got {other:?}") }
}
pub fn as_str(&self) -> &str {
match self { Value::Str(s) => s, other => panic!("expected Str, got {other:?}") }
}
pub fn to_json(&self) -> serde_json::Value {
use serde_json::Value as J;
match self {
Value::Int(n) => J::from(*n),
Value::Float(f) => J::from(*f),
Value::Bool(b) => J::Bool(*b),
Value::Str(s) => J::String(s.clone()),
Value::Bytes(b) => {
let hex: String = b.iter().map(|b| format!("{:02x}", b)).collect();
let mut m = serde_json::Map::new();
m.insert("$bytes".into(), J::String(hex));
J::Object(m)
}
Value::Unit => J::Null,
Value::List(items) => J::Array(items.iter().map(Value::to_json).collect()),
Value::Tuple(items) => J::Array(items.iter().map(Value::to_json).collect()),
Value::Record(fields) => {
let mut m = serde_json::Map::new();
for (k, v) in fields { m.insert(k.clone(), v.to_json()); }
J::Object(m)
}
Value::Variant { name, args } => {
let mut m = serde_json::Map::new();
m.insert("$variant".into(), J::String(name.clone()));
m.insert("args".into(), J::Array(args.iter().map(Value::to_json).collect()));
J::Object(m)
}
Value::Closure { body_hash, .. } => {
let prefix: String = body_hash.iter().take(4)
.map(|b| format!("{b:02x}")).collect();
J::String(format!("<closure {prefix}>"))
}
Value::F64Array { rows, cols, data } => {
let mut m = serde_json::Map::new();
m.insert("$f64_array".into(), J::Bool(true));
m.insert("rows".into(), J::from(*rows));
m.insert("cols".into(), J::from(*cols));
m.insert("data".into(), J::Array(data.iter().map(|f| J::from(*f)).collect()));
J::Object(m)
}
Value::Map(m) => {
let all_str = m.keys().all(|k| matches!(k, MapKey::Str(_)));
if all_str {
let mut out = serde_json::Map::new();
for (k, v) in m {
if let MapKey::Str(s) = k {
out.insert(s.clone(), v.to_json());
}
}
J::Object(out)
} else {
J::Array(m.iter().map(|(k, v)| {
J::Array(vec![k.as_value().to_json(), v.to_json()])
}).collect())
}
}
Value::Set(s) => J::Array(
s.iter().map(|k| k.as_value().to_json()).collect()),
Value::Deque(items) => J::Array(items.iter().map(Value::to_json).collect()),
}
}
pub fn from_json(v: &serde_json::Value) -> Value {
use serde_json::Value as J;
match v {
J::Null => Value::Unit,
J::Bool(b) => Value::Bool(*b),
J::Number(n) => {
if let Some(i) = n.as_i64() { Value::Int(i) }
else if let Some(f) = n.as_f64() { Value::Float(f) }
else { Value::Unit }
}
J::String(s) => Value::Str(s.clone()),
J::Array(items) => Value::List(items.iter().map(Value::from_json).collect()),
J::Object(map) => {
if let (Some(J::String(name)), Some(J::Array(args))) =
(map.get("$variant"), map.get("args"))
{
return Value::Variant {
name: name.clone(),
args: args.iter().map(Value::from_json).collect(),
};
}
if map.len() == 1 {
if let Some(J::String(hex)) = map.get("$bytes") {
if let Some(bytes) = decode_hex(hex) {
return Value::Bytes(bytes);
}
}
}
let mut out = indexmap::IndexMap::new();
for (k, v) in map {
out.insert(k.clone(), Value::from_json(v));
}
Value::Record(out)
}
}
}
}
fn decode_hex(s: &str) -> Option<Vec<u8>> {
if !s.len().is_multiple_of(2) { return None; }
let mut out = Vec::with_capacity(s.len() / 2);
let bytes = s.as_bytes();
for pair in bytes.chunks(2) {
let hi = (pair[0] as char).to_digit(16)?;
let lo = (pair[1] as char).to_digit(16)?;
out.push(((hi << 4) | lo) as u8);
}
Some(out)
}