126 lines
4.0 KiB
Rust
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());
|
|
}
|