Treat singletons and arrays as subtypes rather than coercible (#7181)

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-05-28 16:29:23 +12:00
committed by GitHub
parent 9dfb67cf61
commit 783b6ed76c
71 changed files with 5119 additions and 6067 deletions

View File

@ -14,7 +14,7 @@ use crate::{
/// Apply a function to each element of an array.
pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let array: Vec<KclValue> = args.get_unlabeled_kw_arg("array")?;
let array: Vec<KclValue> = args.get_unlabeled_kw_arg_typed("array", &RuntimeType::any_array(), exec_state)?;
let f: &FunctionSource = args.get_kw_arg("f")?;
let new_array = inner_map(array, f, exec_state, &args).await?;
Ok(KclValue::HomArray {
@ -68,7 +68,7 @@ async fn call_map_closure(
/// For each item in an array, update a value.
pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let array: Vec<KclValue> = args.get_unlabeled_kw_arg("array")?;
let array: Vec<KclValue> = args.get_unlabeled_kw_arg_typed("array", &RuntimeType::any_array(), exec_state)?;
let f: &FunctionSource = args.get_kw_arg("f")?;
let initial: KclValue = args.get_kw_arg("initial")?;
inner_reduce(array, initial, f, exec_state, &args).await
@ -126,61 +126,23 @@ async fn call_reduce_closure(
Ok(out)
}
pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let array = args.get_unlabeled_kw_arg("array")?;
pub async fn push(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (mut array, ty) = args.get_unlabeled_kw_arg_array_and_type("array", exec_state)?;
let item: KclValue = args.get_kw_arg("item")?;
let KclValue::HomArray { value: values, ty } = array else {
let meta = vec![args.source_range];
let actual_type = array.human_friendly_type();
return Err(KclError::Semantic(KclErrorDetails::new(
format!("You can't push to a value of type {actual_type}, only an array"),
meta,
)));
};
let ty = if item.has_type(&ty) {
ty
} else {
// The user pushed an item with a type that differs from the array's
// element type.
RuntimeType::any()
};
let new_array = inner_push(values, item);
Ok(KclValue::HomArray { value: new_array, ty })
}
fn inner_push(mut array: Vec<KclValue>, item: KclValue) -> Vec<KclValue> {
array.push(item);
array
Ok(KclValue::HomArray { value: array, ty })
}
pub async fn pop(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let array = args.get_unlabeled_kw_arg("array")?;
let KclValue::HomArray { value: values, ty } = array else {
let meta = vec![args.source_range];
let actual_type = array.human_friendly_type();
return Err(KclError::Semantic(KclErrorDetails::new(
format!("You can't pop from a value of type {actual_type}, only an array"),
meta,
)));
};
let new_array = inner_pop(values, &args)?;
Ok(KclValue::HomArray { value: new_array, ty })
}
fn inner_pop(array: Vec<KclValue>, args: &Args) -> Result<Vec<KclValue>, KclError> {
pub async fn pop(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (mut array, ty) = args.get_unlabeled_kw_arg_array_and_type("array", exec_state)?;
if array.is_empty() {
return Err(KclError::Semantic(KclErrorDetails::new(
"Cannot pop from an empty array".to_string(),
vec![args.source_range],
)));
}
// Create a new array with all elements except the last one
let new_array = array[..array.len() - 1].to_vec();
Ok(new_array)
array.pop();
Ok(KclValue::HomArray { value: array, ty })
}