diff options
author | Freya Murphy <freya@freyacat.org> | 2024-02-29 17:04:28 -0500 |
---|---|---|
committer | Freya Murphy <freya@freyacat.org> | 2024-02-29 17:04:28 -0500 |
commit | 5d2747e26f51cc2344a6bd95f93457248fdfebd8 (patch) | |
tree | 8755b4068166c3854d26817683ce438a771ab319 /matrix-std/src | |
parent | more mat, sys, and os stdlib functions, better matrix printing, other fixes (diff) | |
download | matrix-5d2747e26f51cc2344a6bd95f93457248fdfebd8.tar.gz matrix-5d2747e26f51cc2344a6bd95f93457248fdfebd8.tar.bz2 matrix-5d2747e26f51cc2344a6bd95f93457248fdfebd8.zip |
fin prob
Diffstat (limited to 'matrix-std/src')
-rw-r--r-- | matrix-std/src/core.rs | 295 | ||||
-rw-r--r-- | matrix-std/src/io.rs | 174 | ||||
-rw-r--r-- | matrix-std/src/iter.rs | 546 | ||||
-rw-r--r-- | matrix-std/src/lib.rs | 50 | ||||
-rw-r--r-- | matrix-std/src/math.rs | 688 | ||||
-rw-r--r-- | matrix-std/src/sys.rs | 252 |
6 files changed, 2005 insertions, 0 deletions
diff --git a/matrix-std/src/core.rs b/matrix-std/src/core.rs new file mode 100644 index 0000000..69b6d97 --- /dev/null +++ b/matrix-std/src/core.rs @@ -0,0 +1,295 @@ +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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + 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<Value>) -> Result<Value> { + let mut rng = rand::thread_rng(); + Ok(Value::Float(rng.gen())) +} + +#[native_func(0..)] +fn rand_range(_: VmArgs, args: Vec<Value>) -> Result<Value> { + 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::<u32>() % (count as u32)) as i64; + let i = min + (rng * step); + Ok(Value::Int(i)) +} + +#[native_func(2)] +fn rand_int(_: VmArgs, args: Vec<Value>) -> Result<Value> { + 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::<u32>() % (count as u32)) as i64; + let i = min + rng; + Ok(Value::Int(i)) +} + +#[native_func(1)] +fn rand_in((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + 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::<usize>() % 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"); +} diff --git a/matrix-std/src/io.rs b/matrix-std/src/io.rs new file mode 100644 index 0000000..19ff074 --- /dev/null +++ b/matrix-std/src/io.rs @@ -0,0 +1,174 @@ +use std::{io::{self, Read, Write}, cell::RefCell, fs::OpenOptions, rc::Rc}; +use matrix_lang::prelude::*; +use matrix_macros::native_func; +use crate::{error, VmArgs, unpack_varargs, unpack_args}; + +#[native_func(0..)] +fn print(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let ([], varags) = unpack_varargs!(args); + for (i, value) in varags.into_iter().enumerate() { + if i != 0 { + print!(" "); + } + print!("{value}"); + } + Ok(Value::Nil) +} + +#[native_func(0..)] +fn println(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let ([], varags) = unpack_varargs!(args); + for (i, value) in varags.into_iter().enumerate() { + if i != 0 { + print!(" "); + } + print!("{value}"); + } + print!("\n"); + Ok(Value::Nil) +} + +#[native_func(0)] +fn readln(_: VmArgs, _: Vec<Value>) -> Result<Value> { + let mut input = String::new(); + match io::stdin().read_line(&mut input) { + Ok(_) => { + match input.pop() { + Some(c) if c == '\n' => {}, + Some(c) => input.push(c), + None => {} + }; + Ok(Value::String(input.into())) + }, + Err(err) => error!("cant read from stdin: {err}") + } +} + +#[native_func(1)] +fn input(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [prompt] = unpack_args!(args); + let mut input = String::new(); + print!("{prompt}"); + let _ = io::stdout().flush(); + match io::stdin().read_line(&mut input) { + Ok(_) => { + match input.pop() { + Some(c) if c == '\n' => {}, + Some(c) => input.push(c), + None => {} + }; + Ok(Value::String(input.into())) + }, + Err(err) => error!("cant read from stdin: {err}") + } +} + +#[native_func(0)] +fn readlines(_: VmArgs, _: Vec<Value>) -> Result<Value> { + let lines = RefCell::new(io::stdin().lines()); + Ok(iter!(move |_,_| { + match lines.borrow_mut().next() { + Some(Ok(line)) => Ok(Value::String(line.into())), + _ => Ok(Value::Nil) + } + })) +} + +#[native_func(2)] +fn file_open(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [path, mode] = unpack_args!(args); + let Value::String(mode) = mode else { + return error!("open mode must be a string") + }; + let Value::String(path) = path else { + return error!("open path must be a string") + }; + let file = match mode.as_ref() { + "r" => OpenOptions::new().read(true).open(path.as_ref()), + "w" => OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()), + "a" => OpenOptions::new().write(true).create(true).append(true).open(path.as_ref()), + "r+" => OpenOptions::new().read(true).write(true).open(path.as_ref()), + "w+" => OpenOptions::new().read(true).write(true).create(true).truncate(true).open(path.as_ref()), + "a+" => OpenOptions::new().read(true).write(true).create(true).append(true).open(path.as_ref()), + _ => return error!("invalid open mode: {mode}") + }; + + match file { + Ok(file) => Ok(Value::File(Rc::new(RefCell::new(file)))), + Err(err) => return error!("cannot open '{path}': {err}") + } +} + +#[native_func(1)] +fn file_read(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [file] = unpack_args!(args); + let Value::File(file) = file else { + return error!("file read requires a file") + }; + let mut contents = String::new(); + if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) { + return error!("cannot read file: '{err}'") + }; + Ok(Value::String(contents.into())) +} + +#[native_func(1)] +fn file_lines(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [file] = unpack_args!(args); + let Value::File(file) = file else { + return error!("file read requires a file") + }; + let mut contents = String::new(); + if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) { + return error!("cannot read file: '{err}'") + }; + let lines: Vec<Rc<str>> = contents.split_inclusive("\n").map(|s| Rc::from(s)).collect(); + let lines = RefCell::new(lines.into_iter()); + Ok(iter!(move |_,_| { + match lines.borrow_mut().next() { + Some(line) => Ok(Value::String(line)), + None => Ok(Value::Nil) + } + })) +} + +#[native_func(2)] +fn file_write(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [file, content] = unpack_args!(args); + let Value::File(file) = file else { + return error!("file write requires a file") + }; + let content = format!("{content}"); + if let Err(err) = file.try_borrow_mut().unwrap().write_all(content.as_bytes()) { + return error!("cannot write file: '{err}'") + }; + Ok(Value::Nil) +} + +#[native_func(0..)] +fn throw(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let ([], varargs) = unpack_varargs!(args); + + let mut str = String::new(); + for (i, v) in varargs.into_iter().enumerate() { + if i != 0 { + str.push_str(" "); + } + str.push_str(&format!("{v}")); + } + + error!("{str}") +} + +pub fn load(vm: &mut Vm) { + vm.load_global_fn(print(), "print"); + vm.load_global_fn(println(), "println"); + vm.load_global_fn(readln(), "readln"); + vm.load_global_fn(input(), "input"); + vm.load_global_fn(readlines(), "readlines"); + vm.load_global_fn(file_open(), "file_open"); + vm.load_global_fn(file_read(), "file_read"); + vm.load_global_fn(file_lines(), "file_lines"); + vm.load_global_fn(file_write(), "file_write"); + vm.load_global_fn(throw(), "throw"); +} diff --git a/matrix-std/src/iter.rs b/matrix-std/src/iter.rs new file mode 100644 index 0000000..638755c --- /dev/null +++ b/matrix-std/src/iter.rs @@ -0,0 +1,546 @@ +use std::{cell::RefCell, rc::Rc}; +use matrix_lang::prelude::*; +use matrix_macros::native_func; +use crate::{error, next, VmArgs, unpack_args, unpack_varargs}; + +use Value as V; + +#[native_func(1)] +fn len(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let len = match value { + V::List(l) => l.len(), + V::Matrix(m) => m.values.len(), + V::String(s) => s.len(), + V::Table(t) => t.len(), + _ => 1 + }; + Ok(V::Int(len as i64)) +} + +#[native_func(1)] +fn sum((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut res = next!(vm, frame, iter)?; + if res == Value::Nil { + return Ok(res) + }; + loop { + let next = next!(vm, frame, iter)?; + if next == Value::Nil { break } + res = (res + next)?; + } + Ok(res) +} + +#[native_func(0..)] +fn range(_: VmArgs, args: Vec<Value>) -> Result<Value> { + 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!("range takes 1 to 3 [Int]'s") + }; + + let i = RefCell::new(min); + Ok(iter!(move |_,_| { + let curr = *(i.borrow()); + *(i.borrow_mut()) = curr + step; + if (step >= 0 && curr >= max) || (step <= 0 && curr <= max) { + return Ok(V::Nil) + } else { + return Ok(V::Int(curr)) + } + })) +} + +#[native_func(1)] +fn iter(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + Ok(value.into_iter()?) +} + +#[native_func(1)] +fn once(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let first = RefCell::new(false); + Ok(iter!(move |_,_| { + if *first.borrow() == false { + *first.borrow_mut() = true; + Ok(value.clone()) + } else { + Ok(Value::Nil) + } + })) +} + +#[native_func(1)] +fn list((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut list = Vec::new(); + loop { + let res = next!(vm, frame, iter)?; + if res == Value::Nil { break } + list.push(res); + } + Ok(Value::List(list.into())) +} + +#[native_func(2)] +fn map(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [fun, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Function(fun) = fun else { + return error!("map 1st arg must be a function") + }; + Ok(iter!(move |(vm,frame),_| { + let input = next!(vm, frame, iter)?; + if input == Value::Nil { + return Ok(input) + } + vm.run_fn(frame, fun.clone(), vec![input]) + })) +} + +#[native_func(2)] +fn fold((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [fun, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Function(fun) = fun else { + return error!("fold 1st arg must be a function") + }; + let mut res = next!(vm, frame, iter)?; + if res == Value::Nil { + return Ok(Value::Nil) + }; + loop { + let next = next!(vm, frame, iter)?; + if next == Value::Nil { + return Ok(res) + }; + res = vm.run_fn(frame, fun.clone(), vec![res, next])?; + } +} + +#[native_func(3)] +fn foldi((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [init, fun, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Function(fun) = fun else { + return error!("foldi 1st arg must be a function") + }; + let mut res = init; + loop { + let next = next!(vm, frame, iter)?; + if next == Value::Nil { + return Ok(res) + }; + res = vm.run_fn(frame, fun.clone(), vec![res, next])?; + } +} + +#[native_func(1)] +fn count((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut len = 0; + loop { + let res = vm.run_fn(frame, iter.clone(), vec![])?; + if res == Value::Nil { break }; + len += 1; + } + Ok(Value::Int(len)) +} + +#[native_func(3)] +fn scan(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [init, fun, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Function(fun) = fun else { + return error!("scan 2nd arg must be a function") + }; + let res = RefCell::new(init); + Ok(iter!(move |(vm,frame),_| { + let next = next!(vm, frame, iter)?; + if next == Value::Nil { + return Ok(Value::Nil) + }; + let res_next = vm.run_fn(frame, fun.clone(), vec![res.borrow().clone(), next])?; + *res.borrow_mut() = res_next; + Ok(res.borrow().clone()) + })) +} + +#[native_func(2)] +fn filter(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [fun, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Function(fun) = fun else { + return error!("filter 1st arg must be a function") + }; + Ok(iter!(move |(vm,frame),_| { + loop { + let next = next!(vm, frame, iter)?; + if next == Value::Nil { + return Ok(Value::Nil) + } + let res = vm.run_fn(frame, fun.clone(), vec![next.clone()])?; + if !!res { + return Ok(next) + } + } + })) +} + +#[native_func(0..)] +fn chain(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let ([], iters) = unpack_varargs!(args); + + let mut chaind = Vec::new(); + for iter in iters { + chaind.push(iter.into_iter_fn()?); + } + + chaind.reverse(); + let chaind = RefCell::new(chaind); + Ok(iter!(move |(vm,frame), _| { + loop { + let curr = match chaind.borrow_mut().last() { + Some(iter) => iter.clone(), + None => return Ok(Value::Nil) + }; + match vm.run_fn(frame, curr.clone(), vec![]) { + Ok(Value::Nil) => { + chaind.borrow_mut().pop(); + continue; + }, + v => return v + }; + } + })) +} + +#[native_func(1)] +fn lines(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [str] = unpack_args!(args); + let Value::String(str) = str else { + return error!("lines arg must be a string") + }; + let lines: Vec<Rc<str>> = str.split("\n").map(|s| Rc::from(s)).collect(); + let res = RefCell::new(lines.into_iter()); + Ok(iter!(move |_,_| { + match res.borrow_mut().next() { + Some(line) => Ok(Value::String(line)), + None => Ok(Value::Nil) + } + })) +} + +#[native_func(2)] +fn skip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [count, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Int(i) = count else { + return error!("skip count requires a int") + }; + for _ in 0..i { + next!(vm, frame, iter)?; + } + Ok(Value::Iter(iter)) +} + +#[native_func(2)] +fn alternate(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [il, ir] = unpack_args!(args); + let il = il.into_iter_fn()?; + let ir = ir.into_iter_fn()?; + let flag = RefCell::new(Some(0)); + Ok(iter!(move |(vm, frame),_| { + let f = *flag.borrow(); + match f { + Some(0) => { + let val = next!(vm, frame, il)?; + if val == Value::Nil { + *flag.borrow_mut() = None; + } else { + *flag.borrow_mut() = Some(1); + } + Ok(val) + }, + Some(1) => { + let val = next!(vm, frame, ir)?; + if val == Value::Nil { + *flag.borrow_mut() = None; + } else { + *flag.borrow_mut() = Some(0); + } + Ok(val) + }, + _ => Ok(Value::Nil) + } + })) +} + +#[native_func(2)] +fn intersperse((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [value, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let flag = RefCell::new(Some(0)); + let next = RefCell::new(next!(vm, frame, iter)?); + Ok(iter!(move |(vm, frame),_| { + let f = *flag.borrow(); + match f { + Some(0) => { + let val = next.borrow(); + if *val == Value::Nil { + *flag.borrow_mut() = None; + } else { + *flag.borrow_mut() = Some(1); + } + Ok(val.clone()) + }, + Some(1) => { + *next.borrow_mut() = next!(vm, frame, iter)?; + if *next.borrow() == Value::Nil { + *flag.borrow_mut() = None; + return Ok(Value::Nil) + } else { + *flag.borrow_mut() = Some(0); + } + let val = value.clone(); + Ok(val) + }, + _ => Ok(Value::Nil) + } + })) +} + +#[native_func(2)] +fn zip(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [il, ir] = unpack_args!(args); + let il = il.into_iter_fn()?; + let ir = ir.into_iter_fn()?; + let flag = RefCell::new(true); + Ok(iter!(move |(vm, frame),_| { + let f = *flag.borrow(); + match f { + true => { + let vl = next!(vm, frame, il)?; + let vr = next!(vm, frame, ir)?; + if vl == Value::Nil || vr == Value::Nil { + *flag.borrow_mut() = false; + return Ok(Value::Nil) + } + Ok(Value::List(vec![vl, vr].into())) + }, + _ => Ok(Value::Nil) + } + })) +} + +#[native_func(1)] +fn unzip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut ll = Vec::new(); + let mut lr = Vec::new(); + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { + break; + } + let Value::List(vals) = val else { + return error!("unzip only works over a iterator of pairs"); + }; + let vals = vals.into_inner(); + if vals.len() != 2 { + return error!("unzip only works over a iterator of pairs"); + } + let [l, r] = vals.try_into().unwrap(); + ll.push(l); + lr.push(r); + } + let ll = Value::List(ll.into()); + let lr = Value::List(lr.into()); + Ok(Value::List(vec![ll, lr].into())) +} + +#[native_func(1)] +fn cycle((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut values = Vec::new(); + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + values.push(val); + } + let idx = RefCell::new(0_usize); + Ok(iter!(move |_,_| { + let i = *idx.borrow(); + *idx.borrow_mut() += 1; + *idx.borrow_mut() %= values.len(); + Ok(values[i].clone()) + })) +} + +#[native_func(2)] +fn take((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [count, iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let Value::Int(count) = count else { + return error!("take requires an int amount to collect") + }; + let mut values = Vec::new(); + for _ in 0..count { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + values.push(val); + } + Ok(Value::List(values.into())) +} + +#[native_func(1)] +fn last((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut last = Value::Nil; + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + last = val; + } + Ok(last) +} + +#[native_func(1)] +fn next((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let val = next!(vm, frame, iter)?; + Ok(val) +} + +#[native_func(1)] +fn min((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut min = Value::Nil; + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + if min == Value::Nil || val < min { + min = val; + } + } + Ok(min) +} + +#[native_func(1)] +fn max((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut max = Value::Nil; + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + if max == Value::Nil || val > max { + max = val; + } + } + Ok(max) +} + +#[native_func(1)] +fn rev((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + let mut values = Vec::new(); + loop { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { break }; + values.push(val); + } + let values = RefCell::new(values); + Ok(iter!(move |_,_| { + match values.borrow_mut().pop() { + Some(v) => Ok(v), + None => Ok(Value::Nil) + } + })) +} + +#[native_func(1)] +fn enumerate(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [iter] = unpack_args!(args); + let iter = iter.into_iter_fn()?; + + let idx = RefCell::new(0_i64); + Ok(iter!(move |(vm, frame),_| { + let val = next!(vm, frame, iter)?; + if val == Value::Nil { + return Ok(Value::Nil) + }; + let curr = *idx.borrow(); + *idx.borrow_mut() += 1; + Ok(Value::List(vec![Value::Int(curr), val].into())) + })) +} + +#[native_func(1)] +fn iterable(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + use Value as V; + match value { + V::Function(_) | + V::List(_) | + V::Range(_) | + V::Iter(_) + => Ok(V::Bool(true)), + _ => Ok(V::Bool(false)) + } +} + +pub fn load(vm: &mut Vm) { + vm.load_global_fn(take(), "take"); + vm.load_global_fn(unzip(), "unzip"); + vm.load_global_fn(count(), "count"); + vm.load_global_fn(len(), "len"); + vm.load_global_fn(sum(), "sum"); + vm.load_global_fn(min(), "min"); + vm.load_global_fn(max(), "max"); + vm.load_global_fn(next(), "next"); + vm.load_global_fn(last(), "last"); + + vm.load_global_fn(list(), "list"); + vm.load_global_fn(fold(), "fold"); + vm.load_global_fn(foldi(), "foldi"); + vm.load_global_fn(scan(), "scan"); + vm.load_global_fn(chain(), "chain"); + vm.load_global_fn(lines(), "lines"); + vm.load_global_fn(skip(), "skip"); + + vm.load_global_fn(once(), "once"); + vm.load_global_fn(iter(), "iter"); + vm.load_global_fn(range(), "range"); + vm.load_global_fn(map(), "map"); + vm.load_global_fn(filter(), "filter"); + vm.load_global_fn(skip(), "skip"); + vm.load_global_fn(zip(), "zip"); + vm.load_global_fn(cycle(), "cycle"); + vm.load_global_fn(alternate(), "alternate"); + vm.load_global_fn(intersperse(), "intersperse"); + vm.load_global_fn(rev(), "rev"); + vm.load_global_fn(enumerate(), "enumerate"); + + vm.load_global_fn(iterable(), "iterable"); +} diff --git a/matrix-std/src/lib.rs b/matrix-std/src/lib.rs new file mode 100644 index 0000000..af4ecab --- /dev/null +++ b/matrix-std/src/lib.rs @@ -0,0 +1,50 @@ +mod core; +mod sys; +mod math; +mod io; +mod iter; + +use matrix_lang::prelude::*; +pub(crate) type VmArgs<'a, 'b> = (&'a mut Vm, &'b mut StackFrame); + +macro_rules! error { + ($($arg:tt)*) => { + Err(::matrix_lang::prelude::exception!(::matrix_lang::prelude::RUNTIME_EXCEPTION, $($arg)*)) + }; +} + +macro_rules! next { + ($vm:expr, $frame:expr, $iter:expr) => { + $vm.run_fn($frame, $iter.clone(), vec![]) + }; +} + +macro_rules! unpack_args { + ($e:expr) => { + $e.try_into().expect("bypassed arity check") + }; +} + +macro_rules! unpack_varargs { + ($e:expr) => {{ + let mut args = $e; + let matrix_lang::prelude::Value::List(varargs) = args.pop().expect("bypassed arity check") else { + panic!("bypassed arity check") + }; + let varargs = varargs.into_inner(); + (args.try_into().expect("bypassed arity check"), varargs) + }}; +} + +pub(crate) use error; +pub(crate) use next; +pub(crate) use unpack_args; +pub(crate) use unpack_varargs; + +pub fn load(vm: &mut Vm) { + core::load(vm); + sys::load(vm); + io::load(vm); + iter::load(vm); + math::load(vm); +} diff --git a/matrix-std/src/math.rs b/matrix-std/src/math.rs new file mode 100644 index 0000000..111544c --- /dev/null +++ b/matrix-std/src/math.rs @@ -0,0 +1,688 @@ +use core::f64; +use std::f64::{consts::{PI, E, TAU}, NAN, INFINITY}; +use matrix_lang::prelude::*; +use matrix_macros::native_func; +use crate::{error, VmArgs, unpack_args}; + +#[native_func(1)] +fn trans(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let mat = match value { + Value::Matrix(m) => m, + Value::List(l) => Matrix::from_list(l.to_vec()).into(), + _ => return error!("trans must be given a matrix") + }; + let values = mat + .cols() + .reduce(|mut a, b| {a.extend(b); a}) + .unwrap() + .into_iter() + .map(|e| e.clone()) + .collect(); + Ok(Value::Matrix(Matrix::new(mat.codomain, mat.domain, values).into())) +} + +fn mat_gauss_row_operation( + r1: usize, + r2: usize, + scale: Value, + mat: &mut Matrix +) -> Result<()> { + for col in 0..mat.domain { + let r1v = mat.get(r1, col)?; + let r2v = mat.get(r2, col)?; + let res = (r1v - (r2v * scale.clone())?)?; + mat.set(r1, col, res)?; + } + Ok(()) +} + +fn mat_swap_rows( + r1: usize, + r2: usize, + mat: &mut Matrix +) -> Result<()> { + let cols = mat.domain; + for col in 0..cols { + let a = mat.get(r1, col)?; + let b = mat.get(r2, col)?; + mat.set(r2, col, a)?; + mat.set(r1, col, b)?; + } + Ok(()) +} + +fn mat_find_non_zero_col( + mat: &Matrix +) -> Option<usize> { + for (i,col) in mat.cols().enumerate() { + for val in col.iter() { + if **val != Value::Int(0) { + return Some(i) + } + } + } + return None +} + +fn mat_scale_pivot_row( + row: usize, + mat: &mut Matrix +) -> Result<()> { + let scale = mat.get(row, row)?; + if scale.is_zero() { + return Ok(()) + } + for col in 0..mat.domain { + let res = (mat.get(row, col)?.clone() / scale.clone())?; + mat.set(row, col, res)?; + } + Ok(()) +} + +fn mat_get_non_zero_pivot_row( + row: usize, + mat: &mut Matrix, +) -> Result<()> { + let col = row; + let test = mat.get(row, col)?; + if test.is_zero() { + for r in row..mat.codomain { + let cur = mat.get(r, col)?; + if !cur.is_zero() { + mat_swap_rows(row, r, mat)?; + break; + } + } + } + mat_scale_pivot_row(row, mat)?; + Ok(()) +} + +fn mat_rref(mat: Matrix, full_rref: bool) -> Result<Matrix> { + let mut mat = mat; + let Some(start) = mat_find_non_zero_col(&mat) else { + return Ok(mat) + }; + let end = mat.domain.min(mat.codomain); + for col in start..end { + let pivot_row = col; + mat_get_non_zero_pivot_row(pivot_row, &mut mat)?; + if mat.get(pivot_row, col)?.is_zero() { + break + } + let min = if full_rref { 0 } else { col }; + for row in min..mat.codomain { + if row == pivot_row { continue; }; + let scale = mat.get(row, col)?; + mat_gauss_row_operation(row, pivot_row, scale, &mut mat)?; + } + } + Ok(mat) +} + +#[native_func(1)] +fn rref(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let mat = match value { + Value::Matrix(m) => m, + Value::List(l) => Matrix::from_list(l.to_vec()).into(), + _ => return error!("rref must be given a matrix") + }; + Ok(Value::Matrix(mat_rref(mat.into_inner(), true)?.into())) +} + +#[native_func(1)] +fn mat_ref(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let mat = match value { + Value::Matrix(m) => m, + Value::List(l) => Matrix::from_list(l.to_vec()).into(), + _ => return error!("ref must be given a matrix") + }; + Ok(Value::Matrix(mat_rref(mat.into_inner(), false)?.into())) +} + +fn mat_det(mat: Matrix) -> Result<Value> { + if mat.domain == 1 { + return Ok(mat.get(0,0)?) + } + if mat.domain == 2 { + let a = mat.get(0,0)? * mat.get(1,1)?; + let b = mat.get(0,1)? * mat.get(1,0)?; + return Ok((a? - b?)?) + } + let mut res = Value::Int(0); + for col in 0..mat.domain { + let sub_values = mat.rows() + .skip(1) + .map(|r| + r.into_iter() + .enumerate() + .filter(|(idx,_)| *idx != col) + .map(|(_, v)| v.clone()) + .collect::<Vec<Value>>() + ) + .reduce(|mut a, b| {a.extend(b); a}) + .unwrap(); + let sub = Matrix::new(mat.domain - 1, mat.domain - 1, sub_values); + let val = mat.get(0, col)?; + let part = (val * mat_det(sub)?)?; + if col % 2 == 0 { + res = (res + part)?; + } else { + res = (res - part)?; + } + } + Ok(res) +} + +#[native_func(1)] +fn det(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let mat = match value { + Value::Matrix(m) if m.domain == m.codomain => m, + Value::List(l) if l.len() == 1 => Matrix::from_list(l.to_vec()).into(), + _ => return error!("det requires a square matrix") + }; + let mat = mat.into_inner(); + Ok(mat_det(mat)?) +} + +fn mat_ident(dim: usize) -> Matrix { + let len = dim * dim; + let mut values = vec![Value::Int(0); len]; + let mut idx = 0; + loop { + if idx >= len { break }; + values[idx] = Value::Int(1); + idx += dim + 1; + } + Matrix::new(dim, dim, values) +} + +#[native_func(1)] +fn ident(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let dim = match value { + Value::Int(i) if i > 0 => i, + Value::Ratio(r) + if *r.denom() == 1 && + *r.numer() > 0 + => *r.numer(), + _ => return error!("ident requries a positive [Int] dimension") + }; + Ok(Value::Matrix(mat_ident(dim as usize).into())) +} + +fn mat_splith(mat: Matrix) -> (Matrix, Matrix) { + let mut m1 = Vec::new(); + let mut m2 = Vec::new(); + + mat.rows() + .for_each(|r| { + let split = r.len() / 2; + r.into_iter().enumerate().for_each(|(i, v)| { + if i < split { + m1.push(v.clone()); + } else { + m2.push(v.clone()); + } + }) + }); + + let m1 = Matrix::new(mat.domain/2, mat.codomain, m1); + let m2 = Matrix::new(mat.domain/2, mat.codomain, m2); + (m1, m2) +} + +#[native_func(1)] +fn inv(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let mat = match value { + Value::Matrix(m) if m.domain == m.codomain => m, + Value::List(l) if l.len() == 1 => Matrix::from_list(l.to_vec()).into(), + _ => return error!("det requires a square matrix") + }; + let mat = mat.into_inner(); + let ident = mat_ident(mat.domain); + let joined = mat.join_right(&ident)?; + let refed = mat_rref(joined, true)?; + let (new_ident, new_inv) = mat_splith(refed); + + if new_ident == ident { + Ok(Value::Matrix(new_inv.into())) + } else { + error!("matrix does not have an inverse") + } +} + +macro_rules! mathr { + ($type:ident) => { + #[native_func(1)] + fn $type(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Int(i)), + V::Ratio(r) => Ok(V::Ratio(r.$type())), + V::Float(f) => Ok(V::Float(f.$type())), + v => error!("cannot compute {} on {v}", stringify!($type)) + } + } + }; +} + +macro_rules! trig { + ($type:ident) => { + #[native_func(1)] + fn $type(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value.floaty() { + V::Float(f) => Ok(V::Float(f.$type())), + V::Complex(c) => Ok(V::Complex(c.$type())), + v => error!("cannot compute {} on {v}", stringify!($type)) + } + } + }; +} + +macro_rules! trigf { + ($type:ident, $str:ident) => { + #[native_func(1)] + fn $str(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value.floaty() { + V::Float(f) => Ok(V::Float(f.$type())), + v => error!("cannot compute {} on {v}", stringify!($str)) + } + } + }; +} + +#[native_func(2)] +fn log(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [base, value] = unpack_args!(args); + match (base.floaty(), value.floaty()) { + (V::Float(base), V::Float(arg)) => Ok(V::Float(arg.log(base))), + (V::Float(base), V::Complex(arg)) => Ok(V::Complex(arg.log(base))), + (V::Complex(base), V::Float(arg)) => Ok(V::Complex(arg.ln() / base.ln())), + (V::Complex(base), V::Complex(arg)) => Ok(V::Complex(arg.ln() / base.ln())), + (base, arg) => error!("cannot compute log base {base} argument {arg}") + } +} + +#[native_func(1)] +fn abs(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Int(i.abs())), + V::Float(f) => Ok(V::Float(f.abs())), + V::Ratio(r) => Ok(V::Ratio(Rational64::new(r.numer().abs(), r.denom().abs()))), + V::Complex(c) => Ok(V::Float(c.norm())), + arg => error!("cannot compute abs for {arg}") + } +} + +#[native_func(1)] +fn fract(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) => Ok(V::Int(0)), + V::Float(f) => Ok(V::Float(f.fract())), + V::Ratio(r) => Ok(V::Ratio(r.fract())), + arg => error!("cannot compute fract for {arg}") + } +} + +#[native_func(1)] +fn sign(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Int(i.signum())), + V::Ratio(r) => Ok(V::Int(r.numer().signum())), + V::Float(f) => Ok(V::Float(f.signum())), + arg => error!("cannot compute sign for {arg}") + } +} + +#[native_func(1)] +fn int(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Int(i)), + V::Ratio(r) => Ok(V::Int(r.numer() / r.denom())), + V::Float(f) => Ok(V::Int(f as i64)), + V::Complex(c) => Ok(V::Int(c.re as i64)), + arg => error!("cannot cast {arg} to int") + } +} + +#[native_func(1)] +fn ratio(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Ratio(Rational64::new(i, 1))), + V::Ratio(r) => Ok(V::Ratio(r)), + V::Float(f) => Ok(V::Ratio(Rational64::approximate_float(f).unwrap_or(Rational64::new(0, 1)))), + V::Complex(c) => Ok(V::Ratio(Rational64::approximate_float(c.re).unwrap_or(Rational64::new(0, 1)))), + arg => error!("cannot cast {arg} to ratio") + } +} + +#[native_func(1)] +fn float(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Float(i as f64)), + V::Ratio(r) => Ok(V::Float((*r.numer() as f64) / (*r.denom() as f64))), + V::Float(f) => Ok(V::Float(f)), + V::Complex(c) => Ok(V::Float(c.re)), + arg => error!("cannot cast {arg} to float") + } +} + +#[native_func(1)] +fn complex(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Complex(Complex64::new(i as f64, 0.0))), + V::Ratio(r) => Ok(V::Complex(Complex64::new((*r.numer() as f64) / (*r.denom() as f64), 0.0))), + V::Float(f) => Ok(V::Complex(Complex64::new(f, 0.0))), + V::Complex(c) => Ok(V::Complex(c)), + arg => error!("cannot cast {arg} to float") + } +} + +#[native_func(1)] +fn mat(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::List(l) => Ok(V::Matrix(Matrix::from_list(l.to_vec()).into())), + V::Matrix(m) => Ok(V::Matrix(m)), + arg => error!("cannot cast {arg} to mat") + } +} + +#[native_func(1)] +fn numer(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Int(i)), + V::Ratio(r) => Ok(V::Int(*r.numer())), + _ => error!("numer can only take a integer or ratio") + } +} + +#[native_func(1)] +fn denom(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) => Ok(V::Int(1)), + V::Ratio(r) => Ok(V::Int(*r.denom())), + _ => error!("denom can only take a integer or ratio") + } +} + +#[native_func(1)] +fn re(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) | V::Float(_) => Ok(value), + V::Complex(c) => Ok(V::Float(c.re)), + _ => error!("re can only take a valid number") + } +} + +#[native_func(1)] +fn im(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) | V::Float(_ )=> Ok(V::Int(0)), + V::Complex(c) => Ok(V::Float(c.im)), + _ => error!("re can only take a valid number") + } +} + +#[native_func(1)] +fn cis(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + match value.floaty() { + Value::Float(f) => Ok(Value::Complex(Complex64::cis(f))), + Value::Complex(c) => Ok((Value::Complex(Complex64::cis(c.re)) * Value::Float((-c.im).exp()))?), + _ => error!("cis can only take floats") + } +} + +#[native_func(1)] +fn is_finite(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) => Ok(V::Bool(true)), + V::Float(f) => Ok(V::Bool(f.is_finite())), + V::Complex(c) => Ok(V::Bool(c.is_finite())), + _ => error!("is_finite can only take a valid number") + } +} + +#[native_func(1)] +fn is_infinite(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)), + V::Float(f) => Ok(V::Bool(f.is_infinite())), + V::Complex(c) => Ok(V::Bool(c.is_infinite())), + _ => error!("is_infinite can only take a valid number") + } +} + +#[native_func(1)] +fn is_nan(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)), + V::Float(f) => Ok(V::Bool(f.is_nan())), + V::Complex(c) => Ok(V::Bool(c.is_nan())), + _ => error!("is_nan can only take a valid number") + } +} + +#[native_func(1)] +fn is_normal(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) => Ok(V::Bool(true)), + V::Float(f) => Ok(V::Bool(f.is_normal())), + V::Complex(c) => Ok(V::Bool(c.is_normal())), + _ => error!("is_normal can only take a valid number") + } +} + +#[native_func(1)] +fn is_subnormal(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)), + V::Float(f) => Ok(V::Bool(f.is_subnormal())), + _ => error!("is_subnormal can only take subnormal") + } +} + +#[native_func(1)] +fn is_sign_positive(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Bool(i > 0)), + V::Ratio(r) => Ok(V::Bool(*r.numer() > 0)), + V::Float(f) => Ok(V::Bool(f.is_sign_positive())), + _ => error!("is_sign_positive can only take a real number") + } +} + +#[native_func(1)] +fn is_sign_negative(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Bool(i < 0)), + V::Ratio(r) => Ok(V::Bool(*r.numer() < 0)), + V::Float(f) => Ok(V::Bool(f.is_sign_negative())), + _ => error!("is_sign_negative can only take a real number") + } +} + +#[native_func(1)] +fn is_zero(_: VmArgs, args: Vec<Value>) -> Result<Value> { + use Value as V; + let [value] = unpack_args!(args); + match value { + V::Int(i) => Ok(V::Bool(i == 0)), + V::Ratio(r) => Ok(V::Bool(*r.numer() == 0 && *r.denom() != 0)), + V::Float(f) => Ok(V::Bool(f == 0.0)), + V::Complex(c) => Ok(V::Bool(c.re == 0.0 && c.im == 0.0)), + _ => error!("is_zero can only take a valid number") + } +} + +#[native_func(2)] +fn mat_joinh(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [l, r] = unpack_args!(args); + let (l, r) = match (l, r) { + (Value::List(l), Value::List(r)) => (Matrix::from_list(l.to_vec()), Matrix::from_list(r.to_vec())), + (Value::List(l), Value::Matrix(r)) => (Matrix::from_list(l.to_vec()), r.into_inner()), + (Value::Matrix(l), Value::List(r)) => (l.into_inner(), Matrix::from_list(r.to_vec())), + (Value::Matrix(l), Value::Matrix(r)) => (l.into_inner(), r.into_inner()), + _ => return error!("mat_joinh takes two matrices") + }; + let mat = l.join_right(&r)?; + Ok(Value::Matrix(mat.into())) +} + +#[native_func(2)] +fn mat_joinv(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [l, r] = unpack_args!(args); + let (l, r) = match (l, r) { + (Value::List(l), Value::List(r)) => (Matrix::from_list(l.to_vec()), Matrix::from_list(r.to_vec())), + (Value::List(l), Value::Matrix(r)) => (Matrix::from_list(l.to_vec()), r.into_inner()), + (Value::Matrix(l), Value::List(r)) => (l.into_inner(), Matrix::from_list(r.to_vec())), + (Value::Matrix(l), Value::Matrix(r)) => (l.into_inner(), r.into_inner()), + _ => return error!("mat_joinv takes two matrices") + }; + let mat = l.join_bottom(&r)?; + Ok(Value::Matrix(mat.into())) +} + +mathr!(floor); +mathr!(ceil); +mathr!(round); +mathr!(trunc); +trig!(sqrt); +trig!(cbrt); +trig!(ln); +trig!(log2); +trig!(log10); +trig!(exp); +trig!(exp2); +trig!(sin); +trig!(cos); +trig!(tan); +trig!(sinh); +trig!(cosh); +trig!(tanh); +trig!(asin); +trig!(acos); +trig!(atan); +trig!(asinh); +trig!(acosh); +trig!(atanh); +trigf!(to_degrees, deg); +trigf!(to_radians, rad); + +pub fn load(vm: &mut Vm) { + vm.load_global_fn(trans(), "trans"); + vm.load_global_fn(mat_ref(), "ref"); + vm.load_global_fn(rref(), "rref"); + vm.load_global_fn(det(), "det"); + vm.load_global_fn(ident(), "ident"); + vm.load_global_fn(inv(), "inv"); + vm.load_global_fn(mat_joinh(), "mat_joinh"); + vm.load_global_fn(mat_joinv(), "mat_joinv"); + + vm.load_global(Value::Float(PI), "pi"); + vm.load_global(Value::Float(TAU), "tau"); + vm.load_global(Value::Float(E), "e"); + vm.load_global(Value::Float(NAN), "nan"); + vm.load_global(Value::Float(NAN), "NaN"); + vm.load_global(Value::Float(INFINITY), "inf"); + + vm.load_global_fn(int(), "int"); + vm.load_global_fn(ratio(), "ratio"); + vm.load_global_fn(float(), "float"); + vm.load_global_fn(complex(), "complex"); + vm.load_global_fn(mat(), "mat"); + vm.load_global_fn(abs(), "abs"); + vm.load_global_fn(sign(), "sign"); + vm.load_global_fn(floor(), "floor"); + vm.load_global_fn(ceil(), "ceil"); + vm.load_global_fn(round(), "round"); + vm.load_global_fn(trunc(), "trunc"); + vm.load_global_fn(fract(), "fract"); + vm.load_global_fn(sqrt(), "sqrt"); + vm.load_global_fn(cbrt(), "cbrt"); + vm.load_global_fn(ln(), "ln"); + vm.load_global_fn(log(), "log"); + vm.load_global_fn(log2(), "log2"); + vm.load_global_fn(log10(), "log10"); + vm.load_global_fn(exp(), "exp"); + vm.load_global_fn(exp2(), "exp2"); + vm.load_global_fn(sin(), "sin"); + vm.load_global_fn(cos(), "cos"); + vm.load_global_fn(tan(), "tan"); + vm.load_global_fn(sinh(), "sinh"); + vm.load_global_fn(cosh(), "cosh"); + vm.load_global_fn(tanh(), "tanh"); + vm.load_global_fn(asin(), "asin"); + vm.load_global_fn(acos(), "acos"); + vm.load_global_fn(atan(), "atan"); + vm.load_global_fn(asinh(), "asinh"); + vm.load_global_fn(acosh(), "acosh"); + vm.load_global_fn(atanh(), "atanh"); + vm.load_global_fn(deg(), "deg"); + vm.load_global_fn(rad(), "rad"); + vm.load_global_fn(cis(), "cis"); + + vm.load_global_fn(denom(), "denom"); + vm.load_global_fn(numer(), "numer"); + vm.load_global_fn(re(), "re"); + vm.load_global_fn(im(), "im"); + + vm.load_global_fn(is_finite(), "is_finite"); + vm.load_global_fn(is_infinite(), "is_infinite"); + vm.load_global_fn(is_nan(), "is_nan"); + vm.load_global_fn(is_zero(), "is_zero"); + vm.load_global_fn(is_normal(), "is_normal"); + vm.load_global_fn(is_subnormal(), "is_subnormal"); + vm.load_global_fn(is_sign_negative(), "is_sign_negative"); + vm.load_global_fn(is_sign_positive(), "is_sign_positive"); +} diff --git a/matrix-std/src/sys.rs b/matrix-std/src/sys.rs new file mode 100644 index 0000000..609e72d --- /dev/null +++ b/matrix-std/src/sys.rs @@ -0,0 +1,252 @@ +use std::{process::{exit, Command, Stdio, Child}, env, rc::Rc, io::{Read, self}, cell::RefCell, fs::{File, self}, os::fd::FromRawFd, sync::OnceLock, path::PathBuf}; + +use matrix_lang::prelude::*; +use matrix_macros::native_func; +use os_info::Info; +use crate::{VmArgs, error, unpack_args}; + +#[native_func(1)] +fn sys_exit(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let Value::Int(i) = value else { + return error!("exit requires a int exit code") + }; + exit(i as i32); +} + +#[native_func(0)] +fn argv(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::List( + Gc::new( + env::args() + .map(|a| Value::String(Rc::from(a.as_str()))) + .collect() + ))) +} + +#[native_func(1)] +fn env(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("env requires a string name") + }; + match std::env::var(value.as_ref()) { + Ok(v) => Ok(Value::String(v.into())), + Err(e) => error!("couldn't read env var: {e}") + } +} + +fn exec_impl(cmd: io::Result<Child>) -> Result<Value> { + let mut child = match cmd { + Ok(c) => c, + Err(e) => return error!("error executing command: {e}") + }; + let status = match child.wait() { + Ok(s) => s, + Err(e) => return error!("error executing command: {e}") + }; + + let stdout = match child.stdout { + Some(ref mut out) => { + let mut buf = String::new(); + let _ = out.read_to_string(&mut buf); + buf + }, + None => String::new() + }; + + let stderr = match child.stderr { + Some(ref mut err) => { + let mut buf = String::new(); + let _ = err.read_to_string(&mut buf); + buf + }, + None => String::new() + }; + + let mut res = ValueMap::new(); + res.insert(Value::from("success"), Value::Bool(status.success()))?; + res.insert(Value::from("code"), Value::Int(status.code().unwrap_or(0) as i64))?; + res.insert(Value::from("out"), Value::String(stdout.into()))?; + res.insert(Value::from("err"), Value::String(stderr.into()))?; + + Ok(Value::Table(res.into())) +} + +#[native_func(2)] +fn exec(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [cmd, args] = unpack_args!(args); + let (cmd, args) = match (cmd, args) { + (Value::String(s), Value::List(l)) => (s, l.into_inner()), + _ => return error!("exec requires a string cmd and string argument list") + }; + let mut sargs = Vec::new(); + for arg in args { + let Value::String(arg) = arg else { + return error!("exec requires a string cmd and string argument list") + }; + sargs.push(arg.to_string()); + }; + let cmd = Command::new(cmd.to_string()) + .args(sargs) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn(); + + exec_impl(cmd) +} + +#[native_func(1)] +fn system(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [cmd] = unpack_args!(args); + let Value::String(cmd) = cmd else { + return error!("system requires a full command argument string") + }; + let sh = String::from("/bin/sh"); + let args = vec!["-c".to_string(), cmd.to_string()]; + + let cmd = Command::new(sh) + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn(); + + exec_impl(cmd) +} + +#[native_func(1)] +fn systemi(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [cmd] = unpack_args!(args); + let Value::String(cmd) = cmd else { + return error!("systemi requires a full command argument string") + }; + let sh = String::from("/bin/sh"); + let args = vec!["-c".to_string(), cmd.to_string()]; + + let cmd = Command::new(sh) + .args(args) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn(); + + exec_impl(cmd) +} + +fn stdin() -> Value { + let f = unsafe { File::from_raw_fd(0) }; + Value::File(Rc::new(RefCell::new(f))) +} + +fn stdout() -> Value { + let f = unsafe { File::from_raw_fd(1) }; + Value::File(Rc::new(RefCell::new(f))) +} + +fn stderr() -> Value { + let f = unsafe { File::from_raw_fd(2) }; + Value::File(Rc::new(RefCell::new(f))) +} + +const OS_INFO: OnceLock<Info> = OnceLock::new(); + +#[native_func(0)] +fn os_type(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).os_type().to_string().into())) +} + +#[native_func(0)] +fn os_version(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).version().to_string().into())) +} + +#[native_func(0)] +fn os_edition(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).edition().unwrap_or("Unknown").into())) +} + +#[native_func(0)] +fn os_bitness(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).bitness().to_string().into())) +} + +#[native_func(0)] +fn os_arch(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).architecture().unwrap_or("Unknown").into())) +} + +#[native_func(0)] +fn cwd(_: VmArgs, _: Vec<Value>) -> Result<Value> { + match env::current_dir() { + Ok(v) => Ok(Value::String(v.into_os_string().into_string().unwrap_or(String::new()).into())), + Err(e) => error!("cant get cwd: {e}") + } +} + +#[native_func(1)] +fn basename(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("basename requires a string path") + }; + let path = PathBuf::from(value.to_string()); + match path.file_name() { + Some(p) => Ok(Value::String(p.to_str().unwrap().into())), + None => Ok(Value::String(value.into())) + } +} + +#[native_func(1)] +fn dirname(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("basename requires a string path") + }; + let path = PathBuf::from(value.to_string()); + let parent = match path.parent() { + Some(p) => p, + None => path.as_path() + }; + let str = parent.as_os_str().to_str().unwrap(); + match str { + "" => Ok(Value::String(".".into())), + s => Ok(Value::String(s.into())) + } +} + +#[native_func(1)] +fn realpath(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("basename requires a string path") + }; + let path = match fs::canonicalize(value.as_ref()) { + Ok(p) => p, + Err(e) => return error!("could not get realpath: {e}") + }; + Ok(Value::String(path.to_str().unwrap().into())) +} + + +pub fn load(vm: &mut Vm) { + vm.load_global_fn(sys_exit(), "exit"); + vm.load_global_fn(argv(), "argv"); + vm.load_global_fn(exec(), "exec"); + vm.load_global_fn(system(), "system"); + vm.load_global_fn(systemi(), "systemi"); + vm.load_global_fn(env(), "env"); + vm.load_global(stdin(), "stdin"); + vm.load_global(stdout(), "stdout"); + vm.load_global(stderr(), "stderr"); + vm.load_global_fn(os_type(), "os_type"); + vm.load_global_fn(os_version(), "os_version"); + vm.load_global_fn(os_edition(), "os_edition"); + vm.load_global_fn(os_bitness(), "os_bitness"); + vm.load_global_fn(os_arch(), "os_arch"); + vm.load_global_fn(cwd(), "cwd"); + vm.load_global_fn(basename(), "basename"); + vm.load_global_fn(dirname(), "dirname"); + vm.load_global_fn(realpath(), "realpath"); +} |