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 {
use super::*;
#[test]
fn test_make_map() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let (_stack, result) = pop(stack);
match result {
Value::Map(m) => assert!(m.is_empty()),
_ => panic!("Expected Map"),
}
}
}
#[test]
fn test_map_set_and_get() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::String("name".into()));
let stack = push(stack, Value::String("Alice".into()));
let stack = map_set(stack);
let stack = push(stack, Value::String("name".into()));
let stack = map_get(stack);
let (stack, flag) = pop(stack);
assert_eq!(flag, Value::Bool(true));
let (_stack, result) = pop(stack);
match result {
Value::String(s) => assert_eq!(s.as_str(), "Alice"),
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_map_set_with_int_key() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::Int(42));
let stack = push(stack, Value::String("answer".into()));
let stack = map_set(stack);
let stack = push(stack, Value::Int(42));
let stack = map_get(stack);
let (stack, flag) = pop(stack);
assert_eq!(flag, Value::Bool(true));
let (_stack, result) = pop(stack);
match result {
Value::String(s) => assert_eq!(s.as_str(), "answer"),
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_map_has() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::String("key".into()));
let stack = push(stack, Value::Int(100));
let stack = map_set(stack);
let stack = crate::stack::dup(stack);
let stack = push(stack, Value::String("key".into()));
let stack = map_has(stack);
let (stack, result) = pop(stack);
assert_eq!(result, Value::Bool(true));
let stack = push(stack, Value::String("missing".into()));
let stack = map_has(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Bool(false));
}
}
#[test]
fn test_map_remove() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::String("a".into()));
let stack = push(stack, Value::Int(1));
let stack = map_set(stack);
let stack = push(stack, Value::String("b".into()));
let stack = push(stack, Value::Int(2));
let stack = map_set(stack);
let stack = push(stack, Value::String("a".into()));
let stack = map_remove(stack);
let stack = crate::stack::dup(stack);
let stack = push(stack, Value::String("a".into()));
let stack = map_has(stack);
let (stack, result) = pop(stack);
assert_eq!(result, Value::Bool(false));
let stack = push(stack, Value::String("b".into()));
let stack = map_has(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Bool(true));
}
}
#[test]
fn test_map_size() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = map_size(stack);
let (stack, result) = pop(stack);
assert_eq!(result, Value::Int(0));
let stack = make_map(stack);
let stack = push(stack, Value::String("a".into()));
let stack = push(stack, Value::Int(1));
let stack = map_set(stack);
let stack = push(stack, Value::String("b".into()));
let stack = push(stack, Value::Int(2));
let stack = map_set(stack);
let stack = map_size(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(2));
}
}
#[test]
fn test_map_empty() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = map_empty(stack);
let (stack, result) = pop(stack);
assert_eq!(result, Value::Bool(true));
let stack = make_map(stack);
let stack = push(stack, Value::String("key".into()));
let stack = push(stack, Value::Int(1));
let stack = map_set(stack);
let stack = map_empty(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Bool(false));
}
}
#[test]
fn test_map_keys_and_values() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::String("x".into()));
let stack = push(stack, Value::Int(10));
let stack = map_set(stack);
let stack = push(stack, Value::String("y".into()));
let stack = push(stack, Value::Int(20));
let stack = map_set(stack);
let stack = crate::stack::dup(stack); let stack = map_keys(stack);
let (stack, keys_result) = pop(stack);
match keys_result {
Value::Variant(v) => {
assert_eq!(v.fields.len(), 2);
}
_ => panic!("Expected Variant"),
}
let stack = map_values(stack);
let (_stack, values_result) = pop(stack);
match values_result {
Value::Variant(v) => {
assert_eq!(v.fields.len(), 2);
}
_ => panic!("Expected Variant"),
}
}
}
#[test]
fn test_map_get_found() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::String("key".into()));
let stack = push(stack, Value::Int(42));
let stack = map_set(stack);
let stack = push(stack, Value::String("key".into()));
let stack = map_get(stack);
let (stack, flag) = pop(stack);
let (_stack, value) = pop(stack);
assert_eq!(flag, Value::Bool(true));
assert_eq!(value, Value::Int(42));
}
}
#[test]
fn test_map_get_not_found() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::String("missing".into()));
let stack = map_get(stack);
let (stack, flag) = pop(stack);
let (_stack, _value) = pop(stack); assert_eq!(flag, Value::Bool(false));
}
}
#[test]
fn test_map_with_bool_key() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::Bool(true));
let stack = push(stack, Value::String("yes".into()));
let stack = map_set(stack);
let stack = push(stack, Value::Bool(false));
let stack = push(stack, Value::String("no".into()));
let stack = map_set(stack);
let stack = push(stack, Value::Bool(true));
let stack = map_get(stack);
let (stack, flag) = pop(stack);
assert_eq!(flag, Value::Bool(true));
let (_stack, result) = pop(stack);
match result {
Value::String(s) => assert_eq!(s.as_str(), "yes"),
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_map_key_overwrite() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::String("key".into()));
let stack = push(stack, Value::Int(100));
let stack = map_set(stack);
let stack = push(stack, Value::String("key".into()));
let stack = push(stack, Value::Int(200));
let stack = map_set(stack);
let stack = crate::stack::dup(stack);
let stack = map_size(stack);
let (stack, size) = pop(stack);
assert_eq!(size, Value::Int(1));
let stack = push(stack, Value::String("key".into()));
let stack = map_get(stack);
let (stack, flag) = pop(stack);
assert_eq!(flag, Value::Bool(true));
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(200));
}
}
#[test]
fn test_map_mixed_key_types() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::String("name".into()));
let stack = push(stack, Value::String("Alice".into()));
let stack = map_set(stack);
let stack = push(stack, Value::Int(42));
let stack = push(stack, Value::String("answer".into()));
let stack = map_set(stack);
let stack = push(stack, Value::Bool(true));
let stack = push(stack, Value::String("yes".into()));
let stack = map_set(stack);
let stack = crate::stack::dup(stack);
let stack = map_size(stack);
let (stack, size) = pop(stack);
assert_eq!(size, Value::Int(3));
let stack = crate::stack::dup(stack);
let stack = push(stack, Value::String("name".into()));
let stack = map_get(stack);
let (stack, flag) = pop(stack);
assert_eq!(flag, Value::Bool(true));
let (stack, result) = pop(stack);
match result {
Value::String(s) => assert_eq!(s.as_str(), "Alice"),
_ => panic!("Expected String for name key"),
}
let stack = crate::stack::dup(stack);
let stack = push(stack, Value::Int(42));
let stack = map_get(stack);
let (stack, flag) = pop(stack);
assert_eq!(flag, Value::Bool(true));
let (stack, result) = pop(stack);
match result {
Value::String(s) => assert_eq!(s.as_str(), "answer"),
_ => panic!("Expected String for int key"),
}
let stack = push(stack, Value::Bool(true));
let stack = map_get(stack);
let (stack, flag) = pop(stack);
assert_eq!(flag, Value::Bool(true));
let (_stack, result) = pop(stack);
match result {
Value::String(s) => assert_eq!(s.as_str(), "yes"),
_ => panic!("Expected String for bool key"),
}
}
}
#[test]
fn test_map_fold_empty() {
unsafe {
use crate::quotations::push_quotation;
let stack = crate::stack::alloc_test_stack();
let stack = make_map(stack);
let stack = push(stack, Value::Int(99));
unsafe extern "C" fn noop(stack: Stack) -> Stack {
stack
}
let fn_ptr = noop as *const () as usize;
let stack = push_quotation(stack, fn_ptr, fn_ptr);
let stack = patch_seq_map_fold(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(99));
}
}
}