use crate::stack::{Stack, pop, push};
use crate::value::Value;
use std::sync::Arc;
pub const MAX_CAPTURES: usize = 1024;
#[allow(improper_ctypes_definitions)]
#[unsafe(no_mangle)]
pub extern "C" fn patch_seq_create_env(size: i32) -> *mut [Value] {
if size < 0 {
panic!("create_env: size cannot be negative: {}", size);
}
let size_usize = size as usize;
if size_usize > MAX_CAPTURES {
panic!(
"create_env: size {} exceeds MAX_CAPTURES ({})",
size_usize, MAX_CAPTURES
);
}
let mut vec: Vec<Value> = Vec::with_capacity(size_usize);
for _ in 0..size {
vec.push(Value::Int(0));
}
Box::into_raw(vec.into_boxed_slice())
}
#[allow(improper_ctypes_definitions)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_env_set(env: *mut [Value], index: i32, value: Value) {
if env.is_null() {
panic!("env_set: null environment pointer");
}
if index < 0 {
panic!("env_set: index cannot be negative: {}", index);
}
let env_slice = unsafe { &mut *env };
let idx = index as usize;
if idx >= env_slice.len() {
panic!(
"env_set: index {} out of bounds for environment of size {}",
index,
env_slice.len()
);
}
env_slice[idx] = value;
}
#[allow(improper_ctypes_definitions)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_env_get(
env_data: *const Value,
env_len: usize,
index: i32,
) -> Value {
if env_data.is_null() {
panic!("env_get: null environment pointer");
}
if index < 0 {
panic!("env_get: index cannot be negative: {}", index);
}
let idx = index as usize;
if idx >= env_len {
panic!(
"env_get: index {} out of bounds for environment of size {}",
index, env_len
);
}
unsafe { (*env_data.add(idx)).clone() }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_env_get_int(
env_data: *const Value,
env_len: usize,
index: i32,
) -> i64 {
if env_data.is_null() {
panic!("env_get_int: null environment pointer");
}
if index < 0 {
panic!("env_get_int: index cannot be negative: {}", index);
}
let idx = index as usize;
if idx >= env_len {
panic!(
"env_get_int: index {} out of bounds for environment of size {}",
index, env_len
);
}
let value = unsafe { &*env_data.add(idx) };
match value {
Value::Int(n) => *n,
_ => panic!(
"env_get_int: expected Int at index {}, got {:?}",
index, value
),
}
}
#[allow(improper_ctypes_definitions)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_env_get_string(
env_data: *const Value,
env_len: usize,
index: i32,
) -> crate::seqstring::SeqString {
if env_data.is_null() {
panic!("env_get_string: null environment pointer");
}
if index < 0 {
panic!("env_get_string: index cannot be negative: {}", index);
}
let idx = index as usize;
if idx >= env_len {
panic!(
"env_get_string: index {} out of bounds for environment of size {}",
index, env_len
);
}
let value = unsafe { &*env_data.add(idx) };
match value {
Value::String(s) => s.clone(),
_ => panic!(
"env_get_string: expected String at index {}, got {:?}",
index, value
),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_env_push_string(
stack: Stack,
env_data: *const Value,
env_len: usize,
index: i32,
) -> Stack {
if env_data.is_null() {
panic!("env_push_string: null environment pointer");
}
if index < 0 {
panic!("env_push_string: index cannot be negative: {}", index);
}
let idx = index as usize;
if idx >= env_len {
panic!(
"env_push_string: index {} out of bounds for environment of size {}",
index, env_len
);
}
let value = unsafe { &*env_data.add(idx) };
match value {
Value::String(s) => unsafe { push(stack, Value::String(s.clone())) },
_ => panic!(
"env_push_string: expected String at index {}, got {:?}",
index, value
),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_env_push_value(
stack: Stack,
env_data: *const Value,
env_len: usize,
index: i32,
) -> Stack {
if env_data.is_null() {
panic!("env_push_value: null environment pointer");
}
if index < 0 {
panic!("env_push_value: index cannot be negative: {}", index);
}
let idx = index as usize;
if idx >= env_len {
panic!(
"env_push_value: index {} out of bounds for environment of size {}",
index, env_len
);
}
let value = unsafe { (*env_data.add(idx)).clone() };
debug_assert!(
!matches!(value, Value::Int(_) | Value::Bool(_) | Value::Float(_)),
"env_push_value called for primitive type {:?} — use the specialized getter",
value
);
unsafe { push(stack, value) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_env_get_bool(
env_data: *const Value,
env_len: usize,
index: i32,
) -> i64 {
if env_data.is_null() {
panic!("env_get_bool: null environment pointer");
}
if index < 0 {
panic!("env_get_bool: index cannot be negative: {}", index);
}
let idx = index as usize;
if idx >= env_len {
panic!(
"env_get_bool: index {} out of bounds for environment of size {}",
index, env_len
);
}
let value = unsafe { &*env_data.add(idx) };
match value {
Value::Bool(b) => {
if *b {
1
} else {
0
}
}
_ => panic!(
"env_get_bool: expected Bool at index {}, got {:?}",
index, value
),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_env_get_float(
env_data: *const Value,
env_len: usize,
index: i32,
) -> f64 {
if env_data.is_null() {
panic!("env_get_float: null environment pointer");
}
if index < 0 {
panic!("env_get_float: index cannot be negative: {}", index);
}
let idx = index as usize;
if idx >= env_len {
panic!(
"env_get_float: index {} out of bounds for environment of size {}",
index, env_len
);
}
let value = unsafe { &*env_data.add(idx) };
match value {
Value::Float(f) => *f,
_ => panic!(
"env_get_float: expected Float at index {}, got {:?}",
index, value
),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_env_get_quotation(
env_data: *const Value,
env_len: usize,
index: i32,
) -> i64 {
if env_data.is_null() {
panic!("env_get_quotation: null environment pointer");
}
if index < 0 {
panic!("env_get_quotation: index cannot be negative: {}", index);
}
let idx = index as usize;
if idx >= env_len {
panic!(
"env_get_quotation: index {} out of bounds for environment of size {}",
index, env_len
);
}
let value = unsafe { &*env_data.add(idx) };
match value {
Value::Quotation { impl_, .. } => *impl_ as i64,
_ => panic!(
"env_get_quotation: expected Quotation at index {}, got {:?}",
index, value
),
}
}
#[allow(improper_ctypes_definitions)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_make_closure(fn_ptr: u64, env: *mut [Value]) -> Value {
if fn_ptr == 0 {
panic!("make_closure: null function pointer");
}
if env.is_null() {
panic!("make_closure: null environment pointer");
}
let env_box = unsafe { Box::from_raw(env) };
let env_arc: Arc<[Value]> = Arc::from(env_box);
Value::Closure {
fn_ptr: fn_ptr as usize,
env: env_arc,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_push_closure(
mut stack: Stack,
fn_ptr: u64,
capture_count: i32,
) -> Stack {
if fn_ptr == 0 {
panic!("push_closure: null function pointer");
}
if capture_count < 0 {
panic!(
"push_closure: capture_count cannot be negative: {}",
capture_count
);
}
let count = capture_count as usize;
let mut captures: Vec<Value> = Vec::with_capacity(count);
for _ in 0..count {
let (new_stack, value) = unsafe { pop(stack) };
captures.push(value);
stack = new_stack;
}
let closure = Value::Closure {
fn_ptr: fn_ptr as usize,
env: Arc::from(captures.into_boxed_slice()),
};
unsafe { push(stack, closure) }
}
pub use patch_seq_create_env as create_env;
pub use patch_seq_env_get as env_get;
pub use patch_seq_env_get_bool as env_get_bool;
pub use patch_seq_env_get_float as env_get_float;
pub use patch_seq_env_get_int as env_get_int;
pub use patch_seq_env_get_quotation as env_get_quotation;
pub use patch_seq_env_get_string as env_get_string;
pub use patch_seq_env_push_string as env_push_string;
pub use patch_seq_env_push_value as env_push_value;
pub use patch_seq_env_set as env_set;
pub use patch_seq_make_closure as make_closure;
pub use patch_seq_push_closure as push_closure;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_env() {
let env = create_env(3);
assert!(!env.is_null());
unsafe {
let _ = Box::from_raw(env);
}
}
#[test]
fn test_env_set_and_get() {
let env = create_env(3);
unsafe {
env_set(env, 0, Value::Int(42));
env_set(env, 1, Value::Bool(true));
env_set(env, 2, Value::Int(99));
}
unsafe {
let env_slice = &*env;
let env_data = env_slice.as_ptr();
let env_len = env_slice.len();
assert_eq!(env_get(env_data, env_len, 0), Value::Int(42));
assert_eq!(env_get(env_data, env_len, 1), Value::Bool(true));
assert_eq!(env_get(env_data, env_len, 2), Value::Int(99));
}
unsafe {
let _ = Box::from_raw(env);
}
}
#[test]
fn test_make_closure() {
let env = create_env(2);
unsafe {
env_set(env, 0, Value::Int(5));
env_set(env, 1, Value::Int(10));
let closure = make_closure(0x1234, env);
match closure {
Value::Closure { fn_ptr, env } => {
assert_eq!(fn_ptr, 0x1234);
assert_eq!(env.len(), 2);
assert_eq!(env[0], Value::Int(5));
assert_eq!(env[1], Value::Int(10));
}
_ => panic!("Expected Closure value"),
}
}
}
#[test]
fn test_push_closure() {
use crate::stack::{pop, push};
use crate::value::Value;
let mut stack = crate::stack::alloc_test_stack();
stack = unsafe { push(stack, Value::Int(10)) };
stack = unsafe { push(stack, Value::Int(5)) };
let fn_ptr = 0x1234;
stack = unsafe { push_closure(stack, fn_ptr, 2) };
let (_stack, closure_value) = unsafe { pop(stack) };
match closure_value {
Value::Closure { fn_ptr: fp, env } => {
assert_eq!(fp, fn_ptr as usize);
assert_eq!(env.len(), 2);
assert_eq!(env[0], Value::Int(5)); assert_eq!(env[1], Value::Int(10)); }
_ => panic!("Expected Closure value, got {:?}", closure_value),
}
}
#[test]
fn test_push_closure_zero_captures() {
use crate::stack::pop;
use crate::value::Value;
let stack = crate::stack::alloc_test_stack();
let fn_ptr = 0x5678;
let stack = unsafe { push_closure(stack, fn_ptr, 0) };
let (_stack, closure_value) = unsafe { pop(stack) };
match closure_value {
Value::Closure { fn_ptr: fp, env } => {
assert_eq!(fp, fn_ptr as usize);
assert_eq!(env.len(), 0);
}
_ => panic!("Expected Closure value, got {:?}", closure_value),
}
}
#[test]
fn test_env_get_bool() {
let env = create_env(2);
unsafe {
env_set(env, 0, Value::Bool(true));
env_set(env, 1, Value::Bool(false));
let env_slice = &*env;
let env_data = env_slice.as_ptr();
let env_len = env_slice.len();
assert_eq!(env_get_bool(env_data, env_len, 0), 1);
assert_eq!(env_get_bool(env_data, env_len, 1), 0);
let _ = Box::from_raw(env);
}
}
#[test]
fn test_env_get_float() {
let env = create_env(2);
unsafe {
env_set(env, 0, Value::Float(1.234));
env_set(env, 1, Value::Float(-5.678));
let env_slice = &*env;
let env_data = env_slice.as_ptr();
let env_len = env_slice.len();
assert!((env_get_float(env_data, env_len, 0) - 1.234).abs() < 0.0001);
assert!((env_get_float(env_data, env_len, 1) - (-5.678)).abs() < 0.0001);
let _ = Box::from_raw(env);
}
}
#[test]
fn test_env_get_quotation() {
let env = create_env(1);
let wrapper: usize = 0xDEADBEEF;
let impl_: usize = 0xCAFEBABE;
unsafe {
env_set(env, 0, Value::Quotation { wrapper, impl_ });
let env_slice = &*env;
let env_data = env_slice.as_ptr();
let env_len = env_slice.len();
assert_eq!(env_get_quotation(env_data, env_len, 0), impl_ as i64);
let _ = Box::from_raw(env);
}
}
}