talc/talc-lang/src/value.rs

573 lines
16 KiB
Rust

use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::{Add, Sub, Mul, Div, Neg};
use std::cmp::Ordering;
use std::rc::Rc;
use std::hash::Hash;
use anyhow::bail;
use num_rational::Rational64;
use num_complex::Complex64;
use anyhow::{anyhow, Result};
use crate::chunk::Chunk;
use crate::symbol::Symbol;
type RcList = Rc<RefCell<Vec<Value>>>;
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
#[derive(Debug)]
pub struct Function {
pub arity: usize,
pub chunk: Chunk,
}
pub struct NativeFunc {
pub arity: usize,
pub f: Box<dyn Fn(Value, Vec<Value>) -> Result<Value>>,
}
impl std::fmt::Debug for NativeFunc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NativeFunc")
.field("arity", &self.arity)
.finish_non_exhaustive()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct HashValue(Value);
impl Eq for HashValue {}
impl TryFrom<Value> for HashValue {
type Error = anyhow::Error;
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
match value {
Value::Nil
| Value::Bool(_)
| Value::Symbol(_)
| Value::Int(_)
| Value::Ratio(_)
| Value::String(_) => Ok(Self(value)),
_ => bail!("value {} cannot be hashed", value),
}
}
}
impl HashValue {
pub fn into_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!(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RangeType {
Open, Closed, Endless,
}
#[derive(Clone, Copy, Debug)]
pub struct Range {
start: i64,
stop: i64,
ty: RangeType,
}
impl Range {
pub fn len(&self) -> Option<usize> {
match self.ty {
RangeType::Open => Some((self.stop - self.start).max(0) as usize),
RangeType::Closed => Some((self.stop - self.start + 1).max(0) as usize),
RangeType::Endless => None,
}
}
pub fn is_empty(&self) -> bool {
match self.ty {
RangeType::Open => self.stop - self.start <= 0,
RangeType::Closed => self.stop - self.start < 0,
RangeType::Endless => false,
}
}
}
impl IntoIterator for Range {
type Item = i64;
type IntoIter = Box<dyn Iterator<Item=i64>>;
fn into_iter(self) -> Self::IntoIter {
match self.ty {
RangeType::Open => Box::new(self.start..self.stop),
RangeType::Closed => Box::new(self.start..=self.stop),
RangeType::Endless => Box::new(self.start..),
}
}
}
#[derive(Clone, Debug)]
pub enum Value {
Nil,
Bool(bool),
Symbol(Symbol),
Range(Range),
Int(i64),
Float(f64),
Ratio(Rational64),
Complex(Complex64),
String(Rc<str>),
List(RcList),
Table(RcTable),
Function(Rc<Function>),
NativeFunc(Rc<NativeFunc>),
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Nil => write!(f, "nil"),
Value::Bool(b) => write!(f, "{b}"),
Value::Symbol(s) => write!(f, ":{}", s.name()),
Value::Range(r) => match r.ty {
RangeType::Open => write!(f, "{}..{}", r.start, r.stop),
RangeType::Closed => write!(f, "{}..={}", r.start, r.stop),
RangeType::Endless => write!(f, "{}..*", r.start),
},
Value::Int(n) => write!(f, "{n}"),
Value::Float(x) => write!(f, "{x:?}"),
Value::Ratio(r) => write!(f, "{}/{}", r.numer(), r.denom()),
Value::Complex(z) => write!(f, "{z}"),
Value::String(s) => write!(f, "{s}"),
Value::List(l) => {
write!(f, "[")?;
for (i, item) in l.borrow().iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "{item}")?;
}
write!(f, "]")
},
Value::Table(t) => {
write!(f, "{{ ")?;
for (i, (k, v)) in t.borrow().iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "({}) = {v}", k.0)?;
}
write!(f, " }}")
},
Value::Function(_) => write!(f, "<function>"),
Value::NativeFunc(_) => write!(f, "<native function>"),
}
}
}
impl Value {
pub fn truthy(&self) -> bool {
match self {
Value::Nil => false,
Value::Bool(b) => *b,
Value::Range(r) => matches!(r.len(), None | Some(1..)),
Value::Int(n) => *n != 0,
Value::Float(x) => *x != 0.0 && !x.is_nan(),
Value::Ratio(r) => !(*r.numer() == 0 && *r.denom() != 0),
Value::Complex(c) => !(c.re == 0.0 && c.im == 0.0 || c.is_nan()),
Value::String(s) => s.len() > 0,
Value::List(l) => l.borrow().len() > 0,
Value::Symbol(_) | Value::Table(_)
| Value::Function(_) | Value::NativeFunc(_) => true,
}
}
}
#[allow(clippy::cast_precision_loss)]
fn ratio_to_f64(r: Rational64) -> f64 {
*r.numer() as f64 / *r.denom() as f64
}
#[allow(clippy::cast_precision_loss)]
fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V;
match (&a, &b) {
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
(V::Ratio(x), V::Float(..)) => (V::Float(ratio_to_f64(*x)), b),
(V::Ratio(x), V::Complex(..)) => (V::Complex(ratio_to_f64(*x).into()), b),
(V::Float(x), V::Complex(..)) => (V::Complex((*x).into()), b),
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
(V::Float(..), V::Ratio(y)) => (a, V::Float(ratio_to_f64(*y))),
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(ratio_to_f64(*y).into())),
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
_ => (a, b),
}
}
impl Neg for Value {
type Output = Result<Self>;
fn neg(self) -> Self::Output {
use Value as V;
match self {
V::Int(x) => Ok(V::Int(-x)),
V::Ratio(x) => Ok(V::Ratio(-x)),
V::Float(x) => Ok(V::Float(-x)),
V::Complex(x) => Ok(V::Complex(-x)),
a => Err(anyhow!("cannot negate {a}"))
}
}
}
impl Add<Value> for Value {
type Output = Result<Self>;
fn add(self, rhs: Value) -> Self::Output {
use Value as V;
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x + y)),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x + y)),
(V::Float(x), V::Float(y)) => Ok(V::Float(x + y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x + y)),
(l, r) => Err(anyhow!("cannot add {l:?} and {r:?}"))
}
}
}
impl Sub<Value> for Value {
type Output = Result<Self>;
fn sub(self, rhs: Value) -> Self::Output {
use Value as V;
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x - y)),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x - y)),
(V::Float(x), V::Float(y)) => Ok(V::Float(x - y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x - y)),
(l, r) => Err(anyhow!("cannot subtract {l:?} and {r:?}"))
}
}
}
impl Mul<Value> for Value {
type Output = Result<Self>;
fn mul(self, rhs: Value) -> Self::Output {
use Value as V;
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x * y)),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x * y)),
(V::Float(x), V::Float(y)) => Ok(V::Float(x * y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x * y)),
(l, r) => Err(anyhow!("cannot multiply {l:?} and {r:?}"))
}
}
}
impl Div<Value> for Value {
type Output = Result<Self>;
fn div(self, rhs: Value) -> Self::Output {
use Value as V;
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Ratio(Rational64::new(x, y))),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x / y)),
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
(l, r) => Err(anyhow!("cannot divide {l:?} and {r:?}"))
}
}
}
#[allow(clippy::cast_sign_loss)]
const fn ipow(n: i64, p: u64) -> i64 {
match (n, p) {
(0, 0) => panic!("power 0^0"),
(0, _) => 0,
(_, 0) => 1,
(n, p) if p > u32::MAX as u64 => {
let (lo, hi) = (p as u32, (p >> 32) as u32);
let (a, b) = (n.pow(lo), n.pow(hi));
a * b.pow(0x1_0000).pow(0x1_0000)
}
(n, p) => n.pow(p as u32),
}
}
#[allow(clippy::cast_sign_loss)]
const fn rpow(n: i64, d: i64, p: i64) -> (i64, i64) {
match p {
0.. => (ipow(n, p as u64), ipow(d, p as u64)),
_ => (ipow(d, (-p) as u64), ipow(n, (-p) as u64)),
}
}
impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value as V;
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x.rem_euclid(y))),
(V::Ratio(_x), V::Ratio(_y)) => todo!("ratio modulo"),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(y))),
(V::Complex(_x), V::Complex(_y)) => todo!("complex modulo"),
(l, r) => Err(anyhow!("cannot modulo {l:?} and {r:?}"))
}
}
pub fn int_div(self, rhs: Value) -> Result<Self> {
use Value as V;
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x.div_euclid(y))),
(V::Ratio(_x), V::Ratio(_y)) => todo!("ratio integer division"),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(y))),
(V::Complex(_x), V::Complex(_y)) => todo!("complex integer division"),
(l, r) => Err(anyhow!("cannot integer divide {l:?} and {r:?}"))
}
}
pub fn pow(self, rhs: Value) -> Result<Self> {
use Value as V;
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
return Ok(V::Ratio(rpow(*(*x).numer(), *(*x).denom(), *y).into()));
}
match promote(self, rhs) {
(V::Int(x), V::Int(y))
=> Ok(V::Ratio(rpow(x, 1, y).into())),
(V::Float(x), V::Float(y))
=> Ok(V::Float(x.powf(y))),
(V::Ratio(x), V::Ratio(y))
=> Ok(V::Float(ratio_to_f64(x).powf(ratio_to_f64(y)))),
(V::Complex(x), V::Complex(y))
=> Ok(V::Complex(x.powc(y))),
(l, r) => Err(anyhow!("cannot exponentiate {l:?} and {r:?}"))
}
}
}
impl PartialEq for Value {
#[allow(clippy::cast_precision_loss)]
fn eq(&self, other: &Self) -> bool {
use Value as V;
use RangeType as Rty;
match (self, other) {
(V::Nil, V::Nil) => true,
(V::Bool(a), V::Bool(b)) => a == b,
(V::Int(a), V::Int(b)) => *a == *b,
(V::Ratio(a), V::Ratio(b)) => *a == *b,
(V::Float(a), V::Float(b)) => *a == *b,
(V::Complex(a), V::Complex(b)) => *a == *b,
(V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b,
(V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b),
(V::Int(a), V::Float(b)) => *a as f64 == *b,
(V::Float(a), V::Int(b)) => *a == *b as f64,
(V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b,
(V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64),
(V::Ratio(a), V::Float(b)) => ratio_to_f64(*a) == *b,
(V::Float(a), V::Ratio(b)) => *a == ratio_to_f64(*b),
(V::Ratio(a), V::Complex(b)) => Complex64::from(ratio_to_f64(*a)) == *b,
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(ratio_to_f64(*b)),
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
(V::Complex(a), V::Float(b)) => *a == Complex64::from(*b),
(V::String(a), V::String(b)) => *a == *b,
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
(V::Symbol(a), V::Symbol(b)) => a == b,
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
(Rty::Open, Rty::Open) => a.start == b.start && a.stop == b.stop,
(Rty::Closed, Rty::Closed) => a.start == b.start && a.stop == b.stop,
(Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1,
(Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop,
(Rty::Endless, Rty::Endless) => a.start == b.start,
_ => false,
}
_ => false,
}
}
}
impl PartialOrd for Value {
#[allow(clippy::cast_precision_loss)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
use Value as V;
match (self, other) {
(V::Nil, V::Nil) => Some(Ordering::Equal),
(V::Bool(a), V::Bool(b)) => a.partial_cmp(b),
(V::Int(a), V::Int(b)) => a.partial_cmp(b),
(V::Ratio(a), V::Ratio(b)) => a.partial_cmp(b),
(V::Float(a), V::Float(b)) => a.partial_cmp(b),
(V::Int(a), V::Ratio(b)) => Rational64::from(*a).partial_cmp(b),
(V::Ratio(a), V::Int(b)) => a.partial_cmp(&Rational64::from(*b)),
(V::Int(a), V::Float(b)) => (*a as f64).partial_cmp(b),
(V::Float(a), V::Int(b)) => a.partial_cmp(&(*b as f64)),
(V::Ratio(a), V::Float(b)) => ratio_to_f64(*a).partial_cmp(b),
(V::Float(a), V::Ratio(b)) => a.partial_cmp(&ratio_to_f64(*b)),
(V::String(a), V::String(b)) => a.partial_cmp(b),
(V::List(a), V::List(b)) => a.borrow().partial_cmp(&*b.borrow()),
(V::Symbol(a), V::Symbol(b)) => a.partial_cmp(b),
_ => None,
}
}
}
impl Value {
pub fn val_cmp(&self, other: &Self) -> anyhow::Result<Ordering> {
match self.partial_cmp(other) {
Some(o) => Ok(o),
None => Err(anyhow!("cannot compare {self:?} and {other:?}")),
}
}
pub fn index(&self, idx: Self) -> Result<Self> {
use Value as V;
match (self, idx) {
(_lhs, V::List(_l)) => todo!("assign index with list"),
(_lhs, V::Range(_r)) => todo!("assign index with range"),
(V::List(l), V::Int(i)) => {
let l = l.borrow();
if i >= 0 && (i as usize) < l.len() {
Ok(l[i as usize].clone())
} else {
Err(anyhow!("index {i} out of bounds for list of length {}", l.len()))
}
},
(V::Range(r), V::Int(i)) => {
if i >= 0 && (
r.ty == RangeType::Endless
|| i < r.stop
|| (r.ty == RangeType::Closed && i == r.stop)
) {
Ok(Value::Int(r.start + i))
} else {
Err(anyhow!("index {i} out of bounds for range {}", self))
}
},
(V::Table(t), i) => {
let t = t.borrow();
let i = i.try_into()?;
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
},
(lhs, rhs) => Err(anyhow!("cannot assign to index {lhs} with {rhs}"))
}
}
pub fn store_index(&self, idx: Self, val: Self) -> Result<()> {
use Value as V;
match (self, idx) {
(V::List(l), V::Int(i)) => {
let mut l = l.borrow_mut();
if i >= 0 && (i as usize) < l.len() {
l[i as usize] = val;
Ok(())
} else {
Err(anyhow!("index {i} out of bounds for list of length {}", l.len()))
}
},
(V::Table(t), i) => {
let mut t = t.borrow_mut();
let i = i.try_into()?;
t.insert(i, val);
Ok(())
},
(l, r) => Err(anyhow!("cannot index {l:?} with {r:?}"))
}
}
pub fn concat(&self, other: &Self) -> Result<Self> {
use Value as V;
match (self, other) {
(V::List(l1), V::List(l2)) => {
let mut l = l1.borrow().clone();
l.extend_from_slice(&l2.borrow());
Ok(V::List(Rc::new(RefCell::new(l))))
},
(V::String(s1), V::String(s2)) => {
let mut s = s1.as_ref().to_owned();
s.push_str(s2);
Ok(V::String(s.into()))
}
(l, r) => Err(anyhow!("cannot concatenate {l:?} and {r:?}"))
}
}
pub fn range(&self, other: &Self, closed: bool) -> Result<Self> {
if let (Value::Int(start), Value::Int(stop)) = (self, other) {
let ty = if closed { RangeType::Closed } else { RangeType::Open };
Ok(Value::Range(Range { start: *start, stop: *stop, ty }))
} else {
bail!("cannot create range between {self} and {other}")
}
}
pub fn range_endless(&self) -> Result<Self> {
if let Value::Int(start) = self {
Ok(Value::Range(Range { start: *start, stop: 0, ty: RangeType::Endless }))
} else {
bail!("cannot create endless range from {self}")
}
}
pub fn to_iter_function(self) -> Result<Self> {
match self {
Self::Function(_) | Self::NativeFunc(_) => Ok(self),
Self::Range(range) => {
let range_iter = RefCell::new(range.into_iter());
let f = move |_, _| {
if let Some(v) = range_iter.borrow_mut().next() {
Ok(Value::Int(v))
} else {
Ok(Value::Nil)
}
};
Ok(Value::NativeFunc(Rc::new(NativeFunc {
arity: 0,
f: Box::new(f),
})))
},
Self::List(list) => {
let idx = RefCell::new(0);
let f = move |_, _| {
let i = *idx.borrow();
if let Some(v) = list.borrow().get(i) {
*idx.borrow_mut() += 1;
Ok(v.clone())
} else {
Ok(Value::Nil)
}
};
Ok(Value::NativeFunc(Rc::new(NativeFunc {
arity: 0,
f: Box::new(f),
})))
},
Self::Table(table) => {
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
let keys = RefCell::new(keys.into_iter());
let f = move |_, _| {
if let Some(v) = keys.borrow_mut().next() {
Ok(v.into_inner())
} else {
Ok(Value::Nil)
}
};
Ok(Value::NativeFunc(Rc::new(NativeFunc {
arity: 0,
f: Box::new(f),
})))
},
_ => bail!("cannot iterate {self}"),
}
}
}