summaryrefslogtreecommitdiff
path: root/matrix-std
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-02-29 17:04:28 -0500
committerFreya Murphy <freya@freyacat.org>2024-02-29 17:04:28 -0500
commit5d2747e26f51cc2344a6bd95f93457248fdfebd8 (patch)
tree8755b4068166c3854d26817683ce438a771ab319 /matrix-std
parentmore mat, sys, and os stdlib functions, better matrix printing, other fixes (diff)
downloadmatrix-5d2747e26f51cc2344a6bd95f93457248fdfebd8.tar.gz
matrix-5d2747e26f51cc2344a6bd95f93457248fdfebd8.tar.bz2
matrix-5d2747e26f51cc2344a6bd95f93457248fdfebd8.zip
fin prob
Diffstat (limited to 'matrix-std')
-rw-r--r--matrix-std/Cargo.toml13
-rw-r--r--matrix-std/src/core.rs295
-rw-r--r--matrix-std/src/io.rs174
-rw-r--r--matrix-std/src/iter.rs546
-rw-r--r--matrix-std/src/lib.rs50
-rw-r--r--matrix-std/src/math.rs688
-rw-r--r--matrix-std/src/sys.rs252
7 files changed, 2018 insertions, 0 deletions
diff --git a/matrix-std/Cargo.toml b/matrix-std/Cargo.toml
new file mode 100644
index 0000000..476272c
--- /dev/null
+++ b/matrix-std/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "matrix-std"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1"
+matrix-lang = { path = "../matrix-lang" }
+matrix-macros = { path = "../matrix-macros" }
+os_info = "3"
+rand = "0.8"
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");
+}