use crate::seqstring::global_string;
use crate::stack::{Stack, drop_stack_value, heap_value_mut, pop, pop_sv, push};
use crate::value::{MapKey, Value, VariantData};
use std::sync::Arc;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_make_map(stack: Stack) -> Stack {
unsafe { push(stack, Value::Map(Box::default())) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_map_get(stack: Stack) -> Stack {
unsafe {
let (stack, key_val) = pop(stack);
let key = MapKey::from_value(&key_val).unwrap_or_else(|| {
panic!(
"map-get: key must be Int, String, or Bool, got {:?}",
key_val
)
});
let (stack, map_val) = pop(stack);
let map = match map_val {
Value::Map(m) => m,
_ => panic!("map-get: expected Map, got {:?}", map_val),
};
match map.get(&key) {
Some(value) => {
let stack = push(stack, value.clone());
push(stack, Value::Bool(true))
}
None => {
let stack = push(stack, Value::Int(0)); push(stack, Value::Bool(false)) }
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_map_set(stack: Stack) -> Stack {
unsafe {
if let Some(Value::Map(map)) = heap_value_mut(stack.sub(3)) {
let (stack, value) = pop(stack);
let (stack, key_val) = pop(stack);
let key = MapKey::from_value(&key_val).unwrap_or_else(|| {
panic!(
"map-set: key must be Int, String, or Bool, got {:?}",
key_val
)
});
map.insert(key, value);
return stack; }
let (stack, value) = pop(stack);
let (stack, key_val) = pop(stack);
let key = MapKey::from_value(&key_val).unwrap_or_else(|| {
panic!(
"map-set: key must be Int, String, or Bool, got {:?}",
key_val
)
});
let (stack, map_val) = pop(stack);
let mut map = match map_val {
Value::Map(m) => *m,
_ => panic!("map-set: expected Map, got {:?}", map_val),
};
map.insert(key, value);
push(stack, Value::Map(Box::new(map)))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_map_has(stack: Stack) -> Stack {
unsafe {
let (stack, key_val) = pop(stack);
let key = MapKey::from_value(&key_val).unwrap_or_else(|| {
panic!(
"map-has?: key must be Int, String, or Bool, got {:?}",
key_val
)
});
let (stack, map_val) = pop(stack);
let map = match map_val {
Value::Map(m) => m,
_ => panic!("map-has?: expected Map, got {:?}", map_val),
};
let has_key = map.contains_key(&key);
push(stack, Value::Bool(has_key))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_map_remove(stack: Stack) -> Stack {
unsafe {
if let Some(Value::Map(map)) = heap_value_mut(stack.sub(2)) {
let (stack, key_val) = pop(stack);
let key = MapKey::from_value(&key_val).unwrap_or_else(|| {
panic!(
"map-remove: key must be Int, String, or Bool, got {:?}",
key_val
)
});
map.remove(&key);
return stack; }
let (stack, key_val) = pop(stack);
let key = MapKey::from_value(&key_val).unwrap_or_else(|| {
panic!(
"map-remove: key must be Int, String, or Bool, got {:?}",
key_val
)
});
let (stack, map_val) = pop(stack);
let mut map = match map_val {
Value::Map(m) => *m,
_ => panic!("map-remove: expected Map, got {:?}", map_val),
};
map.remove(&key);
push(stack, Value::Map(Box::new(map)))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_map_keys(stack: Stack) -> Stack {
unsafe {
let (stack, map_val) = pop(stack);
let map = match map_val {
Value::Map(m) => m,
_ => panic!("map-keys: expected Map, got {:?}", map_val),
};
let keys: Vec<Value> = map.keys().map(|k| k.to_value()).collect();
let variant = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
keys,
)));
push(stack, variant)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_map_values(stack: Stack) -> Stack {
unsafe {
let (stack, map_val) = pop(stack);
let map = match map_val {
Value::Map(m) => m,
_ => panic!("map-values: expected Map, got {:?}", map_val),
};
let values: Vec<Value> = map.values().cloned().collect();
let variant = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
values,
)));
push(stack, variant)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_map_size(stack: Stack) -> Stack {
unsafe {
let (stack, map_val) = pop(stack);
let map = match map_val {
Value::Map(m) => m,
_ => panic!("map-size: expected Map, got {:?}", map_val),
};
push(stack, Value::Int(map.len() as i64))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_map_empty(stack: Stack) -> Stack {
unsafe {
let (stack, map_val) = pop(stack);
let map = match map_val {
Value::Map(m) => m,
_ => panic!("map-empty?: expected Map, got {:?}", map_val),
};
let is_empty = map.is_empty();
push(stack, Value::Bool(is_empty))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_map_each(stack: Stack) -> Stack {
unsafe {
let (stack, callable) = pop(stack);
match &callable {
Value::Quotation { .. } | Value::Closure { .. } => {}
_ => panic!(
"map.each: expected Quotation or Closure, got {:?}",
callable
),
}
let (stack, map_val) = pop(stack);
let map = match &map_val {
Value::Map(m) => m,
_ => panic!("map.each: expected Map, got {:?}", map_val),
};
for (key, value) in map.iter() {
let temp_base = crate::stack::alloc_stack();
let temp_stack = push(temp_base, key.to_value());
let temp_stack = push(temp_stack, value.clone());
let temp_stack = invoke_callable(temp_stack, &callable);
drain_to_base(temp_stack, temp_base);
}
stack
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_map_fold(stack: Stack) -> Stack {
unsafe {
let (stack, callable) = pop(stack);
match &callable {
Value::Quotation { .. } | Value::Closure { .. } => {}
_ => panic!(
"map.fold: expected Quotation or Closure, got {:?}",
callable
),
}
let (stack, mut acc) = pop(stack);
let (stack, map_val) = pop(stack);
let map = match &map_val {
Value::Map(m) => m,
_ => panic!("map.fold: expected Map, got {:?}", map_val),
};
for (key, value) in map.iter() {
let temp_base = crate::stack::alloc_stack();
let temp_stack = push(temp_base, acc);
let temp_stack = push(temp_stack, key.to_value());
let temp_stack = push(temp_stack, value.clone());
let temp_stack = invoke_callable(temp_stack, &callable);
if temp_stack <= temp_base {
panic!("map.fold: quotation consumed accumulator without producing result");
}
let (remaining, new_acc) = pop(temp_stack);
acc = new_acc;
if remaining > temp_base {
drain_to_base(remaining, temp_base);
}
}
push(stack, acc)
}
}
use crate::quotations::invoke_callable;
unsafe fn drain_to_base(mut stack: Stack, base: Stack) {
unsafe {
while stack > base {
let (rest, sv) = pop_sv(stack);
drop_stack_value(sv);
stack = rest;
}
}
}
pub use patch_seq_make_map as make_map;
pub use patch_seq_map_each as map_each;
pub use patch_seq_map_empty as map_empty;
pub use patch_seq_map_fold as map_fold;
pub use patch_seq_map_get as map_get;
pub use patch_seq_map_has as map_has;
pub use patch_seq_map_keys as map_keys;
pub use patch_seq_map_remove as map_remove;
pub use patch_seq_map_set as map_set;
pub use patch_seq_map_size as map_size;
pub use patch_seq_map_values as map_values;
#[cfg(test)]
mod tests;