Compare commits

..

No commits in common. "bc3fb597d5dced14ab2c5c77cffb05339c6143a8" and "9996ca1574b27e9ea7331c00bbb451ddfbe87144" have entirely different histories.

17 changed files with 364 additions and 353 deletions

View file

@ -1,3 +1,8 @@
[workspace] [workspace]
members = ["talc-lang", "talc-bin", "talc-std", "talc-macros"] members = [
"talc-lang",
"talc-bin",
"talc-std",
"talc-macros",
]
resolver = "2" resolver = "2"

View file

@ -174,7 +174,7 @@ pub struct Catch {
pub types: Option<Vec<Symbol>>, pub types: Option<Vec<Symbol>>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, Default)]
pub struct TryTable { pub struct TryTable {
pub catches: Vec<Catch>, pub catches: Vec<Catch>,
pub local_count: usize, pub local_count: usize,

View file

@ -15,7 +15,6 @@ pub mod symbol;
pub mod ast; pub mod ast;
pub mod value; pub mod value;
pub mod exception;
pub mod chunk; pub mod chunk;
pub mod compiler; pub mod compiler;

View file

@ -1,11 +1,11 @@
use std::{collections::HashMap, sync::{Mutex, OnceLock}}; use std::{collections::HashMap, sync::{Arc, Mutex, OnceLock}};
use lazy_static::lazy_static; use lazy_static::lazy_static;
#[derive(Default)] #[derive(Default)]
struct SymbolTable { struct SymbolTable {
names: Vec<&'static str>, names: Vec<Arc<str>>,
values: HashMap<&'static str, Symbol> values: HashMap<Arc<str>, Symbol>
} }
lazy_static! { lazy_static! {
@ -24,8 +24,6 @@ lazy_static! {
pub static ref SYM_FUNCTION: Symbol = symbol!(function); pub static ref SYM_FUNCTION: Symbol = symbol!(function);
pub static ref SYM_NATIVE_FUNC: Symbol = symbol!(native_func); pub static ref SYM_NATIVE_FUNC: Symbol = symbol!(native_func);
pub static ref SYM_END_ITERATION: Symbol = symbol!(end_iteration);
pub static ref SYM_TYPE: Symbol = symbol!(type); pub static ref SYM_TYPE: Symbol = symbol!(type);
pub static ref SYM_MSG: Symbol = symbol!(msg); pub static ref SYM_MSG: Symbol = symbol!(msg);
pub static ref SYM_DATA: Symbol = symbol!(data); pub static ref SYM_DATA: Symbol = symbol!(data);
@ -62,7 +60,7 @@ impl Symbol {
pub fn get(name: &str) -> Self { pub fn get(name: &str) -> Self {
let mut table = get_table().lock().expect("couldn't lock symbol table"); let mut table = get_table().lock().expect("couldn't lock symbol table");
if let Some(sym) = table.values.get(&name) { if let Some(sym) = table.values.get(name) {
return *sym return *sym
} }
@ -70,9 +68,9 @@ impl Symbol {
assert!(symno <= MAX_SYMBOL, "too many symbols"); assert!(symno <= MAX_SYMBOL, "too many symbols");
let sym = Symbol(symno as u32); let sym = Symbol(symno as u32);
let name = String::leak(name.to_owned()); let name: Arc<str> = name.into();
table.names.push(name); table.names.push(name.clone());
table.values.insert(name, sym); table.values.insert(name, sym);
sym sym
@ -101,7 +99,7 @@ impl Symbol {
/// # Panics /// # Panics
/// If the mutex storing the symbol table is poisoned /// If the mutex storing the symbol table is poisoned
pub fn name(self) -> &'static str { pub fn name(self) -> Arc<str> {
let table = get_table().lock().expect("couldn't lock symbol table"); let table = get_table().lock().expect("couldn't lock symbol table");
table.names.get(self.0 as usize).cloned().expect("symbol does not exist") table.names.get(self.0 as usize).cloned().expect("symbol does not exist")
} }

View file

@ -1,5 +1,6 @@
use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display}; use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display};
use crate::{value::{HashValue, Value}, symbol::{SYM_DATA, SYM_MSG, SYM_TYPE}, symbol::Symbol}; use super::{HashValue, RcTable, Value};
use crate::{symbol::{SYM_DATA, SYM_MSG, SYM_TYPE}, symbol::Symbol};
pub type Result<T> = std::result::Result<T, Exception>; pub type Result<T> = std::result::Result<T, Exception>;
@ -51,7 +52,7 @@ impl Exception {
} }
} }
pub fn to_table(self) -> Rc<RefCell<HashMap<HashValue, Value>>> { pub fn to_table(self) -> RcTable {
let mut table = HashMap::new(); let mut table = HashMap::new();
table.insert((*SYM_TYPE).into(), self.ty.into()); table.insert((*SYM_TYPE).into(), self.ty.into());
if let Some(msg) = self.msg { if let Some(msg) = self.msg {
@ -77,16 +78,16 @@ impl Display for Exception {
#[macro_export] #[macro_export]
macro_rules! exception { macro_rules! exception {
($exc_ty:expr, $fstr:literal, $($arg:expr),*) => { ($exc_ty:expr, $fstr:literal, $($arg:expr),*) => {
$crate::exception::Exception::new_with_msg( $crate::value::exception::Exception::new_with_msg(
$exc_ty, $exc_ty,
format!($fstr, $($arg),*).into() format!($fstr, $($arg),*).into()
) )
}; };
($exc_ty:expr, $fstr:literal) => { ($exc_ty:expr, $fstr:literal) => {
$crate::exception::exception!($exc_ty, $fstr,) $crate::value::exception::exception!($exc_ty, $fstr,)
}; };
($exc_ty:expr) => { ($exc_ty:expr) => {
$crate::exception::Exception::new($exc_ty) $crate::value::exception::Exception::new($exc_ty)
}; };
} }
@ -95,7 +96,7 @@ pub use exception;
#[macro_export] #[macro_export]
macro_rules! throw { macro_rules! throw {
($($args:tt)*) => { ($($args:tt)*) => {
return Err($crate::exception::exception!($($args)*)) return Err($crate::value::exception::exception!($($args)*))
}; };
} }

View file

@ -1,8 +1,8 @@
use std::rc::Rc; use std::rc::Rc;
use crate::{chunk::Chunk, Vm, exception::Result}; use crate::{chunk::Chunk, Vm};
use super::{Value}; use super::{Value, exception::Result};
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct FuncAttrs { pub struct FuncAttrs {
@ -10,7 +10,7 @@ pub struct FuncAttrs {
pub variadic: bool, pub variadic: bool,
} }
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Function { pub struct Function {
pub attrs: FuncAttrs, pub attrs: FuncAttrs,
pub chunk: Rc<Chunk>, pub chunk: Rc<Chunk>,

View file

@ -1,6 +1,6 @@
use crate::{symbol::{SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::function::NativeFunc, vmcalliter, Vm, exception::{Result, throw}}; use crate::{symbol::{SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::{cell_take, function::NativeFunc}, vmcalliter, Vm};
use super::{Value, range::RangeType}; use super::{Value, exception::{Result, throw}, range::RangeType};
impl Value { impl Value {
pub fn index(&self, idx: Self) -> Result<Self> { pub fn index(&self, idx: Self) -> Result<Self> {
@ -34,8 +34,8 @@ impl Value {
let col = col.clone(); let col = col.clone();
let func = move |vm: &mut Vm, _| { let func = move |vm: &mut Vm, _| {
match vmcalliter!(vm; ii.clone())? { match vmcalliter!(vm; ii.clone())? {
Some(i) => Ok(col.index(i)?.to_cell()), Some(i) => Ok(col.index(cell_take(i))?.to_cell()),
None => Ok(Value::from(*SYM_END_ITERATION)), None => Ok(Value::Nil)
} }
}; };
Ok(NativeFunc::new(Box::new(func), 0).into()) Ok(NativeFunc::new(Box::new(func), 0).into())
@ -67,7 +67,7 @@ impl Value {
let iter = val.to_iter_function()?; let iter = val.to_iter_function()?;
let mut vals = Vec::new(); let mut vals = Vec::new();
while let Some(v) = vmcalliter!(vm; iter.clone())? { while let Some(v) = vmcalliter!(vm; iter.clone())? {
vals.push(v); vals.push(cell_take(v));
} }
let mut tm = t.borrow_mut(); let mut tm = t.borrow_mut();
match r.ty { match r.ty {
@ -114,7 +114,7 @@ impl Value {
let Some(v) = vmcalliter!(vm; val.clone())? else { let Some(v) = vmcalliter!(vm; val.clone())? else {
throw!(*SYM_INDEX_ERROR, "not enough values provided for store index") throw!(*SYM_INDEX_ERROR, "not enough values provided for store index")
}; };
col.store_index(vm, i, v)?; col.store_index(vm, cell_take(i), cell_take(v))?;
} }
Ok(()) Ok(())
} else { } else {

View file

@ -4,10 +4,10 @@ pub use num_complex::Complex64;
pub use num_rational::Rational64; pub use num_rational::Rational64;
use crate::symbol::{Symbol, SYM_HASH_ERROR}; use crate::symbol::{Symbol, SYM_HASH_ERROR};
use crate::exception::{Exception, throw};
use self::{range::{Range, RangeType}, function::{Function, NativeFunc}}; use self::{range::{Range, RangeType}, function::{Function, NativeFunc}, exception::{Exception, throw}};
pub mod exception;
pub mod function; pub mod function;
pub mod ops; pub mod ops;
pub mod range; pub mod range;
@ -207,3 +207,8 @@ impl_from!(String, String, into);
impl_from!(&str, String, into); impl_from!(&str, String, into);
impl_from!(Box<str>, String, into); impl_from!(Box<str>, String, into);
impl_from!(RefCell<Value>, Cell, rc); impl_from!(RefCell<Value>, Cell, rc);
#[inline]
pub fn cell_take<T: Clone>(cell: Rc<RefCell<T>>) -> T {
Rc::unwrap_or_clone(cell).into_inner()
}

View file

@ -3,9 +3,9 @@ use std::{cell::RefCell, cmp::Ordering, ops::{Add, Div, Mul, Neg, Sub}, rc::Rc};
use num_complex::Complex64; use num_complex::Complex64;
use num_rational::Rational64; use num_rational::Rational64;
use crate::{exception::{throw, Result}, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR}, value::range::RangeType, Vm}; use crate::{symbol::SYM_TYPE_ERROR, value::range::RangeType, Vm};
use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value}; use super::{exception::{throw, Result}, function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value};
impl Value { impl Value {
pub fn truthy(&self) -> bool { pub fn truthy(&self) -> bool {
@ -178,8 +178,8 @@ impl Value {
return Ok(V::Ratio(rpow(*(*x).numer(), *(*x).denom(), *y)?.into())); return Ok(V::Ratio(rpow(*(*x).numer(), *(*x).denom(), *y)?.into()));
} }
match promote(self, rhs) { 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))
(V::Int(x), V::Int(y)) => Ok(V::Ratio(rpow(x, 1, y)?.into())), => Ok(V::Ratio(rpow(x, 1, y)?.into())),
(V::Float(x), V::Float(y)) (V::Float(x), V::Float(y))
=> Ok(V::Float(x.powf(y))), => Ok(V::Float(x.powf(y))),
(V::Ratio(x), V::Ratio(y)) (V::Ratio(x), V::Ratio(y))
@ -319,17 +319,18 @@ impl Value {
Value::Cell(Rc::new(RefCell::new(self))) Value::Cell(Rc::new(RefCell::new(self)))
} }
pub fn iter_unpack(self) -> Option<Self> { pub fn iter_unpack(self) -> Result<Option<Rc<RefCell<Self>>>> {
match self { match self {
Self::Symbol(s) if s == *SYM_END_ITERATION => None, Self::Nil => Ok(None),
v => Some(v), Self::Cell(v) => Ok(Some(v)),
_ => throw!(*SYM_TYPE_ERROR, "expected iterator to return cell or nil")
} }
} }
pub fn iter_pack(v: Option<Self>) -> Self { pub fn iter_pack(v: Option<Self>) -> Self {
match v { match v {
Some(v) => v, Some(v) => v.to_cell(),
None => Value::from(*SYM_END_ITERATION) None => Value::Nil,
} }
} }
@ -339,8 +340,11 @@ impl Value {
Self::Range(range) => { Self::Range(range) => {
let range_iter = RefCell::new(range.into_iter()); let range_iter = RefCell::new(range.into_iter());
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> { let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(range_iter.borrow_mut().next() if let Some(v) = range_iter.borrow_mut().next() {
.map(Value::from))) Ok(Value::from(v).to_cell())
} else {
Ok(Value::Nil)
}
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0).into())
}, },
@ -350,9 +354,9 @@ impl Value {
let pos = *byte_pos.borrow(); let pos = *byte_pos.borrow();
if let Some(v) = &s[pos..].chars().next() { if let Some(v) = &s[pos..].chars().next() {
*byte_pos.borrow_mut() += v.len_utf8(); *byte_pos.borrow_mut() += v.len_utf8();
Ok(Value::from(v.to_string())) Ok(Value::from(v.to_string()).to_cell())
} else { } else {
Ok(Value::from(*SYM_END_ITERATION)) Ok(Value::Nil)
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0).into())
@ -363,9 +367,9 @@ impl Value {
let i = *idx.borrow(); let i = *idx.borrow();
if let Some(v) = list.borrow().get(i) { if let Some(v) = list.borrow().get(i) {
*idx.borrow_mut() += 1; *idx.borrow_mut() += 1;
Ok(v.clone()) Ok(v.clone().to_cell())
} else { } else {
Ok(Value::from(*SYM_END_ITERATION)) Ok(Value::Nil)
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0).into())
@ -374,8 +378,11 @@ impl Value {
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect(); let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
let keys = RefCell::new(keys.into_iter()); let keys = RefCell::new(keys.into_iter());
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> { let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(keys.borrow_mut().next() if let Some(v) = keys.borrow_mut().next() {
.map(HashValue::into_inner))) Ok(v.into_inner().to_cell())
} else {
Ok(Value::Nil)
}
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0).into())
}, },

View file

@ -1,9 +1,10 @@
use std::{cmp::Ordering, collections::HashMap, rc::Rc, sync::{atomic::AtomicBool, Arc}}; use std::{cmp::Ordering, collections::HashMap, rc::Rc, sync::{atomic::AtomicBool, Arc}};
use crate::{ast::{BinaryOp, UnaryOp}, chunk::Instruction, symbol::{Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR}, value::{function::{FuncAttrs, Function, NativeFunc}, Value}, exception::{throw, Exception, Result}}; use crate::{ast::{BinaryOp, UnaryOp}, chunk::Instruction, symbol::{Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR}, value::{cell_take, exception::{throw, Exception, Result}, function::{FuncAttrs, Function, NativeFunc}, Value}};
struct TryFrame { idx: usize, stack_len: usize } struct TryFrame { idx: usize, stack_len: usize }
#[derive(Default)]
struct CallFrame { struct CallFrame {
func: Rc<Function>, func: Rc<Function>,
locals: Vec<Value>, locals: Vec<Value>,
@ -17,9 +18,7 @@ impl CallFrame {
Self { Self {
func, func,
locals, locals,
try_frames: Vec::new(), ..Self::default()
ip: 0,
root: false,
} }
} }
} }
@ -93,8 +92,7 @@ fn get_call_outcome(mut args: Vec<Value>) -> Result<CallOutcome> {
.expect("did not receive vararg") else { .expect("did not receive vararg") else {
panic!("did not receive vararg") panic!("did not receive vararg")
}; };
let varargs = Rc::unwrap_or_clone(varargs).take(); args.extend(cell_take(varargs));
args.extend(varargs);
} }
vm.call_value(f.clone(), args) vm.call_value(f.clone(), args)
}; };
@ -368,12 +366,13 @@ impl Vm {
// [i,cell(v)] -> [i,v] // [i,cell(v)] -> [i,v]
// [i,nil] -> [], ip = n // [i,nil] -> [], ip = n
I::IterTest(n) => { I::IterTest(n) => {
match self.pop().iter_unpack() { match self.pop() {
Some(v) => self.push(v), Value::Cell(c) => self.push(cell_take(c)),
None => { Value::Nil => {
self.pop(); self.pop();
frame.ip = usize::from(n) frame.ip = usize::from(n)
}, },
v => throw!(*SYM_TYPE_ERROR, "iterator returned invalid value {v}")
} }
}, },
// try_frames.push(t, stack.len()) // try_frames.push(t, stack.len())
@ -405,27 +404,9 @@ impl Vm {
if let Value::NativeFunc(nf) = &args[0] { if let Value::NativeFunc(nf) = &args[0] {
let nf = nf.clone(); let nf = nf.clone();
// safety: frame is restored immediately self.call_stack.push(std::mem::take(frame));
// after function call ends let res = (nf.func)(self, args)?;
// ~25% performance improvement in *frame = self.call_stack.pop().expect("no frame to pop");
// code heavy on native function calls
unsafe {
let f = std::ptr::read(frame);
self.call_stack.push(f);
}
let res = (nf.func)(self, args);
// safety: frame was referencing invalid memory due to
// previous unsafe block, write will fix that
unsafe {
let f = self.call_stack.pop().expect("no frame to pop");
std::ptr::write(frame, f);
}
// make sure we restored the value of frame
// before propagating exceptions
let res = res?;
self.stack.push(res); self.stack.push(res);
} else if let Value::Function(func) = &args[0] { } else if let Value::Function(func) = &args[0] {
@ -433,10 +414,10 @@ impl Vm {
if self.call_stack.len() + 1 >= self.stack_max { if self.call_stack.len() + 1 >= self.stack_max {
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow") throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
} }
self.call_stack.push(std::mem::take(frame));
let new_frame = CallFrame::new(func.clone(), args); let func = func.clone();
let old_frame = std::mem::replace(frame, new_frame); *frame = CallFrame::new(func, args);
self.call_stack.push(old_frame);
} else { } else {
unreachable!("already verified by calling get_call_type"); unreachable!("already verified by calling get_call_type");
} }
@ -473,6 +454,6 @@ macro_rules! vmcall {
#[macro_export] #[macro_export]
macro_rules! vmcalliter { macro_rules! vmcalliter {
($($input:tt)*) => { ($($input:tt)*) => {
$crate::vmcall!($($input)*).map(|v| v.iter_unpack()) $crate::vmcall!($($input)*).and_then(|v| v.iter_unpack())
} }
} }

View file

@ -1,6 +1,6 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use talc_lang::{exception, exception::Result, symbol::SYM_TYPE_ERROR, throw, value::{function::NativeFunc, Value}, vmcall, Vm}; use talc_lang::{exception, symbol::SYM_TYPE_ERROR, throw, value::{exception::Result, function::NativeFunc, Value}, vmcall, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;

View file

@ -1,4 +1,4 @@
use talc_lang::{symbol::SYM_TYPE_ERROR, exception::{throw, Exception, Result}, value::Value, Vm}; use talc_lang::{symbol::SYM_TYPE_ERROR, value::{exception::{throw, Exception, Result}, Value}, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::{unpack_args, unpack_varargs}; use crate::{unpack_args, unpack_varargs};

View file

@ -1,6 +1,6 @@
use std::{io::Write, time::{SystemTime, UNIX_EPOCH}}; use std::{io::Write, time::{SystemTime, UNIX_EPOCH}};
use talc_lang::{value::Value, vmcall, Vm, exception::{throw, Result}}; use talc_lang::{value::{exception::{throw, Result}, Value}, vmcall, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::{unpack_args, SYM_IO_ERROR}; use crate::{unpack_args, SYM_IO_ERROR};
@ -33,7 +33,13 @@ pub fn readln(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
#[native_func(0)] #[native_func(0)]
pub fn time(_: &mut Vm, _: Vec<Value>) -> Result<Value> { pub fn time(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards"); let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
Ok(time.as_secs_f64().into()) Ok((time.as_secs() as i64).into())
}
#[native_func(0)]
pub fn time_ms(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
Ok((time.as_millis() as i64).into())
} }
#[native_func(0)] #[native_func(0)]
@ -49,7 +55,7 @@ pub fn time_fn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
vmcall!(vm; func)?; vmcall!(vm; func)?;
let tf = SystemTime::now(); let tf = SystemTime::now();
let time = tf.duration_since(t0).expect("time went backwards"); let time = tf.duration_since(t0).expect("time went backwards");
Ok(time.as_secs_f64().into()) Ok((time.as_nanos() as i64).into())
} }
pub fn load(vm: &mut Vm) { pub fn load(vm: &mut Vm) {
@ -58,6 +64,7 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("readln", readln().into()); vm.set_global_name("readln", readln().into());
vm.set_global_name("time", time().into()); vm.set_global_name("time", time().into());
vm.set_global_name("time_ms", time_ms().into());
vm.set_global_name("time_ns", time_ns().into()); vm.set_global_name("time_ns", time_ns().into());
vm.set_global_name("time_fn", time_fn().into()); vm.set_global_name("time_fn", time_fn().into());
} }

View file

@ -1,37 +1,34 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{symbol::SYM_TYPE_ERROR, exception::Result, throw, value::{function::NativeFunc, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm}; 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 talc_macros::native_func;
use crate::{unpack_args, unpack_varargs}; use crate::{unpack_args, unpack_varargs};
pub fn load(vm: &mut Vm) { pub fn load(vm: &mut Vm) {
// begin
vm.set_global_name("iter", iter().into()); vm.set_global_name("iter", iter().into());
vm.set_global_name("pairs", pairs().into()); vm.set_global_name("pairs", pairs().into());
vm.set_global_name("once", once().into()); vm.set_global_name("once", once().into());
vm.set_global_name("forever", forever().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("map", map().into());
vm.set_global_name("scan", scan().into()); vm.set_global_name("scan", scan().into());
vm.set_global_name("tee", tee().into()); vm.set_global_name("tee", tee().into());
vm.set_global_name("filter", filter().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("take", take().into());
vm.set_global_name("skip", skip().into()); vm.set_global_name("skip", skip().into());
vm.set_global_name("enumerate", enumerate().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("zip", zip().into());
vm.set_global_name("alternate", alternate().into()); vm.set_global_name("alternate", alternate().into());
vm.set_global_name("intersperse", intersperse().into()); vm.set_global_name("intersperse", intersperse().into());
vm.set_global_name("cartprod", cartprod().into()); vm.set_global_name("cartprod", cartprod().into());
vm.set_global_name("cycle", cycle().into());
vm.set_global_name("chain", chain().into()); vm.set_global_name("chain", chain().into());
vm.set_global_name("step", step().into());
vm.set_global_name("rev", rev().into());
// end
vm.set_global_name("list", list().into()); vm.set_global_name("list", list().into());
vm.set_global_name("table", table().into()); vm.set_global_name("table", table().into());
vm.set_global_name("len", len().into()); vm.set_global_name("len", len().into());
@ -74,9 +71,9 @@ pub fn pairs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> { let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
if let Some(k) = keys.borrow_mut().next() { if let Some(k) = keys.borrow_mut().next() {
let v = table.borrow().get(&k).cloned().unwrap_or(Value::Nil); let v = table.borrow().get(&k).cloned().unwrap_or(Value::Nil);
Ok(Value::from(vec![k.into_inner(), v])) Ok(Value::from(vec![k.into_inner(), v]).to_cell())
} else { } else {
Ok(Value::iter_pack(None)) Ok(Value::Nil)
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0).into())
@ -88,7 +85,11 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let v = RefCell::new(Some(val)); let v = RefCell::new(Some(val));
let f = move |_: &mut Vm, _| { let f = move |_: &mut Vm, _| {
Ok(Value::iter_pack(v.borrow_mut().take())) if let Some(v) = v.borrow_mut().take() {
Ok(v.to_cell())
} else {
Ok(Value::Nil)
}
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0).into())
} }
@ -98,7 +99,17 @@ pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args); let [_, val] = unpack_args!(args);
let f = move |_: &mut Vm, _| { let f = move |_: &mut Vm, _| {
Ok(val.clone()) 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()) Ok(NativeFunc::new(Box::new(f), 0).into())
} }
@ -116,8 +127,8 @@ pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
Some(val) => Ok(vmcall!(vm; map.clone(), val)?), Some(val) => Ok(vmcall!(vm; map.clone(), cell_take(val))?.to_cell()),
None => Ok(Value::iter_pack(None)), None => Ok(Value::Nil),
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0).into())
@ -131,10 +142,10 @@ pub fn tee(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
Some(val) => { Some(val) => {
vmcall!(vm; tee.clone(), val.clone())?; vmcall!(vm; tee.clone(), cell_take(val.clone()))?;
Ok(val.into()) Ok(val.into())
} }
None => Ok(Value::iter_pack(None)), None => Ok(Value::Nil),
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0).into())
@ -147,13 +158,18 @@ pub fn scan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let result = RefCell::new(init); let result = RefCell::new(init);
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
let Some(val) = vmcalliter!(vm; iter.clone())? else { match vmcalliter!(vm; iter.clone())? {
result.take(); Some(val) => {
return Ok(Value::iter_pack(None)) let val = cell_take(val);
};
let r = vmcall!(vm; func.clone(), result.take(), val)?; let r = vmcall!(vm; func.clone(), result.take(), val)?;
*result.borrow_mut() = r.clone(); *result.borrow_mut() = r.clone();
Ok(r) Ok(r.to_cell())
},
None => {
result.take();
Ok(Value::Nil)
},
}
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0).into())
} }
@ -165,10 +181,11 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
loop { loop {
let Some(next) = vmcalliter!(vm; iter.clone())? else { let next = match vmcalliter!(vm; iter.clone())? {
return Ok(Value::iter_pack(None)) Some(next) => next,
None => return Ok(Value::Nil),
}; };
let res = vmcall!(vm; filter.clone(), next.clone())?; let res = vmcall!(vm; filter.clone(), cell_take(next.clone()))?;
if res.truthy() { if res.truthy() {
return Ok(next.into()) return Ok(next.into())
} }
@ -177,6 +194,26 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(NativeFunc::new(Box::new(f), 0).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)] #[native_func(2)]
pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, count, iter] = unpack_args!(args); let [_, count, iter] = unpack_args!(args);
@ -191,11 +228,14 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let taken = RefCell::new(0); let taken = RefCell::new(0);
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
if *taken.borrow() >= count { if *taken.borrow() >= count {
return Ok(Value::iter_pack(None)) return Ok(Value::Nil)
} }
let Some(next) = vmcalliter!(vm; iter.clone())? else { let next = match vmcalliter!(vm; iter.clone())? {
Some(next) => next,
None => {
*taken.borrow_mut() = count; *taken.borrow_mut() = count;
return Ok(Value::iter_pack(None)) return Ok(Value::Nil)
}
}; };
*taken.borrow_mut() += 1; *taken.borrow_mut() += 1;
Ok(next.into()) Ok(next.into())
@ -235,124 +275,19 @@ pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let counter = RefCell::new(0); let counter = RefCell::new(0);
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
let Some(next) = vmcalliter!(vm; iter.clone())? else { let next = match vmcalliter!(vm; iter.clone())? {
return Ok(Value::iter_pack(None)) Some(next) => cell_take(next),
None => return Ok(Value::Nil),
}; };
let mut c = counter.borrow_mut(); let mut c = counter.borrow_mut();
let n = *c; let n = *c;
*c += 1; *c += 1;
Ok(Value::from(vec![n.into(), next])) Ok(Value::from(vec![n.into(), next]).to_cell())
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) 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::<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::iter_pack(None))
}
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::iter_pack(None))
} 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 => {
let res = vmcalliter!(vm; iter.clone())?;
if res.is_some() {
*state.borrow_mut() = Step::Going;
}
Ok(Value::iter_pack(res))
},
Step::Going => {
for _ in 0..(by-1) {
if vmcall!(vm; iter.clone())? == Value::Nil {
return Ok(Value::iter_pack(None))
}
}
let Some(x) = vmcalliter!(vm; iter.clone())? else {
return Ok(Value::iter_pack(None))
};
*state.borrow_mut() = Step::Going;
Ok(x)
},
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(x)
}
*lst.borrow_mut() = Some(l);
}
Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop()))
};
Ok(NativeFunc::new(Box::new(f), 0).into())
}
//
// join
//
#[native_func(2..)] #[native_func(2..)]
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, i1, i2], rest) = unpack_varargs!(args); let ([_, i1, i2], rest) = unpack_varargs!(args);
@ -365,11 +300,11 @@ pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut res = Vec::with_capacity(iters.len()); let mut res = Vec::with_capacity(iters.len());
for i in iters.iter() { for i in iters.iter() {
match vmcalliter!(vm; i.clone())? { match vmcalliter!(vm; i.clone())? {
Some(v) => res.push(v), Some(v) => res.push(cell_take(v)),
None => return Ok(Value::iter_pack(None)), None => return Ok(Value::Nil),
}; };
} }
Ok(Value::from(res)) Ok(Value::from(res).to_cell())
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0).into())
@ -387,16 +322,16 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
let mut s = state.borrow_mut(); let mut s = state.borrow_mut();
if s.1 { if s.1 {
return Ok(Value::iter_pack(None)); return Ok(Value::Nil);
} }
let n = s.0; let n = s.0;
s.0 = (s.0 + 1) % iters.len(); s.0 = (s.0 + 1) % iters.len();
drop(s); drop(s);
match vmcalliter!(vm; iters[n].clone())? { match vmcalliter!(vm; iters[n].clone())? {
Some(v) => Ok(v), Some(v) => Ok(v.into()),
None => { None => {
state.borrow_mut().1 = true; state.borrow_mut().1 = true;
Ok(Value::iter_pack(None)) Ok(Value::Nil)
} }
} }
}; };
@ -406,13 +341,14 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[derive(Default)] #[derive(Default)]
enum Intersperse { enum Intersperse {
Init, Waiting, HasNext(Value), #[default] End Init, Waiting, HasNext(Rc<RefCell<Value>>), #[default] End
} }
#[native_func(2)] #[native_func(2)]
pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val, iter] = unpack_args!(args); let [_, val, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let val = val.to_cell();
let state = RefCell::new(Intersperse::Init); let state = RefCell::new(Intersperse::Init);
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
@ -420,11 +356,11 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Intersperse::Init => match vmcalliter!(vm; iter.clone())? { Intersperse::Init => match vmcalliter!(vm; iter.clone())? {
Some(v) => { Some(v) => {
*state.borrow_mut() = Intersperse::Waiting; *state.borrow_mut() = Intersperse::Waiting;
Ok(v) Ok(v.into())
}, },
None => { None => {
*state.borrow_mut() = Intersperse::End; *state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None)) Ok(Value::Nil)
}, },
}, },
Intersperse::Waiting => match vmcalliter!(vm; iter.clone())? { Intersperse::Waiting => match vmcalliter!(vm; iter.clone())? {
@ -434,14 +370,14 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}, },
None => { None => {
*state.borrow_mut() = Intersperse::End; *state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None)) Ok(Value::Nil)
}, },
}, },
Intersperse::HasNext(v) => { Intersperse::HasNext(v) => {
*state.borrow_mut() = Intersperse::Waiting; *state.borrow_mut() = Intersperse::Waiting;
Ok(v) Ok(v.into())
}, },
Intersperse::End => Ok(Value::iter_pack(None)), Intersperse::End => Ok(Value::Nil),
} }
}; };
@ -461,7 +397,7 @@ pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
return vmcall!(vm; iter2.clone()) return vmcall!(vm; iter2.clone())
} }
match vmcalliter!(vm; iter1.clone())? { match vmcalliter!(vm; iter1.clone())? {
Some(v) => Ok(v), Some(v) => Ok(v.into()),
None => { None => {
*done_first.borrow_mut() = true; *done_first.borrow_mut() = true;
vmcall!(vm; iter2.clone()) vmcall!(vm; iter2.clone())
@ -498,12 +434,12 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let v = vmcalliter!(vm; b.clone())?; let v = vmcalliter!(vm; b.clone())?;
let mut s = state.borrow_mut(); let mut s = state.borrow_mut();
match v { match v {
Some(x) => s.b_data.push(x), Some(x) => s.b_data.push(cell_take(x)),
None => { None => {
s.b_done = true; s.b_done = true;
if s.b_idx == 0 { if s.b_idx == 0 {
s.done = true; s.done = true;
return Ok(Value::iter_pack(None)) return Ok(Value::Nil)
} }
s.b_idx = 0; s.b_idx = 0;
} }
@ -521,10 +457,10 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if state.borrow().b_idx == 0 { if state.borrow().b_idx == 0 {
match vmcalliter!(vm; a.clone())? { match vmcalliter!(vm; a.clone())? {
Some(v) => state.borrow_mut().a_val = v, Some(v) => state.borrow_mut().a_val = cell_take(v),
None => { None => {
state.borrow_mut().done = true; state.borrow_mut().done = true;
return Ok(Value::iter_pack(None)) return Ok(Value::Nil)
} }
} }
} }
@ -533,13 +469,118 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
state.borrow_mut().b_idx += 1; state.borrow_mut().b_idx += 1;
Ok(Value::from(vec![a_res, b_res])) Ok(Value::from(vec![a_res, b_res]).to_cell())
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) 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())
}
// //
// end iteration // end iteration
@ -553,7 +594,7 @@ pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let mut result = Vec::new(); let mut result = Vec::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
result.push(value); result.push(cell_take(value));
}; };
Ok(result.into()) Ok(result.into())
} }
@ -564,11 +605,10 @@ pub fn table(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let mut result = HashMap::new(); let mut result = HashMap::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
let Value::List(l) = value else { let Value::List(l) = cell_take(value) else {
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list") throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list")
}; };
let l = Rc::unwrap_or_clone(l).take(); let Ok([k, v]): std::result::Result<[Value; 2], Vec<Value>> = cell_take(l).try_into() else {
let Ok([k, v]): std::result::Result<[Value; 2], Vec<Value>> = l.try_into() else {
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list of length 2") throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list of length 2")
}; };
result.insert(k.try_into()?, v); result.insert(k.try_into()?, v);
@ -601,11 +641,11 @@ pub fn fold(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter: Value = iter.to_iter_function()?; let iter: Value = iter.to_iter_function()?;
let mut result = match vmcalliter!(vm; iter.clone())? { let mut result = match vmcalliter!(vm; iter.clone())? {
Some(v) => v, Some(v) => cell_take(v),
None => return Ok(Value::iter_pack(None)), None => return Ok(Value::Nil),
}; };
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
result = vmcall!(vm; func.clone(), result, value)?; result = vmcall!(vm; func.clone(), result, cell_take(value))?;
} }
Ok(result) Ok(result)
} }
@ -617,7 +657,7 @@ pub fn foldi(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = init; let mut result = init;
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
result = vmcall!(vm; func.clone(), result, value)?; result = vmcall!(vm; func.clone(), result, cell_take(value))?;
} }
Ok(result) Ok(result)
} }
@ -629,7 +669,7 @@ pub fn sum(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = Value::Int(0); let mut result = Value::Int(0);
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
result = (result + value)? result = (result + cell_take(value))?
} }
Ok(result) Ok(result)
} }
@ -641,7 +681,7 @@ pub fn prod(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = Value::Int(1); let mut result = Value::Int(1);
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
result = (result * value)? result = (result * cell_take(value))?
} }
Ok(result) Ok(result)
} }
@ -652,7 +692,7 @@ pub fn any(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
if value.truthy() { if RefCell::borrow(&value).truthy() {
return Ok(true.into()) return Ok(true.into())
} }
} }
@ -665,7 +705,7 @@ pub fn all(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
if !value.truthy() { if !RefCell::borrow(&value).truthy() {
return Ok(false.into()) return Ok(false.into())
} }
} }
@ -677,11 +717,11 @@ pub fn last(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args); let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let mut last = Value::Nil; let mut last = Rc::new(RefCell::new(Value::Nil));
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
last = value; last = value;
} }
Ok(last) Ok(cell_take(last))
} }
#[native_func(2)] #[native_func(2)]
@ -698,7 +738,7 @@ pub fn nth(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
} }
} }
match vmcalliter!(vm; iter)? { match vmcalliter!(vm; iter)? {
Some(v) => Ok(v), Some(v) => Ok(cell_take(v)),
None => Ok(Value::Nil), None => Ok(Value::Nil),
} }
} }
@ -711,7 +751,7 @@ pub fn contains(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for _ in 0.. { for _ in 0.. {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil), None => return Ok(Value::Nil),
Some(v) => if v == val { Some(v) => if *RefCell::borrow(&v) == val {
return Ok(true.into()) return Ok(true.into())
} }
} }
@ -727,7 +767,7 @@ pub fn index_of(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. { for i in 0.. {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil), None => return Ok(Value::Nil),
Some(v) => if v == val { Some(v) => if *RefCell::borrow(&v) == val {
return Ok(i.into()) return Ok(i.into())
} }
} }
@ -743,7 +783,7 @@ pub fn index_if(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. { for i in 0.. {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil), None => return Ok(Value::Nil),
Some(v) => if vmcall!(vm; func.clone(), v)?.truthy() { Some(v) => if vmcall!(vm; func.clone(), cell_take(v))?.truthy() {
return Ok(i.into()) return Ok(i.into())
} }
} }
@ -760,6 +800,7 @@ pub fn find(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil), None => return Ok(Value::Nil),
Some(v) => { Some(v) => {
let v = cell_take(v);
if vmcall!(vm; func.clone(), v.clone())?.truthy() { if vmcall!(vm; func.clone(), v.clone())?.truthy() {
return Ok(v) return Ok(v)
} }
@ -776,6 +817,7 @@ pub fn count(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut map = HashMap::new(); let mut map = HashMap::new();
while let Some(v) = vmcalliter!(vm; iter.clone())? { while let Some(v) = vmcalliter!(vm; iter.clone())? {
let v = cell_take(v);
let hv = v.try_into()?; let hv = v.try_into()?;
map.entry(hv) map.entry(hv)
.and_modify(|v: &mut Value| *v = (v.clone() + Value::Int(1)).unwrap()) .and_modify(|v: &mut Value| *v = (v.clone() + Value::Int(1)).unwrap())

View file

@ -1,7 +1,7 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use talc_lang::{parse_int, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{Complex64, Value}, Vm, exception::Result}; use talc_lang::{parse_int, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{exception::Result, Complex64, Value}, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::{unpack_args, unpack_varargs}; use crate::{unpack_args, unpack_varargs};
@ -142,7 +142,6 @@ fn to_radix_inner(n: i64, radix: u32) -> String {
String::from_utf8(result).expect("string was not valid utf8") String::from_utf8(result).expect("string was not valid utf8")
} }
#[native_func(1)] #[native_func(1)]
pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
@ -234,7 +233,46 @@ fn isqrt_inner(mut n: i64) -> i64 {
c c
} }
pub fn gcd_inner(a: i64, b: i64) -> i64 { #[native_func(1)]
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}")
};
if x < 0 {
throw!(*SYM_TYPE_ERROR, "isqrt: argument must be positive")
}
Ok(isqrt_inner(x).into())
}
#[native_func(1)]
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isprime expected integer argument, got {x:#}")
};
if x < 2 {
return Ok(false.into())
}
for p in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53] {
if x % p == 0 {
return Ok((x == p).into())
}
}
if x < 59 {
return Ok(false.into())
}
let lim = isqrt_inner(x);
let mut i = 54;
while i <= lim {
if x % (i + 1) == 0 { return Ok(false.into()) }
if x % (i + 5) == 0 { return Ok(false.into()) }
i += 6;
}
Ok(true.into())
}
fn gcd_inner(a: i64, b: i64) -> i64 {
let (mut a, mut b) = (a.abs(), b.abs()); let (mut a, mut b) = (a.abs(), b.abs());
if a == 0 { if a == 0 {
return b return b
@ -262,46 +300,6 @@ pub fn gcd_inner(a: i64, b: i64) -> i64 {
} }
#[native_func(1)]
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}")
};
if x < 0 {
throw!(*SYM_TYPE_ERROR, "isqrt: argument must be positive")
}
Ok(isqrt_inner(x).into())
}
#[native_func(1)]
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isprime expected integer argument, got {x:#}")
};
if x < 2 {
return Ok(false.into())
}
for p in [2, 3, 5, 7] {
if x % p == 0 {
return Ok((x == p).into())
}
}
if x < 11 {
return Ok(false.into())
}
let lim = isqrt_inner(x);
let mut i = 12;
while i <= lim+1 {
if x % (i - 1) == 0 { return Ok(false.into()) }
if x % (i + 1) == 0 { return Ok(false.into()) }
i += 6;
}
Ok(true.into())
}
#[native_func(2..)] #[native_func(2..)]
pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, x, y], rest) = unpack_varargs!(args); let ([_, x, y], rest) = unpack_varargs!(args);

