talc/talc-std/src/io.rs

126 lines
4.0 KiB
Rust

use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, time::{SystemTime, UNIX_EPOCH}};
use talc_lang::{exception::{throw, Result}, lstring::LString, symbol::SYM_TYPE_ERROR, value::Value, vmcall, Vm};
use talc_macros::native_func;
use crate::{unpack_args, SYM_IO_ERROR};
#[native_func(1)]
pub fn print(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match &args[1] {
Value::String(s) => std::io::stdout().write_all(s.as_bytes()),
v => write!(std::io::stdout(), "{v}"),
};
if let Err(e) = res {
throw!(*SYM_IO_ERROR, "{e}")
}
Ok(Value::Nil)
}
#[native_func(1)]
pub fn println(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match &args[1] {
Value::String(s) => std::io::stdout().write_all(s.as_bytes()),
v => write!(std::io::stdout(), "{v}"),
};
if let Err(e) = res {
throw!(*SYM_IO_ERROR, "{e}")
}
if let Err(e) = std::io::stdout().write_all(b"\n") {
throw!(*SYM_IO_ERROR, "{e}")
}
Ok(Value::Nil)
}
#[native_func(0)]
pub fn readln(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let mut buf = Vec::new();
if let Err(e) = std::io::stdin().lock().read_until(b'\n', &mut buf) {
throw!(*SYM_IO_ERROR, "{e}")
}
Ok(LString::from(buf).into())
}
#[native_func(0)]
pub fn time(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
Ok(time.as_secs_f64().into())
}
#[native_func(0)]
pub fn time_ns(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
Ok(vec![(time.as_secs() as i64).into(), (time.subsec_nanos() as i64).into()].into())
}
#[native_func(1)]
pub fn time_fn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
let t0 = SystemTime::now();
vmcall!(vm; func)?;
let tf = SystemTime::now();
let time = tf.duration_since(t0).expect("time went backwards");
Ok(time.as_secs_f64().into())
}
#[native_func(1)]
pub fn env(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, key] = unpack_args!(args);
let Value::String(key) = key else {
throw!(*SYM_TYPE_ERROR, "env expected string, got {key:#}")
};
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
}
let val = std::env::var_os(key.to_os_str());
match val {
Some(val) => Ok(LString::from(val.as_bytes()).into()),
None => Ok(Value::Nil),
}
}
#[native_func(2)]
pub fn setenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, key, val] = unpack_args!(args);
let Value::String(key) = key else {
throw!(*SYM_TYPE_ERROR, "setenv expected string, got {key:#}")
};
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
}
let val = val.str();
if val.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable value must not contain a null byte")
}
std::env::set_var(key.to_os_str(), val.to_os_str());
Ok(Value::Nil)
}
#[native_func(1)]
pub fn delenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, key] = unpack_args!(args);
let Value::String(key) = key else {
throw!(*SYM_TYPE_ERROR, "delenv expected string, got {key:#}")
};
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
}
std::env::remove_var(key.to_os_str());
Ok(Value::Nil)
}
pub fn load(vm: &mut Vm) {
vm.set_global_name("print", print().into());
vm.set_global_name("println", println().into());
vm.set_global_name("readln", readln().into());
vm.set_global_name("time", time().into());
vm.set_global_name("time_ns", time_ns().into());
vm.set_global_name("time_fn", time_fn().into());
vm.set_global_name("env", env().into());
vm.set_global_name("setenv", setenv().into());
vm.set_global_name("delenv", delenv().into());
}