diff --git a/Cargo.toml b/Cargo.toml index 3f42c6b..5b5c8af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,4 @@ [workspace] -members = [ - "talc-lang", - "talc-bin", - "talc-std", - "talc-macros", -] +members = ["talc-lang", "talc-bin", "talc-std", "talc-macros"] resolver = "2" + diff --git a/talc-lang/src/symbol.rs b/talc-lang/src/symbol.rs index f47718f..9a941b6 100644 --- a/talc-lang/src/symbol.rs +++ b/talc-lang/src/symbol.rs @@ -1,11 +1,11 @@ -use std::{collections::HashMap, sync::{Arc, Mutex, OnceLock}}; +use std::{collections::HashMap, sync::{Mutex, OnceLock}}; use lazy_static::lazy_static; #[derive(Default)] struct SymbolTable { - names: Vec>, - values: HashMap, Symbol> + names: Vec<&'static str>, + values: HashMap<&'static str, Symbol> } lazy_static! { @@ -60,7 +60,7 @@ impl Symbol { pub fn get(name: &str) -> Self { 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 } @@ -68,9 +68,9 @@ impl Symbol { assert!(symno <= MAX_SYMBOL, "too many symbols"); let sym = Symbol(symno as u32); - let name: Arc = name.into(); + let name = String::leak(name.to_owned()); - table.names.push(name.clone()); + table.names.push(name); table.values.insert(name, sym); sym @@ -99,7 +99,7 @@ impl Symbol { /// # Panics /// If the mutex storing the symbol table is poisoned - pub fn name(self) -> Arc { + pub fn name(self) -> &'static str { let table = get_table().lock().expect("couldn't lock symbol table"); table.names.get(self.0 as usize).cloned().expect("symbol does not exist") } diff --git a/talc-lang/src/vm.rs b/talc-lang/src/vm.rs index 92feaa6..40b0cb0 100644 --- a/talc-lang/src/vm.rs +++ b/talc-lang/src/vm.rs @@ -404,9 +404,20 @@ impl Vm { if let Value::NativeFunc(nf) = &args[0] { let nf = nf.clone(); - self.call_stack.push(std::mem::take(frame)); + // safety: frame is restored immediately after + // function call ends + // ~25% performance improvement in for loop-heavy code + unsafe { + let f = std::ptr::read(frame); + self.call_stack.push(f); + } + let res = (nf.func)(self, args)?; - *frame = self.call_stack.pop().expect("no frame to pop"); + + unsafe { + let f = self.call_stack.pop().expect("no frame to pop"); + std::ptr::write(frame, f); + } self.stack.push(res); } else if let Value::Function(func) = &args[0] { @@ -414,10 +425,10 @@ impl Vm { if self.call_stack.len() + 1 >= self.stack_max { throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow") } - self.call_stack.push(std::mem::take(frame)); - let func = func.clone(); - *frame = CallFrame::new(func, args); + let new_frame = CallFrame::new(func.clone(), args); + let old_frame = std::mem::replace(frame, new_frame); + self.call_stack.push(old_frame); } else { unreachable!("already verified by calling get_call_type"); } diff --git a/talc-std/src/iter.rs b/talc-std/src/iter.rs index 86e5c16..10c7feb 100644 --- a/talc-std/src/iter.rs +++ b/talc-std/src/iter.rs @@ -6,12 +6,14 @@ use talc_macros::native_func; use crate::{unpack_args, unpack_varargs}; pub fn load(vm: &mut Vm) { + // begin vm.set_global_name("iter", iter().into()); vm.set_global_name("pairs", pairs().into()); vm.set_global_name("once", once().into()); vm.set_global_name("forever", forever().into()); vm.set_global_name("do_forever", forever().into()); + // modify vm.set_global_name("map", map().into()); vm.set_global_name("scan", scan().into()); vm.set_global_name("tee", tee().into()); @@ -20,15 +22,18 @@ pub fn load(vm: &mut Vm) { vm.set_global_name("take", take().into()); vm.set_global_name("skip", skip().into()); vm.set_global_name("enumerate", enumerate().into()); + vm.set_global_name("cycle", cycle().into()); + vm.set_global_name("step", step().into()); + vm.set_global_name("rev", rev().into()); + + // join vm.set_global_name("zip", zip().into()); vm.set_global_name("alternate", alternate().into()); vm.set_global_name("intersperse", intersperse().into()); vm.set_global_name("cartprod", cartprod().into()); - vm.set_global_name("cycle", cycle().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("table", table().into()); vm.set_global_name("len", len().into()); @@ -288,6 +293,118 @@ pub fn enumerate(_: &mut Vm, args: Vec) -> Result { Ok(NativeFunc::new(Box::new(f), 0).into()) } + +#[native_func(1)] +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 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) -> Result { + 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) -> Result { + let [_, iter] = unpack_args!(args); + let iter = iter.to_iter_function()?; + + let lst = RefCell::new(None); + let f = move |vm: &mut Vm, _| { + if lst.borrow().is_none() { + let mut l = Vec::new(); + while let Some(x) = vmcalliter!(vm; iter.clone())? { + l.push(cell_take(x)) + } + l.reverse(); + *lst.borrow_mut() = Some(l.into_iter()); + } + match lst.borrow_mut().as_mut().unwrap().next() { + Some(v) => Ok(v.to_cell()), + None => Ok(Value::Nil), + } + + }; + Ok(NativeFunc::new(Box::new(f), 0).into()) +} + + +// +// join +// + + #[native_func(2..)] pub fn zip(_: &mut Vm, args: Vec) -> Result { let ([_, i1, i2], rest) = unpack_varargs!(args); @@ -476,111 +593,6 @@ pub fn cartprod(_: &mut Vm, args: Vec) -> Result { } -#[native_func(1)] -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 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) -> Result { - 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) -> Result { - 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