diff options
Diffstat (limited to '')
-rw-r--r-- | matrix-stdlib/src/core.rs | 181 | ||||
-rw-r--r-- | matrix-stdlib/src/io.rs | 181 | ||||
-rw-r--r-- | matrix-stdlib/src/iter.rs | 544 | ||||
-rw-r--r-- | matrix-stdlib/src/lib.rs | 20 | ||||
-rw-r--r-- | matrix-stdlib/src/math.rs | 518 | ||||
-rw-r--r-- | matrix-stdlib/src/sys.rs | 99 |
6 files changed, 1521 insertions, 22 deletions
diff --git a/matrix-stdlib/src/core.rs b/matrix-stdlib/src/core.rs new file mode 100644 index 0000000..183c142 --- /dev/null +++ b/matrix-stdlib/src/core.rs @@ -0,0 +1,181 @@ +use std::hash::{DefaultHasher, Hash, Hasher}; + +use matrix::{vm::Vm, value::Value, unpack_args, Result, unpack_varargs}; +use matrix_macros::native_func; +use crate::{VmArgs, error}; + + +fn to_radix(r: i64, mut n: i64) -> String { + let mut result = String::new(); + let mut idx = 0; + if n == 0 { + result.push('0'); + return result + } else if n < 0 { + n = -n; + idx = 1; + result.push('-'); + } + while n != 0 { + let c = std::char::from_digit((n % r) as u32, r as u32).unwrap(); + result.insert(idx, c); + n /= r; + } + result +} + +#[native_func(1)] +fn bin(_: VmArgs, args: Vec<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); + if let Err(e) = value.can_hash() { + return Err(e) + }; + let mut hasher = DefaultHasher::new(); + value.hash(&mut hasher); + let fin = hasher.finish(); + Ok(Value::Int(fin as u32 as i64)) +} + +#[native_func(1)] +fn ord(_: VmArgs, args: Vec<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())) +} + +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"); +} diff --git a/matrix-stdlib/src/io.rs b/matrix-stdlib/src/io.rs index 288e99e..d72248c 100644 --- a/matrix-stdlib/src/io.rs +++ b/matrix-stdlib/src/io.rs @@ -1,36 +1,175 @@ -use matrix::{value::Value, self, vm::Vm, Result}; +use std::{io::{self, Read, Write}, cell::RefCell, fs::OpenOptions, rc::Rc}; + +use matrix::{value::Value, self, vm::Vm, Result, unpack_varargs, iter, unpack_args}; use matrix_macros::native_func; +use crate::{error, VmArgs}; -#[native_func(1, true)] -fn print(_vm: &mut Vm, args: Vec<Value>) -> Result<Value> { - let [values] = args.try_into().unwrap(); - if let Value::List(list) = values { - for (i, value) in list.iter().enumerate() { - print!("{}", value.boring_print()); - if i != 0 { - print!(" "); - } +#[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(1, true)] -fn println(_vm: &mut Vm, args: Vec<Value>) -> Result<Value> { - let [values] = args.try_into().unwrap(); - if let Value::List(list) = values { - for (i, value) in list.iter().enumerate() { - print!("{}", value.boring_print()); - if i != 0 { - print!(" "); - } +#[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_native_fn(print()); - vm.load_native_fn(println()); + 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-stdlib/src/iter.rs b/matrix-stdlib/src/iter.rs new file mode 100644 index 0000000..74056ce --- /dev/null +++ b/matrix-stdlib/src/iter.rs @@ -0,0 +1,544 @@ +use std::{cell::RefCell, rc::Rc}; +use matrix::{iter, vm::Vm, value::Value, Result, unpack_varargs, unpack_args}; +use matrix_macros::native_func; +use crate::{error, VmArgs}; + +use Value as V; + +macro_rules! next { + ($vm:expr, $frame:expr, $iter:expr) => { + $vm.run_fn($frame, $iter.clone(), vec![]) + }; +} + +#[native_func(1)] +fn len(_: VmArgs, args: Vec<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 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_inclusive("\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(1)] +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(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value, iter] = unpack_args!(args); + let iter = iter.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, iter)?; + if val == Value::Nil { + *flag.borrow_mut() = None; + } else { + *flag.borrow_mut() = Some(1); + } + Ok(val) + }, + Some(1) => { + let val = value.clone(); + *flag.borrow_mut() = Some(0); + 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; + } + 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(2)] +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(2)] +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-stdlib/src/lib.rs b/matrix-stdlib/src/lib.rs index 6e0cfc1..334de90 100644 --- a/matrix-stdlib/src/lib.rs +++ b/matrix-stdlib/src/lib.rs @@ -1,7 +1,25 @@ -use matrix::vm::Vm; +use matrix::vm::{Vm, StackFrame}; +mod core; +mod sys; +mod math; mod io; +mod iter; + +pub(crate) type VmArgs<'a, 'b> = (&'a mut Vm, &'b mut StackFrame); + +macro_rules! error { + ($($arg:tt)*) => { + Err(format!($($arg)*).into()) + }; +} + +pub(crate) use error; 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-stdlib/src/math.rs b/matrix-stdlib/src/math.rs new file mode 100644 index 0000000..3226af5 --- /dev/null +++ b/matrix-stdlib/src/math.rs @@ -0,0 +1,518 @@ +use core::f64; +use std::f64::{consts::{PI, E, TAU}, NAN, INFINITY}; + +use matrix::{vm::Vm, value::{Value, Matrix}, Result, unpack_args, Rational64, Complex64}; +use matrix_macros::native_func; +use crate::{error, VmArgs}; + +#[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) -> 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 + } + for row in 0..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!("trans must be given a matrix") + }; + Ok(Value::Matrix(mat_rref(mat.into_inner())?.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)?; + 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.promote_trig() { + 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.promote_trig() { + 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.promote_trig(), value.promote_trig()) { + (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 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") + } +} + +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(rref(), "rref"); + vm.load_global_fn(det(), "det"); + vm.load_global_fn(ident(), "ident"); + vm.load_global_fn(inv(), "inv"); + + 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(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(denom(), "denom"); + vm.load_global_fn(numer(), "numer"); + vm.load_global_fn(re(), "re"); + vm.load_global_fn(im(), "im"); +} diff --git a/matrix-stdlib/src/sys.rs b/matrix-stdlib/src/sys.rs new file mode 100644 index 0000000..e91e635 --- /dev/null +++ b/matrix-stdlib/src/sys.rs @@ -0,0 +1,99 @@ +use std::{process::{exit, Command, Stdio}, env, rc::Rc, io::Read}; + +use matrix::{vm::Vm, value::{Value, ValueMap}, unpack_args, Result, gc::Gc}; +use matrix_macros::native_func; +use crate::{VmArgs, error}; + +#[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}") + } +} + +#[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(); + 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())) +} + +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(env(), "env"); +} |