View file

@ -1,5 +1,5 @@
use rand::{seq::SliceRandom, Rng}; use rand::Rng;
use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{range::RangeType, Value}, vmcalliter, Vm, exception::Result}; use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{cell_take, exception::Result, range::RangeType, Value}, vmcalliter, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;
@ -7,7 +7,6 @@ use crate::unpack_args;
pub fn load(vm: &mut Vm) { pub fn load(vm: &mut Vm) {
vm.set_global_name("rand", rand().into()); vm.set_global_name("rand", rand().into());
vm.set_global_name("rand_in", rand_in().into()); vm.set_global_name("rand_in", rand_in().into());
vm.set_global_name("shuf", shuf().into());
} }
#[native_func(0)] #[native_func(0)]
@ -21,54 +20,31 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
match col { match col {
Value::List(l) => { Value::List(l) => {
let l = l.borrow(); let l = l.borrow();
let Some(v) = l.choose(&mut rand::thread_rng()) else { let i = rand::thread_rng().gen_range(0..l.len());
throw!(*SYM_TYPE_ERROR, "rand_in: empty list") Ok(l[i].clone())
};
Ok(v.clone())
}, },
Value::Table(t) => { Value::Table(t) => {
let t = t.borrow(); let t = t.borrow();
if t.is_empty() {
throw!(*SYM_TYPE_ERROR, "rand_in: empty table")
};
let i = rand::thread_rng().gen_range(0..t.len()); let i = rand::thread_rng().gen_range(0..t.len());
let key = t.keys().nth(i).unwrap(); let key = t.keys().nth(i).unwrap();
Ok(key.clone().into_inner()) Ok(key.clone().into_inner())
}, },
Value::Range(r) => { Value::Range(r) => match r.ty {
if r.is_empty() {
throw!(*SYM_TYPE_ERROR, "rand_in: empty range")
}
match r.ty {
RangeType::Open => Ok(Value::Int( RangeType::Open => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..r.stop))), rand::thread_rng().gen_range(r.start..r.stop))),
RangeType::Closed => Ok(Value::Int( RangeType::Closed => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..=r.stop))), rand::thread_rng().gen_range(r.start..=r.stop))),
RangeType::Endless => throw!(*SYM_TYPE_ERROR, "rand_in: endless range"), RangeType::Endless => throw!(*SYM_TYPE_ERROR, "cannot select random element of endless range"),
} },
}
col => { col => {
let iter = col.to_iter_function()?; let iter = col.to_iter_function()?;
let mut values = Vec::new(); let mut values = Vec::new();
while let Some(v) = vmcalliter!(vm; iter.clone())? { while let Some(v) = vmcalliter!(vm; iter.clone())? {
values.push(v); values.push(v);
} }
if values.len() == 0 {
throw!(*SYM_TYPE_ERROR, "rand_in: empty iterator")
}
let i = rand::thread_rng().gen_range(0..values.len()); let i = rand::thread_rng().gen_range(0..values.len());
let v = values.swap_remove(i); let v = values.swap_remove(i);
Ok(v) Ok(cell_take(v))
} }
} }
} }
#[native_func(1)]
pub fn shuf(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, list] = unpack_args!(args);
let Value::List(list) = list else {
throw!(*SYM_TYPE_ERROR, "shuf expected list, got {list:#}")
};
list.borrow_mut().shuffle(&mut rand::thread_rng());
Ok(Value::List(list))
}

