Merge remote-tracking branch 'origin/main' into jess/cleaned-imports
* origin/main: Quick app rename typo fix in settings.md (#6198) Add point-and-click Insert from local project files (#6129) Install and start Vector on macOS CI runners (#6147) Implement polar std function in KCL (#6180) Bump typescript from 5.8.2 to 5.8.3 in /packages/codemirror-lsp-client in the patch group (#6188) Bump @types/node from 22.13.13 to 22.14.0 in /packages/codemirror-lsp-client in the minor group (#6189) Bump the major group in /packages/codemirror-lang-kcl with 2 updates (#6194) Bump taiki-e/install-action from 2.49.30 to 2.49.45 in the patch group (#6185) Bump the patch group with 6 updates (#6186) Bump the patch group in /rust/kcl-language-server with 3 updates (#6183) Bump the patch group in /packages/codemirror-lang-kcl with 2 updates (#6193) Remove unnecessary timeouts waiting for command bar (#6199) Stream handling / Stream idle mode v2; a ton of network related changes (ping; scene indicator -> stream indicator, stream resizing (even on pause)) (#5312) More propagation of numeric types (#6177) Apply type-directed coercions to arguments in calls of user functions (#6179) Erase comment start positions from snapshot tests (#6178) Implement coercion of numeric types for ascription and arithmetic (off by default) (#6175) Reduce the number of reps in the add_lots test (#6174) take things off the batch in a more safe way (#6171)
This commit is contained in:
@ -709,7 +709,7 @@ fn add_to_types(
|
||||
return Err(anyhow::anyhow!("Empty type name"));
|
||||
}
|
||||
|
||||
if DECLARED_TYPES.contains(&name) {
|
||||
if DECLARED_TYPES.contains(&name) || name == "TyF64" {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -769,7 +769,7 @@ fn generate_type(
|
||||
}
|
||||
|
||||
// Skip over TagDeclarator and TagIdentifier since they have custom docs.
|
||||
if name == "TagDeclarator" || name == "TagIdentifier" || name == "TagNode" {
|
||||
if name == "TagDeclarator" || name == "TagIdentifier" || name == "TagNode" || name == "TyF64" {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -930,7 +930,7 @@ fn recurse_and_create_references(
|
||||
schema: &schemars::schema::Schema,
|
||||
types: &BTreeMap<String, schemars::schema::Schema>,
|
||||
) -> Result<schemars::schema::Schema> {
|
||||
if DECLARED_TYPES.contains(&name) {
|
||||
if DECLARED_TYPES.contains(&name) || name == "TyF64" {
|
||||
return Ok(schema.clone());
|
||||
}
|
||||
|
||||
@ -944,7 +944,7 @@ fn recurse_and_create_references(
|
||||
if let Some(reference) = &o.reference {
|
||||
let mut obj = o.clone();
|
||||
let reference = reference.trim_start_matches("#/components/schemas/");
|
||||
if DECLARED_TYPES.contains(&reference) {
|
||||
if DECLARED_TYPES.contains(&reference) || reference == "TyF64" {
|
||||
return Ok(schema.clone());
|
||||
}
|
||||
|
||||
|
@ -67,23 +67,25 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
/// Get the batch of commands to be sent to the engine.
|
||||
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>;
|
||||
|
||||
async fn take_batch(&self) -> Vec<(WebSocketRequest, SourceRange)> {
|
||||
std::mem::take(&mut *self.batch().write().await)
|
||||
}
|
||||
|
||||
/// Get the batch of end commands to be sent to the engine.
|
||||
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>;
|
||||
|
||||
async fn take_batch_end(&self) -> IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)> {
|
||||
std::mem::take(&mut *self.batch_end().write().await)
|
||||
}
|
||||
|
||||
/// Get the command responses from the engine.
|
||||
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>;
|
||||
|
||||
/// Get the artifact commands that have accumulated so far.
|
||||
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>>;
|
||||
|
||||
/// Take the batch of commands that have accumulated so far and clear them.
|
||||
async fn take_batch(&self) -> Vec<(WebSocketRequest, SourceRange)> {
|
||||
std::mem::take(&mut *self.batch().write().await)
|
||||
}
|
||||
|
||||
/// Take the batch of end commands that have accumulated so far and clear them.
|
||||
async fn take_batch_end(&self) -> IndexMap<Uuid, (WebSocketRequest, SourceRange)> {
|
||||
std::mem::take(&mut *self.batch_end().write().await)
|
||||
}
|
||||
|
||||
/// Clear all artifact commands that have accumulated so far.
|
||||
async fn clear_artifact_commands(&self) {
|
||||
self.artifact_commands().write().await.clear();
|
||||
@ -411,7 +413,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
}
|
||||
}
|
||||
|
||||
// Throw away the old batch queue.
|
||||
self.stats().batches_sent.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
// We pop off the responses to cleanup our mappings.
|
||||
|
@ -120,7 +120,7 @@ impl From<KclErrorWithOutputs> for KclError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, Serialize, Deserialize, ts_rs::TS, Clone, PartialEq)]
|
||||
#[derive(Error, Debug, Serialize, ts_rs::TS, Clone, PartialEq)]
|
||||
#[error("{error}")]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -3,11 +3,11 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{types::NumericType, ArtifactId, KclValue};
|
||||
use crate::{docs::StdLibFn, std::get_stdlib_fn, ModuleId, SourceRange};
|
||||
use crate::{docs::StdLibFn, ModuleId, SourceRange};
|
||||
|
||||
/// A CAD modeling operation for display in the feature tree, AKA operations
|
||||
/// timeline.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export_to = "Operation.ts")]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Operation {
|
||||
@ -60,7 +60,7 @@ impl Operation {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export_to = "Operation.ts")]
|
||||
#[serde(tag = "type")]
|
||||
#[expect(clippy::large_enum_variant)]
|
||||
@ -90,7 +90,7 @@ pub enum Group {
|
||||
}
|
||||
|
||||
/// An argument to a CAD modeling operation.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export_to = "Operation.ts")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OpArg {
|
||||
@ -110,7 +110,7 @@ impl OpArg {
|
||||
|
||||
/// A reference to a standard library function. This exists to implement
|
||||
/// `PartialEq` and `Eq` for `Operation`.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export_to = "Operation.ts")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StdLibFnRef {
|
||||
@ -156,25 +156,13 @@ where
|
||||
serializer.serialize_str(&name)
|
||||
}
|
||||
|
||||
fn std_lib_fn_from_name<'de, D>(deserializer: D) -> Result<Box<dyn StdLibFn>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
if let Some(std_lib_fn) = get_stdlib_fn(&s) {
|
||||
Ok(std_lib_fn)
|
||||
} else {
|
||||
Err(serde::de::Error::custom(format!("not a KCL stdlib function: {}", s)))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_false(b: &bool) -> bool {
|
||||
!*b
|
||||
}
|
||||
|
||||
/// A KCL value used in Operations. `ArtifactId`s are used to refer to the
|
||||
/// actual scene objects. Any data not needed in the UI may be omitted.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export_to = "Operation.ts")]
|
||||
#[serde(tag = "type")]
|
||||
pub enum OpKclValue {
|
||||
|
@ -3,7 +3,11 @@ use std::collections::HashMap;
|
||||
use async_recursion::async_recursion;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use super::{cad_op::Group, kcl_value::TypeDef, types::PrimitiveType};
|
||||
use super::{
|
||||
cad_op::Group,
|
||||
kcl_value::TypeDef,
|
||||
types::{PrimitiveType, CHECK_NUMERIC_TYPES},
|
||||
};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
@ -25,7 +29,7 @@ use crate::{
|
||||
},
|
||||
source_range::SourceRange,
|
||||
std::{
|
||||
args::{Arg, KwArgs},
|
||||
args::{Arg, KwArgs, TyF64},
|
||||
FunctionKind,
|
||||
},
|
||||
CompilationError,
|
||||
@ -681,14 +685,14 @@ impl ExecutorContext {
|
||||
let result = self
|
||||
.execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
|
||||
.await?;
|
||||
coerce(&result, &expr.ty, exec_state, expr.into())?
|
||||
apply_ascription(&result, &expr.ty, exec_state, expr.into())?
|
||||
}
|
||||
};
|
||||
Ok(item)
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce(
|
||||
fn apply_ascription(
|
||||
value: &KclValue,
|
||||
ty: &Node<Type>,
|
||||
exec_state: &mut ExecState,
|
||||
@ -697,7 +701,24 @@ fn coerce(
|
||||
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
|
||||
.map_err(|e| KclError::Semantic(e.into()))?;
|
||||
|
||||
value.coerce(&ty, exec_state).ok_or_else(|| {
|
||||
if let KclValue::Number {
|
||||
ty: NumericType::Unknown,
|
||||
value,
|
||||
meta,
|
||||
} = value
|
||||
{
|
||||
// If the number has unknown units but the user is explicitly specifying them, treat the value as having had it's units erased,
|
||||
// rather than forcing the user to explicitly erase them.
|
||||
KclValue::Number {
|
||||
ty: NumericType::Any,
|
||||
value: *value,
|
||||
meta: meta.clone(),
|
||||
}
|
||||
.coerce(&ty, exec_state)
|
||||
} else {
|
||||
value.coerce(&ty, exec_state)
|
||||
}
|
||||
.map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("could not coerce {} value to type {}", value.human_friendly_type(), ty),
|
||||
source_ranges: vec![source_range],
|
||||
@ -953,69 +974,85 @@ impl Node<BinaryExpression> {
|
||||
return Ok(KclValue::Bool { value: raw_value, meta });
|
||||
}
|
||||
|
||||
let (left, lty) = parse_number_as_f64(&left_value, self.left.clone().into())?;
|
||||
let (right, rty) = parse_number_as_f64(&right_value, self.right.clone().into())?;
|
||||
let left = number_as_f64(&left_value, self.left.clone().into())?;
|
||||
let right = number_as_f64(&right_value, self.right.clone().into())?;
|
||||
|
||||
let value = match self.operator {
|
||||
BinaryOperator::Add => KclValue::Number {
|
||||
value: left + right,
|
||||
meta,
|
||||
ty: NumericType::combine_add(lty, rty),
|
||||
},
|
||||
BinaryOperator::Sub => KclValue::Number {
|
||||
value: left - right,
|
||||
meta,
|
||||
ty: NumericType::combine_add(lty, rty),
|
||||
},
|
||||
BinaryOperator::Mul => KclValue::Number {
|
||||
value: left * right,
|
||||
meta,
|
||||
ty: NumericType::combine_mul(lty, rty),
|
||||
},
|
||||
BinaryOperator::Div => KclValue::Number {
|
||||
value: left / right,
|
||||
meta,
|
||||
ty: NumericType::combine_div(lty, rty),
|
||||
},
|
||||
BinaryOperator::Mod => KclValue::Number {
|
||||
value: left % right,
|
||||
meta,
|
||||
ty: NumericType::combine_div(lty, rty),
|
||||
},
|
||||
BinaryOperator::Add => {
|
||||
let (l, r, ty) = NumericType::combine_eq(left, right);
|
||||
self.warn_on_unknown(&ty, "Adding", exec_state);
|
||||
KclValue::Number { value: l + r, meta, ty }
|
||||
}
|
||||
BinaryOperator::Sub => {
|
||||
let (l, r, ty) = NumericType::combine_eq(left, right);
|
||||
self.warn_on_unknown(&ty, "Subtracting", exec_state);
|
||||
KclValue::Number { value: l - r, meta, ty }
|
||||
}
|
||||
BinaryOperator::Mul => {
|
||||
let (l, r, ty) = NumericType::combine_mul(left, right);
|
||||
self.warn_on_unknown(&ty, "Multiplying", exec_state);
|
||||
KclValue::Number { value: l * r, meta, ty }
|
||||
}
|
||||
BinaryOperator::Div => {
|
||||
let (l, r, ty) = NumericType::combine_div(left, right);
|
||||
self.warn_on_unknown(&ty, "Dividing", exec_state);
|
||||
KclValue::Number { value: l / r, meta, ty }
|
||||
}
|
||||
BinaryOperator::Mod => {
|
||||
let (l, r, ty) = NumericType::combine_div(left, right);
|
||||
self.warn_on_unknown(&ty, "Modulo of", exec_state);
|
||||
KclValue::Number { value: l % r, meta, ty }
|
||||
}
|
||||
BinaryOperator::Pow => KclValue::Number {
|
||||
value: left.powf(right),
|
||||
value: left.n.powf(right.n),
|
||||
meta,
|
||||
ty: NumericType::Unknown,
|
||||
},
|
||||
BinaryOperator::Neq => KclValue::Bool {
|
||||
value: left != right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Gt => KclValue::Bool {
|
||||
value: left > right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Gte => KclValue::Bool {
|
||||
value: left >= right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Lt => KclValue::Bool {
|
||||
value: left < right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Lte => KclValue::Bool {
|
||||
value: left <= right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Eq => KclValue::Bool {
|
||||
value: left == right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Neq => {
|
||||
let (l, r, ty) = NumericType::combine_eq(left, right);
|
||||
self.warn_on_unknown(&ty, "Comparing", exec_state);
|
||||
KclValue::Bool { value: l != r, meta }
|
||||
}
|
||||
BinaryOperator::Gt => {
|
||||
let (l, r, ty) = NumericType::combine_eq(left, right);
|
||||
self.warn_on_unknown(&ty, "Comparing", exec_state);
|
||||
KclValue::Bool { value: l > r, meta }
|
||||
}
|
||||
BinaryOperator::Gte => {
|
||||
let (l, r, ty) = NumericType::combine_eq(left, right);
|
||||
self.warn_on_unknown(&ty, "Comparing", exec_state);
|
||||
KclValue::Bool { value: l >= r, meta }
|
||||
}
|
||||
BinaryOperator::Lt => {
|
||||
let (l, r, ty) = NumericType::combine_eq(left, right);
|
||||
self.warn_on_unknown(&ty, "Comparing", exec_state);
|
||||
KclValue::Bool { value: l < r, meta }
|
||||
}
|
||||
BinaryOperator::Lte => {
|
||||
let (l, r, ty) = NumericType::combine_eq(left, right);
|
||||
self.warn_on_unknown(&ty, "Comparing", exec_state);
|
||||
KclValue::Bool { value: l <= r, meta }
|
||||
}
|
||||
BinaryOperator::Eq => {
|
||||
let (l, r, ty) = NumericType::combine_eq(left, right);
|
||||
self.warn_on_unknown(&ty, "Comparing", exec_state);
|
||||
KclValue::Bool { value: l == r, meta }
|
||||
}
|
||||
BinaryOperator::And | BinaryOperator::Or => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn warn_on_unknown(&self, ty: &NumericType, verb: &str, exec_state: &mut ExecState) {
|
||||
if *CHECK_NUMERIC_TYPES && ty == &NumericType::Unknown {
|
||||
// TODO suggest how to fix this
|
||||
exec_state.warn(CompilationError::err(
|
||||
self.as_source_range(),
|
||||
format!("{} numbers which have unknown or incompatible units.", verb),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<UnaryExpression> {
|
||||
@ -1731,21 +1768,15 @@ fn article_for(s: &str) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<(f64, NumericType), KclError> {
|
||||
if let KclValue::Number { value: n, ty, .. } = &v {
|
||||
Ok((*n, ty.clone()))
|
||||
} else {
|
||||
fn number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<TyF64, KclError> {
|
||||
v.as_ty_f64().ok_or_else(|| {
|
||||
let actual_type = v.human_friendly_type();
|
||||
let article = if actual_type.starts_with(['a', 'e', 'i', 'o', 'u']) {
|
||||
"an"
|
||||
} else {
|
||||
"a"
|
||||
};
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
let article = article_for(actual_type);
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: vec![source_range],
|
||||
message: format!("Expected a number, but found {article} {actual_type}",),
|
||||
}))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
impl Node<IfExpression> {
|
||||
@ -1947,32 +1978,102 @@ fn assign_args_to_params(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_args_to_params_kw(
|
||||
fn type_check_params_kw(
|
||||
fn_name: Option<&str>,
|
||||
function_expression: NodeRef<'_, FunctionExpression>,
|
||||
mut args: crate::std::args::KwArgs,
|
||||
args: &mut crate::std::args::KwArgs,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<(), KclError> {
|
||||
for (label, arg) in &args.labeled {
|
||||
for (label, arg) in &mut args.labeled {
|
||||
match function_expression.params.iter().find(|p| &p.identifier.name == label) {
|
||||
Some(p) => {
|
||||
if !p.labeled {
|
||||
exec_state.err(CompilationError::err(
|
||||
arg.source_range,
|
||||
format!(
|
||||
"This function expects an unlabeled first parameter (`{label}`), but it is labelled in the call"
|
||||
"{} expects an unlabeled first parameter (`{label}`), but it is labelled in the call",
|
||||
fn_name
|
||||
.map(|n| format!("The function `{}`", n))
|
||||
.unwrap_or_else(|| "This function".to_owned()),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(ty) = &p.type_ {
|
||||
arg.value = arg
|
||||
.value
|
||||
.coerce(
|
||||
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
|
||||
exec_state,
|
||||
)
|
||||
.map_err(|e| {
|
||||
let mut message = format!(
|
||||
"{label} requires a value with type `{}`, but found {}",
|
||||
ty.inner,
|
||||
arg.value.human_friendly_type(),
|
||||
);
|
||||
if let Some(ty) = e.explicit_coercion {
|
||||
// TODO if we have access to the AST for the argument we could choose which example to suggest.
|
||||
message = format!("{message}\n\nYou may need to add information about the type of the argument, for example:\n using a numeric suffix: `42{ty}`\n or using type ascription: `foo(): number({ty})`");
|
||||
}
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message,
|
||||
source_ranges: vec![arg.source_range],
|
||||
})
|
||||
})?;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
exec_state.err(CompilationError::err(
|
||||
arg.source_range,
|
||||
format!("`{label}` is not an argument of this function"),
|
||||
format!(
|
||||
"`{label}` is not an argument of {}",
|
||||
fn_name
|
||||
.map(|n| format!("`{}`", n))
|
||||
.unwrap_or_else(|| "this function".to_owned()),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(arg) = &mut args.unlabeled {
|
||||
if let Some(p) = function_expression.params.iter().find(|p| !p.labeled) {
|
||||
if let Some(ty) = &p.type_ {
|
||||
arg.value = arg
|
||||
.value
|
||||
.coerce(
|
||||
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
|
||||
exec_state,
|
||||
)
|
||||
.map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"The input argument of {} requires a value with type `{}`, but found {}",
|
||||
fn_name
|
||||
.map(|n| format!("`{}`", n))
|
||||
.unwrap_or_else(|| "this function".to_owned()),
|
||||
ty.inner,
|
||||
arg.value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![arg.source_range],
|
||||
})
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_args_to_params_kw(
|
||||
fn_name: Option<&str>,
|
||||
function_expression: NodeRef<'_, FunctionExpression>,
|
||||
mut args: crate::std::args::KwArgs,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<(), KclError> {
|
||||
type_check_params_kw(fn_name, function_expression, &mut args, exec_state)?;
|
||||
|
||||
// Add the arguments to the memory. A new call frame should have already
|
||||
// been created.
|
||||
let source_ranges = vec![function_expression.into()];
|
||||
@ -2059,6 +2160,7 @@ async fn call_user_defined_function(
|
||||
}
|
||||
|
||||
async fn call_user_defined_function_kw(
|
||||
fn_name: Option<&str>,
|
||||
args: crate::std::args::KwArgs,
|
||||
memory: EnvironmentRef,
|
||||
function_expression: NodeRef<'_, FunctionExpression>,
|
||||
@ -2069,7 +2171,7 @@ async fn call_user_defined_function_kw(
|
||||
// variables shadow variables in the parent scope. The new environment's
|
||||
// parent should be the environment of the closure.
|
||||
exec_state.mut_stack().push_new_env_for_call(memory);
|
||||
if let Err(e) = assign_args_to_params_kw(function_expression, args, exec_state) {
|
||||
if let Err(e) = assign_args_to_params_kw(fn_name, function_expression, args, exec_state) {
|
||||
exec_state.mut_stack().pop_env();
|
||||
return Err(e);
|
||||
}
|
||||
@ -2147,47 +2249,7 @@ impl FunctionSource {
|
||||
));
|
||||
}
|
||||
|
||||
for (label, arg) in &mut args.kw_args.labeled {
|
||||
match ast.params.iter().find(|p| &p.identifier.name == label) {
|
||||
Some(p) => {
|
||||
if !p.labeled {
|
||||
exec_state.err(CompilationError::err(
|
||||
arg.source_range,
|
||||
format!(
|
||||
"The function `{}` expects an unlabeled first parameter (`{label}`), but it is labelled in the call",
|
||||
props.name
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(ty) = &p.type_ {
|
||||
arg.value = arg
|
||||
.value
|
||||
.coerce(
|
||||
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range)
|
||||
.unwrap(),
|
||||
exec_state,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"{label} requires a value with type `{}`, but found {}",
|
||||
ty.inner,
|
||||
arg.value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![callsite],
|
||||
})
|
||||
})?;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
exec_state.err(CompilationError::err(
|
||||
arg.source_range,
|
||||
format!("`{label}` is not an argument of `{}`", props.name),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
type_check_params_kw(Some(&props.name), ast, &mut args.kw_args, exec_state)?;
|
||||
|
||||
if let Some(arg) = &mut args.kw_args.unlabeled {
|
||||
if let Some(p) = ast.params.iter().find(|p| !p.labeled) {
|
||||
@ -2198,13 +2260,13 @@ impl FunctionSource {
|
||||
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
|
||||
exec_state,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
.map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"The input argument of {} requires a value with type `{}`, but found {}",
|
||||
props.name,
|
||||
ty.inner,
|
||||
arg.value.human_friendly_type()
|
||||
arg.value.human_friendly_type(),
|
||||
),
|
||||
source_ranges: vec![callsite],
|
||||
})
|
||||
@ -2266,7 +2328,7 @@ impl FunctionSource {
|
||||
.collect();
|
||||
exec_state.global.operations.push(Operation::GroupBegin {
|
||||
group: Group::FunctionCall {
|
||||
name: fn_name,
|
||||
name: fn_name.clone(),
|
||||
function_source_range: ast.as_source_range(),
|
||||
unlabeled_arg: args
|
||||
.kw_args
|
||||
@ -2278,7 +2340,7 @@ impl FunctionSource {
|
||||
source_range: callsite,
|
||||
});
|
||||
|
||||
call_user_defined_function_kw(args.kw_args, *memory, ast, exec_state, ctx).await
|
||||
call_user_defined_function_kw(fn_name.as_deref(), args.kw_args, *memory, ast, exec_state, ctx).await
|
||||
}
|
||||
FunctionSource::None => unreachable!(),
|
||||
}
|
||||
@ -2603,4 +2665,25 @@ d = b + c
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn user_coercion() {
|
||||
let program = r#"fn foo(x: Axis2d) {
|
||||
return 0
|
||||
}
|
||||
|
||||
foo(x = { direction = [0, 0], origin = [0, 0]})
|
||||
"#;
|
||||
|
||||
parse_execute(program).await.unwrap();
|
||||
|
||||
let program = r#"fn foo(x: Axis3d) {
|
||||
return 0
|
||||
}
|
||||
|
||||
foo(x = { direction = [0, 0], origin = [0, 0]})
|
||||
"#;
|
||||
|
||||
parse_execute(program).await.unwrap_err();
|
||||
}
|
||||
}
|
||||
|
@ -10,16 +10,16 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{ArtifactId, ExecState, Metadata, TagEngineInfo, TagIdentifier, UnitLen},
|
||||
execution::{types::NumericType, ArtifactId, ExecState, Metadata, TagEngineInfo, TagIdentifier, UnitLen},
|
||||
parsing::ast::types::{Node, NodeRef, TagDeclarator, TagNode},
|
||||
std::sketch::PlaneData,
|
||||
std::{args::TyF64, sketch::PlaneData},
|
||||
};
|
||||
|
||||
type Point2D = kcmc::shared::Point2d<f64>;
|
||||
type Point3D = kcmc::shared::Point3d<f64>;
|
||||
|
||||
/// A geometry.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Geometry {
|
||||
@ -47,7 +47,7 @@ impl Geometry {
|
||||
}
|
||||
|
||||
/// A set of geometry.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
#[allow(clippy::vec_box)]
|
||||
@ -66,7 +66,7 @@ impl From<Geometry> for Geometries {
|
||||
}
|
||||
|
||||
/// Data for an imported geometry.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImportedGeometry {
|
||||
@ -79,7 +79,7 @@ pub struct ImportedGeometry {
|
||||
}
|
||||
|
||||
/// Data for a solid or an imported geometry.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
#[allow(clippy::vec_box)]
|
||||
@ -159,7 +159,7 @@ pub struct Helix {
|
||||
pub meta: Vec<Metadata>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Plane {
|
||||
@ -353,7 +353,7 @@ impl Plane {
|
||||
}
|
||||
|
||||
/// A face.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Face {
|
||||
@ -377,7 +377,7 @@ pub struct Face {
|
||||
}
|
||||
|
||||
/// Type for a plane.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display)]
|
||||
#[ts(export)]
|
||||
#[display(style = "camelCase")]
|
||||
pub enum PlaneType {
|
||||
@ -398,7 +398,7 @@ pub enum PlaneType {
|
||||
Uninit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub struct Sketch {
|
||||
@ -463,7 +463,7 @@ impl Sketch {
|
||||
}
|
||||
|
||||
/// A sketch type.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub enum SketchSurface {
|
||||
@ -582,7 +582,7 @@ impl Sketch {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub struct Solid {
|
||||
@ -616,7 +616,7 @@ impl Solid {
|
||||
}
|
||||
|
||||
/// A fillet or a chamfer.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub enum EdgeCut {
|
||||
@ -678,6 +678,12 @@ impl From<[f64; 2]> for Point2d {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[TyF64; 2]> for Point2d {
|
||||
fn from(p: [TyF64; 2]) -> Self {
|
||||
Self { x: p[0].n, y: p[1].n }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[f64; 2]> for Point2d {
|
||||
fn from(p: &[f64; 2]) -> Self {
|
||||
Self { x: p[0], y: p[1] }
|
||||
@ -767,7 +773,7 @@ impl Mul<f64> for Point3d {
|
||||
}
|
||||
|
||||
/// A base path.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BasePath {
|
||||
@ -786,7 +792,7 @@ pub struct BasePath {
|
||||
}
|
||||
|
||||
/// Geometry metadata.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GeoMeta {
|
||||
@ -798,7 +804,7 @@ pub struct GeoMeta {
|
||||
}
|
||||
|
||||
/// A path.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Path {
|
||||
@ -979,19 +985,23 @@ impl Path {
|
||||
}
|
||||
|
||||
/// Where does this path segment start?
|
||||
pub fn get_from(&self) -> &[f64; 2] {
|
||||
&self.get_base().from
|
||||
pub fn get_from(&self) -> [TyF64; 2] {
|
||||
let p = &self.get_base().from;
|
||||
let ty: NumericType = self.get_base().units.into();
|
||||
[TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)]
|
||||
}
|
||||
/// Where does this path segment end?
|
||||
pub fn get_to(&self) -> &[f64; 2] {
|
||||
&self.get_base().to
|
||||
pub fn get_to(&self) -> [TyF64; 2] {
|
||||
let p = &self.get_base().to;
|
||||
let ty: NumericType = self.get_base().units.into();
|
||||
[TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)]
|
||||
}
|
||||
|
||||
/// Length of this path segment, in cartesian plane.
|
||||
pub fn length(&self) -> f64 {
|
||||
match self {
|
||||
pub fn length(&self) -> TyF64 {
|
||||
let n = match self {
|
||||
Self::ToPoint { .. } | Self::Base { .. } | Self::Horizontal { .. } | Self::AngledLineTo { .. } => {
|
||||
linear_distance(self.get_from(), self.get_to())
|
||||
linear_distance(&self.get_base().from, &self.get_base().to)
|
||||
}
|
||||
Self::TangentialArc {
|
||||
base: _,
|
||||
@ -1005,10 +1015,10 @@ impl Path {
|
||||
} => {
|
||||
// The radius can be calculated as the linear distance between `to` and `center`,
|
||||
// or between `from` and `center`. They should be the same.
|
||||
let radius = linear_distance(self.get_from(), center);
|
||||
debug_assert_eq!(radius, linear_distance(self.get_to(), center));
|
||||
let radius = linear_distance(&self.get_base().from, center);
|
||||
debug_assert_eq!(radius, linear_distance(&self.get_base().to, center));
|
||||
// TODO: Call engine utils to figure this out.
|
||||
linear_distance(self.get_from(), self.get_to())
|
||||
linear_distance(&self.get_base().from, &self.get_base().to)
|
||||
}
|
||||
Self::Circle { radius, .. } => 2.0 * std::f64::consts::PI * radius,
|
||||
Self::CircleThreePoint { .. } => {
|
||||
@ -1022,13 +1032,14 @@ impl Path {
|
||||
}
|
||||
Self::Arc { .. } => {
|
||||
// TODO: Call engine utils to figure this out.
|
||||
linear_distance(self.get_from(), self.get_to())
|
||||
linear_distance(&self.get_base().from, &self.get_base().to)
|
||||
}
|
||||
Self::ArcThreePoint { .. } => {
|
||||
// TODO: Call engine utils to figure this out.
|
||||
linear_distance(self.get_from(), self.get_to())
|
||||
linear_distance(&self.get_base().from, &self.get_base().to)
|
||||
}
|
||||
}
|
||||
};
|
||||
TyF64::new(n, self.get_base().units.into())
|
||||
}
|
||||
|
||||
pub fn get_base_mut(&mut self) -> Option<&mut BasePath> {
|
||||
@ -1103,7 +1114,7 @@ fn linear_distance(
|
||||
}
|
||||
|
||||
/// An extrude surface.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub enum ExtrudeSurface {
|
||||
@ -1115,7 +1126,7 @@ pub enum ExtrudeSurface {
|
||||
}
|
||||
|
||||
// Chamfer surface.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChamferSurface {
|
||||
@ -1129,7 +1140,7 @@ pub struct ChamferSurface {
|
||||
}
|
||||
|
||||
// Fillet surface.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FilletSurface {
|
||||
@ -1143,7 +1154,7 @@ pub struct FilletSurface {
|
||||
}
|
||||
|
||||
/// An extruded plane.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExtrudePlane {
|
||||
@ -1157,7 +1168,7 @@ pub struct ExtrudePlane {
|
||||
}
|
||||
|
||||
/// An extruded arc.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExtrudeArc {
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||
parsing::ast::types::{
|
||||
DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node, TagDeclarator, TagNode,
|
||||
},
|
||||
std::StdFnProps,
|
||||
std::{args::TyF64, StdFnProps},
|
||||
CompilationError, KclError, ModuleId, SourceRange,
|
||||
};
|
||||
|
||||
@ -495,6 +495,7 @@ impl KclValue {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_f64(&self) -> Option<f64> {
|
||||
if let KclValue::Number { value, .. } = &self {
|
||||
Some(*value)
|
||||
@ -503,6 +504,14 @@ impl KclValue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_ty_f64(&self) -> Option<TyF64> {
|
||||
if let KclValue::Number { value, ty, .. } = &self {
|
||||
Some(TyF64::new(*value, ty.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bool(&self) -> Option<bool> {
|
||||
if let KclValue::Bool { value, meta: _ } = &self {
|
||||
Some(*value)
|
||||
|
@ -175,7 +175,7 @@ impl std::hash::Hash for TagIdentifier {
|
||||
}
|
||||
|
||||
/// Engine information for a tag.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub struct TagEngineInfo {
|
||||
|
@ -22,6 +22,8 @@ use crate::{
|
||||
CompilationError,
|
||||
};
|
||||
|
||||
use super::types::NumericType;
|
||||
|
||||
/// State for executing a program.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExecState {
|
||||
@ -232,6 +234,13 @@ impl ExecState {
|
||||
self.global.module_infos.get(&id)
|
||||
}
|
||||
|
||||
pub fn current_default_units(&self) -> NumericType {
|
||||
NumericType::Default {
|
||||
len: self.length_unit(),
|
||||
angle: self.angle_unit(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn length_unit(&self) -> UnitLen {
|
||||
self.mod_local.settings.default_length_units
|
||||
}
|
||||
|
@ -14,10 +14,20 @@ use crate::{
|
||||
ast::types::{PrimitiveType as AstPrimitiveType, Type},
|
||||
token::NumericSuffix,
|
||||
},
|
||||
std::args::FromKclValue,
|
||||
std::args::{FromKclValue, TyF64},
|
||||
CompilationError, SourceRange,
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub(super) static ref CHECK_NUMERIC_TYPES: bool = {
|
||||
let env_var = std::env::var("ZOO_NUM_TYS");
|
||||
let Ok(env_var) = env_var else {
|
||||
return false;
|
||||
};
|
||||
!env_var.is_empty()
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RuntimeType {
|
||||
Primitive(PrimitiveType),
|
||||
@ -337,7 +347,7 @@ impl fmt::Display for PrimitiveType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum NumericType {
|
||||
@ -351,62 +361,94 @@ pub enum NumericType {
|
||||
Any,
|
||||
}
|
||||
|
||||
impl Default for NumericType {
|
||||
fn default() -> Self {
|
||||
NumericType::Default {
|
||||
len: UnitLen::default(),
|
||||
angle: UnitAngle::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NumericType {
|
||||
pub fn count() -> Self {
|
||||
pub const fn count() -> Self {
|
||||
NumericType::Known(UnitType::Count)
|
||||
}
|
||||
|
||||
pub fn mm() -> Self {
|
||||
pub const fn mm() -> Self {
|
||||
NumericType::Known(UnitType::Length(UnitLen::Mm))
|
||||
}
|
||||
|
||||
pub const fn radians() -> Self {
|
||||
NumericType::Known(UnitType::Angle(UnitAngle::Radians))
|
||||
}
|
||||
|
||||
pub const fn degrees() -> Self {
|
||||
NumericType::Known(UnitType::Angle(UnitAngle::Degrees))
|
||||
}
|
||||
|
||||
/// Combine two types when we expect them to be equal.
|
||||
pub fn combine_eq(self, other: &NumericType) -> NumericType {
|
||||
if &self == other {
|
||||
self
|
||||
} else {
|
||||
NumericType::Unknown
|
||||
}
|
||||
}
|
||||
pub fn combine_eq(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
|
||||
use NumericType::*;
|
||||
match (a.ty, b.ty) {
|
||||
(at, bt) if at == bt => (a.n, b.n, at),
|
||||
(at, Any) => (a.n, b.n, at),
|
||||
(Any, bt) => (a.n, b.n, bt),
|
||||
(Default { .. }, Default { .. }) | (_, Unknown) | (Unknown, _) => (a.n, b.n, Unknown),
|
||||
|
||||
/// Combine n types when we expect them to be equal.
|
||||
///
|
||||
/// Precondition: tys.len() > 0
|
||||
pub fn combine_n_eq(tys: &[NumericType]) -> NumericType {
|
||||
let ty0 = tys[0].clone();
|
||||
for t in &tys[1..] {
|
||||
if t != &ty0 {
|
||||
return NumericType::Unknown;
|
||||
// Known types and compatible, but needs adjustment.
|
||||
(t @ Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => (a.n, l2.adjust_to(b.n, l1), t),
|
||||
(t @ Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => (a.n, a2.adjust_to(b.n, a1), t),
|
||||
|
||||
// Known but incompatible.
|
||||
(Known(_), Known(_)) => (a.n, b.n, Unknown),
|
||||
|
||||
// Known and unknown => we assume the known one, possibly with adjustment
|
||||
(Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
|
||||
(a.n, b.n, Known(UnitType::Count))
|
||||
}
|
||||
|
||||
(t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) => (a.n, l2.adjust_to(b.n, l1), t),
|
||||
(Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) => (l1.adjust_to(a.n, l2), b.n, t),
|
||||
|
||||
(t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) => (a.n, a2.adjust_to(b.n, a1), t),
|
||||
(Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) => (a1.adjust_to(a.n, a2), b.n, t),
|
||||
}
|
||||
ty0
|
||||
}
|
||||
|
||||
/// Combine two types in addition-like operations.
|
||||
pub fn combine_add(a: NumericType, b: NumericType) -> NumericType {
|
||||
if a == b {
|
||||
return a;
|
||||
/// Combine two types for multiplication-like operations.
|
||||
pub fn combine_mul(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
|
||||
use NumericType::*;
|
||||
match (a.ty, b.ty) {
|
||||
(at @ Default { .. }, bt @ Default { .. }) if at != bt => (a.n, b.n, Unknown),
|
||||
(Known(UnitType::Count) | Default { .. }, bt) => (a.n, b.n, bt),
|
||||
(at, Known(UnitType::Count) | Default { .. }) => (a.n, b.n, at),
|
||||
(Any, Any) => (a.n, b.n, Any),
|
||||
_ => (a.n, b.n, Unknown),
|
||||
}
|
||||
NumericType::Unknown
|
||||
}
|
||||
|
||||
/// Combine two types in multiplication-like operations.
|
||||
pub fn combine_mul(a: NumericType, b: NumericType) -> NumericType {
|
||||
if a == NumericType::count() {
|
||||
return b;
|
||||
/// Combine two types for division-like operations.
|
||||
pub fn combine_div(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
|
||||
use NumericType::*;
|
||||
match (a.ty, b.ty) {
|
||||
(at, bt) if at == bt => (a.n, b.n, Known(UnitType::Count)),
|
||||
(Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
|
||||
(at, Known(UnitType::Count) | Default { .. } | Any) => (a.n, b.n, at),
|
||||
(Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => {
|
||||
(a.n, l2.adjust_to(b.n, l1), Known(UnitType::Count))
|
||||
}
|
||||
(Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => {
|
||||
(a.n, a2.adjust_to(b.n, a1), Known(UnitType::Count))
|
||||
}
|
||||
(Default { len: l1, .. }, Known(UnitType::Length(l2))) => {
|
||||
(l1.adjust_to(a.n, l2), b.n, Known(UnitType::Count))
|
||||
}
|
||||
(Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => {
|
||||
(a1.adjust_to(a.n, a2), b.n, Known(UnitType::Count))
|
||||
}
|
||||
_ => (a.n, b.n, Unknown),
|
||||
}
|
||||
if b == NumericType::count() {
|
||||
return a;
|
||||
}
|
||||
NumericType::Unknown
|
||||
}
|
||||
|
||||
/// Combine two types in division-like operations.
|
||||
pub fn combine_div(a: NumericType, b: NumericType) -> NumericType {
|
||||
if b == NumericType::count() {
|
||||
return a;
|
||||
}
|
||||
NumericType::Unknown
|
||||
}
|
||||
|
||||
pub fn from_parsed(suffix: NumericSuffix, settings: &super::MetaSettings) -> Self {
|
||||
@ -431,12 +473,104 @@ impl NumericType {
|
||||
use NumericType::*;
|
||||
|
||||
match (self, other) {
|
||||
(Unknown, _) | (_, Unknown) => false,
|
||||
(a, b) if a == b => true,
|
||||
(_, Any) => true,
|
||||
(a, b) if a == b => true,
|
||||
(Unknown, _) | (_, Unknown) => false,
|
||||
(_, _) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn example_ty(&self) -> Option<String> {
|
||||
match self {
|
||||
Self::Known(t) => Some(t.to_string()),
|
||||
Self::Default { len, .. } => Some(len.to_string()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce(&self, val: &KclValue) -> Result<KclValue, CoercionError> {
|
||||
let KclValue::Number { value, ty, meta } = val else {
|
||||
return Err(val.into());
|
||||
};
|
||||
|
||||
if !*CHECK_NUMERIC_TYPES {
|
||||
return Ok(val.clone());
|
||||
}
|
||||
|
||||
if ty.subtype(self) {
|
||||
return Ok(KclValue::Number {
|
||||
value: *value,
|
||||
ty: ty.clone(),
|
||||
meta: meta.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
// Not subtypes, but might be able to coerce
|
||||
use NumericType::*;
|
||||
match (ty, self) {
|
||||
// We don't have enough information to coerce.
|
||||
(Unknown, _) => Err(CoercionError::from(val).with_explicit(self.example_ty().unwrap_or("mm".to_owned()))),
|
||||
(_, Unknown) => Err(val.into()),
|
||||
(Any, _) => Ok(KclValue::Number {
|
||||
value: *value,
|
||||
ty: self.clone(),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
|
||||
// We don't actually need to coerce, since we just keep the partially-known type with the value.
|
||||
(Default { .. }, Default { .. }) => Ok(KclValue::Number {
|
||||
value: *value,
|
||||
ty: ty.clone(),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
|
||||
// Known types and compatible, but needs adjustment.
|
||||
(Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => Ok(KclValue::Number {
|
||||
value: l1.adjust_to(*value, *l2),
|
||||
ty: self.clone(),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
(Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => Ok(KclValue::Number {
|
||||
value: a1.adjust_to(*value, *a2),
|
||||
ty: self.clone(),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
|
||||
// Known but incompatible.
|
||||
(Known(_), Known(_)) => Err(val.into()),
|
||||
|
||||
// Known and unknown => we assume the rhs, possibly with adjustment
|
||||
(Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
|
||||
Ok(KclValue::Number {
|
||||
value: *value,
|
||||
ty: Known(UnitType::Count),
|
||||
meta: meta.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
(Known(UnitType::Length(l1)), Default { len: l2, .. })
|
||||
| (Default { len: l1, .. }, Known(UnitType::Length(l2))) => Ok(KclValue::Number {
|
||||
value: l1.adjust_to(*value, *l2),
|
||||
ty: Known(UnitType::Length(*l2)),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
|
||||
(Known(UnitType::Angle(a1)), Default { angle: a2, .. })
|
||||
| (Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => Ok(KclValue::Number {
|
||||
value: a1.adjust_to(*value, *a2),
|
||||
ty: Known(UnitType::Angle(*a2)),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
|
||||
(_, _) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NumericType> for RuntimeType {
|
||||
fn from(t: NumericType) -> RuntimeType {
|
||||
RuntimeType::Primitive(PrimitiveType::Number(t))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UnitLen> for NumericType {
|
||||
@ -485,6 +619,39 @@ pub enum UnitLen {
|
||||
Yards,
|
||||
}
|
||||
|
||||
impl UnitLen {
|
||||
fn adjust_to(self, value: f64, to: UnitLen) -> f64 {
|
||||
if self == to {
|
||||
return value;
|
||||
}
|
||||
|
||||
use UnitLen::*;
|
||||
let (base, base_unit) = match self {
|
||||
Mm => (value, Mm),
|
||||
Cm => (value * 10.0, Mm),
|
||||
M => (value * 1000.0, Mm),
|
||||
Inches => (value, Inches),
|
||||
Feet => (value * 12.0, Inches),
|
||||
Yards => (value * 36.0, Inches),
|
||||
};
|
||||
let (base, base_unit) = match (base_unit, to) {
|
||||
(Mm, Inches) | (Mm, Feet) | (Mm, Yards) => (base / 25.4, Inches),
|
||||
(Inches, Mm) | (Inches, Cm) | (Inches, M) => (base * 25.4, Mm),
|
||||
_ => (base, base_unit),
|
||||
};
|
||||
|
||||
match (base_unit, to) {
|
||||
(Mm, Mm) => base,
|
||||
(Mm, Cm) => base / 10.0,
|
||||
(Mm, M) => base / 1000.0,
|
||||
(Inches, Inches) => base,
|
||||
(Inches, Feet) => base / 12.0,
|
||||
(Inches, Yards) => base / 36.0,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UnitLen {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@ -563,6 +730,19 @@ pub enum UnitAngle {
|
||||
Radians,
|
||||
}
|
||||
|
||||
impl UnitAngle {
|
||||
fn adjust_to(self, value: f64, to: UnitAngle) -> f64 {
|
||||
use std::f64::consts::PI;
|
||||
use UnitAngle::*;
|
||||
match (self, to) {
|
||||
(Degrees, Degrees) => value,
|
||||
(Degrees, Radians) => (value / 180.0) * PI,
|
||||
(Radians, Degrees) => 180.0 * value / PI,
|
||||
(Radians, Radians) => value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UnitAngle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@ -584,6 +764,28 @@ impl TryFrom<NumericSuffix> for UnitAngle {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CoercionError {
|
||||
pub found: Option<RuntimeType>,
|
||||
pub explicit_coercion: Option<String>,
|
||||
}
|
||||
|
||||
impl CoercionError {
|
||||
fn with_explicit(mut self, c: String) -> Self {
|
||||
self.explicit_coercion = Some(c);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'_ KclValue> for CoercionError {
|
||||
fn from(value: &'_ KclValue) -> Self {
|
||||
CoercionError {
|
||||
found: value.principal_type(),
|
||||
explicit_coercion: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KclValue {
|
||||
/// True if `self` has a type which is a subtype of `ty` without coercion.
|
||||
pub fn has_type(&self, ty: &RuntimeType) -> bool {
|
||||
@ -600,7 +802,7 @@ impl KclValue {
|
||||
/// - result.principal_type().unwrap().subtype(ty)
|
||||
///
|
||||
/// If self.principal_type() == ty then result == self
|
||||
pub fn coerce(&self, ty: &RuntimeType, exec_state: &mut ExecState) -> Option<KclValue> {
|
||||
pub fn coerce(&self, ty: &RuntimeType, exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
|
||||
match ty {
|
||||
RuntimeType::Primitive(ty) => self.coerce_to_primitive_type(ty, exec_state),
|
||||
RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, *len, exec_state, false),
|
||||
@ -610,40 +812,52 @@ impl KclValue {
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce_to_primitive_type(&self, ty: &PrimitiveType, exec_state: &mut ExecState) -> Option<KclValue> {
|
||||
fn coerce_to_primitive_type(
|
||||
&self,
|
||||
ty: &PrimitiveType,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<KclValue, CoercionError> {
|
||||
let value = match self {
|
||||
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } if value.len() == 1 => &value[0],
|
||||
_ => self,
|
||||
};
|
||||
match ty {
|
||||
// TODO numeric type coercions
|
||||
PrimitiveType::Number(_ty) => match value {
|
||||
KclValue::Number { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
},
|
||||
PrimitiveType::Number(ty) => ty.coerce(value),
|
||||
PrimitiveType::String => match value {
|
||||
KclValue::String { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
KclValue::String { .. } => Ok(value.clone()),
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Boolean => match value {
|
||||
KclValue::Bool { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
KclValue::Bool { .. } => Ok(value.clone()),
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Sketch => match value {
|
||||
KclValue::Sketch { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
KclValue::Sketch { .. } => Ok(value.clone()),
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Solid => match value {
|
||||
KclValue::Solid { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
KclValue::Solid { .. } => Ok(value.clone()),
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Plane => match value {
|
||||
KclValue::Plane { .. } => Some(value.clone()),
|
||||
KclValue::Plane { .. } => Ok(value.clone()),
|
||||
KclValue::Object { value, meta } => {
|
||||
let origin = value.get("origin").and_then(Point3d::from_kcl_val)?;
|
||||
let x_axis = value.get("xAxis").and_then(Point3d::from_kcl_val)?;
|
||||
let y_axis = value.get("yAxis").and_then(Point3d::from_kcl_val)?;
|
||||
let z_axis = value.get("zAxis").and_then(Point3d::from_kcl_val)?;
|
||||
let origin = value
|
||||
.get("origin")
|
||||
.and_then(Point3d::from_kcl_val)
|
||||
.ok_or(CoercionError::from(self))?;
|
||||
let x_axis = value
|
||||
.get("xAxis")
|
||||
.and_then(Point3d::from_kcl_val)
|
||||
.ok_or(CoercionError::from(self))?;
|
||||
let y_axis = value
|
||||
.get("yAxis")
|
||||
.and_then(Point3d::from_kcl_val)
|
||||
.ok_or(CoercionError::from(self))?;
|
||||
let z_axis = value
|
||||
.get("zAxis")
|
||||
.and_then(Point3d::from_kcl_val)
|
||||
.ok_or(CoercionError::from(self))?;
|
||||
|
||||
let id = exec_state.mod_local.id_generator.next_uuid();
|
||||
let plane = Plane {
|
||||
@ -659,75 +873,87 @@ impl KclValue {
|
||||
meta: meta.clone(),
|
||||
};
|
||||
|
||||
Some(KclValue::Plane { value: Box::new(plane) })
|
||||
Ok(KclValue::Plane { value: Box::new(plane) })
|
||||
}
|
||||
_ => None,
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Face => match value {
|
||||
KclValue::Face { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
KclValue::Face { .. } => Ok(value.clone()),
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Helix => match value {
|
||||
KclValue::Helix { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
KclValue::Helix { .. } => Ok(value.clone()),
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Edge => match value {
|
||||
KclValue::Uuid { .. } => Some(value.clone()),
|
||||
KclValue::TagIdentifier { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
KclValue::Uuid { .. } => Ok(value.clone()),
|
||||
KclValue::TagIdentifier { .. } => Ok(value.clone()),
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Axis2d => match value {
|
||||
KclValue::Object { value: values, meta } => {
|
||||
if values.get("origin")?.has_type(&RuntimeType::point2d())
|
||||
&& values.get("direction")?.has_type(&RuntimeType::point2d())
|
||||
if values
|
||||
.get("origin")
|
||||
.ok_or(CoercionError::from(self))?
|
||||
.has_type(&RuntimeType::point2d())
|
||||
&& values
|
||||
.get("direction")
|
||||
.ok_or(CoercionError::from(self))?
|
||||
.has_type(&RuntimeType::point2d())
|
||||
{
|
||||
return Some(value.clone());
|
||||
return Ok(value.clone());
|
||||
}
|
||||
|
||||
let origin = values.get("origin").and_then(|p| {
|
||||
let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(2), exec_state, true)
|
||||
})?;
|
||||
let direction = values.get("direction").and_then(|p| {
|
||||
let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(2), exec_state, true)
|
||||
})?;
|
||||
|
||||
Some(KclValue::Object {
|
||||
Ok(KclValue::Object {
|
||||
value: [("origin".to_owned(), origin), ("direction".to_owned(), direction)].into(),
|
||||
meta: meta.clone(),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Axis3d => match value {
|
||||
KclValue::Object { value: values, meta } => {
|
||||
if values.get("origin")?.has_type(&RuntimeType::point3d())
|
||||
&& values.get("direction")?.has_type(&RuntimeType::point3d())
|
||||
if values
|
||||
.get("origin")
|
||||
.ok_or(CoercionError::from(self))?
|
||||
.has_type(&RuntimeType::point3d())
|
||||
&& values
|
||||
.get("direction")
|
||||
.ok_or(CoercionError::from(self))?
|
||||
.has_type(&RuntimeType::point3d())
|
||||
{
|
||||
return Some(value.clone());
|
||||
return Ok(value.clone());
|
||||
}
|
||||
|
||||
let origin = values.get("origin").and_then(|p| {
|
||||
let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(3), exec_state, true)
|
||||
})?;
|
||||
let direction = values.get("direction").and_then(|p| {
|
||||
let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(3), exec_state, true)
|
||||
})?;
|
||||
|
||||
Some(KclValue::Object {
|
||||
Ok(KclValue::Object {
|
||||
value: [("origin".to_owned(), origin), ("direction".to_owned(), direction)].into(),
|
||||
meta: meta.clone(),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::ImportedGeometry => match value {
|
||||
KclValue::ImportedGeometry { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
KclValue::ImportedGeometry { .. } => Ok(value.clone()),
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Tag => match value {
|
||||
KclValue::TagDeclarator { .. } => Some(value.clone()),
|
||||
KclValue::TagIdentifier { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
KclValue::TagDeclarator { .. } => Ok(value.clone()),
|
||||
KclValue::TagIdentifier { .. } => Ok(value.clone()),
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -738,37 +964,40 @@ impl KclValue {
|
||||
len: ArrayLen,
|
||||
exec_state: &mut ExecState,
|
||||
allow_shrink: bool,
|
||||
) -> Option<KclValue> {
|
||||
) -> Result<KclValue, CoercionError> {
|
||||
match self {
|
||||
KclValue::HomArray { value, ty: aty } if aty.subtype(ty) => {
|
||||
len.satisfied(value.len(), allow_shrink).map(|len| KclValue::HomArray {
|
||||
KclValue::HomArray { value, ty: aty } if aty.subtype(ty) => len
|
||||
.satisfied(value.len(), allow_shrink)
|
||||
.map(|len| KclValue::HomArray {
|
||||
value: value[..len].to_vec(),
|
||||
ty: aty.clone(),
|
||||
})
|
||||
}
|
||||
value if len.satisfied(1, false).is_some() && value.has_type(ty) => Some(KclValue::HomArray {
|
||||
.ok_or(self.into()),
|
||||
value if len.satisfied(1, false).is_some() && value.has_type(ty) => Ok(KclValue::HomArray {
|
||||
value: vec![value.clone()],
|
||||
ty: ty.clone(),
|
||||
}),
|
||||
KclValue::MixedArray { value, .. } => {
|
||||
let len = len.satisfied(value.len(), allow_shrink)?;
|
||||
let len = len
|
||||
.satisfied(value.len(), allow_shrink)
|
||||
.ok_or(CoercionError::from(self))?;
|
||||
|
||||
let value = value[..len]
|
||||
.iter()
|
||||
.map(|v| v.coerce(ty, exec_state))
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Some(KclValue::HomArray { value, ty: ty.clone() })
|
||||
Ok(KclValue::HomArray { value, ty: ty.clone() })
|
||||
}
|
||||
KclValue::KclNone { .. } if len.satisfied(0, false).is_some() => Some(KclValue::HomArray {
|
||||
KclValue::KclNone { .. } if len.satisfied(0, false).is_some() => Ok(KclValue::HomArray {
|
||||
value: Vec::new(),
|
||||
ty: ty.clone(),
|
||||
}),
|
||||
_ => None,
|
||||
_ => Err(self.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce_to_tuple_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Option<KclValue> {
|
||||
fn coerce_to_tuple_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
|
||||
match self {
|
||||
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } if value.len() == tys.len() => {
|
||||
let mut result = Vec::new();
|
||||
@ -776,50 +1005,54 @@ impl KclValue {
|
||||
result.push(value[i].coerce(t, exec_state)?);
|
||||
}
|
||||
|
||||
Some(KclValue::MixedArray {
|
||||
Ok(KclValue::MixedArray {
|
||||
value: result,
|
||||
meta: Vec::new(),
|
||||
})
|
||||
}
|
||||
KclValue::KclNone { meta, .. } if tys.is_empty() => Some(KclValue::MixedArray {
|
||||
KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::MixedArray {
|
||||
value: Vec::new(),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
value if tys.len() == 1 && value.has_type(&tys[0]) => Some(KclValue::MixedArray {
|
||||
value if tys.len() == 1 && value.has_type(&tys[0]) => Ok(KclValue::MixedArray {
|
||||
value: vec![value.clone()],
|
||||
meta: Vec::new(),
|
||||
}),
|
||||
_ => None,
|
||||
_ => Err(self.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce_to_union_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Option<KclValue> {
|
||||
fn coerce_to_union_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
|
||||
for t in tys {
|
||||
if let Some(v) = self.coerce(t, exec_state) {
|
||||
return Some(v);
|
||||
if let Ok(v) = self.coerce(t, exec_state) {
|
||||
return Ok(v);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
Err(self.into())
|
||||
}
|
||||
|
||||
fn coerce_to_object_type(&self, tys: &[(String, RuntimeType)], _exec_state: &mut ExecState) -> Option<KclValue> {
|
||||
fn coerce_to_object_type(
|
||||
&self,
|
||||
tys: &[(String, RuntimeType)],
|
||||
_exec_state: &mut ExecState,
|
||||
) -> Result<KclValue, CoercionError> {
|
||||
match self {
|
||||
KclValue::Object { value, .. } => {
|
||||
for (s, t) in tys {
|
||||
// TODO coerce fields
|
||||
if !value.get(s)?.has_type(t) {
|
||||
return None;
|
||||
if !value.get(s).ok_or(CoercionError::from(self))?.has_type(t) {
|
||||
return Err(self.into());
|
||||
}
|
||||
}
|
||||
// TODO remove non-required fields
|
||||
Some(self.clone())
|
||||
Ok(self.clone())
|
||||
}
|
||||
KclValue::KclNone { meta, .. } if tys.is_empty() => Some(KclValue::Object {
|
||||
KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::Object {
|
||||
value: HashMap::new(),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
_ => None,
|
||||
_ => Err(self.into()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -859,6 +1092,8 @@ impl KclValue {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::execution::{parse_execute, ExecTestResults};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn values(exec_state: &mut ExecState) -> Vec<KclValue> {
|
||||
@ -988,9 +1223,8 @@ mod test {
|
||||
|
||||
for v in &values[1..] {
|
||||
// Not a subtype
|
||||
assert!(v
|
||||
.coerce(&RuntimeType::Primitive(PrimitiveType::Boolean), &mut exec_state)
|
||||
.is_none());
|
||||
v.coerce(&RuntimeType::Primitive(PrimitiveType::Boolean), &mut exec_state)
|
||||
.unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1024,8 +1258,8 @@ mod test {
|
||||
},
|
||||
&mut exec_state,
|
||||
);
|
||||
assert!(none.coerce(&aty1, &mut exec_state).is_none());
|
||||
assert!(none.coerce(&aty1p, &mut exec_state).is_none());
|
||||
none.coerce(&aty1, &mut exec_state).unwrap_err();
|
||||
none.coerce(&aty1p, &mut exec_state).unwrap_err();
|
||||
|
||||
let tty = RuntimeType::Tuple(vec![]);
|
||||
let tty1 = RuntimeType::Tuple(vec![RuntimeType::solid()]);
|
||||
@ -1038,7 +1272,7 @@ mod test {
|
||||
},
|
||||
&mut exec_state,
|
||||
);
|
||||
assert!(none.coerce(&tty1, &mut exec_state).is_none());
|
||||
none.coerce(&tty1, &mut exec_state).unwrap_err();
|
||||
|
||||
let oty = RuntimeType::Object(vec![]);
|
||||
assert_coerce_results(
|
||||
@ -1107,7 +1341,7 @@ mod test {
|
||||
assert_coerce_results(&obj2, &ty0, &obj2, &mut exec_state);
|
||||
|
||||
let ty1 = RuntimeType::Object(vec![("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
|
||||
assert!(&obj0.coerce(&ty1, &mut exec_state).is_none());
|
||||
obj0.coerce(&ty1, &mut exec_state).unwrap_err();
|
||||
assert_coerce_results(&obj1, &ty1, &obj1, &mut exec_state);
|
||||
assert_coerce_results(&obj2, &ty1, &obj2, &mut exec_state);
|
||||
|
||||
@ -1119,19 +1353,19 @@ mod test {
|
||||
),
|
||||
("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean)),
|
||||
]);
|
||||
assert!(&obj0.coerce(&ty2, &mut exec_state).is_none());
|
||||
assert!(&obj1.coerce(&ty2, &mut exec_state).is_none());
|
||||
obj0.coerce(&ty2, &mut exec_state).unwrap_err();
|
||||
obj1.coerce(&ty2, &mut exec_state).unwrap_err();
|
||||
assert_coerce_results(&obj2, &ty2, &obj2, &mut exec_state);
|
||||
|
||||
// field not present
|
||||
let tyq = RuntimeType::Object(vec![("qux".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
|
||||
assert!(&obj0.coerce(&tyq, &mut exec_state).is_none());
|
||||
assert!(&obj1.coerce(&tyq, &mut exec_state).is_none());
|
||||
assert!(&obj2.coerce(&tyq, &mut exec_state).is_none());
|
||||
obj0.coerce(&tyq, &mut exec_state).unwrap_err();
|
||||
obj1.coerce(&tyq, &mut exec_state).unwrap_err();
|
||||
obj2.coerce(&tyq, &mut exec_state).unwrap_err();
|
||||
|
||||
// field with different type
|
||||
let ty1 = RuntimeType::Object(vec![("bar".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
|
||||
assert!(&obj2.coerce(&ty1, &mut exec_state).is_none());
|
||||
obj2.coerce(&ty1, &mut exec_state).unwrap_err();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -1209,8 +1443,8 @@ mod test {
|
||||
assert_coerce_results(&hom_arr, &tyh, &hom_arr, &mut exec_state);
|
||||
assert_coerce_results(&mixed1, &tym1, &mixed1, &mut exec_state);
|
||||
assert_coerce_results(&mixed2, &tym2, &mixed2, &mut exec_state);
|
||||
assert!(&mixed1.coerce(&tym2, &mut exec_state).is_none());
|
||||
assert!(&mixed2.coerce(&tym1, &mut exec_state).is_none());
|
||||
mixed1.coerce(&tym2, &mut exec_state).unwrap_err();
|
||||
mixed2.coerce(&tym1, &mut exec_state).unwrap_err();
|
||||
|
||||
// Length subtyping
|
||||
let tyhn = RuntimeType::Array(
|
||||
@ -1227,15 +1461,15 @@ mod test {
|
||||
);
|
||||
assert_coerce_results(&hom_arr, &tyhn, &hom_arr, &mut exec_state);
|
||||
assert_coerce_results(&hom_arr, &tyh1, &hom_arr, &mut exec_state);
|
||||
assert!(&hom_arr.coerce(&tyh3, &mut exec_state).is_none());
|
||||
hom_arr.coerce(&tyh3, &mut exec_state).unwrap_err();
|
||||
|
||||
let hom_arr0 = KclValue::HomArray {
|
||||
value: vec![],
|
||||
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
|
||||
};
|
||||
assert_coerce_results(&hom_arr0, &tyhn, &hom_arr0, &mut exec_state);
|
||||
assert!(&hom_arr0.coerce(&tyh1, &mut exec_state).is_none());
|
||||
assert!(&hom_arr0.coerce(&tyh3, &mut exec_state).is_none());
|
||||
hom_arr0.coerce(&tyh1, &mut exec_state).unwrap_err();
|
||||
hom_arr0.coerce(&tyh3, &mut exec_state).unwrap_err();
|
||||
|
||||
// Covariance
|
||||
// let tyh = RuntimeType::Array(Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))), ArrayLen::Known(4));
|
||||
@ -1275,16 +1509,16 @@ mod test {
|
||||
assert_coerce_results(&mixed1, &tyhn, &hom_arr_2, &mut exec_state);
|
||||
assert_coerce_results(&mixed1, &tyh1, &hom_arr_2, &mut exec_state);
|
||||
assert_coerce_results(&mixed0, &tyhn, &hom_arr0, &mut exec_state);
|
||||
assert!(&mixed0.coerce(&tyh, &mut exec_state).is_none());
|
||||
assert!(&mixed0.coerce(&tyh1, &mut exec_state).is_none());
|
||||
mixed0.coerce(&tyh, &mut exec_state).unwrap_err();
|
||||
mixed0.coerce(&tyh1, &mut exec_state).unwrap_err();
|
||||
|
||||
// Homogehous to mixed
|
||||
assert_coerce_results(&hom_arr_2, &tym1, &mixed1, &mut exec_state);
|
||||
assert!(&hom_arr.coerce(&tym1, &mut exec_state).is_none());
|
||||
assert!(&hom_arr_2.coerce(&tym2, &mut exec_state).is_none());
|
||||
hom_arr.coerce(&tym1, &mut exec_state).unwrap_err();
|
||||
hom_arr_2.coerce(&tym2, &mut exec_state).unwrap_err();
|
||||
|
||||
assert!(&mixed0.coerce(&tym1, &mut exec_state).is_none());
|
||||
assert!(&mixed0.coerce(&tym2, &mut exec_state).is_none());
|
||||
mixed0.coerce(&tym1, &mut exec_state).unwrap_err();
|
||||
mixed0.coerce(&tym2, &mut exec_state).unwrap_err();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -1334,8 +1568,8 @@ mod test {
|
||||
RuntimeType::Primitive(PrimitiveType::Boolean),
|
||||
RuntimeType::Primitive(PrimitiveType::String),
|
||||
]);
|
||||
assert!(count.coerce(&tyb, &mut exec_state).is_none());
|
||||
assert!(count.coerce(&tyb2, &mut exec_state).is_none());
|
||||
count.coerce(&tyb, &mut exec_state).unwrap_err();
|
||||
count.coerce(&tyb2, &mut exec_state).unwrap_err();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -1450,6 +1684,196 @@ mod test {
|
||||
assert_coerce_results(&a2d, &ty2d, &a2d, &mut exec_state);
|
||||
assert_coerce_results(&a3d, &ty3d, &a3d, &mut exec_state);
|
||||
assert_coerce_results(&a3d, &ty2d, &a2d, &mut exec_state);
|
||||
assert!(a2d.coerce(&ty3d, &mut exec_state).is_none());
|
||||
a2d.coerce(&ty3d, &mut exec_state).unwrap_err();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn coerce_numeric() {
|
||||
let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock().await);
|
||||
|
||||
let count = KclValue::Number {
|
||||
value: 1.0,
|
||||
ty: NumericType::count(),
|
||||
meta: Vec::new(),
|
||||
};
|
||||
let mm = KclValue::Number {
|
||||
value: 1.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
};
|
||||
let inches = KclValue::Number {
|
||||
value: 1.0,
|
||||
ty: NumericType::Known(UnitType::Length(UnitLen::Inches)),
|
||||
meta: Vec::new(),
|
||||
};
|
||||
let rads = KclValue::Number {
|
||||
value: 1.0,
|
||||
ty: NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
|
||||
meta: Vec::new(),
|
||||
};
|
||||
let default = KclValue::Number {
|
||||
value: 1.0,
|
||||
ty: NumericType::default(),
|
||||
meta: Vec::new(),
|
||||
};
|
||||
let any = KclValue::Number {
|
||||
value: 1.0,
|
||||
ty: NumericType::Any,
|
||||
meta: Vec::new(),
|
||||
};
|
||||
let unknown = KclValue::Number {
|
||||
value: 1.0,
|
||||
ty: NumericType::Unknown,
|
||||
meta: Vec::new(),
|
||||
};
|
||||
|
||||
// Trivial coercions
|
||||
assert_coerce_results(&count, &NumericType::count().into(), &count, &mut exec_state);
|
||||
assert_coerce_results(&mm, &NumericType::mm().into(), &mm, &mut exec_state);
|
||||
assert_coerce_results(&any, &NumericType::Any.into(), &any, &mut exec_state);
|
||||
assert_coerce_results(&unknown, &NumericType::Unknown.into(), &unknown, &mut exec_state);
|
||||
assert_coerce_results(&default, &NumericType::default().into(), &default, &mut exec_state);
|
||||
|
||||
assert_coerce_results(&count, &NumericType::Any.into(), &count, &mut exec_state);
|
||||
assert_coerce_results(&mm, &NumericType::Any.into(), &mm, &mut exec_state);
|
||||
assert_coerce_results(&unknown, &NumericType::Any.into(), &unknown, &mut exec_state);
|
||||
assert_coerce_results(&default, &NumericType::Any.into(), &default, &mut exec_state);
|
||||
|
||||
if !*CHECK_NUMERIC_TYPES {
|
||||
return;
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
default
|
||||
.coerce(
|
||||
&NumericType::Default {
|
||||
len: UnitLen::Yards,
|
||||
angle: UnitAngle::default()
|
||||
}
|
||||
.into(),
|
||||
&mut exec_state
|
||||
)
|
||||
.unwrap(),
|
||||
default
|
||||
);
|
||||
|
||||
// No coercion
|
||||
count.coerce(&NumericType::mm().into(), &mut exec_state).unwrap_err();
|
||||
mm.coerce(&NumericType::count().into(), &mut exec_state).unwrap_err();
|
||||
unknown.coerce(&NumericType::mm().into(), &mut exec_state).unwrap_err();
|
||||
unknown
|
||||
.coerce(&NumericType::default().into(), &mut exec_state)
|
||||
.unwrap_err();
|
||||
|
||||
count.coerce(&NumericType::Unknown.into(), &mut exec_state).unwrap_err();
|
||||
mm.coerce(&NumericType::Unknown.into(), &mut exec_state).unwrap_err();
|
||||
default
|
||||
.coerce(&NumericType::Unknown.into(), &mut exec_state)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
inches
|
||||
.coerce(&NumericType::mm().into(), &mut exec_state)
|
||||
.unwrap()
|
||||
.as_f64()
|
||||
.unwrap()
|
||||
.round(),
|
||||
25.0
|
||||
);
|
||||
assert_eq!(
|
||||
rads.coerce(
|
||||
&NumericType::Known(UnitType::Angle(UnitAngle::Degrees)).into(),
|
||||
&mut exec_state
|
||||
)
|
||||
.unwrap()
|
||||
.as_f64()
|
||||
.unwrap()
|
||||
.round(),
|
||||
57.0
|
||||
);
|
||||
assert_eq!(
|
||||
inches
|
||||
.coerce(&NumericType::default().into(), &mut exec_state)
|
||||
.unwrap()
|
||||
.as_f64()
|
||||
.unwrap()
|
||||
.round(),
|
||||
25.0
|
||||
);
|
||||
assert_eq!(
|
||||
rads.coerce(&NumericType::default().into(), &mut exec_state)
|
||||
.unwrap()
|
||||
.as_f64()
|
||||
.unwrap()
|
||||
.round(),
|
||||
57.0
|
||||
);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_value_and_type(name: &str, result: &ExecTestResults, expected: f64, expected_ty: NumericType) {
|
||||
let mem = result.exec_state.stack();
|
||||
match mem
|
||||
.memory
|
||||
.get_from(name, result.mem_env, SourceRange::default(), 0)
|
||||
.unwrap()
|
||||
{
|
||||
KclValue::Number { value, ty, .. } => {
|
||||
assert_eq!(value.round(), expected);
|
||||
assert_eq!(*ty, expected_ty);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn combine_numeric() {
|
||||
let program = r#"a = 5 + 4
|
||||
b = 5 - 2
|
||||
c = 5mm - 2mm + 10mm
|
||||
d = 5mm - 2 + 10
|
||||
e = 5 - 2mm + 10
|
||||
f = 30mm - 1inch
|
||||
|
||||
g = 2 * 10
|
||||
h = 2 * 10mm
|
||||
i = 2mm * 10mm
|
||||
j = 2_ * 10
|
||||
k = 2_ * 3mm * 3mm
|
||||
|
||||
l = 1 / 10
|
||||
m = 2mm / 1mm
|
||||
n = 10inch / 2mm
|
||||
o = 3mm / 3
|
||||
p = 3_ / 4
|
||||
q = 4inch / 2_
|
||||
"#;
|
||||
|
||||
let result = parse_execute(program).await.unwrap();
|
||||
if *CHECK_NUMERIC_TYPES {
|
||||
assert_eq!(result.exec_state.errors().len(), 2);
|
||||
} else {
|
||||
assert!(result.exec_state.errors().is_empty());
|
||||
}
|
||||
|
||||
assert_value_and_type("a", &result, 9.0, NumericType::default());
|
||||
assert_value_and_type("b", &result, 3.0, NumericType::default());
|
||||
assert_value_and_type("c", &result, 13.0, NumericType::mm());
|
||||
assert_value_and_type("d", &result, 13.0, NumericType::mm());
|
||||
assert_value_and_type("e", &result, 13.0, NumericType::mm());
|
||||
assert_value_and_type("f", &result, 5.0, NumericType::mm());
|
||||
|
||||
assert_value_and_type("g", &result, 20.0, NumericType::default());
|
||||
assert_value_and_type("h", &result, 20.0, NumericType::mm());
|
||||
assert_value_and_type("i", &result, 20.0, NumericType::Unknown);
|
||||
assert_value_and_type("j", &result, 20.0, NumericType::default());
|
||||
assert_value_and_type("k", &result, 18.0, NumericType::Unknown);
|
||||
|
||||
assert_value_and_type("l", &result, 0.0, NumericType::count());
|
||||
assert_value_and_type("m", &result, 2.0, NumericType::count());
|
||||
assert_value_and_type("n", &result, 127.0, NumericType::count());
|
||||
assert_value_and_type("o", &result, 1.0, NumericType::mm());
|
||||
assert_value_and_type("p", &result, 1.0, NumericType::count());
|
||||
assert_value_and_type("q", &result, 2.0, NumericType::Known(UnitType::Length(UnitLen::Inches)));
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ pub mod project;
|
||||
use anyhow::Result;
|
||||
use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use validator::{Validate, ValidateRange};
|
||||
|
||||
const DEFAULT_THEME_COLOR: f64 = 264.5;
|
||||
@ -131,9 +131,14 @@ pub struct AppSettings {
|
||||
/// This setting only applies to the web app. And is temporary until we have Linux support.
|
||||
#[serde(default, alias = "dismissWebBanner", skip_serializing_if = "is_default")]
|
||||
pub dismiss_web_banner: bool,
|
||||
/// When the user is idle, and this is true, the stream will be torn down.
|
||||
#[serde(default, alias = "streamIdleMode", skip_serializing_if = "is_default")]
|
||||
pub stream_idle_mode: bool,
|
||||
/// When the user is idle, teardown the stream after some time.
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "deserialize_stream_idle_mode",
|
||||
alias = "streamIdleMode",
|
||||
skip_serializing_if = "is_default"
|
||||
)]
|
||||
stream_idle_mode: Option<u32>,
|
||||
/// When the user is idle, and this is true, the stream will be torn down.
|
||||
#[serde(default, alias = "allowOrbitInSketchMode", skip_serializing_if = "is_default")]
|
||||
pub allow_orbit_in_sketch_mode: bool,
|
||||
@ -143,7 +148,31 @@ pub struct AppSettings {
|
||||
pub show_debug_panel: bool,
|
||||
}
|
||||
|
||||
// TODO: When we remove backwards compatibility with the old settings file, we can remove this.
|
||||
fn deserialize_stream_idle_mode<'de, D>(deserializer: D) -> Result<Option<u32>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum StreamIdleModeValue {
|
||||
Number(u32),
|
||||
String(String),
|
||||
Boolean(bool),
|
||||
}
|
||||
|
||||
const DEFAULT_TIMEOUT: u32 = 1000 * 60 * 5;
|
||||
|
||||
Ok(match StreamIdleModeValue::deserialize(deserializer) {
|
||||
Ok(StreamIdleModeValue::Number(value)) => Some(value),
|
||||
Ok(StreamIdleModeValue::String(value)) => Some(value.parse::<u32>().unwrap_or(DEFAULT_TIMEOUT)),
|
||||
// The old type of this value. I'm willing to say no one used it but
|
||||
// we can never guarantee it.
|
||||
Ok(StreamIdleModeValue::Boolean(true)) => Some(DEFAULT_TIMEOUT),
|
||||
Ok(StreamIdleModeValue::Boolean(false)) => None,
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq)]
|
||||
#[ts(export)]
|
||||
#[serde(untagged)]
|
||||
@ -626,7 +655,7 @@ textWrapping = true
|
||||
theme_color: None,
|
||||
dismiss_web_banner: false,
|
||||
enable_ssao: None,
|
||||
stream_idle_mode: false,
|
||||
stream_idle_mode: None,
|
||||
allow_orbit_in_sketch_mode: false,
|
||||
show_debug_panel: true,
|
||||
},
|
||||
@ -691,7 +720,7 @@ includeSettings = false
|
||||
dismiss_web_banner: false,
|
||||
enable_ssao: None,
|
||||
show_debug_panel: true,
|
||||
stream_idle_mode: false,
|
||||
stream_idle_mode: None,
|
||||
allow_orbit_in_sketch_mode: false,
|
||||
},
|
||||
modeling: ModelingSettings {
|
||||
@ -759,7 +788,7 @@ defaultProjectName = "projects-$nnn"
|
||||
theme_color: None,
|
||||
dismiss_web_banner: false,
|
||||
enable_ssao: None,
|
||||
stream_idle_mode: false,
|
||||
stream_idle_mode: None,
|
||||
allow_orbit_in_sketch_mode: false,
|
||||
show_debug_panel: true,
|
||||
},
|
||||
@ -841,7 +870,7 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#;
|
||||
dismiss_web_banner: false,
|
||||
enable_ssao: None,
|
||||
show_debug_panel: false,
|
||||
stream_idle_mode: false,
|
||||
stream_idle_mode: None,
|
||||
allow_orbit_in_sketch_mode: false,
|
||||
},
|
||||
modeling: ModelingSettings {
|
||||
|
@ -84,7 +84,7 @@ fn parse_test(test: &Test) {
|
||||
insta::assert_json_snapshot!("ast", parse_res, {
|
||||
".**.start" => 0,
|
||||
".**.end" => 0,
|
||||
".**.comment_start" => 0,
|
||||
".**.commentStart" => 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -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}",
|
||||
@ -503,19 +503,19 @@ impl Args {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn make_user_val_from_point(&self, p: [f64; 2]) -> Result<KclValue, KclError> {
|
||||
pub(crate) fn make_user_val_from_point(&self, p: [TyF64; 2]) -> Result<KclValue, KclError> {
|
||||
let meta = Metadata {
|
||||
source_range: self.source_range,
|
||||
};
|
||||
let x = KclValue::Number {
|
||||
value: p[0],
|
||||
value: p[0].n,
|
||||
meta: vec![meta],
|
||||
ty: NumericType::Unknown,
|
||||
ty: p[0].ty.clone(),
|
||||
};
|
||||
let y = KclValue::Number {
|
||||
value: p[1],
|
||||
value: p[1].n,
|
||||
meta: vec![meta],
|
||||
ty: NumericType::Unknown,
|
||||
ty: p[1].ty.clone(),
|
||||
};
|
||||
Ok(KclValue::MixedArray {
|
||||
value: vec![x, y],
|
||||
@ -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,25 +542,6 @@ impl Args {
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) 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 {
|
||||
value: n,
|
||||
meta: vec![Metadata {
|
||||
source_range: self.source_range,
|
||||
}],
|
||||
ty: ty.clone(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Ok(KclValue::MixedArray {
|
||||
value: array,
|
||||
meta: vec![Metadata {
|
||||
source_range: self.source_range,
|
||||
}],
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn get_number(&self) -> Result<f64, KclError> {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
@ -616,8 +597,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 +607,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 +630,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 +654,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!(),
|
||||
@ -694,10 +677,9 @@ impl Args {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_and_optional_tag<'a, T>(&'a self) -> Result<(T, Option<FaceTag>), KclError>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
||||
{
|
||||
pub(crate) fn get_sketch_data_and_optional_tag(
|
||||
&self,
|
||||
) -> Result<(super::sketch::SketchData, Option<FaceTag>), KclError> {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
@ -718,13 +700,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 +738,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!(),
|
||||
@ -1165,15 +1151,6 @@ impl<'a> FromKclValue<'a> for super::shapes::PolygonData {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for crate::std::polar::PolarCoordsData {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, angle);
|
||||
let_field_of!(obj, length);
|
||||
Some(Self { angle, length })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for crate::execution::Plane {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
arg.as_plane().cloned()
|
||||
|
@ -13,7 +13,7 @@ pub async fn int(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
let num = args.get_number_with_type()?;
|
||||
let converted = inner_int(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(num.map(converted)))
|
||||
Ok(args.make_user_val_from_f64_with_type(num.map_value(converted)))
|
||||
}
|
||||
|
||||
/// Convert a number to an integer.
|
||||
|
@ -6,7 +6,7 @@ use kcl_derive_docs::stdlib;
|
||||
use super::args::FromArgs;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ExecState, KclValue},
|
||||
execution::{types::NumericType, ExecState, KclValue},
|
||||
std::args::{Args, TyF64},
|
||||
};
|
||||
|
||||
@ -67,7 +67,7 @@ pub async fn tan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
pub async fn pi(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_pi()?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::count(result)))
|
||||
}
|
||||
|
||||
/// Return the value of `pi`. Archimedes’ constant (π).
|
||||
@ -96,7 +96,7 @@ pub async fn sqrt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_sqrt(num)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
}
|
||||
|
||||
/// Compute the square root of a number.
|
||||
@ -123,10 +123,10 @@ fn inner_sqrt(num: f64) -> Result<f64, KclError> {
|
||||
|
||||
/// Compute the absolute value of a number.
|
||||
pub async fn abs(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_abs(num)?;
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_abs(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(num.map_value(result)))
|
||||
}
|
||||
|
||||
/// Compute the absolute value of a number.
|
||||
@ -160,10 +160,10 @@ fn inner_abs(num: f64) -> Result<f64, KclError> {
|
||||
|
||||
/// Round a number to the nearest integer.
|
||||
pub async fn round(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_round(num)?;
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_round(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(num.map_value(result)))
|
||||
}
|
||||
|
||||
/// Round a number to the nearest integer.
|
||||
@ -188,10 +188,10 @@ fn inner_round(num: f64) -> Result<f64, KclError> {
|
||||
|
||||
/// Compute the largest integer less than or equal to a number.
|
||||
pub async fn floor(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_floor(num)?;
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_floor(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(num.map_value(result)))
|
||||
}
|
||||
|
||||
/// Compute the largest integer less than or equal to a number.
|
||||
@ -216,10 +216,10 @@ fn inner_floor(num: f64) -> Result<f64, KclError> {
|
||||
|
||||
/// Compute the smallest integer greater than or equal to a number.
|
||||
pub async fn ceil(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_ceil(num)?;
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_ceil(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(num.map_value(result)))
|
||||
}
|
||||
|
||||
/// Compute the smallest integer greater than or equal to a number.
|
||||
@ -335,7 +335,7 @@ pub async fn pow(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
|
||||
let result = inner_pow(nums[0], nums[1])?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
}
|
||||
|
||||
/// Compute the number to a power.
|
||||
@ -365,7 +365,7 @@ pub async fn acos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_acos(num)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||
}
|
||||
|
||||
/// Compute the arccosine of a number (in radians).
|
||||
@ -396,7 +396,7 @@ pub async fn asin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_asin(num)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||
}
|
||||
|
||||
/// Compute the arcsine of a number (in radians).
|
||||
@ -426,7 +426,7 @@ pub async fn atan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_atan(num)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||
}
|
||||
|
||||
/// Compute the arctangent of a number (in radians).
|
||||
@ -456,7 +456,7 @@ pub async fn atan2(_exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
let (y, x) = FromArgs::from_args(&args, 0)?;
|
||||
let result = inner_atan2(y, x)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||
}
|
||||
|
||||
/// Compute the four quadrant arctangent of Y and X (in radians).
|
||||
@ -503,7 +503,7 @@ pub async fn log(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
}
|
||||
let result = inner_log(nums[0], nums[1])?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
}
|
||||
|
||||
/// Compute the logarithm of the number with respect to an arbitrary base.
|
||||
@ -535,7 +535,7 @@ pub async fn log2(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_log2(num)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
}
|
||||
|
||||
/// Compute the base 2 logarithm of the number.
|
||||
@ -563,7 +563,7 @@ pub async fn log10(_exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
let num = args.get_number()?;
|
||||
let result = inner_log10(num)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
}
|
||||
|
||||
/// Compute the base 10 logarithm of the number.
|
||||
@ -591,7 +591,7 @@ pub async fn ln(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
|
||||
let num = args.get_number()?;
|
||||
let result = inner_ln(num)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
}
|
||||
|
||||
/// Compute the natural logarithm of the number.
|
||||
@ -618,7 +618,7 @@ fn inner_ln(num: f64) -> Result<f64, KclError> {
|
||||
pub async fn e(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_e()?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::count())))
|
||||
}
|
||||
|
||||
/// Return the value of Euler’s number `e`.
|
||||
@ -650,7 +650,7 @@ fn inner_e() -> Result<f64, KclError> {
|
||||
pub async fn tau(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_tau()?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::count())))
|
||||
}
|
||||
|
||||
/// Return the value of `tau`. The full circle constant (τ). Equal to 2π.
|
||||
@ -683,7 +683,7 @@ pub async fn to_radians(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
let num = args.get_number()?;
|
||||
let result = inner_to_radians(num)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||
}
|
||||
|
||||
/// Converts a number from degrees to radians.
|
||||
@ -713,7 +713,7 @@ pub async fn to_degrees(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
let num = args.get_number()?;
|
||||
let result = inner_to_degrees(num)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
|
||||
}
|
||||
|
||||
/// Converts a number from radians to degrees.
|
||||
|
@ -18,7 +18,6 @@ pub mod math;
|
||||
pub mod mirror;
|
||||
pub mod patterns;
|
||||
pub mod planes;
|
||||
pub mod polar;
|
||||
pub mod revolve;
|
||||
pub mod segment;
|
||||
pub mod shapes;
|
||||
@ -145,7 +144,6 @@ lazy_static! {
|
||||
Box::new(crate::std::units::M),
|
||||
Box::new(crate::std::units::Cm),
|
||||
Box::new(crate::std::units::Yd),
|
||||
Box::new(crate::std::polar::Polar),
|
||||
Box::new(crate::std::assert::Assert),
|
||||
Box::new(crate::std::assert::AssertEqual),
|
||||
Box::new(crate::std::assert::AssertLessThan),
|
||||
|
@ -1,55 +0,0 @@
|
||||
//! Functions related to polar coordinates.
|
||||
|
||||
use anyhow::Result;
|
||||
use kcl_derive_docs::stdlib;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{ExecState, KclValue},
|
||||
std::args::{Args, TyF64},
|
||||
};
|
||||
|
||||
/// Data for polar coordinates.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PolarCoordsData {
|
||||
/// The angle of the line (in degrees).
|
||||
pub angle: f64,
|
||||
/// The length of the line.
|
||||
pub length: TyF64,
|
||||
}
|
||||
|
||||
/// Convert from polar/sphere coordinates to cartesian coordinates.
|
||||
pub async fn polar(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let data: PolarCoordsData = args.get_data()?;
|
||||
let result = inner_polar(&data)?;
|
||||
|
||||
args.make_user_val_from_f64_array(result.to_vec(), &data.length.ty)
|
||||
}
|
||||
|
||||
/// Convert polar/sphere (azimuth, elevation, distance) coordinates to
|
||||
/// cartesian (x/y/z grid) coordinates.
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn('XZ')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = polar({angle: 30, length: 5}), tag = $thing)
|
||||
/// |> line(end = [0, 5])
|
||||
/// |> line(end = [segEndX(thing), 0])
|
||||
/// |> line(end = [-20, 10])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "polar",
|
||||
}]
|
||||
fn inner_polar(data: &PolarCoordsData) -> Result<[f64; 2], KclError> {
|
||||
let angle = data.angle.to_radians();
|
||||
let x = data.length.n * angle.cos();
|
||||
let y = data.length.n * angle.sin();
|
||||
Ok([x, y])
|
||||
}
|
@ -7,10 +7,10 @@ use kittycad_modeling_cmds::shared::Angle;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
types::{PrimitiveType, RuntimeType},
|
||||
types::{NumericType, PrimitiveType, RuntimeType},
|
||||
ExecState, KclValue, Point2d, Sketch, TagIdentifier,
|
||||
},
|
||||
std::{utils::between, Args},
|
||||
std::{args::TyF64, utils::between, Args},
|
||||
};
|
||||
|
||||
/// Returns the point at the end of the given segment.
|
||||
@ -54,7 +54,7 @@ pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
}
|
||||
}]
|
||||
fn inner_segment_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[f64; 2], KclError> {
|
||||
fn inner_segment_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[TyF64; 2], KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
@ -63,7 +63,7 @@ fn inner_segment_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(path.get_base().to)
|
||||
Ok(path.get_to().clone())
|
||||
}
|
||||
|
||||
/// Returns the segment end of x.
|
||||
@ -71,7 +71,7 @@ pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
|
||||
let result = inner_segment_end_x(&tag, exec_state, args.clone())?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Compute the ending point of the provided line segment along the 'x' axis.
|
||||
@ -95,7 +95,7 @@ pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
}
|
||||
}]
|
||||
fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
|
||||
fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
@ -104,7 +104,7 @@ fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(path.get_base().to[0])
|
||||
Ok(TyF64::new(path.get_base().to[0], path.get_base().units.into()))
|
||||
}
|
||||
|
||||
/// Returns the segment end of y.
|
||||
@ -112,7 +112,7 @@ pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
|
||||
let result = inner_segment_end_y(&tag, exec_state, args.clone())?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Compute the ending point of the provided line segment along the 'y' axis.
|
||||
@ -137,7 +137,7 @@ pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
}
|
||||
}]
|
||||
fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
|
||||
fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
@ -146,7 +146,7 @@ fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(path.get_to()[1])
|
||||
Ok(path.get_to()[1].clone())
|
||||
}
|
||||
|
||||
/// Returns the point at the start of the given segment.
|
||||
@ -190,7 +190,7 @@ pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
}
|
||||
}]
|
||||
fn inner_segment_start(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[f64; 2], KclError> {
|
||||
fn inner_segment_start(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[TyF64; 2], KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
@ -207,7 +207,7 @@ pub async fn segment_start_x(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
|
||||
let result = inner_segment_start_x(&tag, exec_state, args.clone())?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Compute the starting point of the provided line segment along the 'x' axis.
|
||||
@ -231,7 +231,7 @@ pub async fn segment_start_x(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
}
|
||||
}]
|
||||
fn inner_segment_start_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
|
||||
fn inner_segment_start_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
@ -240,7 +240,7 @@ fn inner_segment_start_x(tag: &TagIdentifier, exec_state: &mut ExecState, args:
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(path.get_from()[0])
|
||||
Ok(path.get_from()[0].clone())
|
||||
}
|
||||
|
||||
/// Returns the segment start of y.
|
||||
@ -248,7 +248,7 @@ pub async fn segment_start_y(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
|
||||
let result = inner_segment_start_y(&tag, exec_state, args.clone())?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Compute the starting point of the provided line segment along the 'y' axis.
|
||||
@ -273,7 +273,7 @@ pub async fn segment_start_y(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
}
|
||||
}]
|
||||
fn inner_segment_start_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
|
||||
fn inner_segment_start_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
@ -282,7 +282,7 @@ fn inner_segment_start_y(tag: &TagIdentifier, exec_state: &mut ExecState, args:
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(path.get_from()[1])
|
||||
Ok(path.get_from()[1].clone())
|
||||
}
|
||||
/// Returns the last segment of x.
|
||||
pub async fn last_segment_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
@ -290,7 +290,7 @@ pub async fn last_segment_x(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let result = inner_last_segment_x(sketch, args.clone())?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Extract the 'x' axis value of the last line segment in the provided 2-d
|
||||
@ -315,7 +315,7 @@ pub async fn last_segment_x(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
sketch = { docs = "The sketch whose line segment is being queried"},
|
||||
}
|
||||
}]
|
||||
fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
||||
fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<TyF64, KclError> {
|
||||
let last_line = sketch
|
||||
.paths
|
||||
.last()
|
||||
@ -327,7 +327,7 @@ fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
||||
})?
|
||||
.get_base();
|
||||
|
||||
Ok(last_line.to[0])
|
||||
Ok(TyF64::new(last_line.to[0], last_line.units.into()))
|
||||
}
|
||||
|
||||
/// Returns the last segment of y.
|
||||
@ -336,7 +336,7 @@ pub async fn last_segment_y(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let result = inner_last_segment_y(sketch, args.clone())?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Extract the 'y' axis value of the last line segment in the provided 2-d
|
||||
@ -361,7 +361,7 @@ pub async fn last_segment_y(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
sketch = { docs = "The sketch whose line segment is being queried"},
|
||||
}
|
||||
}]
|
||||
fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
||||
fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<TyF64, KclError> {
|
||||
let last_line = sketch
|
||||
.paths
|
||||
.last()
|
||||
@ -373,14 +373,14 @@ fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
||||
})?
|
||||
.get_base();
|
||||
|
||||
Ok(last_line.to[1])
|
||||
Ok(TyF64::new(last_line.to[1], last_line.units.into()))
|
||||
}
|
||||
|
||||
/// Returns the length of the segment.
|
||||
pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
|
||||
let result = inner_segment_length(&tag, exec_state, args.clone())?;
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Compute the length of the provided line segment.
|
||||
@ -412,7 +412,7 @@ pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
}
|
||||
}]
|
||||
fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
|
||||
fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
@ -421,9 +421,7 @@ fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: A
|
||||
})
|
||||
})?;
|
||||
|
||||
let result = path.length();
|
||||
|
||||
Ok(result)
|
||||
Ok(path.length())
|
||||
}
|
||||
|
||||
/// Returns the angle of the segment.
|
||||
@ -431,7 +429,7 @@ pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
|
||||
|
||||
let result = inner_segment_angle(&tag, exec_state, args.clone())?;
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
|
||||
}
|
||||
|
||||
/// Compute the angle (in degrees) of the provided line segment.
|
||||
@ -476,7 +474,7 @@ pub async fn tangent_to_end(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
|
||||
|
||||
let result = inner_tangent_to_end(&tag, exec_state, args.clone()).await?;
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
|
||||
}
|
||||
|
||||
/// Returns the angle coming out of the end of the segment in degrees.
|
||||
@ -586,7 +584,7 @@ async fn inner_tangent_to_end(tag: &TagIdentifier, exec_state: &mut ExecState, a
|
||||
pub async fn angle_to_match_length_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
|
||||
let result = inner_angle_to_match_length_x(&tag, to, sketch, exec_state, args.clone())?;
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
|
||||
}
|
||||
|
||||
/// Returns the angle to match the given length for x.
|
||||
@ -621,7 +619,7 @@ fn inner_angle_to_match_length_x(
|
||||
})
|
||||
})?;
|
||||
|
||||
let length = path.length();
|
||||
let length = path.length().n;
|
||||
|
||||
let last_line = sketch
|
||||
.paths
|
||||
@ -634,6 +632,7 @@ fn inner_angle_to_match_length_x(
|
||||
})?
|
||||
.get_base();
|
||||
|
||||
// TODO assumption about the units of to
|
||||
let diff = (to - last_line.to[0]).abs();
|
||||
|
||||
let angle_r = (diff / length).acos();
|
||||
@ -649,7 +648,7 @@ fn inner_angle_to_match_length_x(
|
||||
pub async fn angle_to_match_length_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
|
||||
let result = inner_angle_to_match_length_y(&tag, to, sketch, exec_state, args.clone())?;
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
|
||||
}
|
||||
|
||||
/// Returns the angle to match the given length for y.
|
||||
@ -685,7 +684,7 @@ fn inner_angle_to_match_length_y(
|
||||
})
|
||||
})?;
|
||||
|
||||
let length = path.length();
|
||||
let length = path.length().n;
|
||||
|
||||
let last_line = sketch
|
||||
.paths
|
||||
@ -698,6 +697,7 @@ fn inner_angle_to_match_length_y(
|
||||
})?
|
||||
.get_base();
|
||||
|
||||
// TODO assumption about the units of to
|
||||
let diff = (to - last_line.to[1]).abs();
|
||||
|
||||
let angle_r = (diff / length).asin();
|
||||
|
@ -25,7 +25,7 @@ use crate::{
|
||||
};
|
||||
|
||||
/// A sketch surface or a sketch.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(untagged)]
|
||||
pub enum SketchOrSurface {
|
||||
|
@ -840,7 +840,7 @@ async fn inner_angled_line_that_intersects(
|
||||
|
||||
/// Data for start sketch on.
|
||||
/// You can start a sketch on a plane or an solid.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase", untagged)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
@ -892,7 +892,7 @@ pub enum PlaneData {
|
||||
|
||||
/// Start a sketch on a specific plane or face.
|
||||
pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, tag): (SketchData, Option<FaceTag>) = args.get_data_and_optional_tag()?;
|
||||
let (data, tag) = args.get_sketch_data_and_optional_tag()?;
|
||||
|
||||
match inner_start_sketch_on(data, tag, exec_state, &args).await? {
|
||||
SketchSurface::Plane(value) => Ok(KclValue::Plane { value }),
|
||||
|
@ -5,7 +5,7 @@ use kcl_derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
|
||||
use kittycad_modeling_cmds::{self as kcmc};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Serialize;
|
||||
|
||||
use super::DEFAULT_TOLERANCE;
|
||||
use crate::{
|
||||
@ -16,7 +16,7 @@ use crate::{
|
||||
};
|
||||
|
||||
/// A path to sweep along.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(untagged)]
|
||||
pub enum SweepPath {
|
||||
|
@ -6,14 +6,14 @@ use kcl_derive_docs::stdlib;
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{types::UnitLen, ExecState, KclValue},
|
||||
std::Args,
|
||||
std::{args::TyF64, Args},
|
||||
};
|
||||
|
||||
/// Millimeters conversion factor for current projects units.
|
||||
pub async fn mm(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_mm(exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Millimeters conversion factor for current projects units.
|
||||
@ -54,7 +54,7 @@ fn inner_mm(exec_state: &ExecState) -> Result<f64, KclError> {
|
||||
pub async fn inch(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_inch(exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Inches conversion factor for current projects units.
|
||||
@ -95,7 +95,7 @@ fn inner_inch(exec_state: &ExecState) -> Result<f64, KclError> {
|
||||
pub async fn ft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_ft(exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Feet conversion factor for current projects units.
|
||||
@ -137,7 +137,7 @@ fn inner_ft(exec_state: &ExecState) -> Result<f64, KclError> {
|
||||
pub async fn m(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_m(exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Meters conversion factor for current projects units.
|
||||
@ -179,7 +179,7 @@ fn inner_m(exec_state: &ExecState) -> Result<f64, KclError> {
|
||||
pub async fn cm(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_cm(exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Centimeters conversion factor for current projects units.
|
||||
@ -221,7 +221,7 @@ fn inner_cm(exec_state: &ExecState) -> Result<f64, KclError> {
|
||||
pub async fn yd(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_yd(exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Yards conversion factor for current projects units.
|
||||
|
Reference in New Issue
Block a user