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}; 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); if let Err(e) = value.can_hash() { return Err(e) }; let mut hasher = DefaultHasher::new(); value.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())) } 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"); }