use super::*;
use crate::seqstring::global_string;
use crate::stack::{Stack, pop, push};
use crate::value::{Value, VariantData};
use std::sync::Arc;
unsafe extern "C" fn double_quot(stack: Stack) -> Stack {
unsafe {
let (stack, val) = pop(stack);
match val {
Value::Int(n) => push(stack, Value::Int(n * 2)),
_ => panic!("Expected Int"),
}
}
}
unsafe extern "C" fn is_positive_quot(stack: Stack) -> Stack {
unsafe {
let (stack, val) = pop(stack);
match val {
Value::Int(n) => push(stack, Value::Bool(n > 0)),
_ => panic!("Expected Int"),
}
}
}
unsafe extern "C" fn add_quot(stack: Stack) -> Stack {
unsafe {
let (stack, b) = pop(stack);
let (stack, a) = pop(stack);
match (a, b) {
(Value::Int(x), Value::Int(y)) => push(stack, Value::Int(x + y)),
_ => panic!("Expected two Ints"),
}
}
}
#[test]
fn test_list_map_double() {
unsafe {
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![Value::Int(1), Value::Int(2), Value::Int(3)],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let fn_ptr = double_quot as *const () as usize;
let stack = push(
stack,
Value::Quotation {
wrapper: fn_ptr,
impl_: fn_ptr,
},
);
let stack = list_map(stack);
let (_stack, result) = pop(stack);
match result {
Value::Variant(v) => {
assert_eq!(v.fields.len(), 3);
assert_eq!(v.fields[0], Value::Int(2));
assert_eq!(v.fields[1], Value::Int(4));
assert_eq!(v.fields[2], Value::Int(6));
}
_ => panic!("Expected Variant"),
}
}
}
#[test]
fn test_list_filter_positive() {
unsafe {
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![
Value::Int(-1),
Value::Int(2),
Value::Int(-3),
Value::Int(4),
Value::Int(0),
],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let fn_ptr = is_positive_quot as *const () as usize;
let stack = push(
stack,
Value::Quotation {
wrapper: fn_ptr,
impl_: fn_ptr,
},
);
let stack = list_filter(stack);
let (_stack, result) = pop(stack);
match result {
Value::Variant(v) => {
assert_eq!(v.fields.len(), 2);
assert_eq!(v.fields[0], Value::Int(2));
assert_eq!(v.fields[1], Value::Int(4));
}
_ => panic!("Expected Variant"),
}
}
}
#[test]
fn test_list_fold_sum() {
unsafe {
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![
Value::Int(1),
Value::Int(2),
Value::Int(3),
Value::Int(4),
Value::Int(5),
],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let stack = push(stack, Value::Int(0)); let fn_ptr = add_quot as *const () as usize;
let stack = push(
stack,
Value::Quotation {
wrapper: fn_ptr,
impl_: fn_ptr,
},
);
let stack = list_fold(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(15)); }
}
#[test]
fn test_list_fold_empty() {
unsafe {
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let stack = push(stack, Value::Int(42)); let fn_ptr = add_quot as *const () as usize;
let stack = push(
stack,
Value::Quotation {
wrapper: fn_ptr,
impl_: fn_ptr,
},
);
let stack = list_fold(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(42)); }
}
#[test]
fn test_list_length() {
unsafe {
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![Value::Int(1), Value::Int(2), Value::Int(3)],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let stack = list_length(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(3));
}
}
#[test]
fn test_list_empty_true() {
unsafe {
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let stack = list_empty(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Bool(true));
}
}
#[test]
fn test_list_empty_false() {
unsafe {
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![Value::Int(1)],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let stack = list_empty(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Bool(false));
}
}
#[test]
fn test_list_map_empty() {
unsafe {
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let fn_ptr = double_quot as *const () as usize;
let stack = push(
stack,
Value::Quotation {
wrapper: fn_ptr,
impl_: fn_ptr,
},
);
let stack = list_map(stack);
let (_stack, result) = pop(stack);
match result {
Value::Variant(v) => {
assert_eq!(v.fields.len(), 0);
}
_ => panic!("Expected Variant"),
}
}
}
#[test]
fn test_list_map_preserves_tag() {
unsafe {
let list = Value::Variant(Arc::new(VariantData::new(
global_string("CustomTag".to_string()),
vec![Value::Int(1), Value::Int(2)],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let fn_ptr = double_quot as *const () as usize;
let stack = push(
stack,
Value::Quotation {
wrapper: fn_ptr,
impl_: fn_ptr,
},
);
let stack = list_map(stack);
let (_stack, result) = pop(stack);
match result {
Value::Variant(v) => {
assert_eq!(v.tag.as_str(), "CustomTag"); assert_eq!(v.fields[0], Value::Int(2));
assert_eq!(v.fields[1], Value::Int(4));
}
_ => panic!("Expected Variant"),
}
}
}
unsafe extern "C" fn add_captured_closure(
stack: Stack,
env: *const Value,
_env_len: usize,
) -> Stack {
unsafe {
let (stack, val) = pop(stack);
let captured = &*env; match (val, captured) {
(Value::Int(n), Value::Int(c)) => push(stack, Value::Int(n + c)),
_ => panic!("Expected Int"),
}
}
}
#[test]
fn test_list_map_with_closure() {
unsafe {
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![Value::Int(1), Value::Int(2), Value::Int(3)],
)));
let env: std::sync::Arc<[Value]> =
std::sync::Arc::from(vec![Value::Int(10)].into_boxed_slice());
let closure = Value::Closure {
fn_ptr: add_captured_closure as *const () as usize,
env,
};
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let stack = push(stack, closure);
let stack = list_map(stack);
let (_stack, result) = pop(stack);
match result {
Value::Variant(v) => {
assert_eq!(v.fields.len(), 3);
assert_eq!(v.fields[0], Value::Int(11)); assert_eq!(v.fields[1], Value::Int(12)); assert_eq!(v.fields[2], Value::Int(13)); }
_ => panic!("Expected Variant"),
}
}
}
#[test]
fn test_list_get_type_error_index() {
unsafe {
crate::error::clear_runtime_error();
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![Value::Int(1), Value::Int(2)],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let stack = push(stack, Value::Bool(true)); let stack = list_get(stack);
assert!(crate::error::has_runtime_error());
let error = crate::error::take_runtime_error().unwrap();
assert!(error.contains("expected Int"));
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(false));
let (_stack, value) = pop(stack);
assert_eq!(value, Value::Int(0));
}
}
#[test]
fn test_list_get_type_error_list() {
unsafe {
crate::error::clear_runtime_error();
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(42)); let stack = push(stack, Value::Int(0)); let stack = list_get(stack);
assert!(crate::error::has_runtime_error());
let error = crate::error::take_runtime_error().unwrap();
assert!(error.contains("expected Variant"));
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(false));
let (_stack, value) = pop(stack);
assert_eq!(value, Value::Int(0));
}
}
#[test]
fn test_list_set_type_error_index() {
unsafe {
crate::error::clear_runtime_error();
let list = Value::Variant(Arc::new(VariantData::new(
global_string("List".to_string()),
vec![Value::Int(1), Value::Int(2)],
)));
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, list);
let stack = push(stack, Value::Bool(true)); let stack = push(stack, Value::Int(99)); let stack = list_set(stack);
assert!(crate::error::has_runtime_error());
let error = crate::error::take_runtime_error().unwrap();
assert!(error.contains("expected Int"));
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(false));
let (_stack, returned_list) = pop(stack);
assert!(matches!(returned_list, Value::Variant(_)));
}
}
#[test]
fn test_list_set_type_error_list() {
unsafe {
crate::error::clear_runtime_error();
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(42)); let stack = push(stack, Value::Int(0)); let stack = push(stack, Value::Int(99)); let stack = list_set(stack);
assert!(crate::error::has_runtime_error());
let error = crate::error::take_runtime_error().unwrap();
assert!(error.contains("expected Variant"));
let (stack, success) = pop(stack);
assert_eq!(success, Value::Bool(false));
let (_stack, returned) = pop(stack);
assert_eq!(returned, Value::Int(42)); }
}
#[test]
fn test_list_reverse_empty() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let empty_list = Value::Variant(Arc::new(VariantData::new(
crate::seqstring::global_string("List".to_string()),
vec![],
)));
let stack = push(stack, empty_list);
let stack = list_reverse(stack);
let (_stack, result) = pop(stack);
match result {
Value::Variant(v) => {
assert_eq!(v.fields.len(), 0);
assert_eq!(v.tag.as_str(), "List");
}
_ => panic!("Expected Variant"),
}
}
}
#[test]
fn test_list_reverse_single() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let list = Value::Variant(Arc::new(VariantData::new(
crate::seqstring::global_string("List".to_string()),
vec![Value::Int(42)],
)));
let stack = push(stack, list);
let stack = list_reverse(stack);
let (_stack, result) = pop(stack);
match result {
Value::Variant(v) => {
assert_eq!(v.fields.len(), 1);
assert_eq!(v.fields[0], Value::Int(42));
}
_ => panic!("Expected Variant"),
}
}
}
#[test]
fn test_list_reverse_multiple() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let list = Value::Variant(Arc::new(VariantData::new(
crate::seqstring::global_string("List".to_string()),
vec![Value::Int(1), Value::Int(2), Value::Int(3)],
)));
let stack = push(stack, list);
let stack = list_reverse(stack);
let (_stack, result) = pop(stack);
match result {
Value::Variant(v) => {
assert_eq!(v.fields.len(), 3);
assert_eq!(v.fields[0], Value::Int(3));
assert_eq!(v.fields[1], Value::Int(2));
assert_eq!(v.fields[2], Value::Int(1));
assert_eq!(v.tag.as_str(), "List"); }
_ => panic!("Expected Variant"),
}
}
}