removed variadics, prepared for closures

This commit is contained in:
trimill 2024-10-31 12:36:53 -04:00
parent e4b4c981d2
commit 754fbf6c2c
19 changed files with 538 additions and 231 deletions

View file

@ -88,38 +88,54 @@ impl TryFrom<Arg24> for Symbol {
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub enum Instruction { pub enum Instruction {
#[default] #[default]
Nop, Nop, // do nothing
LoadLocal(Arg24), LoadLocal(Arg24), // push nth local onto stack
StoreLocal(Arg24), StoreLocal(Arg24), // pop stack into nth local
NewLocal, NewLocal, // pop stack into a new local
DropLocal(Arg24), DropLocal(Arg24), // remove last n locals
LoadGlobal(Arg24), StoreGlobal(Arg24), LoadGlobal(Arg24), // load global by id
StoreGlobal(Arg24), // store global by id
Const(Arg24), CloseOver(Arg24), // load nth local and convert to cell, write back a copy
Int(Arg24), Closure(Arg24), // load constant function and fill state from stack
Symbol(Arg24), LoadUpvalue(Arg24), // load a cell from closure state to new local
Bool(bool), StoreUpvalue(Arg24), // load a cell from closure state to new local
Nil, LoadClosedLocal(Arg24), // load through cell in nth local
StoreClosedLocal(Arg24), // store through cell in nth local
Dup, DupTwo, Drop(Arg24), Swap, Const(Arg24), // push nth constant
Int(Arg24), // push integer
Symbol(Arg24), // push symbol
Bool(bool), // push boolean
Nil, // push nil
Dup,
DupTwo,
Drop(Arg24),
Swap,
UnaryOp(UnaryOp), UnaryOp(UnaryOp),
BinaryOp(BinaryOp), BinaryOp(BinaryOp),
NewList(u8), GrowList(u8), NewList(u8),
NewTable(u8), GrowTable(u8), GrowList(u8),
NewTable(u8),
GrowTable(u8),
Index, StoreIndex, Index,
StoreIndex,
Jump(Arg24), Jump(Arg24),
JumpTrue(Arg24), JumpTrue(Arg24),
JumpFalse(Arg24), JumpFalse(Arg24),
IterBegin, IterTest(Arg24), IterBegin,
IterTest(Arg24),
BeginTry(Arg24), EndTry, BeginTry(Arg24),
EndTry,
Call(u8), Call(u8),
Return, Return,
@ -137,6 +153,12 @@ impl std::fmt::Display for Instruction {
Symbol::try_from(s).expect("symbol does not exist").name()), Symbol::try_from(s).expect("symbol does not exist").name()),
Self::StoreGlobal(s) => write!(f, "storeglobal {}", Self::StoreGlobal(s) => write!(f, "storeglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()), Symbol::try_from(s).expect("symbol does not exist").name()),
Self::CloseOver(n) => write!(f, "closeover {}", usize::from(n)),
Self::Closure(c) => write!(f, "closure {}", usize::from(c)),
Self::LoadUpvalue(n) => write!(f, "load_upval {}", usize::from(n)),
Self::StoreUpvalue(n) => write!(f, "store_upval {}", usize::from(n)),
Self::LoadClosedLocal(n) => write!(f, "load_closed {}", usize::from(n)),
Self::StoreClosedLocal(n) => write!(f, "store_closed {}", usize::from(n)),
Self::Const(c) => write!(f, "const {}", usize::from(c)), Self::Const(c) => write!(f, "const {}", usize::from(c)),
Self::Int(i) => write!(f, "int {}", i64::from(i)), Self::Int(i) => write!(f, "int {}", i64::from(i)),
Self::Symbol(s) => write!(f, "symbol {}", Self::Symbol(s) => write!(f, "symbol {}",

View file

@ -12,6 +12,7 @@ use crate::value::Value;
pub struct Local { pub struct Local {
name: Rc<LStr>, name: Rc<LStr>,
scope: usize, scope: usize,
closed: bool,
} }
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
@ -46,12 +47,13 @@ impl<'a> Default for Compiler<'a> {
let locals = vec![Local { let locals = vec![Local {
name: lstr!("self").into(), name: lstr!("self").into(),
scope: 0, scope: 0,
closed: false,
}]; }];
Self { Self {
mode: CompilerMode::Function, mode: CompilerMode::Function,
parent: None, parent: None,
chunk: Chunk::new(), chunk: Chunk::new(),
attrs: FuncAttrs { arity: 0, variadic: false }, attrs: FuncAttrs::default(),
scope: 0, scope: 0,
locals, locals,
globals: Vec::new(), globals: Vec::new(),
@ -88,6 +90,7 @@ impl<'a> Compiler<'a> {
new.locals.push(Local { new.locals.push(Local {
name: (*arg).into(), name: (*arg).into(),
scope: 0, scope: 0,
closed: false,
}); });
} }
@ -96,13 +99,15 @@ impl<'a> Compiler<'a> {
pub fn finish(mut self) -> Function { pub fn finish(mut self) -> Function {
self.emit(I::Return); self.emit(I::Return);
Function { chunk: Rc::new(self.chunk), attrs: self.attrs } // TODO closure
Function::from_parts(Rc::new(self.chunk), self.attrs, Vec::new().into())
} }
pub fn finish_repl(mut self) -> (Function, Vec<Local>) { pub fn finish_repl(mut self) -> (Function, Vec<Local>) {
self.emit(I::Return); self.emit(I::Return);
( (
Function { chunk: Rc::new(self.chunk), attrs: self.attrs }, // TODO closure
Function::from_parts(Rc::new(self.chunk), self.attrs, Vec::new().into()),
self.globals self.globals
) )
} }
@ -128,6 +133,7 @@ impl<'a> Compiler<'a> {
&& matches!(instrs.get(instrs.len() - 2), Some(I::Dup)) && matches!(instrs.get(instrs.len() - 2), Some(I::Dup))
&& matches!(instrs.last(), Some( && matches!(instrs.last(), Some(
I::NewLocal | I::StoreLocal(_) | I::StoreGlobal(_) I::NewLocal | I::StoreLocal(_) | I::StoreGlobal(_)
| I::StoreClosedLocal(_)
)) ))
{ {
// can't panic: checked that instrs.len() >= 2 // can't panic: checked that instrs.len() >= 2
@ -144,7 +150,7 @@ impl<'a> Compiler<'a> {
Some( Some(
I::Dup | I::Const(_) | I::Int(_) I::Dup | I::Const(_) | I::Int(_)
| I::Nil | I::Bool(_) | I::Symbol(_) | I::Nil | I::Bool(_) | I::Symbol(_)
| I::LoadLocal(_) | I::LoadLocal(_) | I::LoadClosedLocal(_)
) )
); );
if poppable { if poppable {
@ -221,12 +227,8 @@ impl<'a> Compiler<'a> {
fn load_var(&mut self, name: &LStr) { fn load_var(&mut self, name: &LStr) {
match (self.resolve_local(name), self.resolve_global(name)) { match (self.resolve_local(name), self.resolve_global(name)) {
(Some(n), None) => { (Some(n), None) => self.load_local(n),
self.emit(I::LoadLocal(Arg24::from_usize(n))); (Some(n), Some(m)) if n >= m => self.load_local(n),
},
(Some(n), Some(m)) if n >= m => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
},
_ => { _ => {
let sym = Symbol::get(name); let sym = Symbol::get(name);
self.emit(I::LoadGlobal(Arg24::from_symbol(sym))); self.emit(I::LoadGlobal(Arg24::from_symbol(sym)));
@ -234,10 +236,18 @@ impl<'a> Compiler<'a> {
} }
} }
fn load_local(&mut self, n: usize) {
if self.locals[n].closed {
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
} else {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
}
}
fn declare_local(&mut self, name: &LStr) -> usize { fn declare_local(&mut self, name: &LStr) -> usize {
if let Some(i) = self.resolve_local(name) { if let Some(i) = self.resolve_local(name) {
if self.locals[i].scope == self.scope { if self.locals[i].scope == self.scope && !self.locals[i].closed {
self.emit(I::StoreLocal(Arg24::from_usize(i))); self.emit(I::StoreLocal(Arg24::from_usize(i)));
return i; return i;
} }
} }
@ -245,6 +255,7 @@ impl<'a> Compiler<'a> {
self.locals.push(Local { self.locals.push(Local {
name: name.into(), name: name.into(),
scope: self.scope, scope: self.scope,
closed: false,
}); });
let i = self.locals.len() - 1; let i = self.locals.len() - 1;
@ -253,7 +264,11 @@ impl<'a> Compiler<'a> {
} }
fn store_local(&mut self, i: usize) { fn store_local(&mut self, i: usize) {
self.emit(I::StoreLocal(Arg24::from_usize(i))); if self.locals[i].closed {
self.emit(I::StoreLocal(Arg24::from_usize(i)));
} else {
self.emit(I::StoreClosedLocal(Arg24::from_usize(i)));
}
} }
fn store_global(&mut self, name: &LStr) { fn store_global(&mut self, name: &LStr) {
@ -267,6 +282,7 @@ impl<'a> Compiler<'a> {
self.globals.push(Local { self.globals.push(Local {
name: name.into(), name: name.into(),
scope: self.scope, scope: self.scope,
closed: false,
}); });
} }

View file

@ -31,6 +31,7 @@ lazy_static! {
pub static ref SYM_DATA: Symbol = symbol!(data); pub static ref SYM_DATA: Symbol = symbol!(data);
pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error); pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error);
pub static ref SYM_VALUE_ERROR: Symbol = symbol!(value_error);
pub static ref SYM_NAME_ERROR: Symbol = symbol!(name_error); pub static ref SYM_NAME_ERROR: Symbol = symbol!(name_error);
pub static ref SYM_INDEX_ERROR: Symbol = symbol!(index_error); pub static ref SYM_INDEX_ERROR: Symbol = symbol!(index_error);
pub static ref SYM_HASH_ERROR: Symbol = symbol!(hash_error); pub static ref SYM_HASH_ERROR: Symbol = symbol!(hash_error);

View file

@ -1,27 +1,30 @@
use std::rc::Rc; use std::{cell::RefCell, rc::Rc};
use crate::{chunk::Chunk, Vm, exception::Result}; use crate::{chunk::Chunk, Vm, exception::Result};
use super::Value; use super::{Value, CellValue};
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct FuncAttrs { pub struct FuncAttrs {
pub arity: usize, pub arity: usize,
pub variadic: bool,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Function { pub struct Function {
pub attrs: FuncAttrs, pub attrs: FuncAttrs,
pub chunk: Rc<Chunk>, pub chunk: Rc<Chunk>,
pub state: Box<[CellValue]>,
} }
impl Function { impl Function {
pub fn new(chunk: Rc<Chunk>, arity: usize) -> Self { pub fn new(chunk: Rc<Chunk>, attrs: FuncAttrs, closes: usize) -> Self {
Self { chunk, attrs: FuncAttrs { arity, variadic: false } } let v = Rc::new(RefCell::new(Value::Nil));
let state = std::iter::repeat(v).take(closes).collect();
Self::from_parts(chunk, attrs, state)
} }
pub fn new_variadic(chunk: Rc<Chunk>, arity: usize) -> Self {
Self { chunk, attrs: FuncAttrs { arity, variadic: true } } pub fn from_parts(chunk: Rc<Chunk>, attrs: FuncAttrs, state: Box<[CellValue]>) -> Self {
Self { chunk, attrs, state }
} }
} }
@ -34,10 +37,7 @@ pub struct NativeFunc {
impl NativeFunc { impl NativeFunc {
pub fn new(func: FnNative, arity: usize) -> Self { pub fn new(func: FnNative, arity: usize) -> Self {
Self { func, attrs: FuncAttrs { arity, variadic: false } } Self { func, attrs: FuncAttrs { arity } }
}
pub fn new_variadic(func: FnNative, arity: usize) -> Self {
Self { func, attrs: FuncAttrs { arity, variadic: true } }
} }
} }
@ -50,10 +50,9 @@ impl std::fmt::Debug for NativeFunc {
} }
pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::io::Result<()> { pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::io::Result<()> {
writeln!(w, "{} ({}{})", writeln!(w, "{} ({})",
Value::Function(f.clone()), Value::Function(f.clone()),
f.attrs.arity, f.attrs.arity)?;
if f.attrs.variadic { ".." } else { "" })?;
if !f.chunk.consts.is_empty() { if !f.chunk.consts.is_empty() {
writeln!(w, "constants")?; writeln!(w, "constants")?;
for (i, c) in f.chunk.consts.iter().enumerate() { for (i, c) in f.chunk.consts.iter().enumerate() {

View file

@ -20,6 +20,7 @@ pub mod index;
type RcList = Rc<RefCell<Vec<Value>>>; type RcList = Rc<RefCell<Vec<Value>>>;
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>; type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
pub type CellValue = Rc<RefCell<Value>>;
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub enum Value { pub enum Value {

View file

@ -1,9 +1,9 @@
use std::{cell::RefCell, cmp::Ordering, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub}, rc::Rc}; use std::{cell::RefCell, cmp::Ordering, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub}, rc::Rc};
use num_complex::Complex64; use num_complex::{Complex64, ComplexFloat};
use num_rational::Rational64; use num_rational::Rational64;
use crate::{exception::{throw, Result}, lstring::LString, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR}, value::range::RangeType, Vm}; use crate::{exception::{throw, Result}, lstring::LString, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::range::RangeType, Vm};
use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value}; use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value};
@ -120,10 +120,10 @@ impl Div<Value> for Value {
fn div(self, rhs: Value) -> Self::Output { fn div(self, rhs: Value) -> Self::Output {
use Value as V; use Value as V;
match promote(self, rhs) { match promote(self, rhs) {
(_, V::Int(0)) => throw!(*SYM_TYPE_ERROR, "integer division by 0"), (V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"),
(_, V::Ratio(r)) if *r.numer() == 0 && *r.denom() != 0
=> throw!(*SYM_TYPE_ERROR, "integer division by 0"),
(V::Int(x), V::Int(y)) => Ok(V::Ratio(Rational64::new(x, y))), (V::Int(x), V::Int(y)) => Ok(V::Ratio(Rational64::new(x, y))),
(V::Ratio(_), V::Ratio(r)) if *r.numer() == 0 && *r.denom() != 0
=> throw!(*SYM_VALUE_ERROR, "rational division by 0"),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x / y)), (V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x / y)),
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)), (V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)), (V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
@ -133,9 +133,10 @@ impl Div<Value> for Value {
} }
#[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_sign_loss)]
#[inline]
fn ipow(n: i64, p: u64) -> Result<i64> { fn ipow(n: i64, p: u64) -> Result<i64> {
match (n, p) { match (n, p) {
(0, 0) => throw!(*SYM_TYPE_ERROR, "integer 0 raised to power 0"), (0, 0) => throw!(*SYM_VALUE_ERROR, "integer 0 raised to power 0"),
(0, _) => Ok(0), (0, _) => Ok(0),
(_, 0) => Ok(1), (_, 0) => Ok(1),
(n, p) if p > u32::MAX as u64 => { (n, p) if p > u32::MAX as u64 => {
@ -148,6 +149,7 @@ fn ipow(n: i64, p: u64) -> Result<i64> {
} }
#[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_sign_loss)]
#[inline]
fn rpow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> { fn rpow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
Ok(match p { Ok(match p {
0.. => (ipow(n, p as u64)?, ipow(d, p as u64)?), 0.. => (ipow(n, p as u64)?, ipow(d, p as u64)?),
@ -159,10 +161,20 @@ impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> { pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value as V; use Value as V;
match promote(self, rhs) { match promote(self, rhs) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"),
(V::Int(x), V::Int(y)) => Ok(V::Int(x.rem_euclid(y))), (V::Int(x), V::Int(y)) => Ok(V::Int(x.rem_euclid(y))),
(V::Ratio(_x), V::Ratio(_y)) => todo!("ratio modulo"), (V::Ratio(_), V::Ratio(y)) if *y.numer() == 0 && *y.denom() != 0
=> throw!(*SYM_VALUE_ERROR, "rational modulo by 0"),
(V::Ratio(x), V::Ratio(y)) => {
let n = (x / y).floor();
Ok(Value::Ratio(x - n * y))
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(y))), (V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(y))),
(V::Complex(_x), V::Complex(_y)) => todo!("complex modulo"), (V::Complex(x), V::Complex(y)) => {
let n = x / y;
let n = Complex64::new(n.re().floor(), n.im().floor());
Ok(Value::Complex(x - n * y))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}") (l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}")
} }
} }
@ -170,10 +182,16 @@ impl Value {
pub fn int_div(self, rhs: Value) -> Result<Self> { pub fn int_div(self, rhs: Value) -> Result<Self> {
use Value as V; use Value as V;
match promote(self, rhs) { match promote(self, rhs) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"),
(V::Int(x), V::Int(y)) => Ok(V::Int(x.div_euclid(y))), (V::Int(x), V::Int(y)) => Ok(V::Int(x.div_euclid(y))),
(V::Ratio(_x), V::Ratio(_y)) => todo!("ratio integer division"), (V::Ratio(_), V::Ratio(r)) if *r.numer() == 0 && *r.denom() != 0
=> throw!(*SYM_VALUE_ERROR, "rational division by 0"),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio((x / y).floor())),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(y))), (V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(y))),
(V::Complex(_x), V::Complex(_y)) => todo!("complex integer division"), (V::Complex(x), V::Complex(y)) => {
let n = x / y;
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}") (l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}")
} }
} }

View file

@ -72,17 +72,13 @@ enum CallOutcome {
Partial(Value), Partial(Value),
} }
fn get_call_outcome(mut args: Vec<Value>) -> Result<CallOutcome> { fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
let f = &args[0]; let f = &args[0];
let Some(attrs) = f.func_attrs() else { let Some(attrs) = f.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "cannot call non-function {f:#}") throw!(*SYM_TYPE_ERROR, "cannot call non-function {f:#}")
}; };
let argc = args.len() - 1; let argc = args.len() - 1;
if attrs.variadic && argc >= attrs.arity { if argc == attrs.arity {
let vararg = args.split_off(attrs.arity + 1);
args.push(vararg.into());
Ok(CallOutcome::Call(args))
} else if argc == attrs.arity {
Ok(CallOutcome::Call(args)) Ok(CallOutcome::Call(args))
} else if argc > attrs.arity { } else if argc > attrs.arity {
throw!(*SYM_TYPE_ERROR, "too many arguments for function") throw!(*SYM_TYPE_ERROR, "too many arguments for function")
@ -92,19 +88,11 @@ fn get_call_outcome(mut args: Vec<Value>) -> Result<CallOutcome> {
let nf = move |vm: &mut Vm, inner_args: Vec<Value>| { let nf = move |vm: &mut Vm, inner_args: Vec<Value>| {
let mut ia = inner_args.into_iter(); let mut ia = inner_args.into_iter();
ia.next(); ia.next();
let mut args: Vec<Value> = args.clone().into_iter().chain(ia).collect(); let args: Vec<Value> = args.clone().into_iter().chain(ia).collect();
if attrs.variadic {
let Value::List(varargs) = args.pop()
.expect("did not receive vararg") else {
panic!("did not receive vararg")
};
let varargs = Rc::unwrap_or_clone(varargs).take();
args.extend(varargs);
}
vm.call_value(f.clone(), args) vm.call_value(f.clone(), args)
}; };
let nf = NativeFunc { let nf = NativeFunc {
attrs: FuncAttrs { arity: remaining, variadic: attrs.variadic }, attrs: FuncAttrs { arity: remaining },
func: Box::new(nf), func: Box::new(nf),
}; };
Ok(CallOutcome::Partial(nf.into())) Ok(CallOutcome::Partial(nf.into()))
@ -260,6 +248,49 @@ impl Vm {
let v = self.pop(); let v = self.pop();
self.globals.insert(sym, v); self.globals.insert(sym, v);
}, },
I::CloseOver(n) => {
let n = usize::from(n);
let v = std::mem::replace(&mut frame.locals[n], Value::Nil);
let v = v.to_cell();
frame.locals[n] = v.clone();
self.push(v);
},
I::Closure(n) => {
let f = frame.func.chunk.consts[usize::from(n)].clone();
let Value::Function(f) = f else {
panic!("attempt to build closure from non-closure constant")
};
let mut f = f.as_ref().clone();
let mut args = Vec::with_capacity(f.state.len());
for _ in 0..f.state.len() {
let Value::Cell(c) = self.pop() else {
panic!("attempt to build closure from non-cell local");
};
args.push(c);
}
f.state = args.into_boxed_slice();
self.push(f.into());
},
I::LoadUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(v.borrow().clone());
},
I::StoreUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
*v.borrow_mut() = self.pop();
},
I::LoadClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to load from closed non-cell local");
};
self.push(c.borrow().clone());
},
I::StoreClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to store to closed non-cell local");
};
*c.borrow_mut() = self.pop();
},
// [] -> [consts[n]] // [] -> [consts[n]]
I::Const(n) I::Const(n)
=> self.push(frame.func.chunk.consts[usize::from(n)].clone()), => self.push(frame.func.chunk.consts[usize::from(n)].clone()),
@ -432,7 +463,7 @@ impl Vm {
// before propagating exceptions // before propagating exceptions
let res = res?; let res = res?;
self.stack.push(res); self.push(res);
} else if let Value::Function(func) = &args[0] { } else if let Value::Function(func) = &args[0] {
if self.call_stack.len() + 1 >= self.stack_max { if self.call_stack.len() + 1 >= self.stack_max {

View file

@ -1,17 +1,15 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use syn::{parse::Parse, parse_macro_input, ItemFn, LitInt, Token}; use syn::{parse::Parse, parse_macro_input, ItemFn, LitInt};
use quote::quote; use quote::quote;
struct NativeFuncArgs { struct NativeFuncArgs {
arity: LitInt, arity: LitInt,
variadic: Option<Token![..]>,
} }
impl Parse for NativeFuncArgs { impl Parse for NativeFuncArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let arity = input.parse()?; let arity = input.parse()?;
let variadic = input.parse()?; Ok(Self { arity })
Ok(Self { arity, variadic })
} }
} }
@ -28,7 +26,6 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
let inputs = itemfn.sig.inputs; let inputs = itemfn.sig.inputs;
let output = itemfn.sig.output; let output = itemfn.sig.output;
let arity = args.arity; let arity = args.arity;
let variadic = args.variadic.is_some();
assert!(itemfn.sig.constness.is_none(), "item must not be const"); assert!(itemfn.sig.constness.is_none(), "item must not be const");
assert!(itemfn.sig.asyncness.is_none(), "item must not be async"); assert!(itemfn.sig.asyncness.is_none(), "item must not be async");
@ -41,7 +38,6 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
::talc_lang::value::function::NativeFunc { ::talc_lang::value::function::NativeFunc {
attrs: ::talc_lang::value::function::FuncAttrs{ attrs: ::talc_lang::value::function::FuncAttrs{
arity: #arity, arity: #arity,
variadic: #variadic,
}, },
func: Box::new(|#inputs| #output #block) func: Box::new(|#inputs| #output #block)
} }

View file

@ -1,6 +1,6 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use talc_lang::{exception::{exception, Result}, symbol::SYM_TYPE_ERROR, throw, value::{function::NativeFunc, Value}, vmcall, Vm}; use talc_lang::{exception::{exception, Result}, symbol::{SYM_TYPE_ERROR, SYM_VALUE_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;
@ -14,6 +14,9 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("sort", sort().into()); vm.set_global_name("sort", sort().into());
vm.set_global_name("sort_by", sort_by().into()); vm.set_global_name("sort_by", sort_by().into());
vm.set_global_name("sort_key", sort_key().into()); vm.set_global_name("sort_key", sort_key().into());
vm.set_global_name("pair", pair().into());
vm.set_global_name("fst", fst().into());
vm.set_global_name("snd", snd().into());
} }
#[native_func(2)] #[native_func(2)]
@ -33,7 +36,7 @@ pub fn pop(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "pop expected list, found {list:#}") throw!(*SYM_TYPE_ERROR, "pop expected list, found {list:#}")
}; };
let v = list.borrow_mut().pop(); let v = list.borrow_mut().pop();
v.ok_or_else(|| exception!(*SYM_TYPE_ERROR, "attempt to pop empty list")) v.ok_or_else(|| exception!(*SYM_VALUE_ERROR, "attempt to pop empty list"))
} }
#[native_func(1)] #[native_func(1)]
@ -199,10 +202,42 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Some(Ordering::Greater) => Ok(Value::Int(1)), Some(Ordering::Greater) => Ok(Value::Int(1)),
Some(Ordering::Equal) => Ok(Value::Int(0)), Some(Ordering::Equal) => Ok(Value::Int(0)),
Some(Ordering::Less) => Ok(Value::Int(-1)), Some(Ordering::Less) => Ok(Value::Int(-1)),
None => throw!(*SYM_TYPE_ERROR, "values returned from sort key were incomparable"), None => throw!(*SYM_VALUE_ERROR, "values returned from sort key were incomparable"),
} }
}; };
let nf = NativeFunc::new(Box::new(f), 2).into(); let nf = NativeFunc::new(Box::new(f), 2).into();
sort_inner(&mut list.borrow_mut(), Some(&nf), vm)?; sort_inner(&mut list.borrow_mut(), Some(&nf), vm)?;
Ok(list.into()) Ok(list.into())
} }
#[native_func(2)]
pub fn pair(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
Ok(vec![x, y].into())
}
#[native_func(1)]
pub fn fst(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "fst: expected list")
};
let l = l.borrow();
let [x, _] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "fst: list must have length 2")
};
Ok(x.clone())
}
#[native_func(1)]
pub fn snd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "snd: expected list")
};
let l = l.borrow();
let [_, y] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "snd: list must have length 2")
};
Ok(y.clone())
}

