//! Array operations: map, filter, reduce, find, any, all, length, push, pop, //! sort, reverse, zip, enumerate. use el_types::{Type, TypeEnv}; use super::fn_type; pub fn register(env: &mut TypeEnv) { let arr_int = Type::Array(Box::new(Type::Int)); let arr_str = Type::Array(Box::new(Type::String)); let arr_unk = Type::Array(Box::new(Type::Unknown)); // array_length([T]) -> Int env.register_fn("array_length", fn_type(vec![arr_unk.clone()], Type::Int)); // array_push([T], T) -> [T] env.register_fn("array_push", fn_type(vec![arr_unk.clone(), Type::Unknown], arr_unk.clone())); // array_pop([T]) -> T? env.register_fn("array_pop", fn_type(vec![arr_unk.clone()], Type::Optional(Box::new(Type::Unknown)))); // array_map([T], fn(T) -> U) -> [U] let mapper = Type::Fn { params: vec![Type::Unknown], return_type: Box::new(Type::Unknown) }; env.register_fn("array_map", fn_type(vec![arr_unk.clone(), mapper.clone()], arr_unk.clone())); // array_filter([T], fn(T) -> Bool) -> [T] let predicate = Type::Fn { params: vec![Type::Unknown], return_type: Box::new(Type::Bool) }; env.register_fn("array_filter", fn_type(vec![arr_unk.clone(), predicate.clone()], arr_unk.clone())); // array_reduce([T], U, fn(U, T) -> U) -> U let reducer = Type::Fn { params: vec![Type::Unknown, Type::Unknown], return_type: Box::new(Type::Unknown) }; env.register_fn("array_reduce", fn_type(vec![arr_unk.clone(), Type::Unknown, reducer], Type::Unknown)); // array_find([T], fn(T) -> Bool) -> T? env.register_fn("array_find", fn_type(vec![arr_unk.clone(), predicate.clone()], Type::Optional(Box::new(Type::Unknown)))); // array_any([T], fn(T) -> Bool) -> Bool env.register_fn("array_any", fn_type(vec![arr_unk.clone(), predicate.clone()], Type::Bool)); // array_all([T], fn(T) -> Bool) -> Bool env.register_fn("array_all", fn_type(vec![arr_unk.clone(), predicate], Type::Bool)); // array_sort([Int]) -> [Int] env.register_fn("array_sort", fn_type(vec![arr_int.clone()], arr_int.clone())); // array_reverse([T]) -> [T] env.register_fn("array_reverse", fn_type(vec![arr_unk.clone()], arr_unk.clone())); // array_zip([T], [U]) -> [[T]] (simplified: returns array of unknown) env.register_fn("array_zip", fn_type(vec![arr_unk.clone(), arr_unk.clone()], arr_unk.clone())); // array_enumerate([T]) -> [[T]] (returns pairs as arrays) env.register_fn("array_enumerate", fn_type(vec![arr_unk.clone()], arr_unk.clone())); // array_join([String], String) -> String env.register_fn("array_join", fn_type(vec![arr_str.clone(), Type::String], Type::String)); // array_concat([T], [T]) -> [T] env.register_fn("array_concat", fn_type(vec![arr_unk.clone(), arr_unk.clone()], arr_unk.clone())); // array_slice([T], Int, Int) -> [T] env.register_fn("array_slice", fn_type(vec![arr_unk.clone(), Type::Int, Type::Int], arr_unk)); // array_first([T]) -> T? env.register_fn("array_first", fn_type(vec![arr_int.clone()], Type::Optional(Box::new(Type::Int)))); // array_last([T]) -> T? env.register_fn("array_last", fn_type(vec![arr_int.clone()], Type::Optional(Box::new(Type::Int)))); // array_contains([String], String) -> Bool env.register_fn("array_contains", fn_type(vec![arr_str, Type::String], Type::Bool)); } #[cfg(test)] mod tests { use super::*; fn env() -> TypeEnv { let mut e = TypeEnv::with_builtins(); register(&mut e); e } #[test] fn test_array_length_registered() { assert!(env().lookup_fn("array_length").is_some()); } #[test] fn test_array_map_is_fn_type() { let e = env(); let ty = e.lookup_fn("array_map").unwrap(); assert!(matches!(ty, Type::Fn { .. })); } #[test] fn test_array_filter_registered() { assert!(env().lookup_fn("array_filter").is_some()); } #[test] fn test_array_push_registered() { assert!(env().lookup_fn("array_push").is_some()); } }