View file

@ -1,6 +1,6 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{exception, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{HashValue, Rational64, Value}, Vm, exception::Result}; use talc_lang::{exception, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{exception::Result, HashValue, Value}, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;
@ -59,15 +59,8 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
(Value::Int(x), "ratio") => Ok(Value::Ratio(x.into())), (Value::Int(x), "ratio") => Ok(Value::Ratio(x.into())),
(Value::Int(x), "float") => Ok(Value::Float(x as f64)), (Value::Int(x), "float") => Ok(Value::Float(x as f64)),
(Value::Int(x), "complex") => Ok(Value::Complex((x as f64).into())), (Value::Int(x), "complex") => Ok(Value::Complex((x as f64).into())),
(Value::Ratio(x), "int") => Ok(Value::Int(*x.numer() / *x.denom())),
(Value::Ratio(x), "float") => Ok(Value::Float(*x.numer() as f64 / *x.denom() as f64)), (Value::Ratio(x), "float") => Ok(Value::Float(*x.numer() as f64 / *x.denom() as f64)),
(Value::Ratio(x), "complex") => Ok(Value::Complex((*x.numer() as f64 / *x.denom() as f64).into())), (Value::Ratio(x), "complex") => Ok(Value::Complex((*x.numer() as f64 / *x.denom() as f64).into())),
(Value::Float(x), "int") => Ok(Value::Int(x as i64)),
(Value::Float(x), "ratio") => {
let r = Rational64::approximate_float(x)
.ok_or_else(|| exception!(*SYM_TYPE_ERROR, "float {x:?} could not be converted to ratio"))?;
Ok(Value::Ratio(r))
}
(Value::Float(x), "complex") => Ok(Value::Complex(x.into())), (Value::Float(x), "complex") => Ok(Value::Complex(x.into())),
(Value::String(s), "int") (Value::String(s), "int")
@ -92,19 +85,18 @@ pub fn copy_inner(value: Value) -> Result<Value> {
| Value::Complex(_) | Value::Range(_) | Value::String(_) | Value::Complex(_) | Value::Range(_) | Value::String(_)
=> Ok(value), => Ok(value),
Value::Cell(c) => { Value::Cell(c) => {
let c = Rc::unwrap_or_clone(c).take(); let c = copy_inner(talc_lang::value::cell_take(c))?;
let c = copy_inner(c)?;
Ok(RefCell::new(c).into()) Ok(RefCell::new(c).into())
}, },
Value::List(l) => { Value::List(l) => {
let l = Rc::unwrap_or_clone(l).take(); let l = talc_lang::value::cell_take(l);
let v: Result<Vec<Value>> = l.into_iter() let v: Result<Vec<Value>> = l.into_iter()
.map(copy_inner) .map(copy_inner)
.collect(); .collect();
Ok(v?.into()) Ok(v?.into())
}, },
Value::Table(t) => { Value::Table(t) => {
let t = Rc::unwrap_or_clone(t).take(); let t = talc_lang::value::cell_take(t);
let v: Result<HashMap<HashValue, Value>> = t.into_iter() let v: Result<HashMap<HashValue, Value>> = t.into_iter()
.map(|(k, v)| copy_inner(v).map(|v| (k, v))) .map(|(k, v)| copy_inner(v).map(|v| (k, v)))
.collect(); .collect();