View file

@ -1,26 +1,28 @@
use talc_lang::{exception::{throw, Exception, Result}, symbol::SYM_TYPE_ERROR, value::Value, Vm}; use talc_lang::{exception::{throw, Exception, Result}, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::Value, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::{unpack_args, unpack_varargs}; use crate::unpack_args;
#[native_func(1..)] #[native_func(1)]
pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, ty], varargs) = unpack_varargs!(args); let [_, arg] = unpack_args!(args);
let Value::Symbol(ty) = ty else { let exc = match arg {
throw!(*SYM_TYPE_ERROR, "exception type must be a symbol") Value::Symbol(ty) => Exception::new(ty),
}; Value::List(l) => match l.borrow().as_slice() {
Err(match &*varargs { [Value::Symbol(ty)] | [Value::Symbol(ty), Value::Nil]
[] | [Value::Nil] => Exception::new(*ty),
=> Exception::new(ty), [Value::Symbol(ty), Value::Nil, data]
[Value::Nil, data] => Exception::new_with_data(*ty, data.clone()),
=> Exception::new_with_data(ty, data.clone()), [Value::Symbol(ty), Value::String(s)]
[Value::String(s)] => Exception::new_with_msg(*ty, s.clone()),
=> Exception::new_with_msg(ty, s.clone()), [Value::Symbol(ty), Value::String(s), data]
[Value::String(s), data] => Exception::new_with_msg_data(*ty, s.clone(), data.clone()),
=> Exception::new_with_msg_data(ty, s.clone(), data.clone()), [] | [_] | [_,_] | [_,_,_] => throw!(*SYM_TYPE_ERROR, "wrong argument for throw"),
[_] | [_,_] => throw!(*SYM_TYPE_ERROR, "wrong arguments for throw"), [_,_,_,_,..] => throw!(*SYM_VALUE_ERROR, "too many elements in list argument for throw"),
[_,_,_,..] => throw!(*SYM_TYPE_ERROR, "too many arguments for throw"), }
}) _ => throw!(*SYM_TYPE_ERROR, "throw expected symbol or list"),
};
Err(exc)
} }
#[native_func(1)] #[native_func(1)]
@ -30,6 +32,7 @@ pub fn rethrow(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if let Some(e) = Exception::from_table(&table) { if let Some(e) = Exception::from_table(&table) {
return Err(e) return Err(e)
} }
throw!(*SYM_VALUE_ERROR, "argument not a valid exception")
} }
throw!(*SYM_TYPE_ERROR, "argument not a valid exception") throw!(*SYM_TYPE_ERROR, "argument not a valid exception")
} }

