#[cfg(feature = "no_std")]
use alloc::{boxed::Box, format, rc::Rc, string::{String, ToString}, vec::Vec};
#[cfg(not(feature = "no_std"))]
use std::rc::Rc;
use core::cell::RefCell;
use crate::memory::{bop_alloc, bop_dealloc};
use crate::parser::Stmt;
#[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 struct BopStruct {
module_path: String,
type_name: String,
fields: Vec<(String, Value)>,
}
#[derive(Debug)]
pub struct BopEnumVariant {
module_path: String,
type_name: String,
variant: String,
payload: EnumPayload,
}
pub const BUILTIN_MODULE_PATH: &str = "<builtin>";
pub const ROOT_MODULE_PATH: &str = "<root>";
#[derive(Debug)]
pub enum EnumPayload {
Unit,
Tuple(Vec<Value>),
Struct(Vec<(String, Value)>),
}
pub struct BopFn {
pub params: Vec<String>,
pub captures: Vec<(String, Value)>,
pub body: FnBody,
pub self_name: Option<String>,
}
pub enum FnBody {
Ast(Vec<Stmt>),
Compiled(Rc<dyn core::any::Any + 'static>),
}
impl core::fmt::Debug for BopFn {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BopFn")
.field("params", &self.params)
.field("captures", &self.captures.len())
.field("body", &self.body)
.field("self_name", &self.self_name)
.finish()
}
}
impl core::fmt::Debug for FnBody {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
FnBody::Ast(stmts) => write!(f, "Ast({} stmts)", stmts.len()),
FnBody::Compiled(_) => write!(f, "Compiled(<opaque>)"),
}
}
}
#[derive(Debug)]
pub enum Value {
Int(i64),
Number(f64),
Str(BopStr),
Bool(bool),
None,
Array(BopArray),
Dict(BopDict),
Fn(Rc<BopFn>),
Struct(Box<BopStruct>),
EnumVariant(Box<BopEnumVariant>),
Module(Rc<BopModule>),
Iter(Rc<RefCell<BopIter>>),
}
#[derive(Debug)]
pub enum BopIter {
Array { items: Vec<Value>, pos: usize },
String { chars: Vec<char>, pos: usize },
Dict { keys: Vec<String>, pos: usize },
}
impl BopIter {
pub fn next(&mut self) -> Option<Value> {
match self {
BopIter::Array { items, pos } => {
if *pos < items.len() {
let v = items[*pos].clone();
*pos += 1;
Some(v)
} else {
None
}
}
BopIter::String { chars, pos } => {
if *pos < chars.len() {
let v = Value::new_str(chars[*pos].to_string());
*pos += 1;
Some(v)
} else {
None
}
}
BopIter::Dict { keys, pos } => {
if *pos < keys.len() {
let v = Value::new_str(keys[*pos].clone());
*pos += 1;
Some(v)
} else {
None
}
}
}
}
}
impl Drop for BopIter {
fn drop(&mut self) {
match self {
BopIter::Array { items, .. } => {
bop_dealloc(items.capacity() * core::mem::size_of::<Value>());
}
BopIter::String { chars, .. } => {
bop_dealloc(chars.capacity() * core::mem::size_of::<char>());
}
BopIter::Dict { keys, .. } => {
let key_bytes: usize = keys.iter().map(|k| k.capacity()).sum();
bop_dealloc(
keys.capacity() * core::mem::size_of::<String>() + key_bytes,
);
}
}
}
}
#[derive(Debug)]
pub struct BopModule {
pub path: String,
pub bindings: Vec<(String, Value)>,
pub types: Vec<String>,
}
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))
}
pub fn new_struct(
module_path: String,
type_name: String,
fields: Vec<(String, Value)>,
) -> Self {
let key_bytes: usize = fields.iter().map(|(k, _)| k.capacity()).sum();
bop_alloc(
module_path.capacity()
+ type_name.capacity()
+ fields.capacity() * core::mem::size_of::<(String, Value)>()
+ key_bytes,
);
Value::Struct(Box::new(BopStruct {
module_path,
type_name,
fields,
}))
}
pub fn new_array_iter(items: Vec<Value>) -> Self {
bop_alloc(items.capacity() * core::mem::size_of::<Value>());
Value::Iter(Rc::new(RefCell::new(BopIter::Array { items, pos: 0 })))
}
pub fn new_string_iter(chars: Vec<char>) -> Self {
bop_alloc(chars.capacity() * core::mem::size_of::<char>());
Value::Iter(Rc::new(RefCell::new(BopIter::String { chars, pos: 0 })))
}
pub fn new_dict_iter(keys: Vec<String>) -> Self {
let key_bytes: usize = keys.iter().map(|k| k.capacity()).sum();
bop_alloc(keys.capacity() * core::mem::size_of::<String>() + key_bytes);
Value::Iter(Rc::new(RefCell::new(BopIter::Dict { keys, pos: 0 })))
}
pub fn new_enum_unit(module_path: String, type_name: String, variant: String) -> Self {
bop_alloc(module_path.capacity() + type_name.capacity() + variant.capacity());
Value::EnumVariant(Box::new(BopEnumVariant {
module_path,
type_name,
variant,
payload: EnumPayload::Unit,
}))
}
pub fn new_enum_tuple(
module_path: String,
type_name: String,
variant: String,
items: Vec<Value>,
) -> Self {
bop_alloc(
module_path.capacity()
+ type_name.capacity()
+ variant.capacity()
+ items.capacity() * core::mem::size_of::<Value>(),
);
Value::EnumVariant(Box::new(BopEnumVariant {
module_path,
type_name,
variant,
payload: EnumPayload::Tuple(items),
}))
}
pub fn new_enum_struct(
module_path: String,
type_name: String,
variant: String,
fields: Vec<(String, Value)>,
) -> Self {
let key_bytes: usize = fields.iter().map(|(k, _)| k.capacity()).sum();
bop_alloc(
module_path.capacity()
+ type_name.capacity()
+ variant.capacity()
+ fields.capacity() * core::mem::size_of::<(String, Value)>()
+ key_bytes,
);
Value::EnumVariant(Box::new(BopEnumVariant {
module_path,
type_name,
variant,
payload: EnumPayload::Struct(fields),
}))
}
pub fn new_fn(
params: Vec<String>,
captures: Vec<(String, Value)>,
body: Vec<Stmt>,
self_name: Option<String>,
) -> Self {
Value::Fn(Rc::new(BopFn {
params,
captures,
body: FnBody::Ast(body),
self_name,
}))
}
pub fn new_compiled_fn(
params: Vec<String>,
captures: Vec<(String, Value)>,
body: Rc<dyn core::any::Any + 'static>,
self_name: Option<String>,
) -> Self {
Value::Fn(Rc::new(BopFn {
params,
captures,
body: FnBody::Compiled(body),
self_name,
}))
}
}
impl Clone for Value {
fn clone(&self) -> Self {
match self {
Value::Int(n) => Value::Int(*n),
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))
}
Value::Struct(s) => {
let cloned_mp = s.module_path.clone();
let cloned_tn = s.type_name.clone();
let cloned_fields = s.fields.clone();
let key_bytes: usize =
cloned_fields.iter().map(|(k, _)| k.capacity()).sum();
bop_alloc(
cloned_mp.capacity()
+ cloned_tn.capacity()
+ cloned_fields.capacity()
* core::mem::size_of::<(String, Value)>()
+ key_bytes,
);
Value::Struct(Box::new(BopStruct {
module_path: cloned_mp,
type_name: cloned_tn,
fields: cloned_fields,
}))
}
Value::Fn(f) => Value::Fn(Rc::clone(f)),
Value::Module(m) => Value::Module(Rc::clone(m)),
Value::Iter(it) => Value::Iter(Rc::clone(it)),
Value::EnumVariant(e) => {
let mp = e.module_path.clone();
let tn = e.type_name.clone();
let vn = e.variant.clone();
let base = mp.capacity() + tn.capacity() + vn.capacity();
let payload = match &e.payload {
EnumPayload::Unit => {
bop_alloc(base);
EnumPayload::Unit
}
EnumPayload::Tuple(items) => {
let cloned = items.clone();
bop_alloc(
base + cloned.capacity() * core::mem::size_of::<Value>(),
);
EnumPayload::Tuple(cloned)
}
EnumPayload::Struct(fields) => {
let cloned = fields.clone();
let key_bytes: usize =
cloned.iter().map(|(k, _)| k.capacity()).sum();
bop_alloc(
base
+ cloned.capacity()
* core::mem::size_of::<(String, Value)>()
+ key_bytes,
);
EnumPayload::Struct(cloned)
}
};
Value::EnumVariant(Box::new(BopEnumVariant {
module_path: mp,
type_name: tn,
variant: vn,
payload,
}))
}
}
}
}
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);
}
Value::Struct(s) => {
let key_bytes: usize = s.fields.iter().map(|(k, _)| k.capacity()).sum();
bop_dealloc(
s.module_path.capacity()
+ s.type_name.capacity()
+ s.fields.capacity()
* core::mem::size_of::<(String, Value)>()
+ key_bytes,
);
}
Value::EnumVariant(e) => {
let base = e.module_path.capacity()
+ e.type_name.capacity()
+ e.variant.capacity();
match &e.payload {
EnumPayload::Unit => bop_dealloc(base),
EnumPayload::Tuple(items) => bop_dealloc(
base + items.capacity() * core::mem::size_of::<Value>(),
),
EnumPayload::Struct(fields) => {
let key_bytes: usize =
fields.iter().map(|(k, _)| k.capacity()).sum();
bop_dealloc(
base
+ fields.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::Int(n) => write!(f, "{}", n),
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, "}}")
}
Value::Fn(func) => match &func.self_name {
Some(name) => write!(f, "<fn {}>", name),
None => write!(f, "<fn>"),
},
Value::Module(m) => write!(f, "<module {}>", m.path),
Value::Iter(it) => {
match it.try_borrow() {
Ok(inner) => match &*inner {
BopIter::Array { items, pos } => {
write!(f, "<iter array {}/{}>", pos, items.len())
}
BopIter::String { chars, pos } => {
write!(f, "<iter string {}/{}>", pos, chars.len())
}
BopIter::Dict { keys, pos } => {
write!(f, "<iter dict {}/{}>", pos, keys.len())
}
},
Err(_) => write!(f, "<iter>"),
}
}
Value::Struct(s) => {
write!(f, "{} {{", s.type_name)?;
for (i, (k, v)) in s.fields.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {}: {}", k, v.inspect())?;
}
if !s.fields.is_empty() {
write!(f, " ")?;
}
write!(f, "}}")
}
Value::EnumVariant(e) => match &e.payload {
EnumPayload::Unit => write!(f, "{}::{}", e.type_name, e.variant),
EnumPayload::Tuple(items) => {
write!(f, "{}::{}(", e.type_name, e.variant)?;
for (i, v) in items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v.inspect())?;
}
write!(f, ")")
}
EnumPayload::Struct(fields) => {
write!(f, "{}::{} {{", e.type_name, e.variant)?;
for (i, (k, v)) in fields.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {}: {}", k, v.inspect())?;
}
if !fields.is_empty() {
write!(f, " ")?;
}
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::Int(_) => "int",
Value::Number(_) => "number",
Value::Str(_) => "string",
Value::Bool(_) => "bool",
Value::None => "none",
Value::Array(_) => "array",
Value::Dict(_) => "dict",
Value::Fn(_) => "fn",
Value::Struct(_) => "struct",
Value::EnumVariant(_) => "enum",
Value::Module(_) => "module",
Value::Iter(_) => "iter",
}
}
pub fn display_type_name(&self) -> String {
match self {
Value::Struct(s) => s.type_name.clone(),
Value::EnumVariant(e) => e.type_name.clone(),
other => other.type_name().to_string(),
}
}
pub fn is_truthy(&self) -> bool {
match self {
Value::Bool(b) => *b,
Value::None => false,
Value::Int(n) => *n != 0,
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(),
Value::Fn(_) => true,
Value::Struct(_) => true,
Value::EnumVariant(_) => true,
Value::Module(_) => true,
Value::Iter(_) => true,
}
}
}
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 BopStruct {
pub fn type_name(&self) -> &str {
&self.type_name
}
pub fn module_path(&self) -> &str {
&self.module_path
}
pub fn fields(&self) -> &[(String, Value)] {
&self.fields
}
pub fn field(&self, name: &str) -> Option<&Value> {
self.fields.iter().find(|(k, _)| k == name).map(|(_, v)| v)
}
pub fn set_field(&mut self, name: &str, value: Value) -> bool {
if let Some(entry) = self.fields.iter_mut().find(|(k, _)| k == name) {
entry.1 = value;
true
} else {
false
}
}
}
impl BopEnumVariant {
pub fn type_name(&self) -> &str {
&self.type_name
}
pub fn module_path(&self) -> &str {
&self.module_path
}
pub fn variant(&self) -> &str {
&self.variant
}
pub fn payload(&self) -> &EnumPayload {
&self.payload
}
pub fn field(&self, name: &str) -> Option<&Value> {
match &self.payload {
EnumPayload::Struct(fields) => {
fields.iter().find(|(k, _)| k == name).map(|(_, v)| v)
}
_ => None,
}
}
}
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::Int(x), Value::Int(y)) => x == y,
(Value::Number(x), Value::Number(y)) => x == y,
(Value::Int(x), Value::Number(y)) => (*x as f64) == *y,
(Value::Number(x), Value::Int(y)) => *x == (*y as f64),
(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))
})
}
(Value::Fn(a), Value::Fn(b)) => Rc::ptr_eq(a, b),
(Value::Struct(a), Value::Struct(b)) => {
a.module_path == b.module_path
&& a.type_name == b.type_name
&& a.fields.len() == b.fields.len()
&& a.fields
.iter()
.zip(b.fields.iter())
.all(|((ka, va), (kb, vb))| ka == kb && values_equal(va, vb))
}
(Value::EnumVariant(a), Value::EnumVariant(b)) => {
a.module_path == b.module_path
&& a.type_name == b.type_name
&& a.variant == b.variant
&& match (&a.payload, &b.payload) {
(EnumPayload::Unit, EnumPayload::Unit) => true,
(EnumPayload::Tuple(ax), EnumPayload::Tuple(bx)) => {
ax.len() == bx.len()
&& ax
.iter()
.zip(bx.iter())
.all(|(x, y)| values_equal(x, y))
}
(EnumPayload::Struct(af), EnumPayload::Struct(bf)) => {
af.len() == bf.len()
&& af
.iter()
.zip(bf.iter())
.all(|((ka, va), (kb, vb))| {
ka == kb && values_equal(va, vb)
})
}
_ => false,
}
}
_ => false,
}
}