diff --git a/talc-lang/src/chunk.rs b/talc-lang/src/chunk.rs index d16581f..adeafdf 100644 --- a/talc-lang/src/chunk.rs +++ b/talc-lang/src/chunk.rs @@ -174,7 +174,7 @@ pub struct Catch { pub types: Option>, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct TryTable { pub catches: Vec, pub local_count: usize, diff --git a/talc-lang/src/value/exception.rs b/talc-lang/src/exception.rs similarity index 84% rename from talc-lang/src/value/exception.rs rename to talc-lang/src/exception.rs index 37e08e7..67174a0 100644 --- a/talc-lang/src/value/exception.rs +++ b/talc-lang/src/exception.rs @@ -1,6 +1,5 @@ use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display}; -use super::{HashValue, RcTable, Value}; -use crate::{symbol::{SYM_DATA, SYM_MSG, SYM_TYPE}, symbol::Symbol}; +use crate::{value::{HashValue, Value}, symbol::{SYM_DATA, SYM_MSG, SYM_TYPE}, symbol::Symbol}; pub type Result = std::result::Result; @@ -52,7 +51,7 @@ impl Exception { } } - pub fn to_table(self) -> RcTable { + pub fn to_table(self) -> Rc>> { let mut table = HashMap::new(); table.insert((*SYM_TYPE).into(), self.ty.into()); if let Some(msg) = self.msg { @@ -78,16 +77,16 @@ impl Display for Exception { #[macro_export] macro_rules! exception { ($exc_ty:expr, $fstr:literal, $($arg:expr),*) => { - $crate::value::exception::Exception::new_with_msg( + $crate::exception::Exception::new_with_msg( $exc_ty, format!($fstr, $($arg),*).into() ) }; ($exc_ty:expr, $fstr:literal) => { - $crate::value::exception::exception!($exc_ty, $fstr,) + $crate::exception::exception!($exc_ty, $fstr,) }; ($exc_ty:expr) => { - $crate::value::exception::Exception::new($exc_ty) + $crate::exception::Exception::new($exc_ty) }; } @@ -96,7 +95,7 @@ pub use exception; #[macro_export] macro_rules! throw { ($($args:tt)*) => { - return Err($crate::value::exception::exception!($($args)*)) + return Err($crate::exception::exception!($($args)*)) }; } diff --git a/talc-lang/src/lib.rs b/talc-lang/src/lib.rs index 5af8d77..6894f9b 100644 --- a/talc-lang/src/lib.rs +++ b/talc-lang/src/lib.rs @@ -15,6 +15,7 @@ pub mod symbol; pub mod ast; pub mod value; +pub mod exception; pub mod chunk; pub mod compiler; diff --git a/talc-lang/src/symbol.rs b/talc-lang/src/symbol.rs index 9a941b6..021c2a4 100644 --- a/talc-lang/src/symbol.rs +++ b/talc-lang/src/symbol.rs @@ -24,6 +24,8 @@ lazy_static! { pub static ref SYM_FUNCTION: Symbol = symbol!(function); 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_MSG: Symbol = symbol!(msg); pub static ref SYM_DATA: Symbol = symbol!(data); diff --git a/talc-lang/src/value/function.rs b/talc-lang/src/value/function.rs index f3bd7ae..afdda20 100644 --- a/talc-lang/src/value/function.rs +++ b/talc-lang/src/value/function.rs @@ -1,8 +1,8 @@ use std::rc::Rc; -use crate::{chunk::Chunk, Vm}; +use crate::{chunk::Chunk, Vm, exception::Result}; -use super::{Value, exception::Result}; +use super::{Value}; #[derive(Clone, Copy, Debug, Default)] pub struct FuncAttrs { @@ -10,7 +10,7 @@ pub struct FuncAttrs { pub variadic: bool, } -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Function { pub attrs: FuncAttrs, pub chunk: Rc, diff --git a/talc-lang/src/value/index.rs b/talc-lang/src/value/index.rs index 6ff51ef..3397562 100644 --- a/talc-lang/src/value/index.rs +++ b/talc-lang/src/value/index.rs @@ -1,6 +1,6 @@ -use crate::{symbol::{SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::{cell_take, function::NativeFunc}, vmcalliter, Vm}; +use crate::{symbol::{SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::function::NativeFunc, vmcalliter, Vm, exception::{Result, throw}}; -use super::{Value, exception::{Result, throw}, range::RangeType}; +use super::{Value, range::RangeType}; impl Value { pub fn index(&self, idx: Self) -> Result { @@ -34,8 +34,8 @@ impl Value { let col = col.clone(); let func = move |vm: &mut Vm, _| { match vmcalliter!(vm; ii.clone())? { - Some(i) => Ok(col.index(cell_take(i))?.to_cell()), - None => Ok(Value::Nil) + Some(i) => Ok(col.index(i)?.to_cell()), + None => Ok(Value::from(*SYM_END_ITERATION)), } }; Ok(NativeFunc::new(Box::new(func), 0).into()) @@ -67,7 +67,7 @@ impl Value { let iter = val.to_iter_function()?; let mut vals = Vec::new(); while let Some(v) = vmcalliter!(vm; iter.clone())? { - vals.push(cell_take(v)); + vals.push(v); } let mut tm = t.borrow_mut(); match r.ty { @@ -114,7 +114,7 @@ impl Value { let Some(v) = vmcalliter!(vm; val.clone())? else { throw!(*SYM_INDEX_ERROR, "not enough values provided for store index") }; - col.store_index(vm, cell_take(i), cell_take(v))?; + col.store_index(vm, i, v)?; } Ok(()) } else { diff --git a/talc-lang/src/value/mod.rs b/talc-lang/src/value/mod.rs index 0a02c5d..cee77e5 100644 --- a/talc-lang/src/value/mod.rs +++ b/talc-lang/src/value/mod.rs @@ -4,10 +4,10 @@ pub use num_complex::Complex64; pub use num_rational::Rational64; use crate::symbol::{Symbol, SYM_HASH_ERROR}; +use crate::exception::{Exception, throw}; -use self::{range::{Range, RangeType}, function::{Function, NativeFunc}, exception::{Exception, throw}}; +use self::{range::{Range, RangeType}, function::{Function, NativeFunc}}; -pub mod exception; pub mod function; pub mod ops; pub mod range; @@ -207,8 +207,3 @@ impl_from!(String, String, into); impl_from!(&str, String, into); impl_from!(Box, String, into); impl_from!(RefCell, Cell, rc); - -#[inline] -pub fn cell_take(cell: Rc>) -> T { - Rc::unwrap_or_clone(cell).into_inner() -} diff --git a/talc-lang/src/value/ops.rs b/talc-lang/src/value/ops.rs index 76d5e34..80c2836 100644 --- a/talc-lang/src/value/ops.rs +++ b/talc-lang/src/value/ops.rs @@ -3,9 +3,9 @@ use std::{cell::RefCell, cmp::Ordering, ops::{Add, Div, Mul, Neg, Sub}, rc::Rc}; use num_complex::Complex64; use num_rational::Rational64; -use crate::{symbol::SYM_TYPE_ERROR, value::range::RangeType, Vm}; +use crate::{exception::{throw, Result}, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR}, value::range::RangeType, Vm}; -use super::{exception::{throw, Result}, function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value}; +use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value}; impl Value { pub fn truthy(&self) -> bool { @@ -178,8 +178,8 @@ impl Value { 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::Int(x), V::Int(y)) if y >= 0 => Ok(V::Int(ipow(x, y as u64)?)), + (V::Int(x), V::Int(y)) => Ok(V::Ratio(rpow(x, 1, y)?.into())), (V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(y))), (V::Ratio(x), V::Ratio(y)) @@ -319,20 +319,19 @@ impl Value { Value::Cell(Rc::new(RefCell::new(self))) } - pub fn iter_unpack(self) -> Result>>> { + pub fn iter_unpack(self) -> Option { match self { - Self::Nil => Ok(None), - Self::Cell(v) => Ok(Some(v)), - _ => throw!(*SYM_TYPE_ERROR, "expected iterator to return cell or nil") + Self::Symbol(s) if s == *SYM_END_ITERATION => None, + v => Some(v), } } - pub fn iter_pack(v: Option) -> Self { - match v { - Some(v) => v.to_cell(), - None => Value::Nil, - } - } + pub fn iter_pack(v: Option) -> Self { + match v { + Some(v) => v, + None => Value::from(*SYM_END_ITERATION) + } + } pub fn to_iter_function(self) -> Result { match self { @@ -340,11 +339,8 @@ impl Value { Self::Range(range) => { let range_iter = RefCell::new(range.into_iter()); let f = move |_: &mut Vm, _: Vec| -> Result { - if let Some(v) = range_iter.borrow_mut().next() { - Ok(Value::from(v).to_cell()) - } else { - Ok(Value::Nil) - } + Ok(Value::iter_pack(range_iter.borrow_mut().next() + .map(Value::from))) }; Ok(NativeFunc::new(Box::new(f), 0).into()) }, @@ -354,9 +350,9 @@ impl Value { let pos = *byte_pos.borrow(); if let Some(v) = &s[pos..].chars().next() { *byte_pos.borrow_mut() += v.len_utf8(); - Ok(Value::from(v.to_string()).to_cell()) + Ok(Value::from(v.to_string())) } else { - Ok(Value::Nil) + Ok(Value::from(*SYM_END_ITERATION)) } }; Ok(NativeFunc::new(Box::new(f), 0).into()) @@ -367,9 +363,9 @@ impl Value { let i = *idx.borrow(); if let Some(v) = list.borrow().get(i) { *idx.borrow_mut() += 1; - Ok(v.clone().to_cell()) + Ok(v.clone()) } else { - Ok(Value::Nil) + Ok(Value::from(*SYM_END_ITERATION)) } }; Ok(NativeFunc::new(Box::new(f), 0).into()) @@ -378,11 +374,8 @@ impl Value { let keys: Vec = table.borrow().keys().cloned().collect(); let keys = RefCell::new(keys.into_iter()); let f = move |_: &mut Vm, _: Vec| -> Result { - if let Some(v) = keys.borrow_mut().next() { - Ok(v.into_inner().to_cell()) - } else { - Ok(Value::Nil) - } + Ok(Value::iter_pack(keys.borrow_mut().next() + .map(HashValue::into_inner))) }; Ok(NativeFunc::new(Box::new(f), 0).into()) }, diff --git a/talc-lang/src/vm.rs b/talc-lang/src/vm.rs index 40b0cb0..658876c 100644 --- a/talc-lang/src/vm.rs +++ b/talc-lang/src/vm.rs @@ -1,10 +1,9 @@ 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::{cell_take, exception::{throw, Exception, Result}, function::{FuncAttrs, Function, NativeFunc}, Value}}; +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}}; struct TryFrame { idx: usize, stack_len: usize } -#[derive(Default)] struct CallFrame { func: Rc, locals: Vec, @@ -18,7 +17,9 @@ impl CallFrame { Self { func, locals, - ..Self::default() + try_frames: Vec::new(), + ip: 0, + root: false, } } } @@ -92,7 +93,8 @@ fn get_call_outcome(mut args: Vec) -> Result { .expect("did not receive vararg") else { panic!("did not receive vararg") }; - args.extend(cell_take(varargs)); + let varargs = Rc::unwrap_or_clone(varargs).take(); + args.extend(varargs); } vm.call_value(f.clone(), args) }; @@ -366,13 +368,12 @@ impl Vm { // [i,cell(v)] -> [i,v] // [i,nil] -> [], ip = n I::IterTest(n) => { - match self.pop() { - Value::Cell(c) => self.push(cell_take(c)), - Value::Nil => { + match self.pop().iter_unpack() { + Some(v) => self.push(v), + None => { self.pop(); frame.ip = usize::from(n) }, - v => throw!(*SYM_TYPE_ERROR, "iterator returned invalid value {v}") } }, // try_frames.push(t, stack.len()) @@ -404,20 +405,27 @@ impl Vm { if let Value::NativeFunc(nf) = &args[0] { let nf = nf.clone(); - // safety: frame is restored immediately after - // function call ends - // ~25% performance improvement in for loop-heavy code + // safety: frame is restored immediately + // after function call ends + // ~25% performance improvement in + // code heavy on native function calls unsafe { let f = std::ptr::read(frame); self.call_stack.push(f); } - let res = (nf.func)(self, args)?; + let res = (nf.func)(self, args); - unsafe { - let f = self.call_stack.pop().expect("no frame to pop"); - std::ptr::write(frame, f); - } + // 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); } else if let Value::Function(func) = &args[0] { @@ -465,6 +473,6 @@ macro_rules! vmcall { #[macro_export] macro_rules! vmcalliter { ($($input:tt)*) => { - $crate::vmcall!($($input)*).and_then(|v| v.iter_unpack()) + $crate::vmcall!($($input)*).map(|v| v.iter_unpack()) } } diff --git a/talc-std/src/collection.rs b/talc-std/src/collection.rs index a5a6442..2d862c3 100644 --- a/talc-std/src/collection.rs +++ b/talc-std/src/collection.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use talc_lang::{exception, symbol::SYM_TYPE_ERROR, throw, value::{exception::Result, function::NativeFunc, Value}, vmcall, Vm}; +use talc_lang::{exception, exception::Result, symbol::SYM_TYPE_ERROR, throw, value::{function::NativeFunc, Value}, vmcall, Vm}; use talc_macros::native_func; use crate::unpack_args; diff --git a/talc-std/src/exception.rs b/talc-std/src/exception.rs index d784c98..ebf159b 100644 --- a/talc-std/src/exception.rs +++ b/talc-std/src/exception.rs @@ -1,4 +1,4 @@ -use talc_lang::{symbol::SYM_TYPE_ERROR, value::{exception::{throw, Exception, Result}, Value}, Vm}; +use talc_lang::{symbol::SYM_TYPE_ERROR, exception::{throw, Exception, Result}, value::Value, Vm}; use talc_macros::native_func; use crate::{unpack_args, unpack_varargs}; diff --git a/talc-std/src/io.rs b/talc-std/src/io.rs index a5124c9..b256ed9 100644 --- a/talc-std/src/io.rs +++ b/talc-std/src/io.rs @@ -1,6 +1,6 @@ use std::{io::Write, time::{SystemTime, UNIX_EPOCH}}; -use talc_lang::{value::{exception::{throw, Result}, Value}, vmcall, Vm}; +use talc_lang::{value::Value, vmcall, Vm, exception::{throw, Result}}; use talc_macros::native_func; use crate::{unpack_args, SYM_IO_ERROR}; @@ -33,13 +33,7 @@ pub fn readln(_: &mut Vm, _: Vec) -> Result { #[native_func(0)] pub fn time(_: &mut Vm, _: Vec) -> Result { let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards"); - Ok((time.as_secs() as i64).into()) -} - -#[native_func(0)] -pub fn time_ms(_: &mut Vm, _: Vec) -> Result { - let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards"); - Ok((time.as_millis() as i64).into()) + Ok(time.as_secs_f64().into()) } #[native_func(0)] @@ -55,7 +49,7 @@ pub fn time_fn(vm: &mut Vm, args: Vec) -> Result { vmcall!(vm; func)?; let tf = SystemTime::now(); let time = tf.duration_since(t0).expect("time went backwards"); - Ok((time.as_nanos() as i64).into()) + Ok((time.as_secs_f64() as i64).into()) } pub fn load(vm: &mut Vm) { @@ -64,7 +58,6 @@ pub fn load(vm: &mut Vm) { vm.set_global_name("readln", readln().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_fn", time_fn().into()); } diff --git a/talc-std/src/iter.rs b/talc-std/src/iter.rs index 10c7feb..d168513 100644 --- a/talc-std/src/iter.rs +++ b/talc-std/src/iter.rs @@ -1,6 +1,6 @@ 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_lang::{symbol::SYM_TYPE_ERROR, exception::Result, throw, value::{function::NativeFunc, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm}; use talc_macros::native_func; use crate::{unpack_args, unpack_varargs}; @@ -11,14 +11,12 @@ pub fn load(vm: &mut Vm) { 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()); @@ -76,9 +74,9 @@ pub fn pairs(_: &mut Vm, args: Vec) -> Result { let f = move |_: &mut Vm, _: Vec| -> Result { 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()) + Ok(Value::from(vec![k.into_inner(), v])) } else { - Ok(Value::Nil) + Ok(Value::iter_pack(None)) } }; Ok(NativeFunc::new(Box::new(f), 0).into()) @@ -90,11 +88,7 @@ pub fn once(_: &mut Vm, args: Vec) -> Result { 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(Value::iter_pack(v.borrow_mut().take())) }; Ok(NativeFunc::new(Box::new(f), 0).into()) } @@ -104,17 +98,7 @@ pub fn forever(_: &mut Vm, args: Vec) -> Result { 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) -> Result { - let [_, func] = unpack_args!(args); - - let f = move |vm: &mut Vm, _| { - Ok(vmcall!(vm; func.clone())?.to_cell()) + Ok(val.clone()) }; Ok(NativeFunc::new(Box::new(f), 0).into()) } @@ -132,8 +116,8 @@ pub fn map(_: &mut Vm, args: Vec) -> Result { 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), + Some(val) => Ok(vmcall!(vm; map.clone(), val)?), + None => Ok(Value::iter_pack(None)), } }; Ok(NativeFunc::new(Box::new(f), 0).into()) @@ -147,10 +131,10 @@ pub fn tee(_: &mut Vm, args: Vec) -> Result { let f = move |vm: &mut Vm, _| { match vmcalliter!(vm; iter.clone())? { Some(val) => { - vmcall!(vm; tee.clone(), cell_take(val.clone()))?; + vmcall!(vm; tee.clone(), val.clone())?; Ok(val.into()) } - None => Ok(Value::Nil), + None => Ok(Value::iter_pack(None)), } }; Ok(NativeFunc::new(Box::new(f), 0).into()) @@ -163,18 +147,13 @@ pub fn scan(_: &mut Vm, args: Vec) -> Result { 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) - }, - } + let Some(val) = vmcalliter!(vm; iter.clone())? else { + result.take(); + return Ok(Value::iter_pack(None)) + }; + let r = vmcall!(vm; func.clone(), result.take(), val)?; + *result.borrow_mut() = r.clone(); + Ok(r) }; Ok(NativeFunc::new(Box::new(f), 0).into()) } @@ -186,11 +165,10 @@ pub fn filter(_: &mut Vm, args: Vec) -> Result { let f = move |vm: &mut Vm, _| { loop { - let next = match vmcalliter!(vm; iter.clone())? { - Some(next) => next, - None => return Ok(Value::Nil), + let Some(next) = vmcalliter!(vm; iter.clone())? else { + return Ok(Value::iter_pack(None)) }; - let res = vmcall!(vm; filter.clone(), cell_take(next.clone()))?; + let res = vmcall!(vm; filter.clone(), next.clone())?; if res.truthy() { return Ok(next.into()) } @@ -199,26 +177,6 @@ pub fn filter(_: &mut Vm, args: Vec) -> Result { Ok(NativeFunc::new(Box::new(f), 0).into()) } -#[native_func(2)] -pub fn filter_map(_: &mut Vm, args: Vec) -> Result { - 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) -> Result { let [_, count, iter] = unpack_args!(args); @@ -233,14 +191,11 @@ pub fn take(_: &mut Vm, args: Vec) -> Result { let taken = RefCell::new(0); let f = move |vm: &mut Vm, _| { if *taken.borrow() >= count { - return Ok(Value::Nil) + return Ok(Value::iter_pack(None)) } - let next = match vmcalliter!(vm; iter.clone())? { - Some(next) => next, - None => { - *taken.borrow_mut() = count; - return Ok(Value::Nil) - } + let Some(next) = vmcalliter!(vm; iter.clone())? else { + *taken.borrow_mut() = count; + return Ok(Value::iter_pack(None)) }; *taken.borrow_mut() += 1; Ok(next.into()) @@ -280,14 +235,13 @@ pub fn enumerate(_: &mut Vm, args: Vec) -> Result { 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 Some(next) = vmcalliter!(vm; iter.clone())? else { + return Ok(Value::iter_pack(None)) }; let mut c = counter.borrow_mut(); let n = *c; *c += 1; - Ok(Value::from(vec![n.into(), next]).to_cell()) + Ok(Value::from(vec![n.into(), next])) }; Ok(NativeFunc::new(Box::new(f), 0).into()) @@ -299,12 +253,12 @@ pub fn cycle(_: &mut Vm, args: Vec) -> Result { let [_, iter] = unpack_args!(args); let iter = iter.to_iter_function()?; - let record = RefCell::new((Vec::>>::new(), false, 0)); + let record = RefCell::new((Vec::::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) + return Ok(Value::iter_pack(None)) } let val = r.0[r.2].clone(); r.2 = (r.2 + 1) % r.0.len(); @@ -319,7 +273,7 @@ pub fn cycle(_: &mut Vm, args: Vec) -> Result { let mut r = record.borrow_mut(); r.1 = true; if r.0.is_empty() { - Ok(Value::Nil) + Ok(Value::iter_pack(None)) } else { r.2 = (r.2 + 1) % r.0.len(); Ok(r.0[0].clone().into()) @@ -350,25 +304,23 @@ pub fn step(_: &mut Vm, args: Vec) -> Result { 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), - } + 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::Nil) + return Ok(Value::iter_pack(None)) } } let Some(x) = vmcalliter!(vm; iter.clone())? else { - return Ok(Value::Nil) + return Ok(Value::iter_pack(None)) }; *state.borrow_mut() = Step::Going; - Ok(x.into()) + Ok(x) }, Step::End => Ok(Value::Nil), }; @@ -385,15 +337,11 @@ pub fn rev(_: &mut Vm, args: Vec) -> Result { if lst.borrow().is_none() { let mut l = Vec::new(); while let Some(x) = vmcalliter!(vm; iter.clone())? { - l.push(cell_take(x)) + l.push(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), + *lst.borrow_mut() = Some(l); } + Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop())) }; Ok(NativeFunc::new(Box::new(f), 0).into()) @@ -417,11 +365,11 @@ pub fn zip(_: &mut Vm, args: Vec) -> Result { 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), + Some(v) => res.push(v), + None => return Ok(Value::iter_pack(None)), }; } - Ok(Value::from(res).to_cell()) + Ok(Value::from(res)) }; Ok(NativeFunc::new(Box::new(f), 0).into()) @@ -439,16 +387,16 @@ pub fn alternate(_: &mut Vm, args: Vec) -> Result { let f = move |vm: &mut Vm, _| { let mut s = state.borrow_mut(); if s.1 { - return Ok(Value::Nil); + return Ok(Value::iter_pack(None)); } let n = s.0; s.0 = (s.0 + 1) % iters.len(); drop(s); match vmcalliter!(vm; iters[n].clone())? { - Some(v) => Ok(v.into()), + Some(v) => Ok(v), None => { state.borrow_mut().1 = true; - Ok(Value::Nil) + Ok(Value::iter_pack(None)) } } }; @@ -458,14 +406,13 @@ pub fn alternate(_: &mut Vm, args: Vec) -> Result { #[derive(Default)] enum Intersperse { - Init, Waiting, HasNext(Rc>), #[default] End + Init, Waiting, HasNext(Value), #[default] End } #[native_func(2)] pub fn intersperse(_: &mut Vm, args: Vec) -> Result { 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, _| { @@ -473,11 +420,11 @@ pub fn intersperse(_: &mut Vm, args: Vec) -> Result { Intersperse::Init => match vmcalliter!(vm; iter.clone())? { Some(v) => { *state.borrow_mut() = Intersperse::Waiting; - Ok(v.into()) + Ok(v) }, None => { *state.borrow_mut() = Intersperse::End; - Ok(Value::Nil) + Ok(Value::iter_pack(None)) }, }, Intersperse::Waiting => match vmcalliter!(vm; iter.clone())? { @@ -487,14 +434,14 @@ pub fn intersperse(_: &mut Vm, args: Vec) -> Result { }, None => { *state.borrow_mut() = Intersperse::End; - Ok(Value::Nil) + Ok(Value::iter_pack(None)) }, }, Intersperse::HasNext(v) => { *state.borrow_mut() = Intersperse::Waiting; - Ok(v.into()) + Ok(v) }, - Intersperse::End => Ok(Value::Nil), + Intersperse::End => Ok(Value::iter_pack(None)), } }; @@ -514,7 +461,7 @@ pub fn chain(_: &mut Vm, args: Vec) -> Result { return vmcall!(vm; iter2.clone()) } match vmcalliter!(vm; iter1.clone())? { - Some(v) => Ok(v.into()), + Some(v) => Ok(v), None => { *done_first.borrow_mut() = true; vmcall!(vm; iter2.clone()) @@ -551,12 +498,12 @@ pub fn cartprod(_: &mut Vm, args: Vec) -> Result { let v = vmcalliter!(vm; b.clone())?; let mut s = state.borrow_mut(); match v { - Some(x) => s.b_data.push(cell_take(x)), + Some(x) => s.b_data.push(x), None => { s.b_done = true; if s.b_idx == 0 { s.done = true; - return Ok(Value::Nil) + return Ok(Value::iter_pack(None)) } s.b_idx = 0; } @@ -574,10 +521,10 @@ pub fn cartprod(_: &mut Vm, args: Vec) -> Result { if state.borrow().b_idx == 0 { match vmcalliter!(vm; a.clone())? { - Some(v) => state.borrow_mut().a_val = cell_take(v), + Some(v) => state.borrow_mut().a_val = v, None => { state.borrow_mut().done = true; - return Ok(Value::Nil) + return Ok(Value::iter_pack(None)) } } } @@ -606,7 +553,7 @@ pub fn list(vm: &mut Vm, args: Vec) -> Result { let iter = iter.to_iter_function()?; let mut result = Vec::new(); while let Some(value) = vmcalliter!(vm; iter.clone())? { - result.push(cell_take(value)); + result.push(value); }; Ok(result.into()) } @@ -617,10 +564,11 @@ pub fn table(vm: &mut Vm, args: Vec) -> Result { 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 { + let Value::List(l) = value else { throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list") }; - let Ok([k, v]): std::result::Result<[Value; 2], Vec> = cell_take(l).try_into() else { + let l = Rc::unwrap_or_clone(l).take(); + let Ok([k, v]): std::result::Result<[Value; 2], Vec> = l.try_into() else { throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list of length 2") }; result.insert(k.try_into()?, v); @@ -653,11 +601,11 @@ pub fn fold(vm: &mut Vm, args: Vec) -> Result { 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), + Some(v) => v, + None => return Ok(Value::iter_pack(None)), }; while let Some(value) = vmcalliter!(vm; iter.clone())? { - result = vmcall!(vm; func.clone(), result, cell_take(value))?; + result = vmcall!(vm; func.clone(), result, value)?; } Ok(result) } @@ -669,7 +617,7 @@ pub fn foldi(vm: &mut Vm, args: Vec) -> Result { let mut result = init; while let Some(value) = vmcalliter!(vm; iter.clone())? { - result = vmcall!(vm; func.clone(), result, cell_take(value))?; + result = vmcall!(vm; func.clone(), result, value)?; } Ok(result) } @@ -681,7 +629,7 @@ pub fn sum(vm: &mut Vm, args: Vec) -> Result { let mut result = Value::Int(0); while let Some(value) = vmcalliter!(vm; iter.clone())? { - result = (result + cell_take(value))? + result = (result + value)? } Ok(result) } @@ -693,7 +641,7 @@ pub fn prod(vm: &mut Vm, args: Vec) -> Result { let mut result = Value::Int(1); while let Some(value) = vmcalliter!(vm; iter.clone())? { - result = (result * cell_take(value))? + result = (result * value)? } Ok(result) } @@ -704,7 +652,7 @@ pub fn any(vm: &mut Vm, args: Vec) -> Result { let iter = iter.to_iter_function()?; while let Some(value) = vmcalliter!(vm; iter.clone())? { - if RefCell::borrow(&value).truthy() { + if value.truthy() { return Ok(true.into()) } } @@ -717,7 +665,7 @@ pub fn all(vm: &mut Vm, args: Vec) -> Result { let iter = iter.to_iter_function()?; while let Some(value) = vmcalliter!(vm; iter.clone())? { - if !RefCell::borrow(&value).truthy() { + if !value.truthy() { return Ok(false.into()) } } @@ -729,11 +677,11 @@ pub fn last(vm: &mut Vm, args: Vec) -> Result { let [_, iter] = unpack_args!(args); let iter = iter.to_iter_function()?; - let mut last = Rc::new(RefCell::new(Value::Nil)); + let mut last = Value::Nil; while let Some(value) = vmcalliter!(vm; iter.clone())? { last = value; } - Ok(cell_take(last)) + Ok(last) } #[native_func(2)] @@ -750,7 +698,7 @@ pub fn nth(vm: &mut Vm, args: Vec) -> Result { } } match vmcalliter!(vm; iter)? { - Some(v) => Ok(cell_take(v)), + Some(v) => Ok(v), None => Ok(Value::Nil), } } @@ -763,7 +711,7 @@ pub fn contains(vm: &mut Vm, args: Vec) -> Result { for _ in 0.. { match vmcalliter!(vm; iter.clone())? { None => return Ok(Value::Nil), - Some(v) => if *RefCell::borrow(&v) == val { + Some(v) => if v == val { return Ok(true.into()) } } @@ -779,7 +727,7 @@ pub fn index_of(vm: &mut Vm, args: Vec) -> Result { for i in 0.. { match vmcalliter!(vm; iter.clone())? { None => return Ok(Value::Nil), - Some(v) => if *RefCell::borrow(&v) == val { + Some(v) => if v == val { return Ok(i.into()) } } @@ -795,7 +743,7 @@ pub fn index_if(vm: &mut Vm, args: Vec) -> Result { 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() { + Some(v) => if vmcall!(vm; func.clone(), v)?.truthy() { return Ok(i.into()) } } @@ -812,7 +760,6 @@ pub fn find(vm: &mut Vm, args: Vec) -> Result { 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) } @@ -829,7 +776,6 @@ pub fn count(vm: &mut Vm, args: Vec) -> Result { 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()) diff --git a/talc-std/src/num.rs b/talc-std/src/num.rs index ed6d386..5b1d91e 100644 --- a/talc-std/src/num.rs +++ b/talc-std/src/num.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use lazy_static::lazy_static; -use talc_lang::{parse_int, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{exception::Result, Complex64, Value}, Vm}; +use talc_lang::{parse_int, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{Complex64, Value}, Vm, exception::Result}; use talc_macros::native_func; use crate::{unpack_args, unpack_varargs}; @@ -142,6 +142,7 @@ fn to_radix_inner(n: i64, radix: u32) -> String { String::from_utf8(result).expect("string was not valid utf8") } + #[native_func(1)] pub fn bin(_: &mut Vm, args: Vec) -> Result { let [_, x] = unpack_args!(args); @@ -233,46 +234,7 @@ fn isqrt_inner(mut n: i64) -> i64 { c } -#[native_func(1)] -pub fn isqrt(_: &mut Vm, args: Vec) -> Result { - 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) -> Result { - 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 { +pub fn gcd_inner(a: i64, b: i64) -> i64 { let (mut a, mut b) = (a.abs(), b.abs()); if a == 0 { return b @@ -300,6 +262,46 @@ fn gcd_inner(a: i64, b: i64) -> i64 { } +#[native_func(1)] +pub fn isqrt(_: &mut Vm, args: Vec) -> Result { + 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) -> Result { + 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..)] pub fn gcd(_: &mut Vm, args: Vec) -> Result { let ([_, x, y], rest) = unpack_varargs!(args); diff --git a/talc-std/src/random.rs b/talc-std/src/random.rs index c8e1610..3241008 100644 --- a/talc-std/src/random.rs +++ b/talc-std/src/random.rs @@ -1,5 +1,5 @@ -use rand::Rng; -use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{cell_take, exception::Result, range::RangeType, Value}, vmcalliter, Vm}; +use rand::{seq::SliceRandom, Rng}; +use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{range::RangeType, Value}, vmcalliter, Vm, exception::Result}; use talc_macros::native_func; use crate::unpack_args; @@ -7,6 +7,7 @@ use crate::unpack_args; pub fn load(vm: &mut Vm) { vm.set_global_name("rand", rand().into()); vm.set_global_name("rand_in", rand_in().into()); + vm.set_global_name("shuf", shuf().into()); } #[native_func(0)] @@ -19,32 +20,55 @@ pub fn rand_in(vm: &mut Vm, args: Vec) -> Result { let [_, col] = unpack_args!(args); match col { Value::List(l) => { - let l = l.borrow(); - let i = rand::thread_rng().gen_range(0..l.len()); - Ok(l[i].clone()) + let l = l.borrow(); + let Some(v) = l.choose(&mut rand::thread_rng()) else { + throw!(*SYM_TYPE_ERROR, "rand_in: empty list") + }; + Ok(v.clone()) }, Value::Table(t) => { 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 key = t.keys().nth(i).unwrap(); Ok(key.clone().into_inner()) }, - Value::Range(r) => match r.ty { - RangeType::Open => Ok(Value::Int( - rand::thread_rng().gen_range(r.start..r.stop))), - RangeType::Closed => Ok(Value::Int( - rand::thread_rng().gen_range(r.start..=r.stop))), - RangeType::Endless => throw!(*SYM_TYPE_ERROR, "cannot select random element of endless range"), - }, + Value::Range(r) => { + if r.is_empty() { + throw!(*SYM_TYPE_ERROR, "rand_in: empty range") + } + match r.ty { + RangeType::Open => Ok(Value::Int( + rand::thread_rng().gen_range(r.start..r.stop))), + RangeType::Closed => Ok(Value::Int( + rand::thread_rng().gen_range(r.start..=r.stop))), + RangeType::Endless => throw!(*SYM_TYPE_ERROR, "rand_in: endless range"), + } + } col => { let iter = col.to_iter_function()?; let mut values = Vec::new(); while let Some(v) = vmcalliter!(vm; iter.clone())? { 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 v = values.swap_remove(i); - Ok(cell_take(v)) + Ok(v) } } } + +#[native_func(1)] +pub fn shuf(_: &mut Vm, args: Vec) -> Result { + 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)) +} diff --git a/talc-std/src/value.rs b/talc-std/src/value.rs index ee5edf9..a192756 100644 --- a/talc-std/src/value.rs +++ b/talc-std/src/value.rs @@ -1,6 +1,6 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; -use talc_lang::{exception, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{exception::Result, HashValue, Value}, Vm}; +use talc_lang::{exception, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{HashValue, Rational64, Value}, Vm, exception::Result}; use talc_macros::native_func; use crate::unpack_args; @@ -59,8 +59,15 @@ pub fn as_(_: &mut Vm, args: Vec) -> Result { (Value::Int(x), "ratio") => Ok(Value::Ratio(x.into())), (Value::Int(x), "float") => Ok(Value::Float(x as f64)), (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), "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::String(s), "int") @@ -85,18 +92,19 @@ pub fn copy_inner(value: Value) -> Result { | Value::Complex(_) | Value::Range(_) | Value::String(_) => Ok(value), Value::Cell(c) => { - let c = copy_inner(talc_lang::value::cell_take(c))?; + let c = Rc::unwrap_or_clone(c).take(); + let c = copy_inner(c)?; Ok(RefCell::new(c).into()) }, Value::List(l) => { - let l = talc_lang::value::cell_take(l); + let l = Rc::unwrap_or_clone(l).take(); let v: Result> = l.into_iter() .map(copy_inner) .collect(); Ok(v?.into()) }, Value::Table(t) => { - let t = talc_lang::value::cell_take(t); + let t = Rc::unwrap_or_clone(t).take(); let v: Result> = t.into_iter() .map(|(k, v)| copy_inner(v).map(|v| (k, v))) .collect();