talc/talc-lang/src/value/mod.rs

333 lines
8.0 KiB
Rust

use std::any::Any;
use std::borrow::Cow;
use std::io;
use std::{cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, rc::Rc};
pub use num_complex::Complex64;
use num_complex::ComplexFloat;
pub use num_rational::Rational64;
use crate::lstring::{LStr, LString};
use crate::symbol::{Symbol, SYM_HASH_ERROR};
use crate::exception::{Exception, throw};
use self::{range::{Range, RangeType}, function::{Function, NativeFunc}};
pub mod function;
pub mod ops;
pub mod range;
pub mod index;
type RcList = Rc<RefCell<Vec<Value>>>;
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
#[derive(Clone, Debug, Default)]
pub enum Value {
#[default]
Nil,
Bool(bool),
Symbol(Symbol),
Range(Range),
Int(i64),
Float(f64),
Ratio(Rational64),
Complex(Complex64),
Cell(Rc<RefCell<Value>>),
String(Rc<LStr>),
List(RcList),
Table(RcTable),
Function(Rc<Function>),
NativeFunc(Rc<NativeFunc>),
Native(Rc<dyn NativeValue>),
}
pub trait NativeValue: std::fmt::Debug + Any {
fn get_type(&self) -> Symbol;
fn as_any(&self) -> &dyn Any;
fn to_lstring(&self, w: &mut LString, _repr: bool) -> io::Result<()> {
w.extend(b"<native value>");
Ok(())
}
fn partial_eq(&self, _other: &Value) -> bool {
false
}
fn copy_value(&self) -> Result<Option<Value>, Exception> {
Ok(None)
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = if f.alternate() { Cow::Owned(self.repr()) } else { self.str() };
write!(f, "{s}")
}
}
impl Value {
pub fn new_list(f: impl FnOnce(&mut Vec<Value>)) -> Self {
let mut list = Vec::new();
f(&mut list);
list.into()
}
pub fn new_table(f: impl FnOnce(&mut HashMap<HashValue, Value>)) -> Self {
let mut table = HashMap::new();
f(&mut table);
table.into()
}
pub fn write_to_lstring(&self, w: &mut LString, repr: bool) -> io::Result<()> {
use std::io::Write;
match self {
Self::Nil => write!(w, "nil"),
Self::Bool(b) => write!(w, "{b}"),
Self::Symbol(s) => {
let name = s.name();
w.push_byte(b':');
if name.is_identifier() {
w.extend(name.as_bytes());
} else {
write!(w, "{name:?}")?;
}
Ok(())
},
Self::Range(r) => match r.ty {
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
RangeType::Endless => write!(w, "{}..*", r.start),
},
Self::Int(i) => write!(w, "{i}"),
Self::Float(x) => write!(w, "{x:?}"),
Self::Ratio(r) => write!(w, "{}/{}", r.numer(), r.denom()),
Self::Complex(z) => {
write!(w, "{:?}", z.re())?;
if z.im() >= 0.0 || z.im.is_nan() {
w.push_byte(b'+');
}
write!(w, "{:?}i", z.im())
},
Self::Cell(v) if repr => {
w.write_all(b"cell(")?;
v.borrow().write_to_lstring(w, repr)?;
w.write_all(b")")
},
Self::Cell(v) => v.borrow().write_to_lstring(w, false),
Self::String(s) if repr => write!(w, "{s:?}"),
Self::String(s) => w.write_all(s.as_bytes()),
Self::List(l) => {
w.write_all(b"[")?;
for (i, item) in l.borrow().iter().enumerate() {
if i != 0 {
w.write_all(b", ")?;
}
item.write_to_lstring(w, true)?;
}
w.write_all(b"]")
},
Self::Table(t) => {
w.write_all(b"{ ")?;
for (i, (k, v)) in t.borrow().iter().enumerate() {
if i != 0 {
w.write_all(b", ")?;
}
k.0.write_table_key_repr(w)?;
w.write_all(b" = ")?;
v.write_to_lstring(w, true)?;
}
w.write_all(b" }")
},
Self::Function(g)
=> write!(w, "<function {:?}>", Rc::as_ptr(g)),
Self::NativeFunc(g)
=> write!(w, "<native function {:?}>", Rc::as_ptr(g)),
Self::Native(n) => n.to_lstring(w, repr),
}
}
fn write_table_key_repr(&self, w: &mut LString) -> std::io::Result<()> {
match self {
Self::Nil | Self::Bool(_)
| Self::Int(_) | Self::String(_)
=> self.write_to_lstring(w, true),
Self::Symbol(s) => {
let name = s.name();
if name.is_identifier() {
w.push_lstr(name);
Ok(())
} else {
self.write_to_lstring(w, true)
}
},
_ => {
w.push_byte(b'(');
self.write_to_lstring(w, true)?;
w.push_byte(b')');
Ok(())
}
}
}
pub fn str(&self) -> Cow<'_, LStr> {
if let Value::String(s) = self {
Cow::Borrowed(s)
} else {
let mut s = LString::new();
self.write_to_lstring(&mut s, false).expect("write_to_lstring failed");
Cow::Owned(s)
}
}
pub fn repr(&self) -> LString {
let mut s = LString::new();
self.write_to_lstring(&mut s, true).expect("write_to_lstring failed");
s
}
pub fn get_type(&self) -> Symbol {
use crate::symbol::*;
match self {
Value::Nil => *SYM_NIL,
Value::Bool(_) => *SYM_BOOL,
Value::Symbol(_) => *SYM_SYMBOL,
Value::Range(_) => *SYM_RANGE,
Value::Int(_) => *SYM_INT,
Value::Float(_) => *SYM_FLOAT,
Value::Ratio(_) => *SYM_RATIO,
Value::Complex(_) => *SYM_COMPLEX,
Value::Cell(_) => *SYM_CELL,
Value::String(_) => *SYM_STRING,
Value::List(_) => *SYM_LIST,
Value::Table(_) => *SYM_TABLE,
Value::Function(_) => *SYM_FUNCTION,
Value::NativeFunc(_) => *SYM_NATIVE_FUNC,
Value::Native(n) => n.get_type(),
}
}
pub fn hashable(&self) -> bool {
matches!(
self,
Value::Nil | Value::Bool(_) | Value::Symbol(_)
| Value::Int(_) | Value::Ratio(_) | Value::String(_)
)
}
pub fn downcast_native<T: NativeValue>(&self) -> Option<&T> {
match self {
Value::Native(n) => n.as_any().downcast_ref(),
_ => None
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct HashValue(Value);
impl Eq for HashValue {}
impl TryFrom<Value> for HashValue {
type Error = Exception;
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
if !value.hashable() {
throw!(*SYM_HASH_ERROR, "value {value:#} cannot be hashed")
}
Ok(Self(value))
}
}
impl HashValue {
pub fn into_inner(self) -> Value { self.0 }
pub fn inner(&self) -> &Value { &self.0 }
}
impl Hash for HashValue {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::mem::discriminant(&self.0).hash(state);
match &self.0 {
Value::Nil => (),
Value::Bool(b) => b.hash(state),
Value::Symbol(s) => s.hash(state),
Value::Int(n) => n.hash(state),
Value::Ratio(r) => r.hash(state),
Value::String(s) => s.hash(state),
_ => unreachable!(),
}
}
}
macro_rules! impl_from {
($ty:ty, $var:ident) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(value) }
}
};
($ty:ty, $var:ident, hash) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(value) }
}
impl From<$ty> for HashValue {
fn from(value: $ty) -> Self { Self(Value::$var(value)) }
}
};
($ty:ty, $var:ident, rc) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(Rc::new(value)) }
}
impl From<Rc<$ty>> for Value {
fn from(value: Rc<$ty>) -> Self { Self::$var(value) }
}
};
($ty:ty, $var:ident, rcref) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(Rc::new(RefCell::new(value))) }
}
impl From<RefCell<$ty>> for Value {
fn from(value: RefCell<$ty>) -> Self { Self::$var(Rc::new(value)) }
}
impl From<Rc<RefCell<$ty>>> for Value {
fn from(value: Rc<RefCell<$ty>>) -> Self { Self::$var(value) }
}
};
($ty:ty, $var:ident, into) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(value.into()) }
}
};
}
impl_from!(bool, Bool, hash);
impl_from!(Symbol, Symbol, hash);
impl_from!(Range, Range);
impl_from!(i64, Int, hash);
impl_from!(f64, Float);
impl_from!(Rational64, Ratio, hash);
impl_from!(Complex64, Complex);
impl_from!(HashMap<HashValue, Value>, Table, rcref);
impl_from!(Vec<Value>, List, rcref);
impl_from!(Function, Function, rc);
impl_from!(NativeFunc, NativeFunc, rc);
impl_from!(Rc<LStr>, String);
impl_from!(LString, String, into);
impl_from!(&LStr, String, into);
impl_from!(Box<LStr>, String, into);
impl_from!(Cow<'_, LStr>, String, into);
impl_from!(RefCell<Value>, Cell, rc);
impl From<Rc<dyn NativeValue>> for Value {
fn from(value: Rc<dyn NativeValue>) -> Self {
Self::Native(value)
}
}
impl<T: NativeValue + 'static> From<T> for Value {
fn from(value: T) -> Self {
Self::Native(Rc::new(value))
}
}