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) -> Result { 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) -> Result { 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) -> Result { 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) -> Result { 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) -> Result { 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) -> Result { 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) -> Result { 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) -> Result { 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> = 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) -> Result { 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) -> Result { 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"); }