use std::hash::{DefaultHasher, Hasher}; use rand::Rng; use matrix_lang::prelude::*; use matrix_macros::native_func; use crate::{VmArgs, next, error, unpack_args, unpack_varargs}; fn to_radix(r: i64, mut n: i64) -> String { let mut result = String::new(); let mut idx = 0; if n == 0 { result.push('0'); return result } else if n < 0 { n = -n; idx = 1; result.push('-'); } while n != 0 { let c = std::char::from_digit((n % r) as u32, r as u32).unwrap(); result.insert(idx, c); n /= r; } result } #[native_func(1)] fn bin(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); match value { Value::Int(n) => Ok(Value::String(to_radix(2, n).into())), _ => error!("bin requires a integer agument") } } #[native_func(1)] fn sex(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); match value { Value::Int(n) => Ok(Value::String(to_radix(6, n).into())), _ => error!("sex requires a integer agument") } } #[native_func(1)] fn oct(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); match value { Value::Int(n) => Ok(Value::String(to_radix(8, n).into())), _ => error!("oct requires a integer agument") } } #[native_func(1)] fn hex(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); match value { Value::Int(n) => Ok(Value::String(to_radix(16, n).into())), _ => error!("hex requires a integer agument") } } #[native_func(2)] fn radix(_: VmArgs, args: Vec) -> Result { let [radix, value] = unpack_args!(args); match (radix, value) { (Value::Int(r), Value::Int(n)) => Ok(Value::String(to_radix(r, n).into())), _ => error!("radix requires two integer aguments") } } #[native_func(1..)] fn append(_: VmArgs, args: Vec) -> Result { let ([list], args) = unpack_varargs!(args); let Value::List(mut list) = list else { return error!("append requires a list") }; for arg in args { list.push(arg); }; Ok(Value::List(list)) } #[native_func(2)] fn push(_: VmArgs, args: Vec) -> Result { let [list, value] = unpack_args!(args); let Value::List(mut list) = list else { return error!("push requires a list") }; list.push(value); Ok(Value::List(list)) } #[native_func(1)] fn pop(_: VmArgs, args: Vec) -> Result { let [list] = unpack_args!(args); let Value::List(mut list) = list else { return error!("pop requires a list") }; match list.pop() { Some(v) => Ok(v), None => Ok(Value::Nil) } } #[native_func(2)] fn remove(_: VmArgs, args: Vec) -> Result { let [list, index] = unpack_args!(args); let Value::List(mut list) = list else { return error!("remove requires a list") }; let Value::Int(i) = index else { return error!("remove reuqires a int index"); }; if i < 0 || i as usize >= list.len() { Ok(Value::Nil) } else { Ok(list.remove(i as usize)) } } #[native_func(1)] fn hash(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); let mut hasher = DefaultHasher::new(); value.try_hash(&mut hasher)?; let fin = hasher.finish(); Ok(Value::Int(fin as u32 as i64)) } #[native_func(1)] fn ord(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); let Value::String(str) = value else { return error!("ord requires a 1 length string") }; if str.len() != 1 { return error!("ord requires a 1 length string") } let char = str.chars().next().unwrap(); Ok(Value::Int(char as i64)) } #[native_func(1)] fn chr(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); let Value::Int(i) = value else { return error!("chr requires an int") }; match char::from_u32(i as u32) { Some(c) => Ok(Value::String(String::from(c).into())), None => error!("unable to get char from: {}", i as u32) } } #[native_func(1)] fn str(_: VmArgs, args: Vec) -> Result { let [value] = unpack_args!(args); 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") }; if list.len() > 1 { 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"); vm.load_global_fn(oct(), "oct"); vm.load_global_fn(hex(), "hex"); vm.load_global_fn(radix(), "radix"); vm.load_global_fn(str(), "str"); vm.load_global_fn(append(), "append"); vm.load_global_fn(push(), "push"); vm.load_global_fn(pop(), "pop"); vm.load_global_fn(remove(), "remove"); 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"); }