842 lines
21 KiB
Rust
842 lines
21 KiB
Rust
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
|
|
|
use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{cell_take, exception::Result, function::NativeFunc, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm};
|
|
use talc_macros::native_func;
|
|
|
|
use crate::{unpack_args, unpack_varargs};
|
|
|
|
pub fn load(vm: &mut Vm) {
|
|
// begin
|
|
vm.set_global_name("iter", iter().into());
|
|
vm.set_global_name("pairs", pairs().into());
|
|
vm.set_global_name("once", once().into());
|
|
vm.set_global_name("forever", forever().into());
|
|
vm.set_global_name("do_forever", forever().into());
|
|
|
|
// modify
|
|
vm.set_global_name("map", map().into());
|
|
vm.set_global_name("scan", scan().into());
|
|
vm.set_global_name("tee", tee().into());
|
|
vm.set_global_name("filter", filter().into());
|
|
vm.set_global_name("filter_map", filter_map().into());
|
|
vm.set_global_name("take", take().into());
|
|
vm.set_global_name("skip", skip().into());
|
|
vm.set_global_name("enumerate", enumerate().into());
|
|
vm.set_global_name("cycle", cycle().into());
|
|
vm.set_global_name("step", step().into());
|
|
vm.set_global_name("rev", rev().into());
|
|
|
|
// join
|
|
vm.set_global_name("zip", zip().into());
|
|
vm.set_global_name("alternate", alternate().into());
|
|
vm.set_global_name("intersperse", intersperse().into());
|
|
vm.set_global_name("cartprod", cartprod().into());
|
|
vm.set_global_name("chain", chain().into());
|
|
|
|
// end
|
|
vm.set_global_name("list", list().into());
|
|
vm.set_global_name("table", table().into());
|
|
vm.set_global_name("len", len().into());
|
|
vm.set_global_name("fold", fold().into());
|
|
vm.set_global_name("foldi", foldi().into());
|
|
vm.set_global_name("sum", sum().into());
|
|
vm.set_global_name("prod", prod().into());
|
|
vm.set_global_name("any", any().into());
|
|
vm.set_global_name("all", all().into());
|
|
vm.set_global_name("last", last().into());
|
|
vm.set_global_name("nth", nth().into());
|
|
vm.set_global_name("contains", contains().into());
|
|
vm.set_global_name("index_of", index_of().into());
|
|
vm.set_global_name("index_if", index_if().into());
|
|
vm.set_global_name("find", find().into());
|
|
vm.set_global_name("count", count().into());
|
|
}
|
|
|
|
//
|
|
// begin iteration
|
|
//
|
|
|
|
#[native_func(1)]
|
|
pub fn iter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, v] = unpack_args!(args);
|
|
|
|
v.to_iter_function()
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn pairs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, table] = unpack_args!(args);
|
|
let Value::Table(table) = table else {
|
|
throw!(*SYM_TYPE_ERROR, "pairs expected table, found {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> {
|
|
if let Some(k) = keys.borrow_mut().next() {
|
|
let v = table.borrow().get(&k).cloned().unwrap_or(Value::Nil);
|
|
Ok(Value::from(vec![k.into_inner(), v]).to_cell())
|
|
} else {
|
|
Ok(Value::Nil)
|
|
}
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, val] = unpack_args!(args);
|
|
|
|
let v = RefCell::new(Some(val));
|
|
let f = move |_: &mut Vm, _| {
|
|
if let Some(v) = v.borrow_mut().take() {
|
|
Ok(v.to_cell())
|
|
} else {
|
|
Ok(Value::Nil)
|
|
}
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, val] = unpack_args!(args);
|
|
|
|
let f = move |_: &mut Vm, _| {
|
|
Ok(val.clone().to_cell())
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn do_forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, func] = unpack_args!(args);
|
|
|
|
let f = move |vm: &mut Vm, _| {
|
|
Ok(vmcall!(vm; func.clone())?.to_cell())
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
//
|
|
// chain iteration
|
|
//
|
|
|
|
|
|
|
|
#[native_func(2)]
|
|
pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, map, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let f = move |vm: &mut Vm, _| {
|
|
match vmcalliter!(vm; iter.clone())? {
|
|
Some(val) => Ok(vmcall!(vm; map.clone(), cell_take(val))?.to_cell()),
|
|
None => Ok(Value::Nil),
|
|
}
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn tee(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, tee, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let f = move |vm: &mut Vm, _| {
|
|
match vmcalliter!(vm; iter.clone())? {
|
|
Some(val) => {
|
|
vmcall!(vm; tee.clone(), cell_take(val.clone()))?;
|
|
Ok(val.into())
|
|
}
|
|
None => Ok(Value::Nil),
|
|
}
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(3)]
|
|
pub fn scan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, init, func, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let result = RefCell::new(init);
|
|
let f = move |vm: &mut Vm, _| {
|
|
match vmcalliter!(vm; iter.clone())? {
|
|
Some(val) => {
|
|
let val = cell_take(val);
|
|
let r = vmcall!(vm; func.clone(), result.take(), val)?;
|
|
*result.borrow_mut() = r.clone();
|
|
Ok(r.to_cell())
|
|
},
|
|
None => {
|
|
result.take();
|
|
Ok(Value::Nil)
|
|
},
|
|
}
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, filter, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let f = move |vm: &mut Vm, _| {
|
|
loop {
|
|
let next = match vmcalliter!(vm; iter.clone())? {
|
|
Some(next) => next,
|
|
None => return Ok(Value::Nil),
|
|
};
|
|
let res = vmcall!(vm; filter.clone(), cell_take(next.clone()))?;
|
|
if res.truthy() {
|
|
return Ok(next.into())
|
|
}
|
|
}
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn filter_map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, fmap, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let f = move |vm: &mut Vm, _| {
|
|
loop {
|
|
let next = match vmcalliter!(vm; iter.clone())? {
|
|
Some(next) => next,
|
|
None => return Ok(Value::Nil),
|
|
};
|
|
let res = vmcall!(vm; fmap.clone(), cell_take(next))?;
|
|
if let Value::Cell(_) = res {
|
|
return Ok(res)
|
|
}
|
|
}
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, count, iter] = unpack_args!(args);
|
|
let Value::Int(count) = count else {
|
|
throw!(*SYM_TYPE_ERROR, "take expected integer")
|
|
};
|
|
let Ok(count) = count.try_into() else {
|
|
throw!(*SYM_TYPE_ERROR, "take expected nonnegative integer")
|
|
};
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let taken = RefCell::new(0);
|
|
let f = move |vm: &mut Vm, _| {
|
|
if *taken.borrow() >= count {
|
|
return Ok(Value::Nil)
|
|
}
|
|
let next = match vmcalliter!(vm; iter.clone())? {
|
|
Some(next) => next,
|
|
None => {
|
|
*taken.borrow_mut() = count;
|
|
return Ok(Value::Nil)
|
|
}
|
|
};
|
|
*taken.borrow_mut() += 1;
|
|
Ok(next.into())
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, count, iter] = unpack_args!(args);
|
|
let Value::Int(count) = count else {
|
|
throw!(*SYM_TYPE_ERROR, "count expected integer")
|
|
};
|
|
let Ok(count) = count.try_into() else {
|
|
throw!(*SYM_TYPE_ERROR, "count expected nonnegative integer")
|
|
};
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let skipped = RefCell::new(false);
|
|
let f = move |vm: &mut Vm, _| {
|
|
if *skipped.borrow() {
|
|
return vmcall!(vm; iter.clone())
|
|
}
|
|
*skipped.borrow_mut() = true;
|
|
for _ in 0..count {
|
|
vmcall!(vm; iter.clone())?;
|
|
}
|
|
vmcall!(vm; iter.clone())
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let counter = RefCell::new(0);
|
|
let f = move |vm: &mut Vm, _| {
|
|
let next = match vmcalliter!(vm; iter.clone())? {
|
|
Some(next) => cell_take(next),
|
|
None => return Ok(Value::Nil),
|
|
};
|
|
let mut c = counter.borrow_mut();
|
|
let n = *c;
|
|
*c += 1;
|
|
Ok(Value::from(vec![n.into(), next]).to_cell())
|
|
};
|
|
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
|
|
#[native_func(1)]
|
|
pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let record = RefCell::new((Vec::<Rc<RefCell<Value>>>::new(), false, 0));
|
|
let f = move |vm: &mut Vm, _| {
|
|
if record.borrow().1 {
|
|
let mut r = record.borrow_mut();
|
|
if r.0.is_empty() {
|
|
return Ok(Value::Nil)
|
|
}
|
|
let val = r.0[r.2].clone();
|
|
r.2 = (r.2 + 1) % r.0.len();
|
|
return Ok(val.into())
|
|
}
|
|
match vmcalliter!(vm; iter.clone())? {
|
|
Some(v) => {
|
|
record.borrow_mut().0.push(v.clone());
|
|
Ok(v.into())
|
|
},
|
|
None => {
|
|
let mut r = record.borrow_mut();
|
|
r.1 = true;
|
|
if r.0.is_empty() {
|
|
Ok(Value::Nil)
|
|
} else {
|
|
r.2 = (r.2 + 1) % r.0.len();
|
|
Ok(r.0[0].clone().into())
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[derive(Clone, Copy, Default)]
|
|
enum Step {
|
|
First, Going, #[default] End,
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, by, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
let Value::Int(by) = by else {
|
|
throw!(*SYM_TYPE_ERROR, "step expected integer")
|
|
};
|
|
if by <= 0 {
|
|
throw!(*SYM_TYPE_ERROR, "step expected positive integer")
|
|
}
|
|
|
|
let state = RefCell::new(Step::First);
|
|
let f = move |vm: &mut Vm, _| match state.take() {
|
|
Step::First => {
|
|
match vmcalliter!(vm; iter.clone())? {
|
|
Some(x) => {
|
|
*state.borrow_mut() = Step::Going;
|
|
Ok(x.into())
|
|
},
|
|
None => Ok(Value::Nil),
|
|
}
|
|
},
|
|
Step::Going => {
|
|
for _ in 0..(by-1) {
|
|
if vmcall!(vm; iter.clone())? == Value::Nil {
|
|
return Ok(Value::Nil)
|
|
}
|
|
}
|
|
let Some(x) = vmcalliter!(vm; iter.clone())? else {
|
|
return Ok(Value::Nil)
|
|
};
|
|
*state.borrow_mut() = Step::Going;
|
|
Ok(x.into())
|
|
},
|
|
Step::End => Ok(Value::Nil),
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let lst = RefCell::new(None);
|
|
let f = move |vm: &mut Vm, _| {
|
|
if lst.borrow().is_none() {
|
|
let mut l = Vec::new();
|
|
while let Some(x) = vmcalliter!(vm; iter.clone())? {
|
|
l.push(cell_take(x))
|
|
}
|
|
l.reverse();
|
|
*lst.borrow_mut() = Some(l.into_iter());
|
|
}
|
|
match lst.borrow_mut().as_mut().unwrap().next() {
|
|
Some(v) => Ok(v.to_cell()),
|
|
None => Ok(Value::Nil),
|
|
}
|
|
|
|
};
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
|
|
//
|
|
// join
|
|
//
|
|
|
|
|
|
#[native_func(2..)]
|
|
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let ([_, i1, i2], rest) = unpack_varargs!(args);
|
|
let mut iters = Vec::with_capacity(2 + rest.len());
|
|
for i in [i1, i2].into_iter().chain(rest.into_iter()) {
|
|
iters.push(i.to_iter_function()?);
|
|
}
|
|
|
|
let f = move |vm: &mut Vm, _| {
|
|
let mut res = Vec::with_capacity(iters.len());
|
|
for i in iters.iter() {
|
|
match vmcalliter!(vm; i.clone())? {
|
|
Some(v) => res.push(cell_take(v)),
|
|
None => return Ok(Value::Nil),
|
|
};
|
|
}
|
|
Ok(Value::from(res).to_cell())
|
|
};
|
|
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[native_func(2..)]
|
|
pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let ([_, i1, i2], rest) = unpack_varargs!(args);
|
|
let mut iters = Vec::with_capacity(2 + rest.len());
|
|
for i in [i1, i2].into_iter().chain(rest.into_iter()) {
|
|
iters.push(i.to_iter_function()?);
|
|
}
|
|
|
|
let state = RefCell::new((0, false));
|
|
let f = move |vm: &mut Vm, _| {
|
|
let mut s = state.borrow_mut();
|
|
if s.1 {
|
|
return Ok(Value::Nil);
|
|
}
|
|
let n = s.0;
|
|
s.0 = (s.0 + 1) % iters.len();
|
|
drop(s);
|
|
match vmcalliter!(vm; iters[n].clone())? {
|
|
Some(v) => Ok(v.into()),
|
|
None => {
|
|
state.borrow_mut().1 = true;
|
|
Ok(Value::Nil)
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[derive(Default)]
|
|
enum Intersperse {
|
|
Init, Waiting, HasNext(Rc<RefCell<Value>>), #[default] End
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, val, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
let val = val.to_cell();
|
|
|
|
let state = RefCell::new(Intersperse::Init);
|
|
let f = move |vm: &mut Vm, _| {
|
|
match state.take() {
|
|
Intersperse::Init => match vmcalliter!(vm; iter.clone())? {
|
|
Some(v) => {
|
|
*state.borrow_mut() = Intersperse::Waiting;
|
|
Ok(v.into())
|
|
},
|
|
None => {
|
|
*state.borrow_mut() = Intersperse::End;
|
|
Ok(Value::Nil)
|
|
},
|
|
},
|
|
Intersperse::Waiting => match vmcalliter!(vm; iter.clone())? {
|
|
Some(v) => {
|
|
*state.borrow_mut() = Intersperse::HasNext(v);
|
|
Ok(val.clone())
|
|
},
|
|
None => {
|
|
*state.borrow_mut() = Intersperse::End;
|
|
Ok(Value::Nil)
|
|
},
|
|
},
|
|
Intersperse::HasNext(v) => {
|
|
*state.borrow_mut() = Intersperse::Waiting;
|
|
Ok(v.into())
|
|
},
|
|
Intersperse::End => Ok(Value::Nil),
|
|
}
|
|
};
|
|
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
|
|
#[native_func(2)]
|
|
pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter1, iter2] = unpack_args!(args);
|
|
let iter1 = iter1.to_iter_function()?;
|
|
let iter2 = iter2.to_iter_function()?;
|
|
|
|
let done_first = RefCell::new(false);
|
|
let f = move |vm: &mut Vm, _| {
|
|
if *done_first.borrow() {
|
|
return vmcall!(vm; iter2.clone())
|
|
}
|
|
match vmcalliter!(vm; iter1.clone())? {
|
|
Some(v) => Ok(v.into()),
|
|
None => {
|
|
*done_first.borrow_mut() = true;
|
|
vmcall!(vm; iter2.clone())
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
struct CartProd {
|
|
a_val: Value,
|
|
b_data: Vec<Value>,
|
|
b_idx: usize,
|
|
b_done: bool,
|
|
done: bool,
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, a, b] = unpack_args!(args);
|
|
let a = a.to_iter_function()?;
|
|
let b = b.to_iter_function()?;
|
|
|
|
let state = RefCell::new(CartProd::default());
|
|
let f = move |vm: &mut Vm, _| {
|
|
if state.borrow().done {
|
|
return Ok(Value::Nil)
|
|
}
|
|
|
|
if state.borrow().b_idx >= state.borrow().b_data.len() {
|
|
if !state.borrow().b_done {
|
|
let v = vmcalliter!(vm; b.clone())?;
|
|
let mut s = state.borrow_mut();
|
|
match v {
|
|
Some(x) => s.b_data.push(cell_take(x)),
|
|
None => {
|
|
s.b_done = true;
|
|
if s.b_idx == 0 {
|
|
s.done = true;
|
|
return Ok(Value::Nil)
|
|
}
|
|
s.b_idx = 0;
|
|
}
|
|
};
|
|
} else {
|
|
if state.borrow().b_idx == 0 {
|
|
state.borrow_mut().done = true;
|
|
return Ok(Value::Nil)
|
|
}
|
|
state.borrow_mut().b_idx = 0;
|
|
}
|
|
}
|
|
|
|
let b_res = state.borrow().b_data[state.borrow().b_idx].clone();
|
|
|
|
if state.borrow().b_idx == 0 {
|
|
match vmcalliter!(vm; a.clone())? {
|
|
Some(v) => state.borrow_mut().a_val = cell_take(v),
|
|
None => {
|
|
state.borrow_mut().done = true;
|
|
return Ok(Value::Nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
let a_res = state.borrow().a_val.clone();
|
|
|
|
state.borrow_mut().b_idx += 1;
|
|
|
|
Ok(Value::from(vec![a_res, b_res]).to_cell())
|
|
};
|
|
|
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// end iteration
|
|
//
|
|
|
|
|
|
|
|
#[native_func(1)]
|
|
pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
let mut result = Vec::new();
|
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
|
result.push(cell_take(value));
|
|
};
|
|
Ok(result.into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn table(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
let mut result = HashMap::new();
|
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
|
let Value::List(l) = cell_take(value) else {
|
|
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list")
|
|
};
|
|
let Ok([k, v]): std::result::Result<[Value; 2], Vec<Value>> = cell_take(l).try_into() else {
|
|
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list of length 2")
|
|
};
|
|
result.insert(k.try_into()?, v);
|
|
};
|
|
Ok(result.into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, value] = unpack_args!(args);
|
|
match value {
|
|
Value::String(s) => return Ok((s.len() as i64).into()),
|
|
Value::List(l) => return Ok((l.borrow().len() as i64).into()),
|
|
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
|
|
Value::Range(r) if r.ty != RangeType::Endless
|
|
=> return Ok((r.len().unwrap() as i64).into()),
|
|
_ => (),
|
|
}
|
|
let iter = value.to_iter_function()?;
|
|
let mut len = 0;
|
|
while vmcalliter!(vm; iter.clone())?.is_some() {
|
|
len += 1;
|
|
};
|
|
Ok(len.into())
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn fold(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, func, iter] = unpack_args!(args);
|
|
let iter: Value = iter.to_iter_function()?;
|
|
|
|
let mut result = match vmcalliter!(vm; iter.clone())? {
|
|
Some(v) => cell_take(v),
|
|
None => return Ok(Value::Nil),
|
|
};
|
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
|
result = vmcall!(vm; func.clone(), result, cell_take(value))?;
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
#[native_func(3)]
|
|
pub fn foldi(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, init, func, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let mut result = init;
|
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
|
result = vmcall!(vm; func.clone(), result, cell_take(value))?;
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn sum(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let mut result = Value::Int(0);
|
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
|
result = (result + cell_take(value))?
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn prod(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let mut result = Value::Int(1);
|
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
|
result = (result * cell_take(value))?
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn any(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
|
if RefCell::borrow(&value).truthy() {
|
|
return Ok(true.into())
|
|
}
|
|
}
|
|
Ok(false.into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn all(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
|
if !RefCell::borrow(&value).truthy() {
|
|
return Ok(false.into())
|
|
}
|
|
}
|
|
Ok(true.into())
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn last(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let mut last = Rc::new(RefCell::new(Value::Nil));
|
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
|
last = value;
|
|
}
|
|
Ok(cell_take(last))
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn nth(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, n, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
let Value::Int(n) = n else {
|
|
throw!(*SYM_TYPE_ERROR, "nth expected integer")
|
|
};
|
|
|
|
for _ in 0..n {
|
|
if vmcalliter!(vm; iter.clone())?.is_none() {
|
|
return Ok(Value::Nil)
|
|
}
|
|
}
|
|
match vmcalliter!(vm; iter)? {
|
|
Some(v) => Ok(cell_take(v)),
|
|
None => Ok(Value::Nil),
|
|
}
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn contains(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, val, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
for _ in 0.. {
|
|
match vmcalliter!(vm; iter.clone())? {
|
|
None => return Ok(Value::Nil),
|
|
Some(v) => if *RefCell::borrow(&v) == val {
|
|
return Ok(true.into())
|
|
}
|
|
}
|
|
}
|
|
Ok(false.into())
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn index_of(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, val, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
for i in 0.. {
|
|
match vmcalliter!(vm; iter.clone())? {
|
|
None => return Ok(Value::Nil),
|
|
Some(v) => if *RefCell::borrow(&v) == val {
|
|
return Ok(i.into())
|
|
}
|
|
}
|
|
}
|
|
Ok(Value::Nil)
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn index_if(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, func, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
for i in 0.. {
|
|
match vmcalliter!(vm; iter.clone())? {
|
|
None => return Ok(Value::Nil),
|
|
Some(v) => if vmcall!(vm; func.clone(), cell_take(v))?.truthy() {
|
|
return Ok(i.into())
|
|
}
|
|
}
|
|
}
|
|
Ok(Value::Nil)
|
|
}
|
|
|
|
#[native_func(2)]
|
|
pub fn find(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, func, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
loop {
|
|
match vmcalliter!(vm; iter.clone())? {
|
|
None => return Ok(Value::Nil),
|
|
Some(v) => {
|
|
let v = cell_take(v);
|
|
if vmcall!(vm; func.clone(), v.clone())?.truthy() {
|
|
return Ok(v)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[native_func(1)]
|
|
pub fn count(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
let [_, iter] = unpack_args!(args);
|
|
let iter = iter.to_iter_function()?;
|
|
|
|
let mut map = HashMap::new();
|
|
|
|
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
|
let v = cell_take(v);
|
|
let hv = v.try_into()?;
|
|
map.entry(hv)
|
|
.and_modify(|v: &mut Value| *v = (v.clone() + Value::Int(1)).unwrap())
|
|
.or_insert(Value::Int(1));
|
|
}
|
|
|
|
Ok(map.into())
|
|
}
|
|
|