Move axes to std constants; move helix, revolve, and mirror2d to be declared in KCL (#6105)

Move axes to std constants; move helix, revolve, and mirror2d to be declated in KCL

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-04-03 22:44:52 +13:00
committed by GitHub
parent 3e4505e2e3
commit aad583be2e
167 changed files with 11811 additions and 38646 deletions

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use async_recursion::async_recursion;
use indexmap::IndexMap;
use super::kcl_value::TypeDef;
use super::{kcl_value::TypeDef, types::PrimitiveType};
use crate::{
engine::ExecutionKind,
errors::{KclError, KclErrorDetails},
@ -1059,6 +1059,15 @@ impl Node<UnaryExpression> {
}
let value = &self.argument.get_result(exec_state, ctx).await?;
let err = || {
KclError::Semantic(KclErrorDetails {
message: format!(
"You can only negate numbers, planes, or lines, but this is a {}",
value.human_friendly_type()
),
source_ranges: vec![self.into()],
})
};
match value {
KclValue::Number { value, ty, .. } => {
let meta = vec![Metadata {
@ -1080,13 +1089,63 @@ impl Node<UnaryExpression> {
plane.id = exec_state.next_uuid();
Ok(KclValue::Plane { value: plane })
}
_ => Err(KclError::Semantic(KclErrorDetails {
message: format!(
"You can only negate numbers or planes, but this is a {}",
value.human_friendly_type()
),
source_ranges: vec![self.into()],
})),
KclValue::Object { value: values, meta } => {
// Special-case for negating line-like objects.
let Some(direction) = values.get("direction") else {
return Err(err());
};
let direction = match direction {
KclValue::MixedArray { value: values, meta } => {
let values = values
.iter()
.map(|v| match v {
KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
value: *value * -1.0,
ty: ty.clone(),
meta: meta.clone(),
}),
_ => Err(err()),
})
.collect::<Result<Vec<_>, _>>()?;
KclValue::MixedArray {
value: values,
meta: meta.clone(),
}
}
KclValue::HomArray {
value: values,
ty: ty @ RuntimeType::Primitive(PrimitiveType::Number(_)),
} => {
let values = values
.iter()
.map(|v| match v {
KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
value: *value * -1.0,
ty: ty.clone(),
meta: meta.clone(),
}),
_ => Err(err()),
})
.collect::<Result<Vec<_>, _>>()?;
KclValue::HomArray {
value: values,
ty: ty.clone(),
}
}
_ => return Err(err()),
};
let mut value = values.clone();
value.insert("direction".to_owned(), direction);
Ok(KclValue::Object {
value,
meta: meta.clone(),
})
}
_ => Err(err()),
}
}
}
@ -1272,27 +1331,6 @@ impl Node<CallExpressionKw> {
// exec_state.
let func = fn_name.get_result(exec_state, ctx).await?.clone();
if !ctx.is_isolated_execution().await {
// Track call operation.
let op_labeled_args = args
.kw_args
.labeled
.iter()
.map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
.collect();
exec_state.global.operations.push(Operation::UserDefinedFunctionCall {
name: Some(fn_name.to_string()),
function_source_range: func.function_def_source_range().unwrap_or_default(),
unlabeled_arg: args
.kw_args
.unlabeled
.as_ref()
.map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)),
labeled_args: op_labeled_args,
source_range: callsite,
});
}
let Some(fn_src) = func.as_fn() else {
return Err(KclError::Semantic(KclErrorDetails {
message: "cannot call this because it isn't a function".to_string(),
@ -1300,11 +1338,19 @@ impl Node<CallExpressionKw> {
}));
};
let return_value = fn_src.call_kw(exec_state, ctx, args, callsite).await.map_err(|e| {
// Add the call expression to the source ranges.
// TODO currently ignored by the frontend
e.add_source_ranges(vec![callsite])
})?;
let return_value = fn_src
.call_kw(Some(fn_name.to_string()), exec_state, ctx, args, callsite)
.await
.map_err(|e| {
// Add the call expression to the source ranges.
// TODO currently ignored by the frontend
e.add_source_ranges(vec![callsite])
})?;
if matches!(fn_src, FunctionSource::User { .. }) && !ctx.is_isolated_execution().await {
// Track return operation.
exec_state.global.operations.push(Operation::UserDefinedFunctionReturn);
}
let result = return_value.ok_or_else(move || {
let mut source_ranges: Vec<SourceRange> = vec![callsite];
@ -1318,11 +1364,6 @@ impl Node<CallExpressionKw> {
})
})?;
if !ctx.is_isolated_execution().await {
// Track return operation.
exec_state.global.operations.push(Operation::UserDefinedFunctionReturn);
}
Ok(result)
}
}
@ -1434,11 +1475,14 @@ impl Node<CallExpression> {
source_ranges: vec![source_range],
}));
};
let return_value = fn_src.call(exec_state, ctx, fn_args, source_range).await.map_err(|e| {
// Add the call expression to the source ranges.
// TODO currently ignored by the frontend
e.add_source_ranges(vec![source_range])
})?;
let return_value = fn_src
.call(Some(fn_name.to_string()), exec_state, ctx, fn_args, source_range)
.await
.map_err(|e| {
// Add the call expression to the source ranges.
// TODO currently ignored by the frontend
e.add_source_ranges(vec![source_range])
})?;
let result = return_value.ok_or_else(move || {
let mut source_ranges: Vec<SourceRange> = vec![source_range];
@ -2064,6 +2108,7 @@ async fn call_user_defined_function_kw(
impl FunctionSource {
pub async fn call(
&self,
fn_name: Option<String>,
exec_state: &mut ExecState,
ctx: &ExecutorContext,
mut args: Vec<Arg>,
@ -2081,7 +2126,7 @@ impl FunctionSource {
ctx.clone(),
exec_state.mod_local.pipe_value.clone().map(|v| Arg::new(v, callsite)),
);
self.call_kw(exec_state, ctx, args, callsite).await
self.call_kw(fn_name, exec_state, ctx, args, callsite).await
} else {
Err(KclError::Semantic(KclErrorDetails {
message: format!("{} requires its arguments to be labelled", props.name),
@ -2098,6 +2143,7 @@ impl FunctionSource {
pub async fn call_kw(
&self,
fn_name: Option<String>,
exec_state: &mut ExecState,
ctx: &ExecutorContext,
mut args: crate::std::Args,
@ -2181,6 +2227,26 @@ impl FunctionSource {
}
}
let op = if props.include_in_feature_tree && !ctx.is_isolated_execution().await {
let op_labeled_args = args
.kw_args
.labeled
.iter()
.map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
.collect();
Some(Operation::KclStdLibCall {
name: fn_name.unwrap_or_default(),
unlabeled_arg: args
.unlabeled_kw_arg_unconverted()
.map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)),
labeled_args: op_labeled_args,
source_range: callsite,
is_error: false,
})
} else {
None
};
// Attempt to call the function.
exec_state.mut_stack().push_new_env_for_rust_call();
let mut result = {
@ -2188,7 +2254,15 @@ impl FunctionSource {
let result = func(exec_state, args).await;
exec_state.mut_stack().pop_env();
// TODO support recording op into the feature tree
if let Some(mut op) = op {
op.set_std_lib_call_is_error(result.is_err());
// Track call operation. We do this after the call
// since things like patternTransform may call user code
// before running, and we will likely want to use the
// return value. The call takes ownership of the args,
// so we need to build the op before the call.
exec_state.global.operations.push(op);
}
result
}?;
@ -2197,6 +2271,27 @@ impl FunctionSource {
Ok(Some(result))
}
FunctionSource::User { ast, memory, .. } => {
if !ctx.is_isolated_execution().await {
// Track call operation.
let op_labeled_args = args
.kw_args
.labeled
.iter()
.map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
.collect();
exec_state.global.operations.push(Operation::UserDefinedFunctionCall {
name: fn_name,
function_source_range: ast.as_source_range(),
unlabeled_arg: args
.kw_args
.unlabeled
.as_ref()
.map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)),
labeled_args: op_labeled_args,
source_range: callsite,
});
}
call_user_defined_function_kw(args.kw_args, *memory, ast, exec_state, ctx).await
}
FunctionSource::None => unreachable!(),