Implement coercion of numeric types for ascription and arithmetic (off by default) (#6175)

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-04-07 16:13:15 +12:00
committed by GitHub
parent ce7a967f5f
commit e7b23e1638
72 changed files with 2789 additions and 1604 deletions

View File

@ -8,7 +8,7 @@ use kcmc::{
};
use kittycad_modeling_cmds as kcmc;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use crate::{
errors::{KclError, KclErrorDetails},
@ -72,7 +72,7 @@ impl KwArgs {
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct TyF64 {
@ -92,7 +92,7 @@ impl TyF64 {
}
}
pub fn map(mut self, n: f64) -> Self {
pub fn map_value(mut self, n: f64) -> Self {
self.n = n;
self
}
@ -209,7 +209,7 @@ impl Args {
}));
};
let arg = arg.value.coerce(ty, exec_state).ok_or_else(|| {
let arg = arg.value.coerce(ty, exec_state).map_err(|_| {
let actual_type_name = arg.value.human_friendly_type();
let msg_base = format!(
"This function expected the input argument to be {} but it's actually of type {actual_type_name}",
@ -332,7 +332,7 @@ impl Args {
message: format!("This function requires a value for the special unlabeled first parameter, '{label}'"),
}))?;
let arg = arg.value.coerce(ty, exec_state).ok_or_else(|| {
let arg = arg.value.coerce(ty, exec_state).map_err(|_| {
let actual_type_name = arg.value.human_friendly_type();
let msg_base = format!(
"This function expected the input argument to be {} but it's actually of type {actual_type_name}",
@ -523,7 +523,7 @@ impl Args {
})
}
pub(crate) fn make_user_val_from_f64(&self, f: f64) -> KclValue {
pub(super) fn make_user_val_from_f64(&self, f: f64) -> KclValue {
KclValue::from_number(
f,
vec![Metadata {
@ -532,7 +532,7 @@ impl Args {
)
}
pub(crate) fn make_user_val_from_f64_with_type(&self, f: TyF64) -> KclValue {
pub(super) fn make_user_val_from_f64_with_type(&self, f: TyF64) -> KclValue {
KclValue::from_number_with_type(
f.n,
f.ty,
@ -542,7 +542,7 @@ impl Args {
)
}
pub(crate) fn make_user_val_from_f64_array(&self, f: Vec<f64>, ty: &NumericType) -> Result<KclValue, KclError> {
pub(super) fn make_user_val_from_f64_array(&self, f: Vec<f64>, ty: &NumericType) -> Result<KclValue, KclError> {
let array = f
.into_iter()
.map(|n| KclValue::Number {
@ -616,8 +616,7 @@ impl Args {
let mut numbers = numbers.into_iter();
let a = numbers.next().unwrap();
let b = numbers.next().unwrap();
let ty = a.ty.combine_eq(&b.ty);
Ok((a.n, b.n, ty))
Ok(NumericType::combine_eq(a, b))
}
pub(crate) fn get_sketches(&self, exec_state: &mut ExecState) -> Result<(Vec<Sketch>, Sketch), KclError> {
@ -627,16 +626,15 @@ impl Args {
source_ranges: vec![self.source_range],
}));
};
let sarg = arg0
.value
.coerce(&RuntimeType::sketches(), exec_state)
.ok_or(KclError::Type(KclErrorDetails {
let sarg = arg0.value.coerce(&RuntimeType::sketches(), exec_state).map_err(|_| {
KclError::Type(KclErrorDetails {
message: format!(
"Expected an array of sketches, found {}",
arg0.value.human_friendly_type()
),
source_ranges: vec![self.source_range],
}))?;
})
})?;
let sketches = match sarg {
KclValue::HomArray { value, .. } => value.iter().map(|v| v.as_sketch().unwrap().clone()).collect(),
_ => unreachable!(),
@ -651,10 +649,12 @@ impl Args {
let sarg = arg1
.value
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
.ok_or(KclError::Type(KclErrorDetails {
message: format!("Expected a sketch, found {}", arg1.value.human_friendly_type()),
source_ranges: vec![self.source_range],
}))?;
.map_err(|_| {
KclError::Type(KclErrorDetails {
message: format!("Expected a sketch, found {}", arg1.value.human_friendly_type()),
source_ranges: vec![self.source_range],
})
})?;
let sketch = match sarg {
KclValue::Sketch { value } => *value,
_ => unreachable!(),
@ -673,10 +673,12 @@ impl Args {
let sarg = arg0
.value
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
.ok_or(KclError::Type(KclErrorDetails {
message: format!("Expected a sketch, found {}", arg0.value.human_friendly_type()),
source_ranges: vec![self.source_range],
}))?;
.map_err(|_| {
KclError::Type(KclErrorDetails {
message: format!("Expected a sketch, found {}", arg0.value.human_friendly_type()),
source_ranges: vec![self.source_range],
})
})?;
match sarg {
KclValue::Sketch { value } => Ok(*value),
_ => unreachable!(),
@ -718,13 +720,15 @@ impl Args {
let sarg = arg1
.value
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
.ok_or(KclError::Type(KclErrorDetails {
message: format!(
"Expected a sketch for second argument, found {}",
arg1.value.human_friendly_type()
),
source_ranges: vec![self.source_range],
}))?;
.map_err(|_| {
KclError::Type(KclErrorDetails {
message: format!(
"Expected a sketch for second argument, found {}",
arg1.value.human_friendly_type()
),
source_ranges: vec![self.source_range],
})
})?;
let sketch = match sarg {
KclValue::Sketch { value } => *value,
_ => unreachable!(),
@ -754,13 +758,15 @@ impl Args {
let sarg = arg1
.value
.coerce(&RuntimeType::Primitive(PrimitiveType::Solid), exec_state)
.ok_or(KclError::Type(KclErrorDetails {
message: format!(
"Expected a solid for second argument, found {}",
arg1.value.human_friendly_type()
),
source_ranges: vec![self.source_range],
}))?;
.map_err(|_| {
KclError::Type(KclErrorDetails {
message: format!(
"Expected a solid for second argument, found {}",
arg1.value.human_friendly_type()
),
source_ranges: vec![self.source_range],
})
})?;
let solid = match sarg {
KclValue::Solid { value } => value,
_ => unreachable!(),