View file

@ -1,6 +1,6 @@
use std::{cell::RefCell, collections::HashMap, fs::{File, OpenOptions}, io::{BufRead, BufReader, BufWriter, ErrorKind, IntoInnerError, Read, Write}, net::{TcpListener, TcpStream, ToSocketAddrs}, os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}, process::{Child, Command, Stdio}, time::Duration}; use std::{cell::RefCell, collections::HashMap, fs::{File, OpenOptions}, io::{BufRead, BufReader, BufWriter, ErrorKind, IntoInnerError, Read, Write}, net::{TcpListener, TcpStream, ToSocketAddrs}, os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}, process::{Child, Command, Stdio}, time::Duration};
use talc_lang::{exception::Result, lstring::LString, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{function::NativeFunc, HashValue, NativeValue, Value}, Vm}; use talc_lang::{exception::Result, lstring::LString, symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, HashValue, NativeValue, Value}, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -218,7 +218,7 @@ pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} else { } else {
match OPEN_OPT_MAP.get(&s) { match OPEN_OPT_MAP.get(&s) {
Some(f) => f(&mut oo, true), Some(f) => f(&mut oo, true),
None => throw!(*SYM_TYPE_ERROR, "invalid option for open") None => throw!(*SYM_VALUE_ERROR, "invalid option for open")
}; };
} }
}, },
@ -231,7 +231,7 @@ pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} else { } else {
match OPEN_OPT_MAP.get(s) { match OPEN_OPT_MAP.get(s) {
Some(f) => f(&mut oo, true), Some(f) => f(&mut oo, true),
None => throw!(*SYM_TYPE_ERROR, "invalid option for open") None => throw!(*SYM_VALUE_ERROR, "invalid option for open")
}; };
} }
}, },
@ -256,7 +256,7 @@ pub fn read(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}") throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}")
}; };
let Ok(nbytes) = usize::try_from(nbytes) else { let Ok(nbytes) = usize::try_from(nbytes) else {
throw!(*SYM_TYPE_ERROR, "number of bytes to read must be nonnegative") throw!(*SYM_VALUE_ERROR, "number of bytes to read must be nonnegative")
}; };
let Some(file): Option<&ValueFile> = file.downcast_native() else { let Some(file): Option<&ValueFile> = file.downcast_native() else {
throw!(*SYM_TYPE_ERROR, "read expected file, got {file:#}") throw!(*SYM_TYPE_ERROR, "read expected file, got {file:#}")
@ -310,7 +310,7 @@ pub fn read_until(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "read_until expected string, got {end:#}") throw!(*SYM_TYPE_ERROR, "read_until expected string, got {end:#}")
}; };
if end.is_empty() { if end.is_empty() {
throw!(*SYM_TYPE_ERROR, "read_until: end string must not be empty") throw!(*SYM_VALUE_ERROR, "read_until: end string must not be empty")
} }
let mut file = file.0.borrow_mut(); let mut file = file.0.borrow_mut();
if let BufFile::Buffered { r, .. } = &mut *file { if let BufFile::Buffered { r, .. } = &mut *file {
@ -339,7 +339,7 @@ pub fn read_line(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Err(e) => throw!(*SYM_IO_ERROR, "{e}") Err(e) => throw!(*SYM_IO_ERROR, "{e}")
} }
} else { } else {
throw!(*SYM_TYPE_ERROR, "read_line: file must be buffered") throw!(*SYM_VALUE_ERROR, "read_line: file must be buffered")
} }
} }
@ -398,7 +398,7 @@ pub fn tcp_connect(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "tcp_connect expected string, got {addr:#}") throw!(*SYM_TYPE_ERROR, "tcp_connect expected string, got {addr:#}")
}; };
let Ok(addr) = addr.to_str() else { let Ok(addr) = addr.to_str() else {
throw!(*SYM_TYPE_ERROR, "address must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
}; };
match TcpStream::connect(addr) { match TcpStream::connect(addr) {
Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) { Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
@ -416,12 +416,12 @@ pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout expected string, got {addr:#}") throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout expected string, got {addr:#}")
}; };
let Ok(addr) = addr.to_str() else { let Ok(addr) = addr.to_str() else {
throw!(*SYM_TYPE_ERROR, "address must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
}; };
let timeout = match timeout { let timeout = match timeout {
Value::Int(n) if n >= 0 => Duration::from_secs(n as u64), Value::Int(n) if n >= 0 => Duration::from_secs(n as u64),
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => Duration::from_secs_f64(n), Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => Duration::from_secs_f64(n),
Value::Int(_) | Value::Float(_) => throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout: invalid timeout"), Value::Int(_) | Value::Float(_) => throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout"),
_ => throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout expected int or float, got {timeout:#}") _ => throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout expected int or float, got {timeout:#}")
}; };
let mut addrs = match addr.to_socket_addrs() { let mut addrs = match addr.to_socket_addrs() {
@ -464,7 +464,7 @@ pub fn tcp_listen(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "tcp_connect expected string, got {addr:#}") throw!(*SYM_TYPE_ERROR, "tcp_connect expected string, got {addr:#}")
}; };
let Ok(addr) = addr.to_str() else { let Ok(addr) = addr.to_str() else {
throw!(*SYM_TYPE_ERROR, "address must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
}; };
match TcpListener::bind(addr) { match TcpListener::bind(addr) {
Ok(listener) => { Ok(listener) => {
@ -508,7 +508,7 @@ fn spawn_opt_stdio(
let stdio = unsafe { Stdio::from_raw_fd(fd) }; let stdio = unsafe { Stdio::from_raw_fd(fd) };
func(proc, stdio) func(proc, stdio)
}, },
_ => throw!(*SYM_TYPE_ERROR, "{fname} stdio option expected :inherit, :piped, :null, or file, got {opt:#}") _ => throw!(*SYM_VALUE_ERROR, "{fname} stdio option expected :inherit, :piped, :null, or file, got {opt:#}")
}; };
Ok(()) Ok(())
} }
@ -535,7 +535,7 @@ fn spawn_opt_delenv(fname: &str, proc: &mut Command, delenv: &Value) -> Result<(
}; };
proc.env_remove(e.to_os_str()); proc.env_remove(e.to_os_str());
}, },
_ => throw!(*SYM_TYPE_ERROR, _ => throw!(*SYM_VALUE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}") "{fname} delenv option expected :all or list of strings, got {delenv:#}")
} }
Ok(()) Ok(())
@ -547,7 +547,7 @@ fn spawn_opt_env(fname: &str, proc: &mut Command, env: &Value) -> Result<()> {
Value::Table(t) => for (k, v) in t.borrow().iter() { Value::Table(t) => for (k, v) in t.borrow().iter() {
let Value::String(k) = k.inner() else { let Value::String(k) = k.inner() else {
throw!(*SYM_TYPE_ERROR, throw!(*SYM_TYPE_ERROR,
"{fname} efromnv option expected table with string keys, got {env:#}") "{fname} env option expected table with string keys, got {env:#}")
}; };
proc.env(k.to_os_str(), v.str().to_os_str()); proc.env(k.to_os_str(), v.str().to_os_str());
}, },

View file

@ -1,6 +1,6 @@
use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, time::{SystemTime, UNIX_EPOCH}}; use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, time::{SystemTime, UNIX_EPOCH}};
use talc_lang::{exception::{throw, Result}, lstring::LString, symbol::SYM_TYPE_ERROR, value::Value, vmcall, Vm}; use talc_lang::{exception::{throw, Result}, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::Value, vmcall, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::{unpack_args, SYM_IO_ERROR}; use crate::{unpack_args, SYM_IO_ERROR};
@ -70,7 +70,7 @@ pub fn env(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "env expected string, got {key:#}") throw!(*SYM_TYPE_ERROR, "env expected string, got {key:#}")
}; };
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) { if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte") throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
} }
let val = std::env::var_os(key.to_os_str()); let val = std::env::var_os(key.to_os_str());
match val { match val {
@ -86,11 +86,11 @@ pub fn setenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "setenv expected string, got {key:#}") throw!(*SYM_TYPE_ERROR, "setenv expected string, got {key:#}")
}; };
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) { if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte") throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
} }
let val = val.str(); let val = val.str();
if val.as_bytes().contains(&0) { if val.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable value must not contain a null byte") throw!(*SYM_VALUE_ERROR, "environment variable value must not contain a null byte")
} }
std::env::set_var(key.to_os_str(), val.to_os_str()); std::env::set_var(key.to_os_str(), val.to_os_str());
Ok(Value::Nil) Ok(Value::Nil)
@ -103,7 +103,7 @@ pub fn delenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "delenv expected string, got {key:#}") throw!(*SYM_TYPE_ERROR, "delenv expected string, got {key:#}")
}; };
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) { if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte") throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
} }
std::env::remove_var(key.to_os_str()); std::env::remove_var(key.to_os_str());
Ok(Value::Nil) Ok(Value::Nil)

