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

394 lines
13 KiB
Rust

use std::{cell::RefCell, cmp::Ordering, ops::{Add, Div, Mul, Neg, Sub}, rc::Rc};
use num_complex::Complex64;
use num_rational::Rational64;
use crate::{exception::{throw, Result}, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR}, value::range::RangeType, Vm};
use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value};
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::Cell(v) => v.borrow().truthy(),
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)]
pub 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 => throw!(*SYM_TYPE_ERROR, "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) => throw!(*SYM_TYPE_ERROR, "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) => throw!(*SYM_TYPE_ERROR, "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) => throw!(*SYM_TYPE_ERROR, "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(0)) => throw!(*SYM_TYPE_ERROR, "integer division by 0"),
(_, V::Ratio(r)) if *r.numer() == 0 && *r.denom() != 0
=> throw!(*SYM_TYPE_ERROR, "integer division by 0"),
(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) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}")
}
}
}
#[allow(clippy::cast_sign_loss)]
fn ipow(n: i64, p: u64) -> Result<i64> {
match (n, p) {
(0, 0) => throw!(*SYM_TYPE_ERROR, "integer 0 raised to power 0"),
(0, _) => Ok(0),
(_, 0) => Ok(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));
Ok(a * b.pow(0x1_0000).pow(0x1_0000))
}
(n, p) => Ok(n.pow(p as u32)),
}
}
#[allow(clippy::cast_sign_loss)]
fn rpow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
Ok(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) => throw!(*SYM_TYPE_ERROR, "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) => throw!(*SYM_TYPE_ERROR, "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)) if y >= 0 => Ok(V::Int(ipow(x, y as u64)?)),
(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) => throw!(*SYM_TYPE_ERROR, "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 super::range::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::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
(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) -> Result<Ordering> {
match self.partial_cmp(other) {
Some(o) => Ok(o),
None => throw!(*SYM_TYPE_ERROR, "cannot compare {self:#} and {other:#}"),
}
}
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(l.into())
},
(V::String(s1), V::String(s2)) => {
let mut s = s1.as_ref().to_owned();
s.push_str(s2);
Ok(V::String(s.into()))
}
(V::Table(t1), V::Table(t2)) => {
let mut t = t1.borrow().clone();
t.extend(t2.borrow().iter().map(|(k, v)| (k.clone(), v.clone())));
Ok(t.into())
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot concatenate {l:#} and {r:#}"),
}
}
pub fn append(&self, val: Self) -> Result<Self> {
use Value as V;
match self {
V::List(list) => {
let mut l = list.borrow().clone();
l.push(val);
Ok(l.into())
},
lhs => throw!(*SYM_TYPE_ERROR, "cannot append to {lhs:#}"),
}
}
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(Range { start: *start, stop: *stop, ty }.into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot create range between {self:#} and {other:#}")
}
}
pub fn range_endless(&self) -> Result<Self> {
if let Value::Int(start) = self {
Ok(Range { start: *start, stop: 0, ty: RangeType::Endless }.into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
}
}
pub fn to_cell(self) -> Self {
Value::Cell(Rc::new(RefCell::new(self)))
}
pub fn iter_unpack(self) -> Option<Self> {
match self {
Self::Symbol(s) if s == *SYM_END_ITERATION => None,
v => Some(v),
}
}
pub fn iter_pack(v: Option<Self>) -> Self {
match v {
Some(v) => v,
None => Value::from(*SYM_END_ITERATION)
}
}
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 |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(range_iter.borrow_mut().next()
.map(Value::from)))
};
Ok(NativeFunc::new(Box::new(f), 0).into())
},
Self::String(s) => {
let byte_pos = RefCell::new(0);
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
let pos = *byte_pos.borrow();
if let Some(v) = &s[pos..].chars().next() {
*byte_pos.borrow_mut() += v.len_utf8();
Ok(Value::from(v.to_string()))
} else {
Ok(Value::from(*SYM_END_ITERATION))
}
};
Ok(NativeFunc::new(Box::new(f), 0).into())
},
Self::List(list) => {
let idx = RefCell::new(0);
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
let i = *idx.borrow();
if let Some(v) = list.borrow().get(i) {
*idx.borrow_mut() += 1;
Ok(v.clone())
} else {
Ok(Value::from(*SYM_END_ITERATION))
}
};
Ok(NativeFunc::new(Box::new(f), 0).into())
},
Self::Table(table) => {
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
let keys = RefCell::new(keys.into_iter());
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(keys.borrow_mut().next()
.map(HashValue::into_inner)))
};
Ok(NativeFunc::new(Box::new(f), 0).into())
},
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
}
}
pub fn func_attrs(&self) -> Option<FuncAttrs> {
match self {
Value::Function(f) => Some(f.attrs),
Value::NativeFunc(f) => Some(f.attrs),
_ => None,
}
}
}