#[cfg(not(feature = "std"))]
use alloc::{format, string::{String, ToString}, vec::Vec};
use crate::memory::{bop_alloc, bop_dealloc};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct BopStr(String);
#[derive(Debug)]
pub struct BopArray(Vec<Value>);
#[derive(Debug)]
pub struct BopDict(Vec<(String, Value)>);
#[derive(Debug)]
pub enum Value {
Number(f64),
Str(BopStr),
Bool(bool),
None,
Array(BopArray),
Dict(BopDict),
}
impl Value {
pub fn new_str(s: String) -> Self {
bop_alloc(s.capacity());
Value::Str(BopStr(s))
}
pub fn new_array(items: Vec<Value>) -> Self {
bop_alloc(items.capacity() * core::mem::size_of::<Value>());
Value::Array(BopArray(items))
}
pub fn new_dict(entries: Vec<(String, Value)>) -> Self {
let key_bytes: usize = entries.iter().map(|(k, _)| k.capacity()).sum();
bop_alloc(entries.capacity() * core::mem::size_of::<(String, Value)>() + key_bytes);
Value::Dict(BopDict(entries))
}
}
impl Clone for Value {
fn clone(&self) -> Self {
match self {
Value::Number(n) => Value::Number(*n),
Value::Bool(b) => Value::Bool(*b),
Value::None => Value::None,
Value::Str(s) => {
let cloned = s.0.clone();
bop_alloc(cloned.capacity());
Value::Str(BopStr(cloned))
}
Value::Array(arr) => {
let cloned = arr.0.clone(); bop_alloc(cloned.capacity() * core::mem::size_of::<Value>());
Value::Array(BopArray(cloned))
}
Value::Dict(d) => {
let cloned = d.0.clone(); let key_bytes: usize = cloned.iter().map(|(k, _)| k.capacity()).sum();
bop_alloc(cloned.capacity() * core::mem::size_of::<(String, Value)>() + key_bytes);
Value::Dict(BopDict(cloned))
}
}
}
}
impl Drop for Value {
fn drop(&mut self) {
match self {
Value::Str(s) => bop_dealloc(s.0.capacity()),
Value::Array(arr) => {
bop_dealloc(arr.0.capacity() * core::mem::size_of::<Value>());
}
Value::Dict(d) => {
let key_bytes: usize = d.0.iter().map(|(k, _)| k.capacity()).sum();
bop_dealloc(d.0.capacity() * core::mem::size_of::<(String, Value)>() + key_bytes);
}
_ => {}
}
}
}
impl core::fmt::Display for Value {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Value::Number(n) => {
if *n == (*n as i64 as f64) && *n - *n == 0.0 {
write!(f, "{}", *n as i64)
} else {
write!(f, "{}", n)
}
}
Value::Str(s) => write!(f, "{}", s.0),
Value::Bool(b) => write!(f, "{}", b),
Value::None => write!(f, "none"),
Value::Array(items) => {
write!(f, "[")?;
for (i, item) in items.0.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item.inspect())?;
}
write!(f, "]")
}
Value::Dict(entries) => {
write!(f, "{{")?;
for (i, (k, v)) in entries.0.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "\"{}\": {}", k, v.inspect())?;
}
write!(f, "}}")
}
}
}
}
impl core::fmt::Display for BopStr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Value {
pub fn inspect(&self) -> String {
match self {
Value::Str(s) => format!("\"{}\"", s.0),
other => format!("{}", other),
}
}
pub fn type_name(&self) -> &'static str {
match self {
Value::Number(_) => "number",
Value::Str(_) => "string",
Value::Bool(_) => "bool",
Value::None => "none",
Value::Array(_) => "array",
Value::Dict(_) => "dict",
}
}
pub fn is_truthy(&self) -> bool {
match self {
Value::Bool(b) => *b,
Value::None => false,
Value::Number(n) => *n != 0.0,
Value::Str(s) => !s.0.is_empty(),
Value::Array(a) => !a.0.is_empty(),
Value::Dict(d) => !d.0.is_empty(),
}
}
}
impl BopStr {
pub fn as_str(&self) -> &str {
&self.0
}
}
impl core::ops::Deref for BopStr {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl core::ops::Deref for BopArray {
type Target = [Value];
fn deref(&self) -> &[Value] {
&self.0
}
}
impl core::ops::Deref for BopDict {
type Target = [(String, Value)];
fn deref(&self) -> &[(String, Value)] {
&self.0
}
}
impl BopArray {
pub fn take(&mut self) -> Vec<Value> {
let taken = core::mem::take(&mut self.0);
bop_dealloc(taken.capacity() * core::mem::size_of::<Value>());
taken
}
pub fn set(&mut self, index: usize, val: Value) {
self.0[index] = val;
}
}
impl BopDict {
pub fn set_key(&mut self, key: &str, val: Value) {
if let Some(entry) = self.0.iter_mut().find(|(k, _)| k == key) {
entry.1 = val;
} else {
let old_cap = self.0.capacity();
let key = key.to_string();
bop_alloc(key.capacity());
self.0.push((key, val));
let new_cap = self.0.capacity();
if new_cap > old_cap {
bop_alloc((new_cap - old_cap) * core::mem::size_of::<(String, Value)>());
}
}
}
}
pub fn values_equal(a: &Value, b: &Value) -> bool {
match (a, b) {
(Value::Number(x), Value::Number(y)) => x == y,
(Value::Str(x), Value::Str(y)) => x == y,
(Value::Bool(x), Value::Bool(y)) => x == y,
(Value::None, Value::None) => true,
(Value::Array(x), Value::Array(y)) => {
x.len() == y.len() && x.iter().zip(y.iter()).all(|(a, b)| values_equal(a, b))
}
(Value::Dict(x), Value::Dict(y)) => {
x.len() == y.len()
&& x.iter().all(|(k, v)| {
y.iter()
.find(|(k2, _)| k2 == k)
.is_some_and(|(_, v2)| values_equal(v, v2))
})
}
_ => false,
}
}