View file

@ -1,9 +1,9 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{symbol::SYM_TYPE_ERROR, exception::Result, throw, value::{function::NativeFunc, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm}; use talc_lang::{exception::Result, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, ops::RatioExt, 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;
pub fn load(vm: &mut Vm) { pub fn load(vm: &mut Vm) {
// begin // begin
@ -26,7 +26,9 @@ pub fn load(vm: &mut Vm) {
// join // join
vm.set_global_name("zip", zip().into()); vm.set_global_name("zip", zip().into());
vm.set_global_name("zipn", zipn().into());
vm.set_global_name("alternate", alternate().into()); vm.set_global_name("alternate", alternate().into());
vm.set_global_name("alternaten", alternaten().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("chain", chain().into()); vm.set_global_name("chain", chain().into());
@ -48,6 +50,12 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("index_if", index_if().into()); vm.set_global_name("index_if", index_if().into());
vm.set_global_name("find", find().into()); vm.set_global_name("find", find().into());
vm.set_global_name("count", count().into()); vm.set_global_name("count", count().into());
vm.set_global_name("mean", mean().into());
vm.set_global_name("variance", variance().into());
vm.set_global_name("stdev", stdev().into());
vm.set_global_name("pvariance", pvariance().into());
vm.set_global_name("pstdev", pstdev().into());
vm.set_global_name("median", median().into());
} }
// //
@ -181,10 +189,10 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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);
let Value::Int(count) = count else { let Value::Int(count) = count else {
throw!(*SYM_TYPE_ERROR, "take expected integer") throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
}; };
let Ok(count) = count.try_into() else { let Ok(count) = count.try_into() else {
throw!(*SYM_TYPE_ERROR, "take expected nonnegative integer") throw!(*SYM_VALUE_ERROR, "take expected nonnegative integer, got {count:#}")
}; };
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
@ -207,10 +215,10 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, count, iter] = unpack_args!(args); let [_, count, iter] = unpack_args!(args);
let Value::Int(count) = count else { let Value::Int(count) = count else {
throw!(*SYM_TYPE_ERROR, "count expected integer") throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
}; };
let Ok(count) = count.try_into() else { let Ok(count) = count.try_into() else {
throw!(*SYM_TYPE_ERROR, "count expected nonnegative integer") throw!(*SYM_VALUE_ERROR, "count expected nonnegative integer, got {count:#}")
}; };
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
@ -292,10 +300,10 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, by, iter] = unpack_args!(args); let [_, by, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let Value::Int(by) = by else { let Value::Int(by) = by else {
throw!(*SYM_TYPE_ERROR, "step expected integer") throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
}; };
if by <= 0 { if by <= 0 {
throw!(*SYM_TYPE_ERROR, "step expected positive integer") throw!(*SYM_VALUE_ERROR, "step expected positive integer, got {by:#}")
} }
let state = RefCell::new(Step::First); let state = RefCell::new(Step::First);
@ -350,13 +358,37 @@ pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// //
#[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] = unpack_args!(args);
let mut iters = Vec::with_capacity(2 + rest.len()); let i1 = i1.to_iter_function()?;
for i in [i1, i2].into_iter().chain(rest.into_iter()) { let i2 = i2.to_iter_function()?;
iters.push(i.to_iter_function()?);
} let f = move |vm: &mut Vm, _| {
let mut res = Vec::with_capacity(2);
match vmcalliter!(vm; i1.clone())? {
Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)),
};
match vmcalliter!(vm; i2.clone())? {
Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)),
};
Ok(Value::from(res))
};
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(1)]
pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "zipn expected list, got {args:#}")
};
let iters = args.borrow().iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
let mut res = Vec::with_capacity(iters.len()); let mut res = Vec::with_capacity(iters.len());
@ -372,13 +404,42 @@ pub fn zip(_: &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..)] #[native_func(2)]
pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, i1, i2], rest) = unpack_varargs!(args); let [_, i1, i2] = unpack_args!(args);
let mut iters = Vec::with_capacity(2 + rest.len()); let i1 = i1.to_iter_function()?;
for i in [i1, i2].into_iter().chain(rest.into_iter()) { let i2 = i2.to_iter_function()?;
iters.push(i.to_iter_function()?);
} let state = RefCell::new((false, false));
let f = move |vm: &mut Vm, _| {
let mut s = state.borrow_mut();
if s.1 {
return Ok(Value::iter_pack(None));
}
let n = s.0;
s.0 = !s.0;
drop(s);
let iter = if n { i1.clone() } else { i2.clone() };
if let Some(v) = vmcalliter!(vm; iter)? {
Ok(v)
} else {
state.borrow_mut().1 = true;
Ok(Value::iter_pack(None))
}
};
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(1)]
pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "alternaten expected list, got {args:#}")
};
let iters = args.borrow().iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
let state = RefCell::new((0, false)); let state = RefCell::new((0, false));
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
@ -556,12 +617,14 @@ pub fn table(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = HashMap::new(); let mut result = HashMap::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
let Value::List(l) = value else { let Value::List(l) = value else {
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list") throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list, got {value:#}")
}; };
let l = Rc::unwrap_or_clone(l).take(); let mut l = Rc::unwrap_or_clone(l).take();
let Ok([k, v]): std::result::Result<[Value; 2], Vec<Value>> = l.try_into() else { if l.len() != 2 {
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list of length 2") throw!(*SYM_VALUE_ERROR, "table: iterator yielded list of length {} (expected 2)", l.len())
}; };
let v = l.pop().unwrap();
let k = l.pop().unwrap();
result.insert(k.try_into()?, v); result.insert(k.try_into()?, v);
}; };
Ok(result.into()) Ok(result.into())
@ -775,3 +838,110 @@ pub fn count(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(map.into()) Ok(map.into())
} }
#[native_func(1)]
pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let mut sum = Value::Float(0.0);
let mut count = Value::Int(0);
while let Some(value) = vmcalliter!(vm; iter.clone())? {
sum = (sum + value)?;
count = (count + Value::from(1))?;
}
sum / count
}
fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
let mut m = Value::Float(0.0);
let mut s = Value::Float(0.0);
let mut k = 1;
while let Some(value) = vmcalliter!(vm; iter.clone())? {
let old_m = m.clone();
m = (m.clone() + ((value.clone() - m.clone())?/Value::Int(k))?)?;
s = (s + ((value.clone() - m.clone())?*(value - old_m)?)?)?;
k += 1;
}
s / Value::Int(k - if pop { 1 } else { 2 })
}
#[native_func(1)]
pub fn variance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
variance_inner(vm, iter, false)
}
#[native_func(1)]
pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, false)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}")
})
}
#[native_func(1)]
pub fn pvariance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
variance_inner(vm, iter, true)
}
#[native_func(1)]
pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, true)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}")
})
}
#[derive(PartialEq, PartialOrd)]
struct OrdValue(Value);
impl std::cmp::Eq for OrdValue {}
impl std::cmp::Ord for OrdValue {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)
}
}
#[native_func(1)]
pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let mut vals = Vec::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? {
vals.push(OrdValue(value));
}
let count = vals.len();
if count == 0 {
Ok(Value::Nil)
} else if count % 2 == 0 {
let (_, _, hi) = vals.select_nth_unstable(count/2 - 1);
let (_, _, _) = hi.select_nth_unstable(0);
let m2 = vals.swap_remove(count/2);
let m1 = vals.swap_remove(count/2 - 1);
(m1.0 + m2.0)? / Value::Int(2)
} else {
let (_, _, _) = vals.select_nth_unstable(count/2);
let m = vals.swap_remove(count/2);
m.0 / Value::Int(1)
}
}

