summaryrefslogtreecommitdiff
path: root/matrix-std/src/io.rs
diff options
context:
space:
mode:
Diffstat (limited to 'matrix-std/src/io.rs')
-rw-r--r--matrix-std/src/io.rs174
1 files changed, 174 insertions, 0 deletions
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");
+}