removed variadics, prepared for closures
This commit is contained in:
parent
e4b4c981d2
commit
754fbf6c2c
19 changed files with 538 additions and 231 deletions
|
@ -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 {}",
|
||||||
|
|
|
@ -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,9 +236,17 @@ 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) {
|
||||||
|
if self.locals[i].closed {
|
||||||
self.emit(I::StoreLocal(Arg24::from_usize(i)));
|
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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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:#}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
[Value::Symbol(ty)] | [Value::Symbol(ty), Value::Nil]
|
||||||
|
=> Exception::new(*ty),
|
||||||
|
[Value::Symbol(ty), Value::Nil, data]
|
||||||
|
=> Exception::new_with_data(*ty, data.clone()),
|
||||||
|
[Value::Symbol(ty), Value::String(s)]
|
||||||
|
=> Exception::new_with_msg(*ty, s.clone()),
|
||||||
|
[Value::Symbol(ty), Value::String(s), data]
|
||||||
|
=> Exception::new_with_msg_data(*ty, s.clone(), data.clone()),
|
||||||
|
[] | [_] | [_,_] | [_,_,_] => throw!(*SYM_TYPE_ERROR, "wrong argument for throw"),
|
||||||
|
[_,_,_,_,..] => throw!(*SYM_VALUE_ERROR, "too many elements in list argument for throw"),
|
||||||
|
}
|
||||||
|
_ => throw!(*SYM_TYPE_ERROR, "throw expected symbol or list"),
|
||||||
};
|
};
|
||||||
Err(match &*varargs {
|
Err(exc)
|
||||||
[] | [Value::Nil]
|
|
||||||
=> Exception::new(ty),
|
|
||||||
[Value::Nil, data]
|
|
||||||
=> Exception::new_with_data(ty, data.clone()),
|
|
||||||
[Value::String(s)]
|
|
||||||
=> Exception::new_with_msg(ty, s.clone()),
|
|
||||||
[Value::String(s), data]
|
|
||||||
=> Exception::new_with_msg_data(ty, s.clone(), data.clone()),
|
|
||||||
[_] | [_,_] => throw!(*SYM_TYPE_ERROR, "wrong arguments for throw"),
|
|
||||||
[_,_,_,..] => throw!(*SYM_TYPE_ERROR, "too many arguments for throw"),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
},
|
},
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,14 +358,38 @@ 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());
|
||||||
for i in &iters {
|
for i in &iters {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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))
|
||||||
let Value::Int(a) = a else {
|
} else {
|
||||||
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {a:#}")
|
Value::from(x/g) * Value::from(y)
|
||||||
};
|
|
||||||
prod *= a;
|
|
||||||
g = gcd_inner(g, a);
|
|
||||||
}
|
}
|
||||||
Ok((x/g * prod).into())
|
}
|
||||||
|
|
||||||
|
#[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 {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
|
||||||
|
};
|
||||||
|
let g = gcd_inner(l, a);
|
||||||
|
if g == 0 { return Ok(Value::from(0)) };
|
||||||
|
l = (l/g).wrapping_mul(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::from(l))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -401,27 +429,25 @@ 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)]
|
||||||
pub fn floor(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn floor(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue