use crate::stack::{Stack, push};
use crate::value::Value;
use std::ffi::CStr;
use std::sync::OnceLock;
static ARGS: OnceLock<Vec<String>> = OnceLock::new();
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_args_init(argc: i32, argv: *const *const i8) {
let args: Vec<String> = (0..argc)
.map(|i| {
let ptr = unsafe { *argv.offset(i as isize) };
if ptr.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(ptr).to_str().unwrap_or("").to_owned() }
}
})
.collect();
let _ = ARGS.set(args);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_arg_count(stack: Stack) -> Stack {
let count = ARGS.get().map(|a| a.len()).unwrap_or(0) as i64;
unsafe { push(stack, Value::Int(count)) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_arg_at(stack: Stack) -> Stack {
use crate::stack::pop;
assert!(!stack.is_null(), "arg: stack is empty");
let (rest, value) = unsafe { pop(stack) };
match value {
Value::Int(idx) => {
if idx < 0 {
panic!("arg: index must be non-negative, got {}", idx);
}
let arg = ARGS
.get()
.and_then(|args| args.get(idx as usize))
.cloned()
.unwrap_or_default();
unsafe { push(rest, Value::String(arg.into())) }
}
_ => panic!("arg: expected Int index on stack, got {:?}", value),
}
}
pub use patch_seq_arg_at as arg_at;
pub use patch_seq_arg_count as arg_count;
pub use patch_seq_args_init as args_init;
#[cfg(test)]
mod tests {
use super::*;
use crate::stack::pop;
use crate::tagged_stack::StackValue;
#[test]
fn test_arg_count_no_init() {
unsafe {
let mut buffer: [StackValue; 16] = std::mem::zeroed();
let stack = buffer.as_mut_ptr();
let stack = patch_seq_arg_count(stack);
let (_, value) = pop(stack);
assert!(matches!(value, Value::Int(_)));
}
}
}