Fix the KCL any type and array coercion incorrectly nesting (#6816)
* Add sim test for any type * Fix doc comments to match code * Add array ascription tests * Commit new test output * Fix to not panic when type is undefined * Fix to not panic on use of the any type * Update test and generated output * Fix error message after rebase * Fix subtype of any * Fix KCL to use new keyword args * Fix to not nest MixedArray in HomArray * Update output * Remove all creation of MixedArray and use HomArray instead * Rename MixedArray to Tuple * Fix to coerce arrays the way tuples are done * Restructure to appease the type signature extraction * Fix TS unit test * Update output after switch to HomArray * Update docs * Fix to remove edge case when creating points * Update docs with broken point signature * Fix display of tuples to not collide with arrays * Change push to an array with type mismatch to be an error * Add sim test for push type error * Fix acription to more general array element type * Fix to coerce point types * Change array push to not error when item type differs * Fix coercion tests * Change to only flatten as a last resort and remove flattening tuples * Contort code to appease doc generation * Update docs * Fix coerce axes * Fix flattening test to test arrays instead of tuples * Remove special subtype case for singleton coercion
This commit is contained in:
@ -557,24 +557,23 @@ impl Args {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn make_user_val_from_point(&self, p: [TyF64; 2]) -> Result<KclValue, KclError> {
|
||||
pub(crate) fn make_kcl_val_from_point(&self, p: [f64; 2], ty: NumericType) -> Result<KclValue, KclError> {
|
||||
let meta = Metadata {
|
||||
source_range: self.source_range,
|
||||
};
|
||||
let x = KclValue::Number {
|
||||
value: p[0].n,
|
||||
value: p[0],
|
||||
meta: vec![meta],
|
||||
ty: p[0].ty.clone(),
|
||||
ty: ty.clone(),
|
||||
};
|
||||
let y = KclValue::Number {
|
||||
value: p[1].n,
|
||||
value: p[1],
|
||||
meta: vec![meta],
|
||||
ty: p[1].ty.clone(),
|
||||
ty: ty.clone(),
|
||||
};
|
||||
Ok(KclValue::MixedArray {
|
||||
value: vec![x, y],
|
||||
meta: vec![meta],
|
||||
})
|
||||
let ty = RuntimeType::Primitive(PrimitiveType::Number(ty));
|
||||
|
||||
Ok(KclValue::HomArray { value: vec![x, y], ty })
|
||||
}
|
||||
|
||||
pub(super) fn make_user_val_from_f64_with_type(&self, f: TyF64) -> KclValue {
|
||||
@ -796,7 +795,7 @@ impl<'a> FromKclValue<'a> for Vec<TagIdentifier> {
|
||||
let tags = value.iter().map(|v| v.get_tag_identifier().unwrap()).collect();
|
||||
Some(tags)
|
||||
}
|
||||
KclValue::MixedArray { value, .. } => {
|
||||
KclValue::Tuple { value, .. } => {
|
||||
let tags = value.iter().map(|v| v.get_tag_identifier().unwrap()).collect();
|
||||
Some(tags)
|
||||
}
|
||||
@ -1136,8 +1135,11 @@ impl_from_kcl_for_vec!(TyF64);
|
||||
|
||||
impl<'a> FromKclValue<'a> for SourceRange {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let KclValue::MixedArray { value, meta: _ } = arg else {
|
||||
return None;
|
||||
let value = match arg {
|
||||
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
if value.len() != 3 {
|
||||
return None;
|
||||
@ -1334,7 +1336,7 @@ impl<'a> FromKclValue<'a> for TyF64 {
|
||||
impl<'a> FromKclValue<'a> for [TyF64; 2] {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
|
||||
KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
|
||||
if value.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
@ -1351,7 +1353,7 @@ impl<'a> FromKclValue<'a> for [TyF64; 2] {
|
||||
impl<'a> FromKclValue<'a> for [TyF64; 3] {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
|
||||
KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
|
||||
if value.len() != 3 {
|
||||
return None;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
kcl_value::{FunctionSource, KclValue},
|
||||
types::RuntimeType,
|
||||
ExecState,
|
||||
},
|
||||
source_range::SourceRange,
|
||||
@ -19,9 +20,11 @@ use crate::{
|
||||
pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let array: Vec<KclValue> = args.get_unlabeled_kw_arg("array")?;
|
||||
let f: &FunctionSource = args.get_kw_arg("f")?;
|
||||
let meta = vec![args.source_range.into()];
|
||||
let new_array = inner_map(array, f, exec_state, &args).await?;
|
||||
Ok(KclValue::MixedArray { value: new_array, meta })
|
||||
Ok(KclValue::HomArray {
|
||||
value: new_array,
|
||||
ty: RuntimeType::any(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Apply a function to every element of a list.
|
||||
@ -257,6 +260,31 @@ 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")?;
|
||||
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 {
|
||||
source_ranges: meta,
|
||||
message: format!("You can't push to a value of type {actual_type}, only an array"),
|
||||
}));
|
||||
};
|
||||
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 })
|
||||
}
|
||||
|
||||
/// Append an element to the end of an array.
|
||||
///
|
||||
/// Returns a new array with the element appended.
|
||||
@ -276,28 +304,26 @@ async fn call_reduce_closure(
|
||||
},
|
||||
tags = ["array"]
|
||||
}]
|
||||
async fn inner_push(mut array: Vec<KclValue>, item: KclValue, args: &Args) -> Result<KclValue, KclError> {
|
||||
fn inner_push(mut array: Vec<KclValue>, item: KclValue) -> Vec<KclValue> {
|
||||
array.push(item);
|
||||
Ok(KclValue::MixedArray {
|
||||
value: array,
|
||||
meta: vec![args.source_range.into()],
|
||||
})
|
||||
|
||||
array
|
||||
}
|
||||
|
||||
pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
// Extract the array and the element from the arguments
|
||||
let val: KclValue = args.get_unlabeled_kw_arg("array")?;
|
||||
let item = args.get_kw_arg("item")?;
|
||||
|
||||
let meta = vec![args.source_range];
|
||||
let KclValue::MixedArray { value: array, meta: _ } = val else {
|
||||
let actual_type = val.human_friendly_type();
|
||||
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 {
|
||||
source_ranges: meta,
|
||||
message: format!("You can't push to a value of type {actual_type}, only an array"),
|
||||
message: format!("You can't pop from a value of type {actual_type}, only an array"),
|
||||
}));
|
||||
};
|
||||
inner_push(array, item, &args).await
|
||||
|
||||
let new_array = inner_pop(values, &args)?;
|
||||
|
||||
Ok(KclValue::HomArray { value: new_array, ty })
|
||||
}
|
||||
|
||||
/// Remove the last element from an array.
|
||||
@ -320,7 +346,7 @@ pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
},
|
||||
tags = ["array"]
|
||||
}]
|
||||
async fn inner_pop(array: Vec<KclValue>, args: &Args) -> Result<KclValue, KclError> {
|
||||
fn inner_pop(array: Vec<KclValue>, args: &Args) -> Result<Vec<KclValue>, KclError> {
|
||||
if array.is_empty() {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Cannot pop from an empty array".to_string(),
|
||||
@ -331,24 +357,5 @@ async fn inner_pop(array: Vec<KclValue>, args: &Args) -> Result<KclValue, KclErr
|
||||
// Create a new array with all elements except the last one
|
||||
let new_array = array[..array.len() - 1].to_vec();
|
||||
|
||||
Ok(KclValue::MixedArray {
|
||||
value: new_array,
|
||||
meta: vec![args.source_range.into()],
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn pop(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
// Extract the array from the arguments
|
||||
let val = args.get_unlabeled_kw_arg("array")?;
|
||||
|
||||
let meta = vec![args.source_range];
|
||||
let KclValue::MixedArray { value: array, meta: _ } = val else {
|
||||
let actual_type = val.human_friendly_type();
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: meta,
|
||||
message: format!("You can't pop from a value of type {actual_type}, only an array"),
|
||||
}));
|
||||
};
|
||||
|
||||
inner_pop(array, &args).await
|
||||
Ok(new_array)
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ async fn make_transform<T: GeometryTrait>(
|
||||
})?;
|
||||
let transforms = match transform_fn_return {
|
||||
KclValue::Object { value, meta: _ } => vec![value],
|
||||
KclValue::MixedArray { value, meta: _ } => {
|
||||
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
|
||||
let transforms: Vec<_> = value
|
||||
.into_iter()
|
||||
.map(|val| {
|
||||
@ -671,12 +671,44 @@ impl GeometryTrait for Solid {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::execution::types::NumericType;
|
||||
use crate::execution::types::{NumericType, PrimitiveType};
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_array_to_point3d() {
|
||||
let mut exec_state = ExecState::new(&ExecutorContext::new_mock().await);
|
||||
let input = KclValue::MixedArray {
|
||||
let input = KclValue::HomArray {
|
||||
value: vec![
|
||||
KclValue::Number {
|
||||
value: 1.1,
|
||||
meta: Default::default(),
|
||||
ty: NumericType::mm(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 2.2,
|
||||
meta: Default::default(),
|
||||
ty: NumericType::mm(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 3.3,
|
||||
meta: Default::default(),
|
||||
ty: NumericType::mm(),
|
||||
},
|
||||
],
|
||||
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
|
||||
};
|
||||
let expected = [
|
||||
TyF64::new(1.1, NumericType::mm()),
|
||||
TyF64::new(2.2, NumericType::mm()),
|
||||
TyF64::new(3.3, NumericType::mm()),
|
||||
];
|
||||
let actual = array_to_point3d(&input, Vec::new(), &mut exec_state);
|
||||
assert_eq!(actual.unwrap(), expected);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_tuple_to_point3d() {
|
||||
let mut exec_state = ExecState::new(&ExecutorContext::new_mock().await);
|
||||
let input = KclValue::Tuple {
|
||||
value: vec![
|
||||
KclValue::Number {
|
||||
value: 1.1,
|
||||
|
@ -17,9 +17,9 @@ use crate::{
|
||||
/// Returns the point at the end of the given segment.
|
||||
pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
|
||||
let result = inner_segment_end(&tag, exec_state, args.clone())?;
|
||||
let pt = inner_segment_end(&tag, exec_state, args.clone())?;
|
||||
|
||||
args.make_user_val_from_point(result)
|
||||
args.make_kcl_val_from_point([pt[0].n, pt[1].n], pt[0].ty.clone())
|
||||
}
|
||||
|
||||
/// Compute the ending point of the provided line segment.
|
||||
@ -64,8 +64,11 @@ fn inner_segment_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?;
|
||||
let (p, ty) = path.end_point_components();
|
||||
// Docs generation isn't smart enough to handle ([f64; 2], NumericType).
|
||||
let point = [TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)];
|
||||
|
||||
Ok(path.get_to().clone())
|
||||
Ok(point)
|
||||
}
|
||||
|
||||
/// Returns the segment end of x.
|
||||
@ -156,9 +159,9 @@ fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
|
||||
/// Returns the point at the start of the given segment.
|
||||
pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
|
||||
let result = inner_segment_start(&tag, exec_state, args.clone())?;
|
||||
let pt = inner_segment_start(&tag, exec_state, args.clone())?;
|
||||
|
||||
args.make_user_val_from_point(result)
|
||||
args.make_kcl_val_from_point([pt[0].n, pt[1].n], pt[0].ty.clone())
|
||||
}
|
||||
|
||||
/// Compute the starting point of the provided line segment.
|
||||
@ -203,8 +206,11 @@ fn inner_segment_start(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?;
|
||||
let (p, ty) = path.start_point_components();
|
||||
// Docs generation isn't smart enough to handle ([f64; 2], NumericType).
|
||||
let point = [TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)];
|
||||
|
||||
Ok(path.get_from().to_owned())
|
||||
Ok(point)
|
||||
}
|
||||
|
||||
/// Returns the segment start of x.
|
||||
|
Reference in New Issue
Block a user