use std::{
collections::{HashMap, VecDeque},
env::{self, args, vars},
fs,
io::{stdin, stdout, Write},
ops::{Add, Div, Mul, Rem, Sub},
process::{self, Stdio},
sync::Arc,
thread,
time::{Duration, SystemTime},
};
use readformat::readf;
use crate::{dyn_fns, mutex::Mut, runtime::*, sasm::sasm_write, *};
#[macro_export]
macro_rules! type_err {
($stack:expr, $a:expr, $b:expr) => {
$stack.err(ErrorKind::InvalidType($a.to_owned(), $b.to_owned()))?
};
}
macro_rules! array {
($stack:expr, $i:expr) => {
|| {
$stack.error(ErrorKind::PropertyNotFound(
"array".to_owned(),
$i.to_string(),
))
}
};
}
pub fn print(stack: &mut Stack) -> OError {
let Value::Str(s) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("print".to_owned()));
};
print!("{s}");
stdout().lock().flush().unwrap();
Ok(())
}
pub fn clone(stack: &mut Stack) -> OError {
let o = stack.pop();
stack.push(Arc::new(Mut::new(o.lock_ro().clone())));
Ok(())
}
pub fn dup(stack: &mut Stack) -> OError {
let o = stack.peek();
stack.push(o);
Ok(())
}
pub fn dup2(stack: &mut Stack) -> OError {
let o = stack.peek();
stack.push(o.clone());
stack.push(o);
Ok(())
}
pub fn pop(stack: &mut Stack) -> OError {
stack.pop();
Ok(())
}
pub fn swap(stack: &mut Stack) -> OError {
let a = stack.pop();
let b = stack.pop();
stack.push(a);
stack.push(b);
Ok(())
}
pub fn mswap(stack: &mut Stack) -> OError {
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("nswap".to_owned()));
};
let mut array = VecDeque::with_capacity(i as usize);
for _ in 0..i {
array.push_back(stack.pop());
}
for _ in 0..i {
stack.push(array.pop_front().unwrap());
}
Ok(())
}
pub fn settype(stack: &mut Stack) -> OError {
let Value::Str(s) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("settype".to_owned()));
};
let o = stack.pop();
let kind = runtime(|rt| rt.get_type_by_name(&s))
.ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))?;
set_type_internal(&o, kind);
stack.push(o);
Ok(())
}
pub fn settypeid(stack: &mut Stack) -> OError {
let Value::Int(i) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("settype".to_owned()));
};
let o = stack.pop();
let kind = runtime(|rt| rt.get_type_by_id(i as u32))
.ok_or_else(|| stack.error(ErrorKind::TypeNotFound(format!(";{i}"))))?;
set_type_internal(&o, kind);
stack.push(o);
Ok(())
}
fn set_type_internal(o: &Arc<Mut<Object>>, kind: Arc<Mut<Type>>) {
let mut obj = o.lock();
kind.lock_ro().write_into(&mut obj);
obj.kind = kind;
}
pub fn gettype(stack: &mut Stack) -> OError {
let o = stack.pop();
stack.push(Value::Str(o.lock_ro().kind.lock_ro().get_name()).spl());
Ok(())
}
pub fn gettypeid(stack: &mut Stack) -> OError {
let o = stack.pop();
stack.push(Value::Int(o.lock_ro().kind.lock_ro().get_id() as i32).spl());
Ok(())
}
pub fn barray_new(stack: &mut Stack) -> OError {
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("banew".to_owned()));
};
if i < 0 {
return stack.err(ErrorKind::InvalidCall("banew".to_owned()));
}
stack.push(Value::ByteArray(vec![0u8; i as usize]).spl());
Ok(())
}
pub fn array_new(stack: &mut Stack) -> OError {
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("anew".to_owned()));
};
if i < 0 {
return stack.err(ErrorKind::InvalidCall("anew".to_owned()));
}
stack.push(Value::Array(vec![Value::Null.spl(); i as usize]).spl());
Ok(())
}
pub fn array_len(stack: &mut Stack) -> OError {
let binding = stack.pop();
let len = match binding.lock_ro().native {
Value::Array(ref a) => a.len(),
Value::ByteArray(ref a) => a.len(),
_ => return stack.err(ErrorKind::InvalidCall("array-len".to_owned())),
};
stack.push(Value::Mega(len as i128).spl());
Ok(())
}
pub fn array_get(stack: &mut Stack) -> OError {
let binding = stack.pop();
let Value::Mega(i) = stack.pop().lock_ro().native else {
return stack.err(ErrorKind::InvalidCall("array-get".to_owned()));
};
let o = match binding.lock_ro().native {
Value::Array(ref a) => a.get(i as usize).cloned(),
Value::ByteArray(ref a) => a.get(i as usize).map(|x| Value::Int(*x as i32).spl()),
_ => return stack.err(ErrorKind::InvalidCall("array-get".to_owned())),
};
stack.push(o.ok_or_else(array!(stack, i))?);
Ok(())
}
pub fn array_set(stack: &mut Stack) -> OError {
let binding = &stack.pop();
let binding = &mut binding.lock().native;
require_on_stack!(i, Mega, stack, "array-set");
let o = stack.pop();
if let Value::Array(ref mut a) = binding {
stack.push(a.get(i as usize).ok_or_else(array!(stack, i))?.clone());
*a.get_mut(i as usize).ok_or_else(array!(stack, i))? = o;
} else if let Value::ByteArray(ref mut a) = binding {
let Value::Int(o) = o.lock_ro().native else {
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
};
stack.push(Value::Int(*a.get(i as usize).ok_or_else(array!(stack, i))? as i32).spl());
*a.get_mut(i as usize).ok_or_else(array!(stack, i))? = o as u8;
} else {
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
};
Ok(())
}
pub fn eq(stack: &mut Stack) -> OError {
let b = stack.pop();
let a = stack.pop();
stack.push(Value::Int(if a == b { 1 } else { 0 }).spl());
Ok(())
}
pub fn lt(stack: &mut Stack) -> OError {
let b = stack.pop();
let a = stack.pop();
stack.push(Value::Int(if a < b { 1 } else { 0 }).spl());
Ok(())
}
pub fn gt(stack: &mut Stack) -> OError {
let b = stack.pop();
let a = stack.pop();
stack.push(Value::Int(if a > b { 1 } else { 0 }).spl());
Ok(())
}
pub fn not(stack: &mut Stack) -> OError {
let o = stack.pop();
stack.push(Value::Int(if o.lock_ro().is_truthy() { 0 } else { 1 }).spl());
Ok(())
}
pub fn and(stack: &mut Stack) -> OError {
let a = stack.pop();
let b = stack.pop();
stack.push(
Value::Int(if a.lock_ro().is_truthy() && b.lock_ro().is_truthy() {
1
} else {
0
})
.spl(),
);
Ok(())
}
pub fn or(stack: &mut Stack) -> OError {
let b = stack.pop();
let a = stack.pop();
stack.push(if a.lock_ro().is_truthy() { a } else { b });
Ok(())
}
macro_rules! impl_op {
($a:expr, $b:expr, $op:tt, $err:expr, $($kind:tt,)*) => {
match ($a, $b) {
$(
(Value::$kind(a), Value::$kind(b)) => Value::$kind(a.$op(b)),
)*
_ => $err?,
}
};
}
pub fn plus(stack: &mut Stack) -> OError {
let b = stack.pop().lock_ro().native.clone();
let a = stack.pop().lock_ro().native.clone();
stack.push(
impl_op!(
a,
b,
add,
stack.err(ErrorKind::InvalidCall("plus".to_owned())),
Mega,
Long,
Int,
Float,
Double,
)
.spl(),
);
Ok(())
}
pub fn minus(stack: &mut Stack) -> OError {
let b = stack.pop().lock_ro().native.clone();
let a = stack.pop().lock_ro().native.clone();
stack.push(
impl_op!(
a,
b,
sub,
stack.err(ErrorKind::InvalidCall("minus".to_owned())),
Mega,
Long,
Int,
Float,
Double,
)
.spl(),
);
Ok(())
}
pub fn slash(stack: &mut Stack) -> OError {
let b = stack.pop().lock_ro().native.clone();
let a = stack.pop().lock_ro().native.clone();
stack.push(
impl_op!(
a,
b,
div,
stack.err(ErrorKind::InvalidCall("slash".to_owned())),
Mega,
Long,
Int,
Float,
Double,
)
.spl(),
);
Ok(())
}
pub fn star(stack: &mut Stack) -> OError {
let b = stack.pop().lock_ro().native.clone();
let a = stack.pop().lock_ro().native.clone();
stack.push(
impl_op!(
a,
b,
mul,
stack.err(ErrorKind::InvalidCall("star".to_owned())),
Mega,
Long,
Int,
Float,
Double,
)
.spl(),
);
Ok(())
}
pub fn pow(stack: &mut Stack) -> OError {
let b = stack.pop().lock_ro().native.clone();
let a = stack.pop().lock_ro().native.clone();
stack.push(
match (a, b) {
(Value::Mega(a), Value::Mega(b)) => Value::Mega(a.pow(b as u32)),
(Value::Long(a), Value::Long(b)) => Value::Long(a.pow(b as u32)),
(Value::Int(a), Value::Int(b)) => Value::Int(a.pow(b as u32)),
(Value::Float(a), Value::Float(b)) => Value::Float(a.powf(b)),
(Value::Double(a), Value::Double(b)) => Value::Double(a.powf(b)),
_ => (stack.err(ErrorKind::InvalidCall("pow".to_owned())))?,
}
.spl(),
);
Ok(())
}
pub fn percent(stack: &mut Stack) -> OError {
let b = stack.pop().lock_ro().native.clone();
let a = stack.pop().lock_ro().native.clone();
stack.push(
impl_op!(
a,
b,
rem,
stack.err(ErrorKind::InvalidCall("percent".to_owned())),
Mega,
Long,
Int,
)
.spl(),
);
Ok(())
}
pub fn to_int(stack: &mut Stack) -> OError {
let o = stack.pop().lock_ro().native.clone();
stack.push(
Value::Int(match o {
Value::Null => type_err!(stack, "null", "int"),
Value::Int(x) => x,
Value::Long(x) => x as i32,
Value::Mega(x) => x as i32,
Value::Float(x) => x as i32,
Value::Double(x) => x as i32,
Value::Func(_) => type_err!(stack, "func", "int"),
Value::Array(_) => type_err!(stack, "array", "int"),
Value::Str(x) => x
.parse()
.map_err(|_| stack.error(ErrorKind::Parse(x, "int".to_owned())))?,
Value::ByteArray(x) => x.len() as i32,
})
.spl(),
);
Ok(())
}
pub fn to_long(stack: &mut Stack) -> OError {
let o = stack.pop().lock_ro().native.clone();
stack.push(
Value::Long(match o {
Value::Null => type_err!(stack, "null", "long"),
Value::Int(x) => x as i64,
Value::Long(x) => x,
Value::Mega(x) => x as i64,
Value::Float(x) => x as i64,
Value::Double(x) => x as i64,
Value::Func(_) => type_err!(stack, "func", "long"),
Value::Array(_) => type_err!(stack, "array", "long"),
Value::Str(x) => x
.parse()
.map_err(|_| stack.error(ErrorKind::Parse(x, "long".to_owned())))?,
Value::ByteArray(x) => x.len() as i64,
})
.spl(),
);
Ok(())
}
pub fn to_mega(stack: &mut Stack) -> OError {
let o = stack.pop().lock_ro().native.clone();
stack.push(
Value::Mega(match o {
Value::Null => type_err!(stack, "null", "mega"),
Value::Int(x) => x as i128,
Value::Long(x) => x as i128,
Value::Mega(x) => x,
Value::Float(x) => x as i128,
Value::Double(x) => x as i128,
Value::Func(_) => type_err!(stack, "func", "mega"),
Value::Array(_) => type_err!(stack, "array", "mega"),
Value::Str(x) => x
.parse()
.map_err(|_| stack.error(ErrorKind::Parse(x, "mega".to_owned())))?,
Value::ByteArray(x) => x.len() as i128,
})
.spl(),
);
Ok(())
}
pub fn to_float(stack: &mut Stack) -> OError {
let o = stack.pop().lock_ro().native.clone();
stack.push(
Value::Float(match o {
Value::Null => type_err!(stack, "null", "float"),
Value::Int(x) => x as f32,
Value::Long(x) => x as f32,
Value::Mega(x) => x as f32,
Value::Float(x) => x,
Value::Double(x) => x as f32,
Value::Func(_) => type_err!(stack, "func", "float"),
Value::Array(_) => type_err!(stack, "array", "float"),
Value::Str(x) => x
.parse()
.map_err(|_| stack.error(ErrorKind::Parse(x, "float".to_owned())))?,
Value::ByteArray(_) => type_err!(stack, "bytearray", "float"),
})
.spl(),
);
Ok(())
}
pub fn to_double(stack: &mut Stack) -> OError {
let o = stack.pop().lock_ro().native.clone();
stack.push(
Value::Double(match o {
Value::Null => type_err!(stack, "null", "double"),
Value::Int(x) => x as f64,
Value::Long(x) => x as f64,
Value::Mega(x) => x as f64,
Value::Float(x) => x as f64,
Value::Double(x) => x,
Value::Func(_) => type_err!(stack, "func", "double"),
Value::Array(_) => type_err!(stack, "array", "double"),
Value::Str(x) => x
.parse()
.map_err(|_| stack.error(ErrorKind::Parse(x, "double".to_owned())))?,
Value::ByteArray(_) => type_err!(stack, "bytearray", "double"),
})
.spl(),
);
Ok(())
}
pub fn to_array(stack: &mut Stack) -> OError {
let o = stack.pop().lock_ro().native.clone();
stack.push(
Value::Array(match o {
Value::Null => type_err!(stack, "null", "array"),
Value::Int(_) => type_err!(stack, "int", "array"),
Value::Long(_) => type_err!(stack, "long", "array"),
Value::Mega(_) => type_err!(stack, "mega", "array"),
Value::Float(_) => type_err!(stack, "float", "array"),
Value::Double(_) => type_err!(stack, "double", "array"),
Value::Func(_) => type_err!(stack, "func", "array"),
Value::Array(x) => x,
Value::Str(x) => x
.chars()
.map(|x| Value::Int(x as u32 as i32).spl())
.collect(),
Value::ByteArray(x) => x
.iter()
.cloned()
.map(|x| Value::Int(x as i32).spl())
.collect(),
})
.spl(),
);
Ok(())
}
pub fn to_str(stack: &mut Stack) -> OError {
let o = stack.pop().lock_ro().native.clone();
stack.push(
Value::Str(match o {
Value::Null => type_err!(stack, "null", "str"),
Value::Int(x) => x.to_string(),
Value::Long(x) => x.to_string(),
Value::Mega(x) => x.to_string(),
Value::Float(x) => x.to_string(),
Value::Double(x) => x.to_string(),
Value::Func(_) => type_err!(stack, "func", "str"),
Value::Array(x) => {
let iter: Vec<_> = x
.into_iter()
.map(|x| match &x.lock_ro().native {
Value::Int(x) => char::from_u32(*x as u32).ok_or_else(|| {
stack.error(ErrorKind::InvalidType(
format!("int-{x}"),
"__str-element".to_owned(),
))
}),
_ => stack.err(ErrorKind::InvalidType(
"?".to_owned(),
"__str-element".to_owned(),
)),
})
.collect();
let mut fixed = String::with_capacity(iter.len());
for item in iter {
fixed.push(item?);
}
fixed
}
Value::Str(x) => x,
Value::ByteArray(x) => String::from_utf8(x).map_err(|_| {
stack.error(ErrorKind::InvalidType(
"!utf8".to_owned(),
"utf8".to_owned(),
))
})?,
})
.spl(),
);
Ok(())
}
pub fn to_bytearray(stack: &mut Stack) -> OError {
let o = stack.pop().lock_ro().native.clone();
stack.push(
Value::ByteArray(match o {
Value::Null => type_err!(stack, "null", "array"),
Value::Int(_) => type_err!(stack, "int", "array"),
Value::Long(_) => type_err!(stack, "long", "array"),
Value::Mega(_) => type_err!(stack, "mega", "array"),
Value::Float(_) => type_err!(stack, "float", "array"),
Value::Double(_) => type_err!(stack, "double", "array"),
Value::Func(_) => type_err!(stack, "func", "array"),
Value::Array(x) => x
.iter()
.cloned()
.map(|x| {
Ok(match &x.lock_ro().native {
Value::Int(x) => *x as u8,
Value::Long(x) => *x as u8,
Value::Mega(x) => *x as u8,
_ => stack.err(ErrorKind::InvalidType(
x.lock_ro().kind.lock_ro().get_name(),
"byte".to_owned(),
))?,
})
})
.collect::<Result<Vec<_>, _>>()?,
Value::Str(x) => x.into_bytes(),
Value::ByteArray(x) => x,
})
.spl(),
);
Ok(())
}
pub fn call(stack: &mut Stack) -> OError {
let Value::Func(a) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("call".to_owned()));
};
stack.call(&a)
}
pub fn callp(stack: &mut Stack) -> OError {
let Value::Func(a) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("callp".to_owned()));
};
stack.call(&a)?;
for _ in 0..a.ret_count {
stack.pop();
}
Ok(())
}
pub fn trace(stack: &mut Stack) -> OError {
let trace = stack.trace();
stack.push(Value::Array(trace.into_iter().map(|x| Value::Str(x).spl()).collect()).spl());
Ok(())
}
pub fn mr_trace(stack: &mut Stack) -> OError {
let trace = stack.mr_trace();
stack.push(
Value::Array(
trace
.into_iter()
.map(|x| Value::Array(x.into_iter().map(|x| x.spl()).collect()).spl())
.collect(),
)
.spl(),
);
Ok(())
}
pub fn exit(stack: &mut Stack) -> OError {
let Value::Int(a) = stack.pop().lock_ro().native.clone().try_mega_to_int() else {
return stack.err(ErrorKind::InvalidCall("exit".to_owned()));
};
process::exit(a)
}
pub fn exec(stack: &mut Stack) -> OError {
let Value::Func(a) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("exec".to_owned()));
};
unsafe {
let f = stack.pop_frame(0);
let r = a.to_call.call(stack);
stack.push_frame(f);
r
}
}
pub fn exec2(stack: &mut Stack) -> OError {
let Value::Func(a) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("exec2".to_owned()));
};
unsafe {
let f = stack.pop_frame(0);
let f1 = stack.pop_frame(0);
let r = a.to_call.call(stack);
stack.push_frame(f1);
stack.push_frame(f);
r
}
}
pub fn exec3(stack: &mut Stack) -> OError {
let Value::Func(a) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("exec3".to_owned()));
};
unsafe {
let f = stack.pop_frame(0);
let f1 = stack.pop_frame(0);
let f2 = stack.pop_frame(0);
let r = a.to_call.call(stack);
stack.push_frame(f2);
stack.push_frame(f1);
stack.push_frame(f);
r
}
}
pub fn stop(stack: &mut Stack) -> OError {
let Value::Int(i) = stack.pop().lock_ro().native.clone().try_mega_to_int() else {
return stack.err(ErrorKind::InvalidCall("stop".to_owned()));
};
stack.return_accumultor += i as u32;
Ok(())
}
pub fn argv(stack: &mut Stack) -> OError {
stack.push(Value::Array(args().into_iter().map(|x| Value::Str(x).spl()).collect()).spl());
Ok(())
}
pub fn get_env(stack: &mut Stack) -> OError {
stack.push(
Value::Array(
vars()
.into_iter()
.map(|x| Value::Array(vec![Value::Str(x.0).spl(), Value::Str(x.1).spl()]).spl())
.collect(),
)
.spl(),
);
Ok(())
}
pub fn read_file(stack: &mut Stack) -> OError {
let Value::Str(s) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("read_file".to_owned()));
};
stack.push(
Value::Str(
fs::read_to_string(s).map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?,
)
.spl(),
);
Ok(())
}
pub fn alit_end(stack: &mut Stack) -> OError {
let s = stack.pop();
let popped = stack.pop_until(s);
stack.push(Value::Array(popped).spl());
Ok(())
}
pub fn import(stack: &mut Stack) -> OError {
let Value::Str(mut s) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("import".to_owned()));
};
if let Some(x) = s.strip_prefix('#') {
s = find_in_splpath(x).unwrap_or(x.to_owned());
} else if let Some(x) = s.strip_prefix('@') {
s = x.to_owned();
} else {
s = stack
.peek_frame(1)
.origin
.file
.rsplit_once('/')
.map(|x| x.0)
.unwrap_or(".")
.to_owned()
+ "/"
+ &s;
s = s.trim_start_matches("./").to_owned();
}
let mut fallback = s
.as_str()
.rsplit_once(|x| x == '#')
.map(|(.., x)| x.to_owned())
.unwrap_or(s.clone());
while fallback.contains("/../") {
let mut fb = readf("{}/../{}", &fallback).unwrap();
if let Some(x) = fb[0].rsplit_once('/') {
fallback = x.0.to_owned() + &fb[1];
} else {
fallback = fb.swap_remove(1);
}
}
let fallback = runtime(|x| {
for (&p, &data) in &x.embedded_files {
if fallback == p {
return Some(data.to_owned());
}
}
None
});
if stack.include_file(
&(*fs::canonicalize(s.clone())
.unwrap_or_else(|_| s.clone().into())
.as_os_str()
.to_string_lossy())
.to_owned(),
) {
stack.push(Value::Str(s.clone()).spl());
dup(stack)?;
read_file(stack).or_else(|x| {
if let Some(fallback) = fallback {
stack.push(Value::Str(fallback.to_owned()).spl());
Ok(())
} else {
Err(x)
}
})?;
dyn_fns::wrap(if s.ends_with(".sasm") {
dyn_fns::dyn_sasmf
} else {
dyn_fns::dyn_readf
})(stack)?;
call(stack)?;
}
Ok(())
}
pub fn readln(stack: &mut Stack) -> OError {
let mut s = String::new();
if stdin()
.read_line(&mut s)
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?
== 0
{
stack.push(Value::Null.spl());
return Ok(());
}
let s = if let Some(s) = s.strip_suffix("\r\n") {
s.to_owned()
} else {
s
};
let s = if let Some(s) = s.strip_suffix('\n') {
s.to_owned()
} else {
s
};
stack.push(Value::Str(s).spl());
Ok(())
}
pub fn command(stack: &mut Stack) -> OError {
let binding = stack.pop();
let Value::Array(ref a) = binding.lock_ro().native else {
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
};
let mut args = Vec::new();
for item in a.iter() {
if let Value::Str(ref s) = item.lock_ro().native {
args.push(s.to_owned());
}
}
if args.is_empty() {
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
}
let child = process::Command::new(&args[0])
.args(&args[1..])
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?;
stack.push(Value::Long(child.id() as i64).spl());
runtime(|rt| rt.child(child));
Ok(())
}
pub fn command_wait(stack: &mut Stack) -> OError {
command_wait_impl(stack, Stdio::inherit)
}
pub fn command_wait_silent(stack: &mut Stack) -> OError {
command_wait_impl(stack, Stdio::null)
}
pub fn command_wait_impl(stack: &mut Stack, stdio: fn() -> Stdio) -> OError {
let binding = stack.pop();
let Value::Array(ref a) = binding.lock_ro().native else {
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
};
let mut args = Vec::new();
for item in a.iter() {
if let Value::Str(ref s) = item.lock_ro().native {
args.push(s.to_owned());
} else {
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
}
}
if args.is_empty() {
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
}
stack.push(
Value::Int(
process::Command::new(&args[0])
.args(&args[1..])
.stdin(stdio())
.stdout(stdio())
.stderr(stdio())
.spawn()
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?
.wait()
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?
.code()
.unwrap_or(-1),
)
.spl(),
);
Ok(())
}
pub fn str_to_bytes(stack: &mut Stack) -> OError {
require_on_stack!(s, Str, stack, "str-to-bytes");
stack.push(Value::ByteArray(s.bytes().collect()).spl());
Ok(())
}
pub fn bytes_to_str(stack: &mut Stack) -> OError {
if stack.peek().lock_ro().kind.lock_ro().get_name() == "bytearray" {
require_on_stack!(a, ByteArray, stack, "bytes-to-str");
stack.push(Value::Str(String::from_utf8_lossy(&a[..]).into_owned()).spl());
return Ok(());
}
require_byte_array_on_stack!(a, stack, "bytes-to-str");
stack.push(Value::Str(String::from_utf8_lossy(&a).into_owned()).spl());
Ok(())
}
pub fn acopy(stack: &mut Stack) -> OError {
require_on_stack!(len, Mega, stack, "acopy");
require_on_stack!(idx_dest, Mega, stack, "acopy");
require_on_stack!(idx_src, Mega, stack, "acopy");
let dest_array = stack.pop();
let kind = dest_array.lock_ro().kind.lock_ro().get_name();
if kind == "array" {
require_mut_array!(dest, dest_array, stack, "acopy");
require_array_on_stack!(src, stack, "acopy");
if (src.len() as i128) < idx_src + len
|| idx_src < 0
|| (dest.len() as i128) < idx_dest + len
|| idx_dest < 0
{
stack.err(ErrorKind::InvalidCall("acopy".to_owned()))?;
}
let len = len as usize;
let idx_src = idx_src as usize;
let idx_dest = idx_dest as usize;
(&mut dest[idx_dest..idx_dest + len]).clone_from_slice(&src[idx_src..idx_src + len]);
}
if kind == "bytearray" {
require_mut!(dest, ByteArray, dest_array, stack, "acopy");
require_byte_array_on_stack!(src, stack, "acopy");
if (src.len() as i128) < idx_src + len
|| idx_src < 0
|| (dest.len() as i128) < idx_dest + len
|| idx_dest < 0
{
stack.err(ErrorKind::InvalidCall("acopy".to_owned()))?;
}
let len = len as usize;
let idx_src = idx_src as usize;
let idx_dest = idx_dest as usize;
(&mut dest[idx_dest..idx_dest + len]).copy_from_slice(&src[idx_src..idx_src + len]);
}
stack.push(dest_array);
Ok(())
}
pub fn throw(stack: &mut Stack) -> OError {
let obj = stack.pop();
if let Value::Str(ref s) = obj.lock_ro().native {
if obj.lock_ro().kind.lock_ro().get_id()
== get_type("str")
.expect("str type must exist")
.lock_ro()
.get_id()
{
stack.err(ErrorKind::Custom(s.to_owned()))?;
}
}
stack.err(ErrorKind::CustomObject(obj))
}
pub fn write_sasm(stack: &mut Stack) -> OError {
require_on_stack!(code, Str, stack, "write-sasm");
stack.push(
Value::Str(
lexer::lex(false, code)
.map(|x| sasm_write(x))
.map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?,
)
.spl(),
);
Ok(())
}
pub fn write_file_sasm(stack: &mut Stack) -> OError {
require_on_stack!(file, Str, stack, "write-file-sasm");
stack.push(
Value::Str(
lexer::lex(
file.ends_with(".isbpl"),
fs::read_to_string(file).map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?,
)
.map(|x| sasm_write(x))
.map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?,
)
.spl(),
);
Ok(())
}
pub fn fork(stack: &mut Stack) -> OError {
require_on_stack!(callable, Func, stack, "fork");
let mut new_stack = stack.clone();
let rt = fork_runtime();
thread::spawn(move || {
rt.set();
if let Some(err) = new_stack.call(&callable).err() {
println!("{err:?}");
};
});
Ok(())
}
pub fn time(stack: &mut Stack) -> OError {
require_on_stack!(sleep_ms, Mega, stack, "time");
if sleep_ms != 0 {
thread::sleep(Duration::from_millis(sleep_ms as u64));
}
stack.push(
(SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis() as i128)
.spl(),
);
Ok(())
}
pub fn str_readf(stack: &mut Stack) -> OError {
require_on_stack!(string, Str, stack, "str-readf");
require_on_stack!(pat, Str, stack, "str-readf");
let Some(result) = readf(&pat, &string) else {
stack.push(Value::Null.spl());
return Ok(());
};
stack.push(Value::Array(result.into_iter().map(<String as SPL>::spl).collect()).spl());
Ok(())
}
pub fn str_to_mega_radix(stack: &mut Stack) -> OError {
require_int_on_stack!(radix, stack, "str-to-mega-radix");
require_on_stack!(str, Str, stack, "str-to-mega-radix");
let Ok(result) = i128::from_str_radix(&str, radix as u32) else {
stack.push(Value::Null.spl());
return Ok(());
};
stack.push(Value::Mega(result).spl());
return Ok(());
}
pub fn mega_to_str_radix(stack: &mut Stack) -> OError {
require_int_on_stack!(radix, stack, "mega-to-str-radix");
require_on_stack!(mega, Mega, stack, "mega-to-str-radix");
let mut result = Vec::with_capacity((mega as f64).powf(1.0 / radix as f64) as usize + 2);
let neg = mega < 0;
let mut mega = mega;
if neg {
mega = -mega;
result.push('-' as u32);
}
while mega != 0 {
let i = (mega % radix as i128) as u32;
result.push(if i < 10 { '0' as u32 } else { 'a' as u32 - 10 } + i);
mega = mega / radix as i128;
}
result.reverse();
stack.push(
Value::Str(String::from_iter(
result
.into_iter()
.map(|x| char::from_u32(x).expect("invalid radix")),
))
.spl(),
);
return Ok(());
}
pub fn properties(stack: &mut Stack) -> OError {
let o = stack.pop();
let o = o.lock_ro();
let additional: Vec<AMObject> = vec![
Value::Array(vec![
":".to_owned().spl(),
o.kind.lock_ro().get_name().spl(),
])
.spl(),
Value::Array(vec![";".to_owned().spl(), o.native.clone().spl()]).spl(),
];
stack.push(
Value::Array(
o.property_map
.iter()
.map(|(k, v)| Value::Array(vec![k.clone().spl(), v.clone()]).spl())
.chain(additional.into_iter())
.collect(),
)
.spl(),
);
Ok(())
}
pub fn from_properties(stack: &mut Stack) -> OError {
require_array_on_stack!(props, stack, "from-properties");
let mut map = HashMap::with_capacity(props.len());
for prop in props {
require_array!(prop, prop, stack, "from-properties");
if prop.len() != 2 {
stack.err(ErrorKind::InvalidCall("from-properties".to_string()))?;
}
let Value::Str(ref s) = prop[0].lock_ro().native else {
return Err(stack.error(ErrorKind::InvalidCall("from-properties".to_string())));
};
map.insert(s.to_owned(), prop[1].clone());
}
let Value::Str(kind) = map
.get(":")
.ok_or(stack.error(ErrorKind::InvalidCall("from-properties".to_string())))?
.lock_ro()
.native
.clone()
else {
return Err(stack.error(ErrorKind::InvalidCall("from-properties".to_string())));
};
let kind = runtime(|rt| rt.get_type_by_name(&kind))
.ok_or(stack.error(ErrorKind::TypeNotFound(kind.to_owned())))?;
let native = map
.get(";")
.ok_or(stack.error(ErrorKind::InvalidCall("from-properties".to_owned())))?
.lock_ro()
.native
.clone();
map.remove(";");
map.remove(":");
stack.push(Arc::new(Mut::new(Object {
kind,
native,
property_map: map,
})));
Ok(())
}
pub fn list_files(stack: &mut Stack) -> OError {
require_on_stack!(dir, Str, stack, "list-files");
stack.push(
match fs::read_dir(&dir)
.map_err(|_| stack.error(ErrorKind::IO(format!("Not a directory: {}", &dir))))
{
Ok(it) => Value::Array(
it.filter(|x| x.is_ok())
.map(|x| {
if let Ok(x) = x {
Value::Str(x.file_name().to_string_lossy().into_owned()).spl()
} else {
unreachable!()
}
})
.collect(),
)
.spl(),
Err(_) => Value::Null.spl(),
},
);
Ok(())
}
pub fn delete_file(stack: &mut Stack) -> OError {
require_on_stack!(file, Str, stack, "delete-file");
stack.push(Value::Int(if fs::remove_file(file).is_ok() { 1 } else { 0 }).spl());
Ok(())
}
pub fn delete_dir(stack: &mut Stack) -> OError {
require_on_stack!(dir, Str, stack, "delete-dir");
stack.push(
Value::Int(if fs::remove_dir_all(dir).is_ok() {
1
} else {
0
})
.spl(),
);
Ok(())
}
pub fn chdir(stack: &mut Stack) -> OError {
require_on_stack!(dir, Str, stack, "chdir");
env::set_current_dir(dir).map_err(|e| stack.error(ErrorKind::IO(e.to_string())))?;
Ok(())
}
pub fn register(r: &mut Stack, o: Arc<Frame>) {
type Fn = fn(&mut Stack) -> OError;
let fns: [(&str, Fn, u32); 71] = [
("pop", pop, 0),
("dup", dup, 2),
("dup2", dup2, 3),
("clone", clone, 1),
("swap", swap, 2),
("mswap", mswap, 2),
("print", print, 0),
("gettype", gettype, 1),
("gettypeid", gettypeid, 1),
("settype", settype, 1),
("settypeid", settypeid, 1),
("banew", barray_new, 1),
("anew", array_new, 1),
("array-len", array_len, 1),
("array-get", array_get, 1),
("array-set", array_set, 1),
("eq", eq, 1),
("lt", lt, 1),
("gt", gt, 1),
("not", not, 1),
("and", and, 1),
("or", or, 1),
("+", plus, 1),
("-", minus, 1),
("/", slash, 1),
("*", star, 1),
("%", percent, 1),
("pow", pow, 1),
("_int", to_int, 1),
("_long", to_long, 1),
("_mega", to_mega, 1),
("_float", to_float, 1),
("_double", to_double, 1),
("_array", to_array, 1),
("_str", to_str, 1),
("_barray", to_bytearray, 1),
("call", call, 0),
("callp", callp, 0),
("trace", trace, 1),
("mr-trace", mr_trace, 1),
("exit", exit, 0),
("exec", exec, 0),
("exec2", exec2, 0),
("exec3", exec3, 0),
("stop", stop, 0),
("argv", argv, 1),
("get-env", get_env, 1),
("read-file", read_file, 1),
("alit-end", alit_end, 1),
("import", import, 0),
("readln", readln, 1),
("command", command, 1),
("command-wait", command_wait, 1),
("command-wait-silent", command_wait_silent, 1),
("str-to-bytes", str_to_bytes, 1),
("bytes-to-str", bytes_to_str, 1),
("acopy", acopy, 1),
("throw", throw, 0),
("write-sasm", write_sasm, 1),
("write-file-sasm", write_file_sasm, 1),
("fork", fork, 0),
("sleeptime", time, 0),
("str-readf", str_readf, 1),
("str-to-mega-radix", str_to_mega_radix, 1),
("mega-to-str-radix", mega_to_str_radix, 1),
("properties", properties, 1),
("from-properties", from_properties, 1),
("list-files", list_files, 1),
("delete-file", delete_file, 1),
("delete-dir", delete_dir, 1),
("chdir", chdir, 0),
];
for f in fns {
r.define_func(
f.0.to_owned(),
AFunc::new(Func {
ret_count: f.2,
to_call: FuncImpl::Native(f.1),
run_as_base: false,
origin: o.clone(),
fname: None,
name: f.0.to_owned(),
}),
);
}
}