use super::{
iterator::collect_pair,
value_sort::{sort_by_key, sort_values},
};
use crate::prelude::*;
use std::{cmp::Ordering, ops::DerefMut};
pub fn make_module() -> KMap {
let result = KMap::with_type("core.list");
result.add_fn("clear", |ctx| {
let expected_error = "|List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), []) => {
l.data_mut().clear();
Ok(KValue::List(l.clone()))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("contains", |ctx| {
let expected_error = "|List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), [value]) => {
let l = l.clone();
let value = value.clone();
for candidate in l.data().iter() {
match ctx
.vm
.run_binary_op(BinaryOp::Equal, value.clone(), candidate.clone())
{
Ok(KValue::Bool(false)) => {}
Ok(KValue::Bool(true)) => return Ok(true.into()),
Ok(unexpected) => {
return runtime_error!(
"list.contains: Expected Bool from comparison, found '{}'",
unexpected.type_as_string()
);
}
Err(e) => return Err(e),
}
}
Ok(false.into())
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("extend", |ctx| {
let expected_error = "|List, Iterable|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), [KValue::List(other)]) => {
l.data_mut().extend(other.data().iter().cloned());
Ok(KValue::List(l.clone()))
}
(KValue::List(l), [KValue::Tuple(other)]) => {
l.data_mut().extend(other.iter().cloned());
Ok(KValue::List(l.clone()))
}
(KValue::List(l), [iterable]) if iterable.is_iterable() => {
let l = l.clone();
let iterable = iterable.clone();
let iterator = ctx.vm.make_iterator(iterable)?;
{
let mut list_data = l.data_mut();
let (size_hint, _) = iterator.size_hint();
list_data.reserve(size_hint);
for value in iterator.map(collect_pair) {
match value {
KIteratorOutput::Value(value) => list_data.push(value.clone()),
KIteratorOutput::Error(error) => return Err(error),
_ => unreachable!(),
}
}
}
Ok(KValue::List(l))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("fill", |ctx| {
let expected_error = "|List, Any|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), [value]) => {
for v in l.data_mut().iter_mut() {
*v = value.clone();
}
Ok(KValue::List(l.clone()))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("first", |ctx| {
let expected_error = "|List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), []) => match l.data().first() {
Some(value) => Ok(value.clone()),
None => Ok(KValue::Null),
},
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("get", |ctx| {
let (list, index, default) = {
use KValue::{List, Null, Number};
let expected_error = "|List, Number|, or |List, Number, Any|";
match ctx.instance_and_args(is_list, expected_error)? {
(List(list), [Number(n)]) => (list, n, Null),
(List(list), [Number(n), default]) => (list, n, default.clone()),
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args);
}
}
};
if *index >= 0 {
match list.data().get(usize::from(index)) {
Some(value) => Ok(value.clone()),
None => Ok(default),
}
} else {
Ok(default)
}
});
result.add_fn("insert", |ctx| {
let expected_error = "|List, Number, Any|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), [KValue::Number(n), value]) => {
let index: usize = n.into();
if *n < 0.0 || index > l.data().len() {
return runtime_error!("index out of bounds");
}
l.data_mut().insert(index, value.clone());
Ok(KValue::List(l.clone()))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("is_empty", |ctx| {
let expected_error = "|List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), []) => Ok(l.data().is_empty().into()),
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("last", |ctx| {
let expected_error = "|List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), []) => match l.data().last() {
Some(value) => Ok(value.clone()),
None => Ok(KValue::Null),
},
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("pop", |ctx| {
let expected_error = "|List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), []) => match l.data_mut().pop() {
Some(value) => Ok(value),
None => Ok(KValue::Null),
},
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("push", |ctx| {
let expected_error = "|List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), [value]) => {
l.data_mut().push(value.clone());
Ok(KValue::List(l.clone()))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("remove", |ctx| {
let expected_error = "|List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), [KValue::Number(n)]) => {
let index: usize = n.into();
if *n < 0.0 || index >= l.data().len() {
return runtime_error!("index out of bounds");
}
Ok(l.data_mut().remove(index))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("resize", |ctx| {
let expected_error = "|List, Number|, or |List, Number, Any|";
match ctx.instance_and_args(is_list, expected_error)? {
(_, [KValue::Number(n), ..]) if *n < 0.0 => {
runtime_error!("expected a non-negative size")
}
(KValue::List(l), [KValue::Number(n)]) => {
l.data_mut().resize(n.into(), KValue::Null);
Ok(KValue::List(l.clone()))
}
(KValue::List(l), [KValue::Number(n), value]) => {
l.data_mut().resize(n.into(), value.clone());
Ok(KValue::List(l.clone()))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("resize_with", |ctx| {
let expected_error = "|List, Number, || -> Any|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), [KValue::Number(n), f]) if f.is_callable() => {
if *n < 0.0 {
return runtime_error!("expected a non-negative size");
}
let new_size = usize::from(n);
let len = l.len();
let l = l.clone();
let f = f.clone();
match len.cmp(&new_size) {
Ordering::Greater => l.data_mut().truncate(new_size),
Ordering::Less => {
l.data_mut().reserve(new_size);
for _ in 0..new_size - len {
let new_value = ctx.vm.call_function(f.clone(), &[])?;
l.data_mut().push(new_value);
}
}
Ordering::Equal => {}
}
Ok(KValue::List(l))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("retain", |ctx| {
let result = {
let expected_error = "|List, Any|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), [f]) if f.is_callable() => {
let l = l.clone();
let f = f.clone();
let mut write_index = 0;
for read_index in 0..l.len() {
let value = l.data()[read_index].clone();
match ctx.vm.call_function(f.clone(), value.clone()) {
Ok(KValue::Bool(result)) => {
if result {
l.data_mut()[write_index] = value;
write_index += 1;
}
}
Ok(unexpected) => {
return unexpected_type(
"a Bool to returned from the predicate",
&unexpected,
);
}
Err(error) => return Err(error),
}
}
l.data_mut().resize(write_index, KValue::Null);
l
}
(KValue::List(l), [value]) => {
let l = l.clone();
let value = value.clone();
let mut error = None;
l.data_mut().retain(|x| {
if error.is_some() {
return true;
}
match ctx
.vm
.run_binary_op(BinaryOp::Equal, x.clone(), value.clone())
{
Ok(KValue::Bool(true)) => true,
Ok(KValue::Bool(false)) => false,
Ok(unexpected) => {
error = Some(unexpected_type(
"a Bool from the equality comparison",
&unexpected,
));
true
}
Err(e) => {
error = Some(Err(e));
true
}
}
});
if let Some(error) = error {
return error;
}
l
}
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args);
}
}
};
Ok(KValue::List(result))
});
result.add_fn("reverse", |ctx| {
let expected_error = "|List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), []) => {
l.data_mut().reverse();
Ok(KValue::List(l.clone()))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("sort", |ctx| {
let expected_error = "|List|, or |List, |Any| -> Any|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), []) => {
let l = l.clone();
let mut data = l.data_mut();
sort_values(ctx.vm, &mut data)?;
Ok(KValue::List(l.clone()))
}
(KValue::List(l), [f]) if f.is_callable() => {
let l = l.clone();
let sorted = sort_by_key(ctx.vm, l.data().as_ref(), f.clone())?;
for (target_value, (_key, source_value)) in
l.data_mut().iter_mut().zip(sorted.into_iter())
{
*target_value = source_value;
}
Ok(KValue::List(l))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("swap", |ctx| {
let expected_error = "|List, List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(a), [KValue::List(b)]) => {
std::mem::swap(a.data_mut().deref_mut(), b.data_mut().deref_mut());
Ok(KValue::Null)
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("to_tuple", |ctx| {
let expected_error = "|List|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), []) => Ok(KValue::Tuple(l.data().as_slice().into())),
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result.add_fn("transform", |ctx| {
let expected_error = "|List, |Any| -> Any|";
match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(l), [f]) if f.is_callable() => {
let l = l.clone();
let f = f.clone();
for value in l.data_mut().iter_mut() {
*value = match ctx.vm.call_function(f.clone(), value.clone()) {
Ok(result) => result,
Err(error) => return Err(error),
}
}
Ok(KValue::List(l))
}
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
result
}
fn is_list(value: &KValue) -> bool {
matches!(value, KValue::List(_))
}