Compare commits
3 commits
9996ca1574
...
bc3fb597d5
Author | SHA1 | Date | |
---|---|---|---|
bc3fb597d5 | |||
2c54025d00 | |||
3e497d3ebc |
17 changed files with 353 additions and 364 deletions
|
@ -1,8 +1,3 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = ["talc-lang", "talc-bin", "talc-std", "talc-macros"]
|
||||||
"talc-lang",
|
|
||||||
"talc-bin",
|
|
||||||
"talc-std",
|
|
||||||
"talc-macros",
|
|
||||||
]
|
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
|
@ -174,7 +174,7 @@ pub struct Catch {
|
||||||
pub types: Option<Vec<Symbol>>,
|
pub types: Option<Vec<Symbol>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TryTable {
|
pub struct TryTable {
|
||||||
pub catches: Vec<Catch>,
|
pub catches: Vec<Catch>,
|
||||||
pub local_count: usize,
|
pub local_count: usize,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display};
|
use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display};
|
||||||
use super::{HashValue, RcTable, Value};
|
use crate::{value::{HashValue, Value}, symbol::{SYM_DATA, SYM_MSG, SYM_TYPE}, symbol::Symbol};
|
||||||
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>;
|
||||||
|
|
||||||
|
@ -52,7 +51,7 @@ impl Exception {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_table(self) -> RcTable {
|
pub fn to_table(self) -> Rc<RefCell<HashMap<HashValue, Value>>> {
|
||||||
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 {
|
||||||
|
@ -78,16 +77,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::value::exception::Exception::new_with_msg(
|
$crate::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::value::exception::exception!($exc_ty, $fstr,)
|
$crate::exception::exception!($exc_ty, $fstr,)
|
||||||
};
|
};
|
||||||
($exc_ty:expr) => {
|
($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_export]
|
||||||
macro_rules! throw {
|
macro_rules! throw {
|
||||||
($($args:tt)*) => {
|
($($args:tt)*) => {
|
||||||
return Err($crate::value::exception::exception!($($args)*))
|
return Err($crate::exception::exception!($($args)*))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ 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;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::{collections::HashMap, sync::{Arc, Mutex, OnceLock}};
|
use std::{collections::HashMap, sync::{Mutex, OnceLock}};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct SymbolTable {
|
struct SymbolTable {
|
||||||
names: Vec<Arc<str>>,
|
names: Vec<&'static str>,
|
||||||
values: HashMap<Arc<str>, Symbol>
|
values: HashMap<&'static str, Symbol>
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
@ -24,6 +24,8 @@ 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);
|
||||||
|
@ -60,7 +62,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,9 +70,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: Arc<str> = name.into();
|
let name = String::leak(name.to_owned());
|
||||||
|
|
||||||
table.names.push(name.clone());
|
table.names.push(name);
|
||||||
table.values.insert(name, sym);
|
table.values.insert(name, sym);
|
||||||
|
|
||||||
sym
|
sym
|
||||||
|
@ -99,7 +101,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) -> Arc<str> {
|
pub fn name(self) -> &'static 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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::rc::Rc;
|
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)]
|
#[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, Default)]
|
#[derive(Debug)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub attrs: FuncAttrs,
|
pub attrs: FuncAttrs,
|
||||||
pub chunk: Rc<Chunk>,
|
pub chunk: Rc<Chunk>,
|
||||||
|
|
|
@ -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 {
|
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(cell_take(i))?.to_cell()),
|
Some(i) => Ok(col.index(i)?.to_cell()),
|
||||||
None => Ok(Value::Nil)
|
None => Ok(Value::from(*SYM_END_ITERATION)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
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(cell_take(v));
|
vals.push(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, cell_take(i), cell_take(v))?;
|
col.store_index(vm, i, v)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -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}, exception::{Exception, throw}};
|
use self::{range::{Range, RangeType}, function::{Function, NativeFunc}};
|
||||||
|
|
||||||
pub mod exception;
|
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub mod ops;
|
pub mod ops;
|
||||||
pub mod range;
|
pub mod range;
|
||||||
|
@ -207,8 +207,3 @@ 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()
|
|
||||||
}
|
|
||||||
|
|
|
@ -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::{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 {
|
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))
|
(V::Int(x), V::Int(y)) if y >= 0 => Ok(V::Int(ipow(x, y as u64)?)),
|
||||||
=> Ok(V::Ratio(rpow(x, 1, y)?.into())),
|
(V::Int(x), V::Int(y)) => 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,18 +319,17 @@ impl Value {
|
||||||
Value::Cell(Rc::new(RefCell::new(self)))
|
Value::Cell(Rc::new(RefCell::new(self)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_unpack(self) -> Result<Option<Rc<RefCell<Self>>>> {
|
pub fn iter_unpack(self) -> Option<Self> {
|
||||||
match self {
|
match self {
|
||||||
Self::Nil => Ok(None),
|
Self::Symbol(s) if s == *SYM_END_ITERATION => None,
|
||||||
Self::Cell(v) => Ok(Some(v)),
|
v => 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.to_cell(),
|
Some(v) => v,
|
||||||
None => Value::Nil,
|
None => Value::from(*SYM_END_ITERATION)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,11 +339,8 @@ 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> {
|
||||||
if let Some(v) = range_iter.borrow_mut().next() {
|
Ok(Value::iter_pack(range_iter.borrow_mut().next()
|
||||||
Ok(Value::from(v).to_cell())
|
.map(Value::from)))
|
||||||
} else {
|
|
||||||
Ok(Value::Nil)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||||
},
|
},
|
||||||
|
@ -354,9 +350,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()).to_cell())
|
Ok(Value::from(v.to_string()))
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nil)
|
Ok(Value::from(*SYM_END_ITERATION))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||||
|
@ -367,9 +363,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().to_cell())
|
Ok(v.clone())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nil)
|
Ok(Value::from(*SYM_END_ITERATION))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||||
|
@ -378,11 +374,8 @@ 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> {
|
||||||
if let Some(v) = keys.borrow_mut().next() {
|
Ok(Value::iter_pack(keys.borrow_mut().next()
|
||||||
Ok(v.into_inner().to_cell())
|
.map(HashValue::into_inner)))
|
||||||
} else {
|
|
||||||
Ok(Value::Nil)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
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::{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 }
|
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>,
|
||||||
|
@ -18,7 +17,9 @@ impl CallFrame {
|
||||||
Self {
|
Self {
|
||||||
func,
|
func,
|
||||||
locals,
|
locals,
|
||||||
..Self::default()
|
try_frames: Vec::new(),
|
||||||
|
ip: 0,
|
||||||
|
root: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +93,8 @@ 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")
|
||||||
};
|
};
|
||||||
args.extend(cell_take(varargs));
|
let varargs = Rc::unwrap_or_clone(varargs).take();
|
||||||
|
args.extend(varargs);
|
||||||
}
|
}
|
||||||
vm.call_value(f.clone(), args)
|
vm.call_value(f.clone(), args)
|
||||||
};
|
};
|
||||||
|
@ -366,13 +368,12 @@ 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() {
|
match self.pop().iter_unpack() {
|
||||||
Value::Cell(c) => self.push(cell_take(c)),
|
Some(v) => self.push(v),
|
||||||
Value::Nil => {
|
None => {
|
||||||
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())
|
||||||
|
@ -404,9 +405,27 @@ impl Vm {
|
||||||
if let Value::NativeFunc(nf) = &args[0] {
|
if let Value::NativeFunc(nf) = &args[0] {
|
||||||
let nf = nf.clone();
|
let nf = nf.clone();
|
||||||
|
|
||||||
self.call_stack.push(std::mem::take(frame));
|
// safety: frame is restored immediately
|
||||||
let res = (nf.func)(self, args)?;
|
// after function call ends
|
||||||
*frame = self.call_stack.pop().expect("no frame to pop");
|
// ~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);
|
||||||
|
|
||||||
|
// 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] {
|
||||||
|
@ -414,10 +433,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 func = func.clone();
|
let new_frame = CallFrame::new(func.clone(), args);
|
||||||
*frame = CallFrame::new(func, args);
|
let old_frame = std::mem::replace(frame, new_frame);
|
||||||
|
self.call_stack.push(old_frame);
|
||||||
} else {
|
} else {
|
||||||
unreachable!("already verified by calling get_call_type");
|
unreachable!("already verified by calling get_call_type");
|
||||||
}
|
}
|
||||||
|
@ -454,6 +473,6 @@ macro_rules! vmcall {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! vmcalliter {
|
macro_rules! vmcalliter {
|
||||||
($($input:tt)*) => {
|
($($input:tt)*) => {
|
||||||
$crate::vmcall!($($input)*).and_then(|v| v.iter_unpack())
|
$crate::vmcall!($($input)*).map(|v| v.iter_unpack())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::cmp::Ordering;
|
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 talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
|
|
@ -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 talc_macros::native_func;
|
||||||
|
|
||||||
use crate::{unpack_args, unpack_varargs};
|
use crate::{unpack_args, unpack_varargs};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{io::Write, time::{SystemTime, UNIX_EPOCH}};
|
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 talc_macros::native_func;
|
||||||
|
|
||||||
use crate::{unpack_args, SYM_IO_ERROR};
|
use crate::{unpack_args, SYM_IO_ERROR};
|
||||||
|
@ -33,13 +33,7 @@ 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() as i64).into())
|
Ok(time.as_secs_f64().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)]
|
||||||
|
@ -55,7 +49,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_nanos() as i64).into())
|
Ok(time.as_secs_f64().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(vm: &mut Vm) {
|
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("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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,37 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
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 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());
|
||||||
|
@ -71,9 +74,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]).to_cell())
|
Ok(Value::from(vec![k.into_inner(), v]))
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nil)
|
Ok(Value::iter_pack(None))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||||
|
@ -85,11 +88,7 @@ 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, _| {
|
||||||
if let Some(v) = v.borrow_mut().take() {
|
Ok(Value::iter_pack(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())
|
||||||
}
|
}
|
||||||
|
@ -99,17 +98,7 @@ 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().to_cell())
|
Ok(val.clone())
|
||||||
};
|
|
||||||
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())
|
||||||
}
|
}
|
||||||
|
@ -127,8 +116,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(), cell_take(val))?.to_cell()),
|
Some(val) => Ok(vmcall!(vm; map.clone(), val)?),
|
||||||
None => Ok(Value::Nil),
|
None => Ok(Value::iter_pack(None)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||||
|
@ -142,10 +131,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(), cell_take(val.clone()))?;
|
vmcall!(vm; tee.clone(), val.clone())?;
|
||||||
Ok(val.into())
|
Ok(val.into())
|
||||||
}
|
}
|
||||||
None => Ok(Value::Nil),
|
None => Ok(Value::iter_pack(None)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||||
|
@ -158,18 +147,13 @@ 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, _| {
|
||||||
match vmcalliter!(vm; iter.clone())? {
|
let Some(val) = vmcalliter!(vm; iter.clone())? else {
|
||||||
Some(val) => {
|
result.take();
|
||||||
let val = cell_take(val);
|
return Ok(Value::iter_pack(None))
|
||||||
|
};
|
||||||
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.to_cell())
|
Ok(r)
|
||||||
},
|
|
||||||
None => {
|
|
||||||
result.take();
|
|
||||||
Ok(Value::Nil)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||||
}
|
}
|
||||||
|
@ -181,11 +165,10 @@ 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 next = match vmcalliter!(vm; iter.clone())? {
|
let Some(next) = vmcalliter!(vm; iter.clone())? else {
|
||||||
Some(next) => next,
|
return Ok(Value::iter_pack(None))
|
||||||
None => return Ok(Value::Nil),
|
|
||||||
};
|
};
|
||||||
let res = vmcall!(vm; filter.clone(), cell_take(next.clone()))?;
|
let res = vmcall!(vm; filter.clone(), next.clone())?;
|
||||||
if res.truthy() {
|
if res.truthy() {
|
||||||
return Ok(next.into())
|
return Ok(next.into())
|
||||||
}
|
}
|
||||||
|
@ -194,26 +177,6 @@ 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);
|
||||||
|
@ -228,14 +191,11 @@ 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::Nil)
|
return Ok(Value::iter_pack(None))
|
||||||
}
|
}
|
||||||
let next = match vmcalliter!(vm; iter.clone())? {
|
let Some(next) = vmcalliter!(vm; iter.clone())? else {
|
||||||
Some(next) => next,
|
|
||||||
None => {
|
|
||||||
*taken.borrow_mut() = count;
|
*taken.borrow_mut() = count;
|
||||||
return Ok(Value::Nil)
|
return Ok(Value::iter_pack(None))
|
||||||
}
|
|
||||||
};
|
};
|
||||||
*taken.borrow_mut() += 1;
|
*taken.borrow_mut() += 1;
|
||||||
Ok(next.into())
|
Ok(next.into())
|
||||||
|
@ -275,19 +235,124 @@ 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 next = match vmcalliter!(vm; iter.clone())? {
|
let Some(next) = vmcalliter!(vm; iter.clone())? else {
|
||||||
Some(next) => cell_take(next),
|
return Ok(Value::iter_pack(None))
|
||||||
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]).to_cell())
|
Ok(Value::from(vec![n.into(), next]))
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
|
@ -300,11 +365,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(cell_take(v)),
|
Some(v) => res.push(v),
|
||||||
None => return Ok(Value::Nil),
|
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())
|
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||||
|
@ -322,16 +387,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::Nil);
|
return Ok(Value::iter_pack(None));
|
||||||
}
|
}
|
||||||
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.into()),
|
Some(v) => Ok(v),
|
||||||
None => {
|
None => {
|
||||||
state.borrow_mut().1 = true;
|
state.borrow_mut().1 = true;
|
||||||
Ok(Value::Nil)
|
Ok(Value::iter_pack(None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -341,14 +406,13 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
enum Intersperse {
|
enum Intersperse {
|
||||||
Init, Waiting, HasNext(Rc<RefCell<Value>>), #[default] End
|
Init, Waiting, HasNext(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, _| {
|
||||||
|
@ -356,11 +420,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.into())
|
Ok(v)
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
*state.borrow_mut() = Intersperse::End;
|
*state.borrow_mut() = Intersperse::End;
|
||||||
Ok(Value::Nil)
|
Ok(Value::iter_pack(None))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Intersperse::Waiting => match vmcalliter!(vm; iter.clone())? {
|
Intersperse::Waiting => match vmcalliter!(vm; iter.clone())? {
|
||||||
|
@ -370,14 +434,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::Nil)
|
Ok(Value::iter_pack(None))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Intersperse::HasNext(v) => {
|
Intersperse::HasNext(v) => {
|
||||||
*state.borrow_mut() = Intersperse::Waiting;
|
*state.borrow_mut() = Intersperse::Waiting;
|
||||||
Ok(v.into())
|
Ok(v)
|
||||||
},
|
},
|
||||||
Intersperse::End => Ok(Value::Nil),
|
Intersperse::End => Ok(Value::iter_pack(None)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -397,7 +461,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.into()),
|
Some(v) => Ok(v),
|
||||||
None => {
|
None => {
|
||||||
*done_first.borrow_mut() = true;
|
*done_first.borrow_mut() = true;
|
||||||
vmcall!(vm; iter2.clone())
|
vmcall!(vm; iter2.clone())
|
||||||
|
@ -434,12 +498,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(cell_take(x)),
|
Some(x) => s.b_data.push(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::Nil)
|
return Ok(Value::iter_pack(None))
|
||||||
}
|
}
|
||||||
s.b_idx = 0;
|
s.b_idx = 0;
|
||||||
}
|
}
|
||||||
|
@ -457,10 +521,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 = cell_take(v),
|
Some(v) => state.borrow_mut().a_val = v,
|
||||||
None => {
|
None => {
|
||||||
state.borrow_mut().done = true;
|
state.borrow_mut().done = true;
|
||||||
return Ok(Value::Nil)
|
return Ok(Value::iter_pack(None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -469,118 +533,13 @@ 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]).to_cell())
|
Ok(Value::from(vec![a_res, b_res]))
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
||||||
|
@ -594,7 +553,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(cell_take(value));
|
result.push(value);
|
||||||
};
|
};
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
|
@ -605,10 +564,11 @@ 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) = cell_take(value) else {
|
let Value::List(l) = value else {
|
||||||
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list")
|
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 {
|
let l = Rc::unwrap_or_clone(l).take();
|
||||||
|
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);
|
||||||
|
@ -641,11 +601,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) => cell_take(v),
|
Some(v) => v,
|
||||||
None => return Ok(Value::Nil),
|
None => return Ok(Value::iter_pack(None)),
|
||||||
};
|
};
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
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)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
@ -657,7 +617,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, cell_take(value))?;
|
result = vmcall!(vm; func.clone(), result, value)?;
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
@ -669,7 +629,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 + cell_take(value))?
|
result = (result + value)?
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
@ -681,7 +641,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 * cell_take(value))?
|
result = (result * value)?
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
@ -692,7 +652,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 RefCell::borrow(&value).truthy() {
|
if value.truthy() {
|
||||||
return Ok(true.into())
|
return Ok(true.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -705,7 +665,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 !RefCell::borrow(&value).truthy() {
|
if !value.truthy() {
|
||||||
return Ok(false.into())
|
return Ok(false.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -717,11 +677,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 = Rc::new(RefCell::new(Value::Nil));
|
let mut last = Value::Nil;
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||||
last = value;
|
last = value;
|
||||||
}
|
}
|
||||||
Ok(cell_take(last))
|
Ok(last)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -738,7 +698,7 @@ pub fn nth(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match vmcalliter!(vm; iter)? {
|
match vmcalliter!(vm; iter)? {
|
||||||
Some(v) => Ok(cell_take(v)),
|
Some(v) => Ok(v),
|
||||||
None => Ok(Value::Nil),
|
None => Ok(Value::Nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -751,7 +711,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 *RefCell::borrow(&v) == val {
|
Some(v) => if v == val {
|
||||||
return Ok(true.into())
|
return Ok(true.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -767,7 +727,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 *RefCell::borrow(&v) == val {
|
Some(v) => if v == val {
|
||||||
return Ok(i.into())
|
return Ok(i.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -783,7 +743,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(), cell_take(v))?.truthy() {
|
Some(v) => if vmcall!(vm; func.clone(), v)?.truthy() {
|
||||||
return Ok(i.into())
|
return Ok(i.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -800,7 +760,6 @@ 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)
|
||||||
}
|
}
|
||||||
|
@ -817,7 +776,6 @@ 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())
|
||||||
|
|
|
@ -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::{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 talc_macros::native_func;
|
||||||
|
|
||||||
use crate::{unpack_args, unpack_varargs};
|
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")
|
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);
|
||||||
|
@ -233,46 +234,7 @@ fn isqrt_inner(mut n: i64) -> i64 {
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
pub fn gcd_inner(a: i64, b: i64) -> i64 {
|
||||||
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
|
||||||
|
@ -300,6 +262,46 @@ 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);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use rand::Rng;
|
use rand::{seq::SliceRandom, Rng};
|
||||||
use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{cell_take, exception::Result, range::RangeType, Value}, vmcalliter, Vm};
|
use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{range::RangeType, Value}, vmcalliter, Vm, exception::Result};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -7,6 +7,7 @@ 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)]
|
||||||
|
@ -20,31 +21,54 @@ 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 i = rand::thread_rng().gen_range(0..l.len());
|
let Some(v) = l.choose(&mut rand::thread_rng()) else {
|
||||||
Ok(l[i].clone())
|
throw!(*SYM_TYPE_ERROR, "rand_in: empty list")
|
||||||
|
};
|
||||||
|
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) => match r.ty {
|
Value::Range(r) => {
|
||||||
|
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, "cannot select random element of endless range"),
|
RangeType::Endless => throw!(*SYM_TYPE_ERROR, "rand_in: 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(cell_take(v))
|
Ok(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))
|
||||||
|
}
|
||||||
|
|
|
@ -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::{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 talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -59,8 +59,15 @@ 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")
|
||||||
|
@ -85,18 +92,19 @@ 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 = 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())
|
Ok(RefCell::new(c).into())
|
||||||
},
|
},
|
||||||
Value::List(l) => {
|
Value::List(l) => {
|
||||||
let l = talc_lang::value::cell_take(l);
|
let l = Rc::unwrap_or_clone(l).take();
|
||||||
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 = talc_lang::value::cell_take(t);
|
let t = Rc::unwrap_or_clone(t).take();
|
||||||
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();
|
||||||
|
|
Loading…
Reference in a new issue