573 lines
16 KiB
Rust
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}"),
|
|
}
|
|
}
|
|
}
|