View file

@ -28,6 +28,7 @@ pub fn load_all(vm: &mut Vm) {
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref SYM_IO_ERROR: Symbol = symbol!(io_error); pub static ref SYM_IO_ERROR: Symbol = symbol!(io_error);
pub static ref SYM_FORMAT_ERROR: Symbol = symbol!(format_error);
} }
@ -38,16 +39,3 @@ macro_rules! unpack_args {
} }
pub(crate) use unpack_args; pub(crate) use unpack_args;
macro_rules! unpack_varargs {
($e:expr) => {{
let mut args = $e;
let Value::List(varargs) = args.pop().expect("bypassed arity check") else {
panic!("bypassed arity check")
};
let varargs = ::std::rc::Rc::unwrap_or_clone(varargs).into_inner();
(args.try_into().expect("bypassed arity check"), varargs)
}};
}
pub(crate) use unpack_varargs;

View file

@ -1,10 +1,10 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use talc_lang::{exception::Result, lstring::LString, parse_int, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{ops::RatioExt, Complex64, Value}, Vm}; use talc_lang::{exception::Result, lstring::LString, parse_int, symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, Complex64, Value}, vmcalliter, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::{unpack_args, unpack_varargs}; use crate::unpack_args;
lazy_static! { lazy_static! {
static ref SYM_NAN: Symbol = Symbol::get("nan"); static ref SYM_NAN: Symbol = Symbol::get("nan");
@ -61,7 +61,9 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("from_radix", from_radix().into()); vm.set_global_name("from_radix", from_radix().into());
vm.set_global_name("gcd", gcd().into()); vm.set_global_name("gcd", gcd().into());
vm.set_global_name("gcdn", gcdn().into());
vm.set_global_name("lcm", lcm().into()); vm.set_global_name("lcm", lcm().into());
vm.set_global_name("lcmn", lcmn().into());
vm.set_global_name("isqrt", isqrt().into()); vm.set_global_name("isqrt", isqrt().into());
vm.set_global_name("isprime", isprime().into()); vm.set_global_name("isprime", isprime().into());
vm.set_global_name("factors", factors().into()); vm.set_global_name("factors", factors().into());
@ -96,6 +98,7 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("cbrt", cbrt().into()); vm.set_global_name("cbrt", cbrt().into());
vm.set_global_name("ln", ln().into()); vm.set_global_name("ln", ln().into());
vm.set_global_name("log2", log2().into()); vm.set_global_name("log2", log2().into());
vm.set_global_name("log10", log10().into());
vm.set_global_name("exp", exp().into()); vm.set_global_name("exp", exp().into());
vm.set_global_name("exp2", exp2().into()); vm.set_global_name("exp2", exp2().into());
@ -191,7 +194,7 @@ pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "to_radix expected integer arguments, got {x:#} and {radix:#}") throw!(*SYM_TYPE_ERROR, "to_radix expected integer arguments, got {x:#} and {radix:#}")
}; };
if *radix < 2 || *radix > 36 { if *radix < 2 || *radix > 36 {
throw!(*SYM_TYPE_ERROR, "to_radix expected radix in range 0..=36") throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
} }
Ok(to_radix_inner(*x, *radix as u32, false).into()) Ok(to_radix_inner(*x, *radix as u32, false).into())
} }
@ -203,7 +206,7 @@ pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "to_radix_upper expected integer arguments, got {x:#} and {radix:#}") throw!(*SYM_TYPE_ERROR, "to_radix_upper expected integer arguments, got {x:#} and {radix:#}")
}; };
if *radix < 2 || *radix > 36 { if *radix < 2 || *radix > 36 {
throw!(*SYM_TYPE_ERROR, "to_radix_upper expected radix in range 0..=36") throw!(*SYM_VALUE_ERROR, "to_radix_upper expected radix in range 0..=36")
} }
Ok(to_radix_inner(*x, *radix as u32, true).into()) Ok(to_radix_inner(*x, *radix as u32, true).into())
} }
@ -215,11 +218,11 @@ pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "from_radix expected string and integer arguments, got {s:#} and {radix:#}") throw!(*SYM_TYPE_ERROR, "from_radix expected string and integer arguments, got {s:#} and {radix:#}")
}; };
if *radix < 2 || *radix > 36 { if *radix < 2 || *radix > 36 {
throw!(*SYM_TYPE_ERROR, "from_radix expected radix in range 0..=36") throw!(*SYM_VALUE_ERROR, "from_radix expected radix in range 0..=36")
} }
match parse_int(s.as_ref(), *radix as u32) { match parse_int(s.as_ref(), *radix as u32) {
Ok(v) => Ok(v.into()), Ok(v) => Ok(v.into()),
Err(_) => throw!(*SYM_TYPE_ERROR, "string was not a valid integer in given radix"), Err(_) => throw!(*SYM_VALUE_ERROR, "string was not a valid integer in given radix"),
} }
} }
@ -286,7 +289,7 @@ pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}")
}; };
if x < 0 { if x < 0 {
throw!(*SYM_TYPE_ERROR, "isqrt: argument must be positive") throw!(*SYM_VALUE_ERROR, "isqrt: argument must be positive")
} }
Ok(isqrt_inner(x).into()) Ok(isqrt_inner(x).into())
} }
@ -319,44 +322,69 @@ pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(true.into()) 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] = unpack_args!(args);
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
}; };
let Value::Int(y) = y else { let Value::Int(y) = y else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {y:#}")
}; };
let mut g = gcd_inner(x, y); Ok(gcd_inner(x, y).into())
for a in rest { }
#[native_func(1)]
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let mut g = 0;
while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else { let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {a:#}") throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
}; };
g = gcd_inner(g, a); g = gcd_inner(g, a);
} }
Ok(g.into()) Ok(g.into())
} }
#[native_func(2..)] #[native_func(2)]
pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, x, y], rest) = unpack_varargs!(args); let [_, x, y] = unpack_args!(args);
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
}; };
let Value::Int(y) = y else { let Value::Int(y) = y else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
}; };
let mut g = gcd_inner(x, y); let g = gcd_inner(x, y);
let mut prod = y; if g == 0 {
for a in rest { Ok(Value::from(0))
} else {
Value::from(x/g) * Value::from(y)
}
}
#[native_func(1)]
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let mut l = 1;
while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else { let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {a:#}") throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
}; };
prod *= a; let g = gcd_inner(l, a);
g = gcd_inner(g, a); if g == 0 { return Ok(Value::from(0)) };
l = (l/g).wrapping_mul(a);
} }
Ok((x/g * prod).into())
Ok(Value::from(l))
} }
#[native_func(1)] #[native_func(1)]
@ -401,26 +429,24 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// numeric operations // numeric operations
// //
#[native_func(1..)] #[native_func(2)]
pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, mut x], rest) = unpack_varargs!(args); let [_, x, y] = unpack_args!(args);
for val in rest { if y < x {
if val < x { Ok(y)
x = val; } else {
} Ok(x)
} }
Ok(x)
} }
#[native_func(1..)] #[native_func(2)]
pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, mut x], rest) = unpack_varargs!(args); let [_, x, y] = unpack_args!(args);
for val in rest { if y > x {
if val > x { Ok(y)
x = val; } else {
} Ok(x)
} }
Ok(x)
} }
#[native_func(1)] #[native_func(1)]
@ -690,6 +716,7 @@ float_func!(sqrt);
float_func!(cbrt); float_func!(cbrt);
float_func!(ln); float_func!(ln);
float_func!(log2); float_func!(log2);
float_func!(log10);
float_func!(exp); float_func!(exp);
float_func!(exp2); float_func!(exp2);

