use crate::{KFunction, Ptr, Result, prelude::*};
use std::{
fmt::{self, Write},
result::Result as StdResult,
};
#[derive(Clone, Default)]
pub enum KValue {
#[default]
Null,
Bool(bool),
Number(KNumber),
Range(KRange),
List(KList),
Tuple(KTuple),
Map(KMap),
Str(KString),
Function(KFunction),
NativeFunction(KNativeFunction),
Iterator(KIterator),
Object(KObject),
TemporaryTuple(RegisterSlice),
}
impl KValue {
pub fn deep_copy(&self) -> Result<KValue> {
let result = match &self {
KValue::List(l) => {
let result = l
.data()
.iter()
.map(|v| v.deep_copy())
.collect::<Result<_>>()?;
KList::with_data(result).into()
}
KValue::Tuple(t) => {
let result = t
.iter()
.map(|v| v.deep_copy())
.collect::<Result<Vec<_>>>()?;
KValue::Tuple(result.into())
}
KValue::Map(m) => {
let data = m
.data()
.iter()
.map(|(k, v)| v.deep_copy().map(|v| (k.clone(), v)))
.collect::<Result<_>>()?;
let meta = m.meta_map().map(|meta| meta.borrow().clone());
KMap::with_contents(data, meta).into()
}
KValue::Iterator(i) => i.make_copy()?.into(),
KValue::Object(o) => o.try_borrow()?.copy().into(),
_ => self.clone(),
};
Ok(result)
}
pub fn is_callable(&self) -> bool {
use KValue::*;
match self {
Function(f) if f.flags.is_generator() => false,
Function(_) | NativeFunction(_) => true,
Map(m) => m.contains_meta_key(&MetaKey::Call),
Object(o) => o.try_borrow().is_ok_and(|o| o.is_callable()),
_ => false,
}
}
pub fn is_generator(&self) -> bool {
matches!(self, KValue::Function(f) if f.flags.is_generator())
}
pub fn is_hashable(&self) -> bool {
use KValue::*;
match self {
Null | Bool(_) | Number(_) | Range(_) | Str(_) => true,
Tuple(t) => t.is_hashable(),
_ => false,
}
}
pub fn is_indexable(&self) -> bool {
use KValue::*;
match self {
List(_) | Map(_) | Str(_) | Tuple(_) => true,
Object(o) => o.try_borrow().is_ok_and(|o| o.size().is_some()),
_ => false,
}
}
pub fn is_iterable(&self) -> bool {
use KValue::*;
match self {
Range(_) | List(_) | Tuple(_) | Str(_) | Iterator(_) => true,
Map(m) => {
if m.meta_map().is_some() {
m.contains_meta_key(&UnaryOp::Iterator.into())
|| m.contains_meta_key(&UnaryOp::Next.into())
} else {
true
}
}
Object(o) => o
.try_borrow()
.is_ok_and(|o| !matches!(o.is_iterable(), IsIterable::NotIterable)),
_ => false,
}
}
pub fn is_same_instance(&self, other: &Self) -> bool {
use KValue::*;
match (self, other) {
(Map(a), Map(b)) => a.is_same_instance(b),
(Object(a), Object(b)) => a.is_same_instance(b),
(List(a), List(b)) => a.is_same_instance(b),
(Tuple(a), Tuple(b)) => a.is_same_instance(b),
_ => false,
}
}
pub fn type_as_string(&self) -> KString {
use KValue::*;
match &self {
Null => TYPE_NULL.with(|x| x.clone()),
Bool(_) => TYPE_BOOL.with(|x| x.clone()),
Number(_) => TYPE_NUMBER.with(|x| x.clone()),
List(_) => TYPE_LIST.with(|x| x.clone()),
Range { .. } => TYPE_RANGE.with(|x| x.clone()),
Map(m) if m.meta_map().is_some() => m
.meta_type()
.unwrap_or_else(|| TYPE_OBJECT.with(|x| x.clone())),
Map(_) => TYPE_MAP.with(|x| x.clone()),
Str(_) => TYPE_STRING.with(|x| x.clone()),
Tuple(_) => TYPE_TUPLE.with(|x| x.clone()),
Function(f) if f.flags.is_generator() => TYPE_GENERATOR.with(|x| x.clone()),
Function(_) | NativeFunction(_) => TYPE_FUNCTION.with(|x| x.clone()),
Object(o) => o.try_borrow().map_or_else(
|_| "Error: object already borrowed".into(),
|o| o.type_string(),
),
Iterator(_) => TYPE_ITERATOR.with(|x| x.clone()),
TemporaryTuple { .. } => TYPE_TEMPORARY_TUPLE.with(|x| x.clone()),
}
}
pub fn display(&self, ctx: &mut DisplayContext) -> Result<()> {
use KValue::*;
let _ = match self {
Null => write!(ctx, "null"),
Bool(b) => write!(ctx, "{b}"),
Number(n) => write!(ctx, "{n}"),
Range(r) => write!(ctx, "{r}"),
Function(f) => {
if ctx.debug_enabled() {
write!(ctx, "|| (chunk: {}, ip: {})", Ptr::address(&f.chunk), f.ip)
} else {
write!(ctx, "||")
}
}
NativeFunction(f) => {
if ctx.debug_enabled() {
write!(ctx, "|| ({})", Ptr::address(&f.function))
} else {
write!(ctx, "||")
}
}
Iterator(_) => write!(ctx, "Iterator"),
TemporaryTuple(RegisterSlice { start, count }) => {
write!(ctx, "TemporaryTuple [{start}..{}]", start + count)
}
Str(s) => {
if ctx.is_contained() || ctx.debug_enabled() {
write!(ctx, "\'{s}\'")
} else {
write!(ctx, "{s}")
}
}
List(l) => return l.display(ctx),
Tuple(t) => return t.display(ctx),
Map(m) => return m.display(ctx),
Object(o) => return o.try_borrow()?.display(ctx),
};
Ok(())
}
}
thread_local! {
static TYPE_NULL: KString = "Null".into();
static TYPE_BOOL: KString = "Bool".into();
static TYPE_NUMBER: KString = "Number".into();
static TYPE_LIST: KString = "List".into();
static TYPE_RANGE: KString = "Range".into();
static TYPE_MAP: KString = "Map".into();
static TYPE_OBJECT: KString = "Object".into();
static TYPE_STRING: KString = "String".into();
static TYPE_TUPLE: KString = "Tuple".into();
static TYPE_FUNCTION: KString = "Function".into();
static TYPE_GENERATOR: KString = "Generator".into();
static TYPE_ITERATOR: KString = "Iterator".into();
static TYPE_TEMPORARY_TUPLE: KString = "TemporaryTuple".into();
}
impl fmt::Debug for KValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.type_as_string())
}
}
impl From<()> for KValue {
fn from(_: ()) -> Self {
Self::Null
}
}
impl From<bool> for KValue {
fn from(value: bool) -> Self {
Self::Bool(value)
}
}
impl From<KNumber> for KValue {
fn from(value: KNumber) -> Self {
Self::Number(value)
}
}
impl From<KRange> for KValue {
fn from(value: KRange) -> Self {
Self::Range(value)
}
}
impl From<&str> for KValue {
fn from(value: &str) -> Self {
Self::Str(value.into())
}
}
impl From<String> for KValue {
fn from(value: String) -> Self {
Self::Str(value.into())
}
}
impl From<KString> for KValue {
fn from(value: KString) -> Self {
Self::Str(value)
}
}
impl From<KList> for KValue {
fn from(value: KList) -> Self {
Self::List(value)
}
}
impl From<KTuple> for KValue {
fn from(value: KTuple) -> Self {
Self::Tuple(value)
}
}
impl From<KMap> for KValue {
fn from(value: KMap) -> Self {
Self::Map(value)
}
}
impl From<KObject> for KValue {
fn from(value: KObject) -> Self {
Self::Object(value)
}
}
impl From<KIterator> for KValue {
fn from(value: KIterator) -> Self {
Self::Iterator(value)
}
}
impl From<KNativeFunction> for KValue {
fn from(value: KNativeFunction) -> Self {
Self::NativeFunction(value)
}
}
impl<T> From<T> for KValue
where
T: KotoFunction,
{
fn from(value: T) -> Self {
Self::NativeFunction(KNativeFunction::new(value))
}
}
impl<T> From<Option<T>> for KValue
where
T: Into<KValue>,
{
fn from(value: Option<T>) -> Self {
match value {
Some(value) => value.into(),
None => KValue::Null,
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RegisterSlice {
pub start: u8,
pub count: u8,
}
impl TryFrom<KValue> for bool {
type Error = KValue;
fn try_from(value: KValue) -> StdResult<Self, KValue> {
if let KValue::Bool(b) = value {
Ok(b)
} else {
Err(value)
}
}
}
macro_rules! impl_try_from_value_string {
($($type:ty),+) => {
$(
impl TryFrom<KValue> for $type {
type Error = KValue;
fn try_from(value: KValue) -> StdResult<Self, KValue> {
if let KValue::Str(s) = value {
Ok(s.as_str().into())
} else {
Err(value)
}
}
}
)+
};
}
macro_rules! impl_try_from_value_string_ref {
($($type:ty),+) => {
$(
impl<'a> TryFrom<&'a KValue> for $type {
type Error = &'a KValue;
fn try_from(value: &'a KValue) -> StdResult<Self, &'a KValue> {
if let KValue::Str(s) = value {
Ok(s.as_str().into())
} else {
Err(value)
}
}
}
)+
};
}
macro_rules! impl_try_from_value_number {
($($type:ty),+) => {
$(
impl TryFrom<KValue> for $type {
type Error = KValue;
fn try_from(value: KValue) -> StdResult<Self, KValue> {
if let KValue::Number(n) = value {
Ok(n.into())
} else {
Err(value)
}
}
}
)+
};
}
impl_try_from_value_string!(String, Box<str>, std::rc::Rc<str>, std::sync::Arc<str>);
impl_try_from_value_string_ref!(&'a str, std::borrow::Cow<'a, str>);
impl_try_from_value_number!(
f32, f64, i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_mem_size() {
assert!(size_of::<KString>() <= 16);
assert!(size_of::<KList>() <= 16);
assert!(size_of::<KMap>() <= 16);
assert!(size_of::<KObject>() <= 16);
assert!(size_of::<KFunction>() <= 24);
assert!(size_of::<KValue>() <= 24);
}
#[test]
fn try_from_kvalue() {
assert_eq!(
&String::try_from(KValue::from("testing")).unwrap(),
"testing"
);
assert_eq!(i32::try_from(KValue::from(-123.45)).unwrap(), -123);
assert!(matches!(bool::try_from(KValue::Null), Err(KValue::Null)));
}
}