use crate::stack::{Stack, drop_stack_value, get_stack_base, pop, pop_sv, push, stack_value_size};
use crate::value::{Value, VariantData};
use std::sync::Arc;
#[inline]
pub(super) fn stack_depth(stack: Stack) -> usize {
if stack.is_null() {
return 0;
}
let base = get_stack_base();
if base.is_null() {
return 0;
}
(stack as usize - base as usize) / stack_value_size()
}
unsafe fn drain_stack_to_base(mut stack: Stack, base: Stack) {
unsafe {
while stack > base {
let (rest, sv) = pop_sv(stack);
drop_stack_value(sv);
stack = rest;
}
}
}
unsafe fn call_with_value(base: Stack, value: Value, callable: &Value) -> Stack {
unsafe {
let stack = push(base, value);
crate::quotations::invoke_callable(stack, callable)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_list_map(stack: Stack) -> Stack {
unsafe {
let (stack, callable) = pop(stack);
match &callable {
Value::Quotation { .. } | Value::Closure { .. } => {}
_ => panic!(
"list-map: expected Quotation or Closure, got {:?}",
callable
),
}
let (stack, list_val) = pop(stack);
let variant_data = match list_val {
Value::Variant(v) => v,
_ => panic!("list-map: expected Variant (list), got {:?}", list_val),
};
let mut results = Vec::with_capacity(variant_data.fields.len());
for field in variant_data.fields.iter() {
let temp_base = crate::stack::alloc_stack();
let temp_stack = call_with_value(temp_base, field.clone(), &callable);
if temp_stack <= temp_base {
panic!("list-map: quotation consumed element without producing result");
}
let (remaining, result) = pop(temp_stack);
results.push(result);
if remaining > temp_base {
drain_stack_to_base(remaining, temp_base);
}
}
let new_variant = Value::Variant(Arc::new(VariantData::new(
variant_data.tag.clone(),
results,
)));
push(stack, new_variant)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_list_filter(stack: Stack) -> Stack {
unsafe {
let (stack, callable) = pop(stack);
match &callable {
Value::Quotation { .. } | Value::Closure { .. } => {}
_ => panic!(
"list-filter: expected Quotation or Closure, got {:?}",
callable
),
}
let (stack, list_val) = pop(stack);
let variant_data = match list_val {
Value::Variant(v) => v,
_ => panic!("list-filter: expected Variant (list), got {:?}", list_val),
};
let mut results = Vec::new();
for field in variant_data.fields.iter() {
let temp_base = crate::stack::alloc_stack();
let temp_stack = call_with_value(temp_base, field.clone(), &callable);
if temp_stack <= temp_base {
panic!("list-filter: quotation consumed element without producing result");
}
let (remaining, result) = pop(temp_stack);
let keep = match result {
Value::Bool(b) => b,
_ => panic!("list-filter: quotation must return Bool, got {:?}", result),
};
if keep {
results.push(field.clone());
}
if remaining > temp_base {
drain_stack_to_base(remaining, temp_base);
}
}
let new_variant = Value::Variant(Arc::new(VariantData::new(
variant_data.tag.clone(),
results,
)));
push(stack, new_variant)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_list_fold(stack: Stack) -> Stack {
unsafe {
let (stack, callable) = pop(stack);
match &callable {
Value::Quotation { .. } | Value::Closure { .. } => {}
_ => panic!(
"list-fold: expected Quotation or Closure, got {:?}",
callable
),
}
let (stack, init) = pop(stack);
let (stack, list_val) = pop(stack);
let variant_data = match list_val {
Value::Variant(v) => v,
_ => panic!("list-fold: expected Variant (list), got {:?}", list_val),
};
let mut acc = init;
for field in variant_data.fields.iter() {
let temp_base = crate::stack::alloc_stack();
let temp_stack = push(temp_base, acc);
let temp_stack = push(temp_stack, field.clone());
let temp_stack = match &callable {
Value::Quotation { wrapper, .. } => {
let fn_ref: unsafe extern "C" fn(Stack) -> Stack =
std::mem::transmute(*wrapper);
fn_ref(temp_stack)
}
Value::Closure { fn_ptr, env } => {
let fn_ref: unsafe extern "C" fn(Stack, *const Value, usize) -> Stack =
std::mem::transmute(*fn_ptr);
fn_ref(temp_stack, env.as_ptr(), env.len())
}
_ => unreachable!(),
};
if temp_stack <= temp_base {
panic!("list-fold: quotation consumed inputs without producing result");
}
let (remaining, new_acc) = pop(temp_stack);
acc = new_acc;
if remaining > temp_base {
drain_stack_to_base(remaining, temp_base);
}
}
push(stack, acc)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_list_each(stack: Stack) -> Stack {
unsafe {
let (stack, callable) = pop(stack);
match &callable {
Value::Quotation { .. } | Value::Closure { .. } => {}
_ => panic!(
"list-each: expected Quotation or Closure, got {:?}",
callable
),
}
let (stack, list_val) = pop(stack);
let variant_data = match list_val {
Value::Variant(v) => v,
_ => panic!("list-each: expected Variant (list), got {:?}", list_val),
};
for field in variant_data.fields.iter() {
let temp_base = crate::stack::alloc_stack();
let temp_stack = call_with_value(temp_base, field.clone(), &callable);
if temp_stack > temp_base {
drain_stack_to_base(temp_stack, temp_base);
}
}
stack
}
}