diff --git a/Cargo.lock b/Cargo.lock index 6414157..a59e6f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -181,6 +181,17 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "heck" version = "0.4.1" @@ -251,6 +262,7 @@ dependencies = [ "anyhow", "matrix", "matrix-macros", + "rand", ] [[package]] @@ -329,6 +341,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.78" @@ -357,6 +375,36 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "regex" version = "1.10.3" @@ -491,6 +539,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" diff --git a/matrix-stdlib/Cargo.toml b/matrix-stdlib/Cargo.toml index 1c6b0ac..bdbd2f9 100644 --- a/matrix-stdlib/Cargo.toml +++ b/matrix-stdlib/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" anyhow = "1" matrix = { path = "../matrix" } matrix-macros = { path = "../matrix-macros" } +rand = "0.8" diff --git a/matrix-stdlib/src/core.rs b/matrix-stdlib/src/core.rs index 183c142..2c76497 100644 --- a/matrix-stdlib/src/core.rs +++ b/matrix-stdlib/src/core.rs @@ -2,7 +2,8 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use matrix::{vm::Vm, value::Value, unpack_args, Result, unpack_varargs}; use matrix_macros::native_func; -use crate::{VmArgs, error}; +use rand::Rng; +use crate::{VmArgs, next, error}; fn to_radix(r: i64, mut n: i64) -> String { @@ -162,6 +163,116 @@ fn str(_: VmArgs, args: Vec) -> Result { Ok(Value::String(format!("{value}").into())) } +fn partition(list: &mut [Value], lo: usize, hi: usize) -> (usize, usize) { + let pivot = list[(lo + hi) / 2].clone(); + let mut lt = lo; + let mut eq = lo; + let mut gt = hi; + + while eq <= gt { + if list[eq] < pivot { + list.swap(eq, lt); + lt += 1; + eq += 1; + } else if list[eq] > pivot { + list.swap(eq, gt); + gt -= 1; + } else { + eq += 1; + } + } + + (lt, gt) +} + +fn quicksort(list: &mut [Value], lo: usize, hi: usize) { + if lo < hi { + let (lt, gt) = partition(list, lo, hi); + if lt > 0 { + quicksort(list, lo, lt - 1); + } + quicksort(list, gt + 1, hi); + } +} + +#[native_func(1)] +fn sort(_: VmArgs, args: Vec) -> Result { + let [value] = unpack_args!(args); + let Value::List(mut list) = value else { + return error!("sort requires a list") + }; + let hi = list.len() - 1; + quicksort(list.as_mut(), 0, hi); + Ok(Value::List(list)) +} + +#[native_func(0)] +fn rand(_: VmArgs, _: Vec) -> Result { + let mut rng = rand::thread_rng(); + Ok(Value::Float(rng.gen())) +} + +#[native_func(0..)] +fn rand_range(_: VmArgs, args: Vec) -> Result { + use Value as V; + let ([], varargs) = unpack_varargs!(args); + let (min, max, step) = match varargs.as_slice() { + [V::Int(max)] => { + (0, *max, 1) + }, + [V::Int(min), V::Int(max)] => { + (*min, *max, 1) + }, + [V::Int(min), V::Int(max), V::Int(step)] => { + (*min, *max, *step) + }, + _ => return error!("rand_range takes 1 to 3 [Int]'s") + }; + if max <= min { + return Ok(Value::Int(min)) + } + let count = ((max - 1 - min) / step) + 1; + let rng = (rand::thread_rng().gen::() % (count as u32)) as i64; + let i = min + (rng * step); + Ok(Value::Int(i)) +} + +#[native_func(2)] +fn rand_int(_: VmArgs, args: Vec) -> Result { + use Value as V; + let (min, max) = match args.as_slice() { + [V::Int(min), V::Int(max)] => { + (*min, *max + 1) + }, + _ => return error!("rand_int takes 2 [Int]'s") + }; + if max <= min { + return Ok(Value::Int(min)) + } + + let count = max - min; + let rng = (rand::thread_rng().gen::() % (count as u32)) as i64; + let i = min + rng; + Ok(Value::Int(i)) +} + +#[native_func(1)] +fn rand_in((vm, frame): VmArgs, args: Vec) -> Result { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut vals = Vec::new(); + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break } + vals.push(val); + } + if vals.is_empty() { + return Ok(Value::Nil) + } + let idx = rand::thread_rng().gen::() % vals.len(); + Ok(vals[idx].clone()) +} + pub fn load(vm: &mut Vm) { vm.load_global_fn(bin(), "bin"); vm.load_global_fn(sex(), "sex"); @@ -178,4 +289,10 @@ pub fn load(vm: &mut Vm) { vm.load_global_fn(hash(), "hash"); vm.load_global_fn(ord(), "ord"); vm.load_global_fn(chr(), "chr"); + + vm.load_global_fn(sort(), "sort"); + vm.load_global_fn(rand(), "rand"); + vm.load_global_fn(rand_range(), "rand_range"); + vm.load_global_fn(rand_int(), "rand_int"); + vm.load_global_fn(rand_in(), "rand_in"); } diff --git a/matrix-stdlib/src/iter.rs b/matrix-stdlib/src/iter.rs index 74056ce..630e52c 100644 --- a/matrix-stdlib/src/iter.rs +++ b/matrix-stdlib/src/iter.rs @@ -1,16 +1,10 @@ use std::{cell::RefCell, rc::Rc}; use matrix::{iter, vm::Vm, value::Value, Result, unpack_varargs, unpack_args}; use matrix_macros::native_func; -use crate::{error, VmArgs}; +use crate::{error, next, VmArgs}; use Value as V; -macro_rules! next { - ($vm:expr, $frame:expr, $iter:expr) => { - $vm.run_fn($frame, $iter.clone(), vec![]) - }; -} - #[native_func(1)] fn len(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); diff --git a/matrix-stdlib/src/lib.rs b/matrix-stdlib/src/lib.rs index 334de90..b4ab658 100644 --- a/matrix-stdlib/src/lib.rs +++ b/matrix-stdlib/src/lib.rs @@ -14,7 +14,14 @@ macro_rules! error { }; } +macro_rules! next { + ($vm:expr, $frame:expr, $iter:expr) => { + $vm.run_fn($frame, $iter.clone(), vec![]) + }; +} + pub(crate) use error; +pub(crate) use next; pub fn load(vm: &mut Vm) { core::load(vm);