View file

@ -1,5 +1,5 @@
use rand::{seq::SliceRandom, Rng}; use rand::{seq::SliceRandom, Rng};
use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{range::RangeType, Value}, vmcalliter, Vm, exception::Result}; use talc_lang::{exception::Result, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{range::RangeType, Value}, vmcalliter, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;
@ -22,14 +22,14 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::List(l) => { Value::List(l) => {
let l = l.borrow(); let l = l.borrow();
let Some(v) = l.choose(&mut rand::thread_rng()) else { let Some(v) = l.choose(&mut rand::thread_rng()) else {
throw!(*SYM_TYPE_ERROR, "rand_in: empty list") throw!(*SYM_VALUE_ERROR, "rand_in: empty list")
}; };
Ok(v.clone()) Ok(v.clone())
}, },
Value::Table(t) => { Value::Table(t) => {
let t = t.borrow(); let t = t.borrow();
if t.is_empty() { if t.is_empty() {
throw!(*SYM_TYPE_ERROR, "rand_in: empty table") throw!(*SYM_VALUE_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();
@ -37,14 +37,14 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
}, },
Value::Range(r) => { Value::Range(r) => {
if r.is_empty() { if r.is_empty() {
throw!(*SYM_TYPE_ERROR, "rand_in: empty range") throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
} }
match r.ty { match r.ty {
RangeType::Open => Ok(Value::Int( RangeType::Open => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..r.stop))), rand::thread_rng().gen_range(r.start..r.stop))),
RangeType::Closed => Ok(Value::Int( RangeType::Closed => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..=r.stop))), rand::thread_rng().gen_range(r.start..=r.stop))),
RangeType::Endless => throw!(*SYM_TYPE_ERROR, "rand_in: endless range"), RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
} }
} }
col => { col => {
@ -54,7 +54,7 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
values.push(v); values.push(v);
} }
if values.is_empty() { if values.is_empty() {
throw!(*SYM_TYPE_ERROR, "rand_in: empty iterator") throw!(*SYM_VALUE_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);

View file

@ -1,6 +1,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use talc_lang::{exception::{exception, Result}, lstring::LString, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{NativeValue, Value}, Vm}; use talc_lang::{exception::{exception, Result}, lstring::LString, symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{NativeValue, Value}, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use regex::{Captures, Match, Regex}; use regex::{Captures, Match, Regex};
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -73,17 +73,17 @@ fn regex_from<'a>(v: &'a Value, name: &str) -> Result<Cow<'a, Regex>> {
match v { match v {
Value::String(s) => { Value::String(s) => {
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "regex must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "regex must be valid UTF-8")
}; };
Regex::new(s) Regex::new(s)
.map(Cow::Owned) .map(Cow::Owned)
.map_err(|e| exception!(*SYM_TYPE_ERROR, "invalid regex: {e}")) .map_err(|e| exception!(*SYM_VALUE_ERROR, "invalid regex: {e}"))
}, },
Value::Native(n) if n.get_type() == *SYM_STD_REGEX => { Value::Native(n) if n.get_type() == *SYM_STD_REGEX => {
n.as_any().downcast_ref::<ValueRegex>() n.as_any().downcast_ref::<ValueRegex>()
.map(|vr| Cow::Borrowed(&vr.0)) .map(|vr| Cow::Borrowed(&vr.0))
.ok_or_else(|| exception!( .ok_or_else(|| exception!(
*SYM_TYPE_ERROR, "BEES {name} expected string or regex, got {v:#}")) *SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}"))
}, },
_ => throw!(*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}") _ => throw!(*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}")
} }
@ -103,7 +103,7 @@ pub fn matches(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "matches expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "matches expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let re = regex_from(&re, "matches")?; let re = regex_from(&re, "matches")?;
Ok(re.is_match(s).into()) Ok(re.is_match(s).into())
@ -116,7 +116,7 @@ pub fn match_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "match_once expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "match_once expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let re = regex_from(&re, "match_once")?; let re = regex_from(&re, "match_once")?;
Ok(re.find(s).map_or(Value::Nil, match_to_value)) Ok(re.find(s).map_or(Value::Nil, match_to_value))
@ -129,7 +129,7 @@ pub fn _match(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "match expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "match expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let re = regex_from(&re, "match")?; let re = regex_from(&re, "match")?;
Ok(re.find_iter(s).map(match_to_value).collect::<Vec<Value>>().into()) Ok(re.find_iter(s).map(match_to_value).collect::<Vec<Value>>().into())
@ -142,7 +142,7 @@ pub fn captures_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "captures_once expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "captures_once expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let re = regex_from(&re, "captures_once")?; let re = regex_from(&re, "captures_once")?;
Ok(re.captures(s).map_or(Value::Nil, captures_to_value)) Ok(re.captures(s).map_or(Value::Nil, captures_to_value))
@ -155,7 +155,7 @@ pub fn captures(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "captures expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "captures expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let re = regex_from(&re, "captures")?; let re = regex_from(&re, "captures")?;
Ok(re.captures_iter(s).map(captures_to_value).collect::<Vec<Value>>().into()) Ok(re.captures_iter(s).map(captures_to_value).collect::<Vec<Value>>().into())
@ -171,10 +171,10 @@ pub fn replace_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "replace_once expected string or function, got {rep:#}") throw!(*SYM_TYPE_ERROR, "replace_once expected string or function, got {rep:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let Ok(rep) = rep.to_str() else { let Ok(rep) = rep.to_str() else {
throw!(*SYM_TYPE_ERROR, "replacement string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "replacement string must be valid UTF-8")
}; };
let re = regex_from(&re, "replace_once")?; let re = regex_from(&re, "replace_once")?;
Ok(LString::from(re.replace(s, rep)).into()) Ok(LString::from(re.replace(s, rep)).into())
@ -190,10 +190,10 @@ pub fn replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "replace expected string or function, got {rep:#}") throw!(*SYM_TYPE_ERROR, "replace expected string or function, got {rep:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let Ok(rep) = rep.to_str() else { let Ok(rep) = rep.to_str() else {
throw!(*SYM_TYPE_ERROR, "replacement string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "replacement string must be valid UTF-8")
}; };
let re = regex_from(&re, "replace")?; let re = regex_from(&re, "replace")?;
Ok(LString::from(re.replace_all(s, rep)).into()) Ok(LString::from(re.replace_all(s, rep)).into())
@ -206,7 +206,7 @@ pub fn split_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "split_once expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "split_once expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "string to split must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "string to split must be valid UTF-8")
}; };
let re = regex_from(&re, "split_once")?; let re = regex_from(&re, "split_once")?;
let mut parts = re.splitn(s, 2); let mut parts = re.splitn(s, 2);
@ -224,7 +224,7 @@ pub fn split(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "split expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "split expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "string to split must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "string to split must be valid UTF-8")
}; };
let re = regex_from(&re, "split")?; let re = regex_from(&re, "split")?;
let parts: Vec<Value> = re.split(s) let parts: Vec<Value> = re.split(s)

View file

@ -1,7 +1,7 @@
use talc_lang::{exception::Result, lformat, lstring::LString, symbol::SYM_TYPE_ERROR, throw, value::Value, Vm}; use talc_lang::{exception::Result, lformat, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::Value, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::unpack_args; use crate::{unpack_args, SYM_FORMAT_ERROR};
pub fn load(vm: &mut Vm) { pub fn load(vm: &mut Vm) {
vm.set_global_name("ord", ord().into()); vm.set_global_name("ord", ord().into());
@ -28,10 +28,10 @@ pub fn ord(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}; };
let mut chars = s.chars(); let mut chars = s.chars();
let Some(c) = chars.next() else { let Some(c) = chars.next() else {
throw!(*SYM_TYPE_ERROR, "argument to ord must have length 1") throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
}; };
if chars.next().is_some() { if chars.next().is_some() {
throw!(*SYM_TYPE_ERROR, "argument to ord must have length 1") throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
}; };
Ok(Value::Int(c as u32 as i64)) Ok(Value::Int(c as u32 as i64))
} }
@ -43,10 +43,10 @@ pub fn chr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "chr expected integer argument, got {i:#}") throw!(*SYM_TYPE_ERROR, "chr expected integer argument, got {i:#}")
}; };
let Ok(i) = u32::try_from(i) else { let Ok(i) = u32::try_from(i) else {
throw!(*SYM_TYPE_ERROR, "argument to chr is not a valid codepoint") throw!(*SYM_VALUE_ERROR, "argument to chr is not a valid codepoint")
}; };
let Some(c) = char::from_u32(i) else { let Some(c) = char::from_u32(i) else {
throw!(*SYM_TYPE_ERROR, "argument to chr is not a valid codepoint") throw!(*SYM_VALUE_ERROR, "argument to chr is not a valid codepoint")
}; };
Ok(Value::String(LString::from(c).into())) Ok(Value::String(LString::from(c).into()))
} }
@ -164,7 +164,7 @@ pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let bytes: Vec<u8> = b.borrow().iter() let bytes: Vec<u8> = b.borrow().iter()
.map(|v| match v { .map(|v| match v {
Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8), Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8),
_ => throw!(*SYM_TYPE_ERROR, "str_of_bytes expected list of integers in 0..=255"), _ => throw!(*SYM_VALUE_ERROR, "str_of_bytes expected list of integers in 0..=255"),
}).collect::<Result<Vec<u8>>>()?; }).collect::<Result<Vec<u8>>>()?;
Ok(LString::from(bytes).into()) Ok(LString::from(bytes).into())
} }
@ -185,7 +185,7 @@ pub fn _format(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Some(b @ (b'#' | b'?')) => { Some(b @ (b'#' | b'?')) => {
let fargs = fargs.borrow(); let fargs = fargs.borrow();
let Some(a) = fargs.get(faidx) else { let Some(a) = fargs.get(faidx) else {
throw!(*SYM_TYPE_ERROR, "not enough args for format string") throw!(*SYM_FORMAT_ERROR, "not enough args for format string")
}; };
faidx += 1; faidx += 1;
if b == b'?' { if b == b'?' {
@ -195,7 +195,7 @@ pub fn _format(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} }
}, },
_ => throw!(*SYM_TYPE_ERROR, "invalid format code") _ => throw!(*SYM_FORMAT_ERROR, "invalid format code")
} }
} else { } else {
res.push_byte(b); res.push_byte(b);

View file

@ -1,6 +1,6 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{exception::{exception, Result}, lformat, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{ops::RatioExt, HashValue, Rational64, Value}, Vm}; use talc_lang::{exception::{exception, Result}, lformat, parse_float, parse_int, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, HashValue, Rational64, Value}, Vm};
use talc_macros::native_func; use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;
@ -65,7 +65,7 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
(Value::Float(x), b"int") => Ok(Value::Int(x as i64)), (Value::Float(x), b"int") => Ok(Value::Int(x as i64)),
(Value::Float(x), b"ratio") => { (Value::Float(x), b"ratio") => {
let r = Rational64::approximate_float(x) let r = Rational64::approximate_float(x)
.ok_or_else(|| exception!(*SYM_TYPE_ERROR, "float {x:?} could not be converted to ratio"))?; .ok_or_else(|| exception!(*SYM_VALUE_ERROR, "float {x:?} could not be converted to ratio"))?;
Ok(Value::Ratio(r)) Ok(Value::Ratio(r))
} }
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())), (Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
@ -73,12 +73,12 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
(Value::String(s), b"int") (Value::String(s), b"int")
=> parse_int(s.as_ref(), 10) => parse_int(s.as_ref(), 10)
.map(i64::into) .map(i64::into)
.map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as integer")), .map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as integer")),
(Value::String(s), b"float") (Value::String(s), b"float")
=> parse_float(s.as_ref()) => parse_float(s.as_ref())
.map(f64::into) .map(f64::into)
.map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as float")), .map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as float")),
(v, _) => throw!(*SYM_TYPE_ERROR, (v, _) => throw!(*SYM_TYPE_ERROR,
"cannot convert value of type {} to type {}", v.get_type().name(), ty.name()) "cannot convert value of type {} to type {}", v.get_type().name(), ty.name())
@ -158,7 +158,7 @@ pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn uncell(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn uncell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, cell] = unpack_args!(args); let [_, cell] = unpack_args!(args);
let Value::Cell(cell) = cell else { let Value::Cell(cell) = cell else {
throw!(*SYM_TYPE_ERROR, "value is not a cell") throw!(*SYM_TYPE_ERROR, "uncell: value is not a cell")
}; };
Ok(Rc::unwrap_or_clone(cell).into_inner()) Ok(Rc::unwrap_or_clone(cell).into_inner())
} }
@ -167,7 +167,7 @@ pub fn uncell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn cell_replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn cell_replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, cell, value] = unpack_args!(args); let [_, cell, value] = unpack_args!(args);
let Value::Cell(cell) = cell else { let Value::Cell(cell) = cell else {
throw!(*SYM_TYPE_ERROR, "value is not a cell") throw!(*SYM_TYPE_ERROR, "cell_replace: value is not a cell")
}; };
Ok(cell.replace(value)) Ok(cell.replace(value))
} }
@ -176,7 +176,7 @@ pub fn cell_replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, cell] = unpack_args!(args); let [_, cell] = unpack_args!(args);
let Value::Cell(cell) = cell else { let Value::Cell(cell) = cell else {
throw!(*SYM_TYPE_ERROR, "value is not a cell") throw!(*SYM_TYPE_ERROR, "cell_take: value is not a cell")
}; };
Ok(cell.replace(Value::Nil)) Ok(cell.replace(Value::Nil))
} }