code motion: factor our execution::types module
Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
		@ -23,7 +23,9 @@ use crate::{
 | 
			
		||||
const TYPES_DIR: &str = "../../docs/kcl/types";
 | 
			
		||||
const LANG_TOPICS: [&str; 5] = ["Types", "Modules", "Settings", "Known Issues", "Constants"];
 | 
			
		||||
// These types are declared in std.
 | 
			
		||||
const DECLARED_TYPES: [&str; 9] = ["number", "string", "tag", "bool", "Sketch", "Solid", "Plane", "Helix", "Face"];
 | 
			
		||||
const DECLARED_TYPES: [&str; 9] = [
 | 
			
		||||
    "number", "string", "tag", "bool", "Sketch", "Solid", "Plane", "Helix", "Face",
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
fn init_handlebars() -> Result<handlebars::Handlebars<'static>> {
 | 
			
		||||
    let mut hbs = handlebars::Handlebars::new();
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ use tower_lsp::lsp_types::{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    execution::{kcl_value::NumericType, Sketch},
 | 
			
		||||
    execution::{types::NumericType, Sketch},
 | 
			
		||||
    std::Primitive,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ use kittycad_modeling_cmds::coord::{System, KITTYCAD, OPENGL, VULKAN};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclErrorDetails,
 | 
			
		||||
    execution::kcl_value::{UnitAngle, UnitLen},
 | 
			
		||||
    execution::types::{UnitAngle, UnitLen},
 | 
			
		||||
    parsing::ast::types::{Annotation, Expr, Node, ObjectProperty},
 | 
			
		||||
    KclError, SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ use indexmap::IndexMap;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use super::{kcl_value::NumericType, ArtifactId, KclValue};
 | 
			
		||||
use super::{types::NumericType, ArtifactId, KclValue};
 | 
			
		||||
use crate::{docs::StdLibFn, std::get_stdlib_fn, SourceRange};
 | 
			
		||||
 | 
			
		||||
/// A CAD modeling operation for display in the feature tree, AKA operations
 | 
			
		||||
 | 
			
		||||
@ -8,9 +8,10 @@ use crate::{
 | 
			
		||||
    execution::{
 | 
			
		||||
        annotations,
 | 
			
		||||
        cad_op::{OpArg, OpKclValue, Operation},
 | 
			
		||||
        kcl_value::{FunctionSource, NumericType, RuntimeType},
 | 
			
		||||
        kcl_value::FunctionSource,
 | 
			
		||||
        memory,
 | 
			
		||||
        state::ModuleState,
 | 
			
		||||
        types::{NumericType, RuntimeType},
 | 
			
		||||
        BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, PlaneType, TagEngineInfo,
 | 
			
		||||
        TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@ -104,7 +104,7 @@ impl From<SolidOrSketchOrImportedGeometry> for crate::execution::KclValue {
 | 
			
		||||
                            .into_iter()
 | 
			
		||||
                            .map(|s| crate::execution::KclValue::Solid { value: Box::new(s) })
 | 
			
		||||
                            .collect(),
 | 
			
		||||
                        ty: crate::execution::kcl_value::RuntimeType::solid(),
 | 
			
		||||
                        ty: crate::execution::types::RuntimeType::solid(),
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{annotations, kcl_value::UnitLen, ExecState, ExecutorContext, ImportedGeometry},
 | 
			
		||||
    execution::{annotations, types::UnitLen, ExecState, ExecutorContext, ImportedGeometry},
 | 
			
		||||
    fs::FileSystem,
 | 
			
		||||
    parsing::ast::types::{Annotation, Node},
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
 | 
			
		||||
@ -1,29 +1,20 @@
 | 
			
		||||
use std::{collections::HashMap, fmt};
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
 | 
			
		||||
use super::{
 | 
			
		||||
    memory::{self, EnvironmentRef},
 | 
			
		||||
    MetaSettings, Point3d,
 | 
			
		||||
};
 | 
			
		||||
use super::{memory::EnvironmentRef, MetaSettings};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclErrorDetails,
 | 
			
		||||
    execution::{
 | 
			
		||||
        types::{NumericType, PrimitiveType, RuntimeType},
 | 
			
		||||
        ExecState, ExecutorContext, Face, Helix, ImportedGeometry, Metadata, Plane, Sketch, Solid, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::{
 | 
			
		||||
        ast::types::{
 | 
			
		||||
            DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node,
 | 
			
		||||
            PrimitiveType as AstPrimitiveType, TagDeclarator, TagNode, Type,
 | 
			
		||||
        },
 | 
			
		||||
        token::NumericSuffix,
 | 
			
		||||
    },
 | 
			
		||||
    std::{
 | 
			
		||||
        args::{Arg, FromKclValue},
 | 
			
		||||
        StdFnProps,
 | 
			
		||||
    parsing::ast::types::{
 | 
			
		||||
        DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node, TagDeclarator, TagNode,
 | 
			
		||||
    },
 | 
			
		||||
    std::{args::Arg, StdFnProps},
 | 
			
		||||
    CompilationError, KclError, ModuleId, SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -559,269 +550,6 @@ impl KclValue {
 | 
			
		||||
        Ok(*b)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// True if `self` has a type which is a subtype of `ty` without coercion.
 | 
			
		||||
    pub fn has_type(&self, ty: &RuntimeType) -> bool {
 | 
			
		||||
        let Some(self_ty) = self.principal_type() else {
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self_ty.subtype(ty)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Coerce `self` to a new value which has `ty` as it's closest supertype.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If the result is Some, then:
 | 
			
		||||
    ///   - 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> {
 | 
			
		||||
        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),
 | 
			
		||||
            RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, exec_state),
 | 
			
		||||
            RuntimeType::Union(tys) => self.coerce_to_union_type(tys, exec_state),
 | 
			
		||||
            RuntimeType::Object(tys) => self.coerce_to_object_type(tys, exec_state),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_primitive_type(&self, ty: &PrimitiveType, exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        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::String => match value {
 | 
			
		||||
                KclValue::String { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Boolean => match value {
 | 
			
		||||
                KclValue::Bool { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Sketch => match value {
 | 
			
		||||
                KclValue::Sketch { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Solid => match value {
 | 
			
		||||
                KclValue::Solid { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Plane => match value {
 | 
			
		||||
                KclValue::Plane { .. } => Some(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 id = exec_state.mod_local.id_generator.next_uuid();
 | 
			
		||||
                    let plane = Plane {
 | 
			
		||||
                        id,
 | 
			
		||||
                        artifact_id: id.into(),
 | 
			
		||||
                        origin,
 | 
			
		||||
                        x_axis,
 | 
			
		||||
                        y_axis,
 | 
			
		||||
                        z_axis,
 | 
			
		||||
                        value: super::PlaneType::Uninit,
 | 
			
		||||
                        // TODO use length unit from origin
 | 
			
		||||
                        units: exec_state.length_unit(),
 | 
			
		||||
                        meta: meta.clone(),
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    Some(KclValue::Plane { value: Box::new(plane) })
 | 
			
		||||
                }
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Face => match value {
 | 
			
		||||
                KclValue::Face { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Helix => match value {
 | 
			
		||||
                KclValue::Helix { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::ImportedGeometry => match value {
 | 
			
		||||
                KclValue::ImportedGeometry { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Tag => match value {
 | 
			
		||||
                KclValue::TagDeclarator { .. } => Some(value.clone()),
 | 
			
		||||
                KclValue::TagIdentifier { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_array_type(&self, ty: &RuntimeType, len: ArrayLen, exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::HomArray { value, ty: aty } => {
 | 
			
		||||
                // TODO could check types of values individually
 | 
			
		||||
                if aty != ty {
 | 
			
		||||
                    return None;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let value = match len {
 | 
			
		||||
                    ArrayLen::None => value.clone(),
 | 
			
		||||
                    ArrayLen::NonEmpty => {
 | 
			
		||||
                        if value.is_empty() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value.clone()
 | 
			
		||||
                    }
 | 
			
		||||
                    ArrayLen::Known(n) => {
 | 
			
		||||
                        if n != value.len() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value[..n].to_vec()
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                Some(KclValue::HomArray { value, ty: ty.clone() })
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::MixedArray { value, .. } => {
 | 
			
		||||
                let value = match len {
 | 
			
		||||
                    ArrayLen::None => value.clone(),
 | 
			
		||||
                    ArrayLen::NonEmpty => {
 | 
			
		||||
                        if value.is_empty() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value.clone()
 | 
			
		||||
                    }
 | 
			
		||||
                    ArrayLen::Known(n) => {
 | 
			
		||||
                        if n != value.len() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value[..n].to_vec()
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                let value = value
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|v| v.coerce(ty, exec_state))
 | 
			
		||||
                    .collect::<Option<Vec<_>>>()?;
 | 
			
		||||
 | 
			
		||||
                Some(KclValue::HomArray { value, ty: ty.clone() })
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::KclNone { .. } if len.satisfied(0) => Some(KclValue::HomArray {
 | 
			
		||||
                value: Vec::new(),
 | 
			
		||||
                ty: ty.clone(),
 | 
			
		||||
            }),
 | 
			
		||||
            value if len.satisfied(1) => {
 | 
			
		||||
                if value.has_type(ty) {
 | 
			
		||||
                    Some(KclValue::HomArray {
 | 
			
		||||
                        value: vec![value.clone()],
 | 
			
		||||
                        ty: ty.clone(),
 | 
			
		||||
                    })
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_tuple_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
 | 
			
		||||
                if value.len() < tys.len() {
 | 
			
		||||
                    return None;
 | 
			
		||||
                }
 | 
			
		||||
                let mut result = Vec::new();
 | 
			
		||||
                for (i, t) in tys.iter().enumerate() {
 | 
			
		||||
                    result.push(value[i].coerce(t, exec_state)?);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Some(KclValue::MixedArray {
 | 
			
		||||
                    value: result,
 | 
			
		||||
                    meta: Vec::new(),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::KclNone { meta, .. } if tys.is_empty() => Some(KclValue::MixedArray {
 | 
			
		||||
                value: Vec::new(),
 | 
			
		||||
                meta: meta.clone(),
 | 
			
		||||
            }),
 | 
			
		||||
            value if tys.len() == 1 => {
 | 
			
		||||
                if value.has_type(&tys[0]) {
 | 
			
		||||
                    Some(KclValue::MixedArray {
 | 
			
		||||
                        value: vec![value.clone()],
 | 
			
		||||
                        meta: Vec::new(),
 | 
			
		||||
                    })
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_union_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        for t in tys {
 | 
			
		||||
            if let Some(v) = self.coerce(t, exec_state) {
 | 
			
		||||
                return Some(v);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_object_type(&self, tys: &[(String, RuntimeType)], _exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::Object { value, .. } => {
 | 
			
		||||
                for (s, t) in tys {
 | 
			
		||||
                    // TODO coerce fields
 | 
			
		||||
                    if !value.get(s)?.has_type(t) {
 | 
			
		||||
                        return None;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                // TODO remove non-required fields
 | 
			
		||||
                Some(self.clone())
 | 
			
		||||
            }
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn principal_type(&self) -> Option<RuntimeType> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
 | 
			
		||||
            KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(ty.clone()))),
 | 
			
		||||
            KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)),
 | 
			
		||||
            KclValue::Object { value, .. } => {
 | 
			
		||||
                let properties = value
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|(k, v)| v.principal_type().map(|t| (k.clone(), t)))
 | 
			
		||||
                    .collect::<Option<Vec<_>>>()?;
 | 
			
		||||
                Some(RuntimeType::Object(properties))
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
 | 
			
		||||
            KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
 | 
			
		||||
            KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
 | 
			
		||||
            KclValue::Face { .. } => Some(RuntimeType::Primitive(PrimitiveType::Face)),
 | 
			
		||||
            KclValue::Helix { .. } => Some(RuntimeType::Primitive(PrimitiveType::Helix)),
 | 
			
		||||
            KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
 | 
			
		||||
            KclValue::MixedArray { value, .. } => Some(RuntimeType::Tuple(
 | 
			
		||||
                value.iter().map(|v| v.principal_type()).collect::<Option<Vec<_>>>()?,
 | 
			
		||||
            )),
 | 
			
		||||
            KclValue::HomArray { ty, value, .. } => {
 | 
			
		||||
                Some(RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(value.len())))
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::TagIdentifier(_) | KclValue::TagDeclarator(_) => Some(RuntimeType::Primitive(PrimitiveType::Tag)),
 | 
			
		||||
            KclValue::Function { .. }
 | 
			
		||||
            | KclValue::Module { .. }
 | 
			
		||||
            | KclValue::KclNone { .. }
 | 
			
		||||
            | KclValue::Type { .. }
 | 
			
		||||
            | KclValue::Uuid { .. } => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// If this memory item is a function, call it with the given arguments, return its val as Ok.
 | 
			
		||||
    /// If it's not a function, return Err.
 | 
			
		||||
    pub async fn call_fn(
 | 
			
		||||
@ -935,486 +663,3 @@ impl KclValue {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub enum RuntimeType {
 | 
			
		||||
    Primitive(PrimitiveType),
 | 
			
		||||
    Array(Box<RuntimeType>, ArrayLen),
 | 
			
		||||
    Union(Vec<RuntimeType>),
 | 
			
		||||
    Tuple(Vec<RuntimeType>),
 | 
			
		||||
    Object(Vec<(String, RuntimeType)>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RuntimeType {
 | 
			
		||||
    /// `[Sketch; 1+]`
 | 
			
		||||
    pub fn sketches() -> Self {
 | 
			
		||||
        RuntimeType::Array(
 | 
			
		||||
            Box::new(RuntimeType::Primitive(PrimitiveType::Sketch)),
 | 
			
		||||
            ArrayLen::NonEmpty,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// `[Solid; 1+]`
 | 
			
		||||
    pub fn solids() -> Self {
 | 
			
		||||
        RuntimeType::Array(
 | 
			
		||||
            Box::new(RuntimeType::Primitive(PrimitiveType::Solid)),
 | 
			
		||||
            ArrayLen::NonEmpty,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn solid() -> Self {
 | 
			
		||||
        RuntimeType::Primitive(PrimitiveType::Solid)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn imported() -> Self {
 | 
			
		||||
        RuntimeType::Primitive(PrimitiveType::ImportedGeometry)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn from_parsed(
 | 
			
		||||
        value: Type,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Self, CompilationError> {
 | 
			
		||||
        match value {
 | 
			
		||||
            Type::Primitive(pt) => Self::from_parsed_primitive(pt, exec_state, source_range),
 | 
			
		||||
            Type::Array { ty, len } => {
 | 
			
		||||
                Self::from_parsed_primitive(ty, exec_state, source_range).map(|t| RuntimeType::Array(Box::new(t), len))
 | 
			
		||||
            }
 | 
			
		||||
            Type::Union { tys } => tys
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|t| Self::from_parsed_primitive(t.inner, exec_state, source_range))
 | 
			
		||||
                .collect::<Result<Vec<_>, CompilationError>>()
 | 
			
		||||
                .map(RuntimeType::Union),
 | 
			
		||||
            Type::Object { properties } => properties
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|p| {
 | 
			
		||||
                    RuntimeType::from_parsed(p.type_.unwrap().inner, exec_state, source_range)
 | 
			
		||||
                        .map(|ty| (p.identifier.inner.name, ty))
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Result<Vec<_>, CompilationError>>()
 | 
			
		||||
                .map(RuntimeType::Object),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn from_parsed_primitive(
 | 
			
		||||
        value: AstPrimitiveType,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Self, CompilationError> {
 | 
			
		||||
        Ok(match value {
 | 
			
		||||
            AstPrimitiveType::String => RuntimeType::Primitive(PrimitiveType::String),
 | 
			
		||||
            AstPrimitiveType::Boolean => RuntimeType::Primitive(PrimitiveType::Boolean),
 | 
			
		||||
            AstPrimitiveType::Number(suffix) => RuntimeType::Primitive(PrimitiveType::Number(
 | 
			
		||||
                NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
 | 
			
		||||
            )),
 | 
			
		||||
            AstPrimitiveType::Named(name) => {
 | 
			
		||||
                let ty_val = exec_state
 | 
			
		||||
                    .stack()
 | 
			
		||||
                    .get(&format!("{}{}", memory::TYPE_PREFIX, name.name), source_range)
 | 
			
		||||
                    .map_err(|_| CompilationError::err(source_range, format!("Unknown type: {}", name.name)))?;
 | 
			
		||||
 | 
			
		||||
                match ty_val {
 | 
			
		||||
                    KclValue::Type { value, .. } => match value {
 | 
			
		||||
                        TypeDef::RustRepr(ty, _) => RuntimeType::Primitive(ty.clone()),
 | 
			
		||||
                        TypeDef::Alias(ty) => ty.clone(),
 | 
			
		||||
                    },
 | 
			
		||||
                    _ => unreachable!(),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn human_friendly_type(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            RuntimeType::Primitive(ty) => ty.to_string(),
 | 
			
		||||
            RuntimeType::Array(ty, ArrayLen::None) => format!("an array of {}", ty.display_multiple()),
 | 
			
		||||
            RuntimeType::Array(ty, ArrayLen::NonEmpty) => format!("one or more {}", ty.display_multiple()),
 | 
			
		||||
            RuntimeType::Array(ty, ArrayLen::Known(n)) => format!("an array of {n} {}", ty.display_multiple()),
 | 
			
		||||
            RuntimeType::Union(tys) => tys
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(Self::human_friendly_type)
 | 
			
		||||
                .collect::<Vec<_>>()
 | 
			
		||||
                .join(" or "),
 | 
			
		||||
            RuntimeType::Tuple(tys) => format!(
 | 
			
		||||
                "an array with values of types ({})",
 | 
			
		||||
                tys.iter().map(Self::human_friendly_type).collect::<Vec<_>>().join(", ")
 | 
			
		||||
            ),
 | 
			
		||||
            RuntimeType::Object(_) => format!("an object with fields {}", self),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Subtype with no coercion, including refining numeric types.
 | 
			
		||||
    fn subtype(&self, sup: &RuntimeType) -> bool {
 | 
			
		||||
        use RuntimeType::*;
 | 
			
		||||
 | 
			
		||||
        match (self, sup) {
 | 
			
		||||
            (Primitive(t1), Primitive(t2)) => t1 == t2,
 | 
			
		||||
            // TODO arrays could be covariant
 | 
			
		||||
            (Array(t1, l1), Array(t2, l2)) => t1 == t2 && l1.subtype(*l2),
 | 
			
		||||
            (Tuple(t1), Tuple(t2)) => t1 == t2,
 | 
			
		||||
            (Tuple(t1), Array(t2, l2)) => (l2.satisfied(t1.len())) && t1.iter().all(|t| t == &**t2),
 | 
			
		||||
            (Union(ts1), Union(ts2)) => ts1.iter().all(|t| ts2.contains(t)),
 | 
			
		||||
            (t1, Union(ts2)) => ts2.contains(t1),
 | 
			
		||||
            // TODO record subtyping - subtype can be larger, fields can be covariant.
 | 
			
		||||
            (Object(t1), Object(t2)) => t1 == t2,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn display_multiple(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            RuntimeType::Primitive(ty) => ty.display_multiple(),
 | 
			
		||||
            RuntimeType::Array(..) => "arrays".to_owned(),
 | 
			
		||||
            RuntimeType::Union(tys) => tys
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|t| t.display_multiple())
 | 
			
		||||
                .collect::<Vec<_>>()
 | 
			
		||||
                .join(" or "),
 | 
			
		||||
            RuntimeType::Tuple(_) => "arrays".to_owned(),
 | 
			
		||||
            RuntimeType::Object(_) => format!("objects with fields {self}"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for RuntimeType {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            RuntimeType::Primitive(t) => t.fmt(f),
 | 
			
		||||
            RuntimeType::Array(t, l) => match l {
 | 
			
		||||
                ArrayLen::None => write!(f, "[{t}]"),
 | 
			
		||||
                ArrayLen::NonEmpty => write!(f, "[{t}; 1+]"),
 | 
			
		||||
                ArrayLen::Known(n) => write!(f, "[{t}; {n}]"),
 | 
			
		||||
            },
 | 
			
		||||
            RuntimeType::Tuple(ts) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "[{}]",
 | 
			
		||||
                ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
 | 
			
		||||
            ),
 | 
			
		||||
            RuntimeType::Union(ts) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "{}",
 | 
			
		||||
                ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(" | ")
 | 
			
		||||
            ),
 | 
			
		||||
            RuntimeType::Object(items) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "{{ {} }}",
 | 
			
		||||
                items
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|(n, t)| format!("{n}: {t}"))
 | 
			
		||||
                    .collect::<Vec<_>>()
 | 
			
		||||
                    .join(", ")
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, ts_rs::TS, JsonSchema)]
 | 
			
		||||
pub enum ArrayLen {
 | 
			
		||||
    None,
 | 
			
		||||
    NonEmpty,
 | 
			
		||||
    Known(usize),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ArrayLen {
 | 
			
		||||
    pub fn subtype(self, other: ArrayLen) -> bool {
 | 
			
		||||
        match (self, other) {
 | 
			
		||||
            (_, ArrayLen::None) => true,
 | 
			
		||||
            (ArrayLen::NonEmpty, ArrayLen::NonEmpty) => true,
 | 
			
		||||
            (ArrayLen::Known(size), ArrayLen::NonEmpty) if size > 0 => true,
 | 
			
		||||
            (ArrayLen::Known(s1), ArrayLen::Known(s2)) if s1 == s2 => true,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// True if the length constraint is satisfied by the supplied length.
 | 
			
		||||
    fn satisfied(self, len: usize) -> bool {
 | 
			
		||||
        match self {
 | 
			
		||||
            ArrayLen::None => true,
 | 
			
		||||
            ArrayLen::NonEmpty => len > 0,
 | 
			
		||||
            ArrayLen::Known(s) => len == s,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub enum PrimitiveType {
 | 
			
		||||
    Number(NumericType),
 | 
			
		||||
    String,
 | 
			
		||||
    Boolean,
 | 
			
		||||
    Tag,
 | 
			
		||||
    Sketch,
 | 
			
		||||
    Solid,
 | 
			
		||||
    Plane,
 | 
			
		||||
    Helix,
 | 
			
		||||
    Face,
 | 
			
		||||
    ImportedGeometry,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PrimitiveType {
 | 
			
		||||
    fn display_multiple(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
 | 
			
		||||
            PrimitiveType::Number(_) => "numbers".to_owned(),
 | 
			
		||||
            PrimitiveType::String => "strings".to_owned(),
 | 
			
		||||
            PrimitiveType::Boolean => "bools".to_owned(),
 | 
			
		||||
            PrimitiveType::Sketch => "Sketches".to_owned(),
 | 
			
		||||
            PrimitiveType::Solid => "Solids".to_owned(),
 | 
			
		||||
            PrimitiveType::Plane => "Planes".to_owned(),
 | 
			
		||||
            PrimitiveType::Helix => "Helices".to_owned(),
 | 
			
		||||
            PrimitiveType::Face => "Faces".to_owned(),
 | 
			
		||||
            PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
 | 
			
		||||
            PrimitiveType::Tag => "tags".to_owned(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for PrimitiveType {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
 | 
			
		||||
            PrimitiveType::Number(_) => write!(f, "number"),
 | 
			
		||||
            PrimitiveType::String => write!(f, "string"),
 | 
			
		||||
            PrimitiveType::Boolean => write!(f, "bool"),
 | 
			
		||||
            PrimitiveType::Tag => write!(f, "tag"),
 | 
			
		||||
            PrimitiveType::Sketch => write!(f, "Sketch"),
 | 
			
		||||
            PrimitiveType::Solid => write!(f, "Solid"),
 | 
			
		||||
            PrimitiveType::Plane => write!(f, "Plane"),
 | 
			
		||||
            PrimitiveType::Face => write!(f, "Face"),
 | 
			
		||||
            PrimitiveType::Helix => write!(f, "Helix"),
 | 
			
		||||
            PrimitiveType::ImportedGeometry => write!(f, "imported geometry"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum NumericType {
 | 
			
		||||
    // Specified by the user (directly or indirectly)
 | 
			
		||||
    Known(UnitType),
 | 
			
		||||
    // Unspecified, using defaults
 | 
			
		||||
    Default { len: UnitLen, angle: UnitAngle },
 | 
			
		||||
    // Exceeded the ability of the type system to track.
 | 
			
		||||
    Unknown,
 | 
			
		||||
    // Type info has been explicitly cast away.
 | 
			
		||||
    Any,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NumericType {
 | 
			
		||||
    pub fn count() -> Self {
 | 
			
		||||
        NumericType::Known(UnitType::Count)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ty0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Combine two types in addition-like operations.
 | 
			
		||||
    pub fn combine_add(a: NumericType, b: NumericType) -> NumericType {
 | 
			
		||||
        if a == b {
 | 
			
		||||
            return a;
 | 
			
		||||
        }
 | 
			
		||||
        NumericType::Unknown
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Combine two types in multiplication-like operations.
 | 
			
		||||
    pub fn combine_mul(a: NumericType, b: NumericType) -> NumericType {
 | 
			
		||||
        if a == NumericType::count() {
 | 
			
		||||
            return b;
 | 
			
		||||
        }
 | 
			
		||||
        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 {
 | 
			
		||||
        match suffix {
 | 
			
		||||
            NumericSuffix::None => NumericType::Default {
 | 
			
		||||
                len: settings.default_length_units,
 | 
			
		||||
                angle: settings.default_angle_units,
 | 
			
		||||
            },
 | 
			
		||||
            NumericSuffix::Count => NumericType::Known(UnitType::Count),
 | 
			
		||||
            NumericSuffix::Mm => NumericType::Known(UnitType::Length(UnitLen::Mm)),
 | 
			
		||||
            NumericSuffix::Cm => NumericType::Known(UnitType::Length(UnitLen::Cm)),
 | 
			
		||||
            NumericSuffix::M => NumericType::Known(UnitType::Length(UnitLen::M)),
 | 
			
		||||
            NumericSuffix::Inch => NumericType::Known(UnitType::Length(UnitLen::Inches)),
 | 
			
		||||
            NumericSuffix::Ft => NumericType::Known(UnitType::Length(UnitLen::Feet)),
 | 
			
		||||
            NumericSuffix::Yd => NumericType::Known(UnitType::Length(UnitLen::Yards)),
 | 
			
		||||
            NumericSuffix::Deg => NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
 | 
			
		||||
            NumericSuffix::Rad => NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitLen> for NumericType {
 | 
			
		||||
    fn from(value: UnitLen) -> Self {
 | 
			
		||||
        NumericType::Known(UnitType::Length(value))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitAngle> for NumericType {
 | 
			
		||||
    fn from(value: UnitAngle) -> Self {
 | 
			
		||||
        NumericType::Known(UnitType::Angle(value))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum UnitType {
 | 
			
		||||
    Count,
 | 
			
		||||
    Length(UnitLen),
 | 
			
		||||
    Angle(UnitAngle),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for UnitType {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            UnitType::Count => write!(f, "_"),
 | 
			
		||||
            UnitType::Length(l) => l.fmt(f),
 | 
			
		||||
            UnitType::Angle(a) => a.fmt(f),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO called UnitLen so as not to clash with UnitLength in settings)
 | 
			
		||||
/// A unit of length.
 | 
			
		||||
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum UnitLen {
 | 
			
		||||
    #[default]
 | 
			
		||||
    Mm,
 | 
			
		||||
    Cm,
 | 
			
		||||
    M,
 | 
			
		||||
    Inches,
 | 
			
		||||
    Feet,
 | 
			
		||||
    Yards,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for UnitLen {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            UnitLen::Mm => write!(f, "mm"),
 | 
			
		||||
            UnitLen::Cm => write!(f, "cm"),
 | 
			
		||||
            UnitLen::M => write!(f, "m"),
 | 
			
		||||
            UnitLen::Inches => write!(f, "in"),
 | 
			
		||||
            UnitLen::Feet => write!(f, "ft"),
 | 
			
		||||
            UnitLen::Yards => write!(f, "yd"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TryFrom<NumericSuffix> for UnitLen {
 | 
			
		||||
    type Error = ();
 | 
			
		||||
 | 
			
		||||
    fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
 | 
			
		||||
        match suffix {
 | 
			
		||||
            NumericSuffix::Mm => Ok(Self::Mm),
 | 
			
		||||
            NumericSuffix::Cm => Ok(Self::Cm),
 | 
			
		||||
            NumericSuffix::M => Ok(Self::M),
 | 
			
		||||
            NumericSuffix::Inch => Ok(Self::Inches),
 | 
			
		||||
            NumericSuffix::Ft => Ok(Self::Feet),
 | 
			
		||||
            NumericSuffix::Yd => Ok(Self::Yards),
 | 
			
		||||
            _ => Err(()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<crate::UnitLength> for UnitLen {
 | 
			
		||||
    fn from(unit: crate::UnitLength) -> Self {
 | 
			
		||||
        match unit {
 | 
			
		||||
            crate::UnitLength::Cm => UnitLen::Cm,
 | 
			
		||||
            crate::UnitLength::Ft => UnitLen::Feet,
 | 
			
		||||
            crate::UnitLength::In => UnitLen::Inches,
 | 
			
		||||
            crate::UnitLength::M => UnitLen::M,
 | 
			
		||||
            crate::UnitLength::Mm => UnitLen::Mm,
 | 
			
		||||
            crate::UnitLength::Yd => UnitLen::Yards,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitLen> for crate::UnitLength {
 | 
			
		||||
    fn from(unit: UnitLen) -> Self {
 | 
			
		||||
        match unit {
 | 
			
		||||
            UnitLen::Cm => crate::UnitLength::Cm,
 | 
			
		||||
            UnitLen::Feet => crate::UnitLength::Ft,
 | 
			
		||||
            UnitLen::Inches => crate::UnitLength::In,
 | 
			
		||||
            UnitLen::M => crate::UnitLength::M,
 | 
			
		||||
            UnitLen::Mm => crate::UnitLength::Mm,
 | 
			
		||||
            UnitLen::Yards => crate::UnitLength::Yd,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitLen> for kittycad_modeling_cmds::units::UnitLength {
 | 
			
		||||
    fn from(unit: UnitLen) -> Self {
 | 
			
		||||
        match unit {
 | 
			
		||||
            UnitLen::Cm => kittycad_modeling_cmds::units::UnitLength::Centimeters,
 | 
			
		||||
            UnitLen::Feet => kittycad_modeling_cmds::units::UnitLength::Feet,
 | 
			
		||||
            UnitLen::Inches => kittycad_modeling_cmds::units::UnitLength::Inches,
 | 
			
		||||
            UnitLen::M => kittycad_modeling_cmds::units::UnitLength::Meters,
 | 
			
		||||
            UnitLen::Mm => kittycad_modeling_cmds::units::UnitLength::Millimeters,
 | 
			
		||||
            UnitLen::Yards => kittycad_modeling_cmds::units::UnitLength::Yards,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A unit of angle.
 | 
			
		||||
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum UnitAngle {
 | 
			
		||||
    #[default]
 | 
			
		||||
    Degrees,
 | 
			
		||||
    Radians,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for UnitAngle {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            UnitAngle::Degrees => write!(f, "deg"),
 | 
			
		||||
            UnitAngle::Radians => write!(f, "rad"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TryFrom<NumericSuffix> for UnitAngle {
 | 
			
		||||
    type Error = ();
 | 
			
		||||
 | 
			
		||||
    fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
 | 
			
		||||
        match suffix {
 | 
			
		||||
            NumericSuffix::Deg => Ok(Self::Degrees),
 | 
			
		||||
            NumericSuffix::Rad => Ok(Self::Radians),
 | 
			
		||||
            _ => Err(()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1055,7 +1055,7 @@ mod env {
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::execution::kcl_value::{FunctionSource, NumericType};
 | 
			
		||||
    use crate::execution::{kcl_value::FunctionSource, types::NumericType};
 | 
			
		||||
 | 
			
		||||
    fn sr() -> SourceRange {
 | 
			
		||||
        SourceRange::default()
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ pub(crate) use import::{
 | 
			
		||||
    import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
 | 
			
		||||
};
 | 
			
		||||
use indexmap::IndexMap;
 | 
			
		||||
pub use kcl_value::{KclObjectFields, KclValue, PrimitiveType, UnitAngle, UnitLen};
 | 
			
		||||
pub use kcl_value::{KclObjectFields, KclValue};
 | 
			
		||||
use kcmc::{
 | 
			
		||||
    each_cmd as mcmd,
 | 
			
		||||
    ok_response::{output::TakeSnapshot, OkModelingCmdResponse},
 | 
			
		||||
@ -34,6 +34,7 @@ use crate::{
 | 
			
		||||
    execution::{
 | 
			
		||||
        artifact::build_artifact_graph,
 | 
			
		||||
        cache::{CacheInformation, CacheResult},
 | 
			
		||||
        types::{UnitAngle, UnitLen},
 | 
			
		||||
    },
 | 
			
		||||
    fs::FileManager,
 | 
			
		||||
    modules::{ModuleId, ModulePath},
 | 
			
		||||
@ -55,6 +56,7 @@ mod import;
 | 
			
		||||
pub(crate) mod kcl_value;
 | 
			
		||||
mod memory;
 | 
			
		||||
mod state;
 | 
			
		||||
pub(crate) mod types;
 | 
			
		||||
 | 
			
		||||
/// Outcome of executing a program.  This is used in TS.
 | 
			
		||||
#[derive(Debug, Clone, Serialize, ts_rs::TS)]
 | 
			
		||||
 | 
			
		||||
@ -12,10 +12,9 @@ use crate::{
 | 
			
		||||
    execution::{
 | 
			
		||||
        annotations,
 | 
			
		||||
        id_generator::IdGenerator,
 | 
			
		||||
        kcl_value,
 | 
			
		||||
        memory::{ProgramMemory, Stack},
 | 
			
		||||
        Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, EnvironmentRef, ExecOutcome, ExecutorSettings, KclValue,
 | 
			
		||||
        Operation, UnitAngle, UnitLen,
 | 
			
		||||
        types, Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, EnvironmentRef, ExecOutcome, ExecutorSettings,
 | 
			
		||||
        KclValue, Operation, UnitAngle, UnitLen,
 | 
			
		||||
    },
 | 
			
		||||
    modules::{ModuleId, ModuleInfo, ModuleLoader, ModulePath, ModuleRepr, ModuleSource},
 | 
			
		||||
    parsing::ast::types::Annotation,
 | 
			
		||||
@ -310,8 +309,8 @@ impl ModuleState {
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct MetaSettings {
 | 
			
		||||
    pub default_length_units: kcl_value::UnitLen,
 | 
			
		||||
    pub default_angle_units: kcl_value::UnitAngle,
 | 
			
		||||
    pub default_length_units: types::UnitLen,
 | 
			
		||||
    pub default_angle_units: types::UnitAngle,
 | 
			
		||||
    pub std_path: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -326,12 +325,12 @@ impl MetaSettings {
 | 
			
		||||
            match &*p.inner.key.name {
 | 
			
		||||
                annotations::SETTINGS_UNIT_LENGTH => {
 | 
			
		||||
                    let value = annotations::expect_ident(&p.inner.value)?;
 | 
			
		||||
                    let value = kcl_value::UnitLen::from_str(value, annotation.as_source_range())?;
 | 
			
		||||
                    let value = types::UnitLen::from_str(value, annotation.as_source_range())?;
 | 
			
		||||
                    self.default_length_units = value;
 | 
			
		||||
                }
 | 
			
		||||
                annotations::SETTINGS_UNIT_ANGLE => {
 | 
			
		||||
                    let value = annotations::expect_ident(&p.inner.value)?;
 | 
			
		||||
                    let value = kcl_value::UnitAngle::from_str(value, annotation.as_source_range())?;
 | 
			
		||||
                    let value = types::UnitAngle::from_str(value, annotation.as_source_range())?;
 | 
			
		||||
                    self.default_angle_units = value;
 | 
			
		||||
                }
 | 
			
		||||
                name => {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										770
									
								
								rust/kcl-lib/src/execution/types.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										770
									
								
								rust/kcl-lib/src/execution/types.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,770 @@
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use super::{
 | 
			
		||||
    memory::{self},
 | 
			
		||||
    Point3d,
 | 
			
		||||
};
 | 
			
		||||
use crate::{
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{KclValue, TypeDef},
 | 
			
		||||
        ExecState, Plane,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::{
 | 
			
		||||
        ast::types::{PrimitiveType as AstPrimitiveType, Type},
 | 
			
		||||
        token::NumericSuffix,
 | 
			
		||||
    },
 | 
			
		||||
    std::args::FromKclValue,
 | 
			
		||||
    CompilationError, SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub enum RuntimeType {
 | 
			
		||||
    Primitive(PrimitiveType),
 | 
			
		||||
    Array(Box<RuntimeType>, ArrayLen),
 | 
			
		||||
    Union(Vec<RuntimeType>),
 | 
			
		||||
    Tuple(Vec<RuntimeType>),
 | 
			
		||||
    Object(Vec<(String, RuntimeType)>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RuntimeType {
 | 
			
		||||
    /// `[Sketch; 1+]`
 | 
			
		||||
    pub fn sketches() -> Self {
 | 
			
		||||
        RuntimeType::Array(
 | 
			
		||||
            Box::new(RuntimeType::Primitive(PrimitiveType::Sketch)),
 | 
			
		||||
            ArrayLen::NonEmpty,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// `[Solid; 1+]`
 | 
			
		||||
    pub fn solids() -> Self {
 | 
			
		||||
        RuntimeType::Array(
 | 
			
		||||
            Box::new(RuntimeType::Primitive(PrimitiveType::Solid)),
 | 
			
		||||
            ArrayLen::NonEmpty,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn solid() -> Self {
 | 
			
		||||
        RuntimeType::Primitive(PrimitiveType::Solid)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn imported() -> Self {
 | 
			
		||||
        RuntimeType::Primitive(PrimitiveType::ImportedGeometry)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn from_parsed(
 | 
			
		||||
        value: Type,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Self, CompilationError> {
 | 
			
		||||
        match value {
 | 
			
		||||
            Type::Primitive(pt) => Self::from_parsed_primitive(pt, exec_state, source_range),
 | 
			
		||||
            Type::Array { ty, len } => {
 | 
			
		||||
                Self::from_parsed_primitive(ty, exec_state, source_range).map(|t| RuntimeType::Array(Box::new(t), len))
 | 
			
		||||
            }
 | 
			
		||||
            Type::Union { tys } => tys
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|t| Self::from_parsed_primitive(t.inner, exec_state, source_range))
 | 
			
		||||
                .collect::<Result<Vec<_>, CompilationError>>()
 | 
			
		||||
                .map(RuntimeType::Union),
 | 
			
		||||
            Type::Object { properties } => properties
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|p| {
 | 
			
		||||
                    RuntimeType::from_parsed(p.type_.unwrap().inner, exec_state, source_range)
 | 
			
		||||
                        .map(|ty| (p.identifier.inner.name, ty))
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Result<Vec<_>, CompilationError>>()
 | 
			
		||||
                .map(RuntimeType::Object),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn from_parsed_primitive(
 | 
			
		||||
        value: AstPrimitiveType,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Self, CompilationError> {
 | 
			
		||||
        Ok(match value {
 | 
			
		||||
            AstPrimitiveType::String => RuntimeType::Primitive(PrimitiveType::String),
 | 
			
		||||
            AstPrimitiveType::Boolean => RuntimeType::Primitive(PrimitiveType::Boolean),
 | 
			
		||||
            AstPrimitiveType::Number(suffix) => RuntimeType::Primitive(PrimitiveType::Number(
 | 
			
		||||
                NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
 | 
			
		||||
            )),
 | 
			
		||||
            AstPrimitiveType::Named(name) => {
 | 
			
		||||
                let ty_val = exec_state
 | 
			
		||||
                    .stack()
 | 
			
		||||
                    .get(&format!("{}{}", memory::TYPE_PREFIX, name.name), source_range)
 | 
			
		||||
                    .map_err(|_| CompilationError::err(source_range, format!("Unknown type: {}", name.name)))?;
 | 
			
		||||
 | 
			
		||||
                match ty_val {
 | 
			
		||||
                    KclValue::Type { value, .. } => match value {
 | 
			
		||||
                        TypeDef::RustRepr(ty, _) => RuntimeType::Primitive(ty.clone()),
 | 
			
		||||
                        TypeDef::Alias(ty) => ty.clone(),
 | 
			
		||||
                    },
 | 
			
		||||
                    _ => unreachable!(),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn human_friendly_type(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            RuntimeType::Primitive(ty) => ty.to_string(),
 | 
			
		||||
            RuntimeType::Array(ty, ArrayLen::None) => format!("an array of {}", ty.display_multiple()),
 | 
			
		||||
            RuntimeType::Array(ty, ArrayLen::NonEmpty) => format!("one or more {}", ty.display_multiple()),
 | 
			
		||||
            RuntimeType::Array(ty, ArrayLen::Known(n)) => format!("an array of {n} {}", ty.display_multiple()),
 | 
			
		||||
            RuntimeType::Union(tys) => tys
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(Self::human_friendly_type)
 | 
			
		||||
                .collect::<Vec<_>>()
 | 
			
		||||
                .join(" or "),
 | 
			
		||||
            RuntimeType::Tuple(tys) => format!(
 | 
			
		||||
                "an array with values of types ({})",
 | 
			
		||||
                tys.iter().map(Self::human_friendly_type).collect::<Vec<_>>().join(", ")
 | 
			
		||||
            ),
 | 
			
		||||
            RuntimeType::Object(_) => format!("an object with fields {}", self),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Subtype with no coercion, including refining numeric types.
 | 
			
		||||
    fn subtype(&self, sup: &RuntimeType) -> bool {
 | 
			
		||||
        use RuntimeType::*;
 | 
			
		||||
 | 
			
		||||
        match (self, sup) {
 | 
			
		||||
            (Primitive(t1), Primitive(t2)) => t1 == t2,
 | 
			
		||||
            // TODO arrays could be covariant
 | 
			
		||||
            (Array(t1, l1), Array(t2, l2)) => t1 == t2 && l1.subtype(*l2),
 | 
			
		||||
            (Tuple(t1), Tuple(t2)) => t1 == t2,
 | 
			
		||||
            (Tuple(t1), Array(t2, l2)) => (l2.satisfied(t1.len())) && t1.iter().all(|t| t == &**t2),
 | 
			
		||||
            (Union(ts1), Union(ts2)) => ts1.iter().all(|t| ts2.contains(t)),
 | 
			
		||||
            (t1, Union(ts2)) => ts2.contains(t1),
 | 
			
		||||
            // TODO record subtyping - subtype can be larger, fields can be covariant.
 | 
			
		||||
            (Object(t1), Object(t2)) => t1 == t2,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn display_multiple(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            RuntimeType::Primitive(ty) => ty.display_multiple(),
 | 
			
		||||
            RuntimeType::Array(..) => "arrays".to_owned(),
 | 
			
		||||
            RuntimeType::Union(tys) => tys
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|t| t.display_multiple())
 | 
			
		||||
                .collect::<Vec<_>>()
 | 
			
		||||
                .join(" or "),
 | 
			
		||||
            RuntimeType::Tuple(_) => "arrays".to_owned(),
 | 
			
		||||
            RuntimeType::Object(_) => format!("objects with fields {self}"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for RuntimeType {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            RuntimeType::Primitive(t) => t.fmt(f),
 | 
			
		||||
            RuntimeType::Array(t, l) => match l {
 | 
			
		||||
                ArrayLen::None => write!(f, "[{t}]"),
 | 
			
		||||
                ArrayLen::NonEmpty => write!(f, "[{t}; 1+]"),
 | 
			
		||||
                ArrayLen::Known(n) => write!(f, "[{t}; {n}]"),
 | 
			
		||||
            },
 | 
			
		||||
            RuntimeType::Tuple(ts) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "[{}]",
 | 
			
		||||
                ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
 | 
			
		||||
            ),
 | 
			
		||||
            RuntimeType::Union(ts) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "{}",
 | 
			
		||||
                ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(" | ")
 | 
			
		||||
            ),
 | 
			
		||||
            RuntimeType::Object(items) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "{{ {} }}",
 | 
			
		||||
                items
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|(n, t)| format!("{n}: {t}"))
 | 
			
		||||
                    .collect::<Vec<_>>()
 | 
			
		||||
                    .join(", ")
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, ts_rs::TS, JsonSchema)]
 | 
			
		||||
pub enum ArrayLen {
 | 
			
		||||
    None,
 | 
			
		||||
    NonEmpty,
 | 
			
		||||
    Known(usize),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ArrayLen {
 | 
			
		||||
    pub fn subtype(self, other: ArrayLen) -> bool {
 | 
			
		||||
        match (self, other) {
 | 
			
		||||
            (_, ArrayLen::None) => true,
 | 
			
		||||
            (ArrayLen::NonEmpty, ArrayLen::NonEmpty) => true,
 | 
			
		||||
            (ArrayLen::Known(size), ArrayLen::NonEmpty) if size > 0 => true,
 | 
			
		||||
            (ArrayLen::Known(s1), ArrayLen::Known(s2)) if s1 == s2 => true,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// True if the length constraint is satisfied by the supplied length.
 | 
			
		||||
    fn satisfied(self, len: usize) -> bool {
 | 
			
		||||
        match self {
 | 
			
		||||
            ArrayLen::None => true,
 | 
			
		||||
            ArrayLen::NonEmpty => len > 0,
 | 
			
		||||
            ArrayLen::Known(s) => len == s,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub enum PrimitiveType {
 | 
			
		||||
    Number(NumericType),
 | 
			
		||||
    String,
 | 
			
		||||
    Boolean,
 | 
			
		||||
    Tag,
 | 
			
		||||
    Sketch,
 | 
			
		||||
    Solid,
 | 
			
		||||
    Plane,
 | 
			
		||||
    Helix,
 | 
			
		||||
    Face,
 | 
			
		||||
    ImportedGeometry,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PrimitiveType {
 | 
			
		||||
    fn display_multiple(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
 | 
			
		||||
            PrimitiveType::Number(_) => "numbers".to_owned(),
 | 
			
		||||
            PrimitiveType::String => "strings".to_owned(),
 | 
			
		||||
            PrimitiveType::Boolean => "bools".to_owned(),
 | 
			
		||||
            PrimitiveType::Sketch => "Sketches".to_owned(),
 | 
			
		||||
            PrimitiveType::Solid => "Solids".to_owned(),
 | 
			
		||||
            PrimitiveType::Plane => "Planes".to_owned(),
 | 
			
		||||
            PrimitiveType::Helix => "Helices".to_owned(),
 | 
			
		||||
            PrimitiveType::Face => "Faces".to_owned(),
 | 
			
		||||
            PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
 | 
			
		||||
            PrimitiveType::Tag => "tags".to_owned(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for PrimitiveType {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
 | 
			
		||||
            PrimitiveType::Number(_) => write!(f, "number"),
 | 
			
		||||
            PrimitiveType::String => write!(f, "string"),
 | 
			
		||||
            PrimitiveType::Boolean => write!(f, "bool"),
 | 
			
		||||
            PrimitiveType::Tag => write!(f, "tag"),
 | 
			
		||||
            PrimitiveType::Sketch => write!(f, "Sketch"),
 | 
			
		||||
            PrimitiveType::Solid => write!(f, "Solid"),
 | 
			
		||||
            PrimitiveType::Plane => write!(f, "Plane"),
 | 
			
		||||
            PrimitiveType::Face => write!(f, "Face"),
 | 
			
		||||
            PrimitiveType::Helix => write!(f, "Helix"),
 | 
			
		||||
            PrimitiveType::ImportedGeometry => write!(f, "imported geometry"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum NumericType {
 | 
			
		||||
    // Specified by the user (directly or indirectly)
 | 
			
		||||
    Known(UnitType),
 | 
			
		||||
    // Unspecified, using defaults
 | 
			
		||||
    Default { len: UnitLen, angle: UnitAngle },
 | 
			
		||||
    // Exceeded the ability of the type system to track.
 | 
			
		||||
    Unknown,
 | 
			
		||||
    // Type info has been explicitly cast away.
 | 
			
		||||
    Any,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NumericType {
 | 
			
		||||
    pub fn count() -> Self {
 | 
			
		||||
        NumericType::Known(UnitType::Count)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ty0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Combine two types in addition-like operations.
 | 
			
		||||
    pub fn combine_add(a: NumericType, b: NumericType) -> NumericType {
 | 
			
		||||
        if a == b {
 | 
			
		||||
            return a;
 | 
			
		||||
        }
 | 
			
		||||
        NumericType::Unknown
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Combine two types in multiplication-like operations.
 | 
			
		||||
    pub fn combine_mul(a: NumericType, b: NumericType) -> NumericType {
 | 
			
		||||
        if a == NumericType::count() {
 | 
			
		||||
            return b;
 | 
			
		||||
        }
 | 
			
		||||
        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 {
 | 
			
		||||
        match suffix {
 | 
			
		||||
            NumericSuffix::None => NumericType::Default {
 | 
			
		||||
                len: settings.default_length_units,
 | 
			
		||||
                angle: settings.default_angle_units,
 | 
			
		||||
            },
 | 
			
		||||
            NumericSuffix::Count => NumericType::Known(UnitType::Count),
 | 
			
		||||
            NumericSuffix::Mm => NumericType::Known(UnitType::Length(UnitLen::Mm)),
 | 
			
		||||
            NumericSuffix::Cm => NumericType::Known(UnitType::Length(UnitLen::Cm)),
 | 
			
		||||
            NumericSuffix::M => NumericType::Known(UnitType::Length(UnitLen::M)),
 | 
			
		||||
            NumericSuffix::Inch => NumericType::Known(UnitType::Length(UnitLen::Inches)),
 | 
			
		||||
            NumericSuffix::Ft => NumericType::Known(UnitType::Length(UnitLen::Feet)),
 | 
			
		||||
            NumericSuffix::Yd => NumericType::Known(UnitType::Length(UnitLen::Yards)),
 | 
			
		||||
            NumericSuffix::Deg => NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
 | 
			
		||||
            NumericSuffix::Rad => NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitLen> for NumericType {
 | 
			
		||||
    fn from(value: UnitLen) -> Self {
 | 
			
		||||
        NumericType::Known(UnitType::Length(value))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitAngle> for NumericType {
 | 
			
		||||
    fn from(value: UnitAngle) -> Self {
 | 
			
		||||
        NumericType::Known(UnitType::Angle(value))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum UnitType {
 | 
			
		||||
    Count,
 | 
			
		||||
    Length(UnitLen),
 | 
			
		||||
    Angle(UnitAngle),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for UnitType {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            UnitType::Count => write!(f, "_"),
 | 
			
		||||
            UnitType::Length(l) => l.fmt(f),
 | 
			
		||||
            UnitType::Angle(a) => a.fmt(f),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO called UnitLen so as not to clash with UnitLength in settings)
 | 
			
		||||
/// A unit of length.
 | 
			
		||||
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum UnitLen {
 | 
			
		||||
    #[default]
 | 
			
		||||
    Mm,
 | 
			
		||||
    Cm,
 | 
			
		||||
    M,
 | 
			
		||||
    Inches,
 | 
			
		||||
    Feet,
 | 
			
		||||
    Yards,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for UnitLen {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            UnitLen::Mm => write!(f, "mm"),
 | 
			
		||||
            UnitLen::Cm => write!(f, "cm"),
 | 
			
		||||
            UnitLen::M => write!(f, "m"),
 | 
			
		||||
            UnitLen::Inches => write!(f, "in"),
 | 
			
		||||
            UnitLen::Feet => write!(f, "ft"),
 | 
			
		||||
            UnitLen::Yards => write!(f, "yd"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TryFrom<NumericSuffix> for UnitLen {
 | 
			
		||||
    type Error = ();
 | 
			
		||||
 | 
			
		||||
    fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
 | 
			
		||||
        match suffix {
 | 
			
		||||
            NumericSuffix::Mm => Ok(Self::Mm),
 | 
			
		||||
            NumericSuffix::Cm => Ok(Self::Cm),
 | 
			
		||||
            NumericSuffix::M => Ok(Self::M),
 | 
			
		||||
            NumericSuffix::Inch => Ok(Self::Inches),
 | 
			
		||||
            NumericSuffix::Ft => Ok(Self::Feet),
 | 
			
		||||
            NumericSuffix::Yd => Ok(Self::Yards),
 | 
			
		||||
            _ => Err(()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<crate::UnitLength> for UnitLen {
 | 
			
		||||
    fn from(unit: crate::UnitLength) -> Self {
 | 
			
		||||
        match unit {
 | 
			
		||||
            crate::UnitLength::Cm => UnitLen::Cm,
 | 
			
		||||
            crate::UnitLength::Ft => UnitLen::Feet,
 | 
			
		||||
            crate::UnitLength::In => UnitLen::Inches,
 | 
			
		||||
            crate::UnitLength::M => UnitLen::M,
 | 
			
		||||
            crate::UnitLength::Mm => UnitLen::Mm,
 | 
			
		||||
            crate::UnitLength::Yd => UnitLen::Yards,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitLen> for crate::UnitLength {
 | 
			
		||||
    fn from(unit: UnitLen) -> Self {
 | 
			
		||||
        match unit {
 | 
			
		||||
            UnitLen::Cm => crate::UnitLength::Cm,
 | 
			
		||||
            UnitLen::Feet => crate::UnitLength::Ft,
 | 
			
		||||
            UnitLen::Inches => crate::UnitLength::In,
 | 
			
		||||
            UnitLen::M => crate::UnitLength::M,
 | 
			
		||||
            UnitLen::Mm => crate::UnitLength::Mm,
 | 
			
		||||
            UnitLen::Yards => crate::UnitLength::Yd,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitLen> for kittycad_modeling_cmds::units::UnitLength {
 | 
			
		||||
    fn from(unit: UnitLen) -> Self {
 | 
			
		||||
        match unit {
 | 
			
		||||
            UnitLen::Cm => kittycad_modeling_cmds::units::UnitLength::Centimeters,
 | 
			
		||||
            UnitLen::Feet => kittycad_modeling_cmds::units::UnitLength::Feet,
 | 
			
		||||
            UnitLen::Inches => kittycad_modeling_cmds::units::UnitLength::Inches,
 | 
			
		||||
            UnitLen::M => kittycad_modeling_cmds::units::UnitLength::Meters,
 | 
			
		||||
            UnitLen::Mm => kittycad_modeling_cmds::units::UnitLength::Millimeters,
 | 
			
		||||
            UnitLen::Yards => kittycad_modeling_cmds::units::UnitLength::Yards,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A unit of angle.
 | 
			
		||||
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum UnitAngle {
 | 
			
		||||
    #[default]
 | 
			
		||||
    Degrees,
 | 
			
		||||
    Radians,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for UnitAngle {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            UnitAngle::Degrees => write!(f, "deg"),
 | 
			
		||||
            UnitAngle::Radians => write!(f, "rad"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TryFrom<NumericSuffix> for UnitAngle {
 | 
			
		||||
    type Error = ();
 | 
			
		||||
 | 
			
		||||
    fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
 | 
			
		||||
        match suffix {
 | 
			
		||||
            NumericSuffix::Deg => Ok(Self::Degrees),
 | 
			
		||||
            NumericSuffix::Rad => Ok(Self::Radians),
 | 
			
		||||
            _ => Err(()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl KclValue {
 | 
			
		||||
    /// True if `self` has a type which is a subtype of `ty` without coercion.
 | 
			
		||||
    pub fn has_type(&self, ty: &RuntimeType) -> bool {
 | 
			
		||||
        let Some(self_ty) = self.principal_type() else {
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self_ty.subtype(ty)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Coerce `self` to a new value which has `ty` as it's closest supertype.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If the result is Some, then:
 | 
			
		||||
    ///   - 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> {
 | 
			
		||||
        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),
 | 
			
		||||
            RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, exec_state),
 | 
			
		||||
            RuntimeType::Union(tys) => self.coerce_to_union_type(tys, exec_state),
 | 
			
		||||
            RuntimeType::Object(tys) => self.coerce_to_object_type(tys, exec_state),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_primitive_type(&self, ty: &PrimitiveType, exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        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::String => match value {
 | 
			
		||||
                KclValue::String { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Boolean => match value {
 | 
			
		||||
                KclValue::Bool { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Sketch => match value {
 | 
			
		||||
                KclValue::Sketch { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Solid => match value {
 | 
			
		||||
                KclValue::Solid { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Plane => match value {
 | 
			
		||||
                KclValue::Plane { .. } => Some(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 id = exec_state.mod_local.id_generator.next_uuid();
 | 
			
		||||
                    let plane = Plane {
 | 
			
		||||
                        id,
 | 
			
		||||
                        artifact_id: id.into(),
 | 
			
		||||
                        origin,
 | 
			
		||||
                        x_axis,
 | 
			
		||||
                        y_axis,
 | 
			
		||||
                        z_axis,
 | 
			
		||||
                        value: super::PlaneType::Uninit,
 | 
			
		||||
                        // TODO use length unit from origin
 | 
			
		||||
                        units: exec_state.length_unit(),
 | 
			
		||||
                        meta: meta.clone(),
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    Some(KclValue::Plane { value: Box::new(plane) })
 | 
			
		||||
                }
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Face => match value {
 | 
			
		||||
                KclValue::Face { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Helix => match value {
 | 
			
		||||
                KclValue::Helix { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::ImportedGeometry => match value {
 | 
			
		||||
                KclValue::ImportedGeometry { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Tag => match value {
 | 
			
		||||
                KclValue::TagDeclarator { .. } => Some(value.clone()),
 | 
			
		||||
                KclValue::TagIdentifier { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_array_type(&self, ty: &RuntimeType, len: ArrayLen, exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::HomArray { value, ty: aty } => {
 | 
			
		||||
                // TODO could check types of values individually
 | 
			
		||||
                if aty != ty {
 | 
			
		||||
                    return None;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let value = match len {
 | 
			
		||||
                    ArrayLen::None => value.clone(),
 | 
			
		||||
                    ArrayLen::NonEmpty => {
 | 
			
		||||
                        if value.is_empty() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value.clone()
 | 
			
		||||
                    }
 | 
			
		||||
                    ArrayLen::Known(n) => {
 | 
			
		||||
                        if n != value.len() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value[..n].to_vec()
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                Some(KclValue::HomArray { value, ty: ty.clone() })
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::MixedArray { value, .. } => {
 | 
			
		||||
                let value = match len {
 | 
			
		||||
                    ArrayLen::None => value.clone(),
 | 
			
		||||
                    ArrayLen::NonEmpty => {
 | 
			
		||||
                        if value.is_empty() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value.clone()
 | 
			
		||||
                    }
 | 
			
		||||
                    ArrayLen::Known(n) => {
 | 
			
		||||
                        if n != value.len() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value[..n].to_vec()
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                let value = value
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|v| v.coerce(ty, exec_state))
 | 
			
		||||
                    .collect::<Option<Vec<_>>>()?;
 | 
			
		||||
 | 
			
		||||
                Some(KclValue::HomArray { value, ty: ty.clone() })
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::KclNone { .. } if len.satisfied(0) => Some(KclValue::HomArray {
 | 
			
		||||
                value: Vec::new(),
 | 
			
		||||
                ty: ty.clone(),
 | 
			
		||||
            }),
 | 
			
		||||
            value if len.satisfied(1) => {
 | 
			
		||||
                if value.has_type(ty) {
 | 
			
		||||
                    Some(KclValue::HomArray {
 | 
			
		||||
                        value: vec![value.clone()],
 | 
			
		||||
                        ty: ty.clone(),
 | 
			
		||||
                    })
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_tuple_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
 | 
			
		||||
                if value.len() < tys.len() {
 | 
			
		||||
                    return None;
 | 
			
		||||
                }
 | 
			
		||||
                let mut result = Vec::new();
 | 
			
		||||
                for (i, t) in tys.iter().enumerate() {
 | 
			
		||||
                    result.push(value[i].coerce(t, exec_state)?);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Some(KclValue::MixedArray {
 | 
			
		||||
                    value: result,
 | 
			
		||||
                    meta: Vec::new(),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::KclNone { meta, .. } if tys.is_empty() => Some(KclValue::MixedArray {
 | 
			
		||||
                value: Vec::new(),
 | 
			
		||||
                meta: meta.clone(),
 | 
			
		||||
            }),
 | 
			
		||||
            value if tys.len() == 1 => {
 | 
			
		||||
                if value.has_type(&tys[0]) {
 | 
			
		||||
                    Some(KclValue::MixedArray {
 | 
			
		||||
                        value: vec![value.clone()],
 | 
			
		||||
                        meta: Vec::new(),
 | 
			
		||||
                    })
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_union_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        for t in tys {
 | 
			
		||||
            if let Some(v) = self.coerce(t, exec_state) {
 | 
			
		||||
                return Some(v);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_object_type(&self, tys: &[(String, RuntimeType)], _exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::Object { value, .. } => {
 | 
			
		||||
                for (s, t) in tys {
 | 
			
		||||
                    // TODO coerce fields
 | 
			
		||||
                    if !value.get(s)?.has_type(t) {
 | 
			
		||||
                        return None;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                // TODO remove non-required fields
 | 
			
		||||
                Some(self.clone())
 | 
			
		||||
            }
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn principal_type(&self) -> Option<RuntimeType> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
 | 
			
		||||
            KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(ty.clone()))),
 | 
			
		||||
            KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)),
 | 
			
		||||
            KclValue::Object { value, .. } => {
 | 
			
		||||
                let properties = value
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|(k, v)| v.principal_type().map(|t| (k.clone(), t)))
 | 
			
		||||
                    .collect::<Option<Vec<_>>>()?;
 | 
			
		||||
                Some(RuntimeType::Object(properties))
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
 | 
			
		||||
            KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
 | 
			
		||||
            KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
 | 
			
		||||
            KclValue::Face { .. } => Some(RuntimeType::Primitive(PrimitiveType::Face)),
 | 
			
		||||
            KclValue::Helix { .. } => Some(RuntimeType::Primitive(PrimitiveType::Helix)),
 | 
			
		||||
            KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
 | 
			
		||||
            KclValue::MixedArray { value, .. } => Some(RuntimeType::Tuple(
 | 
			
		||||
                value.iter().map(|v| v.principal_type()).collect::<Option<Vec<_>>>()?,
 | 
			
		||||
            )),
 | 
			
		||||
            KclValue::HomArray { ty, value, .. } => {
 | 
			
		||||
                Some(RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(value.len())))
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::TagIdentifier(_) | KclValue::TagDeclarator(_) => Some(RuntimeType::Primitive(PrimitiveType::Tag)),
 | 
			
		||||
            KclValue::Function { .. }
 | 
			
		||||
            | KclValue::Module { .. }
 | 
			
		||||
            | KclValue::KclNone { .. }
 | 
			
		||||
            | KclValue::Type { .. }
 | 
			
		||||
            | KclValue::Uuid { .. } => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -198,9 +198,9 @@ impl Type {
 | 
			
		||||
                hasher.update(b"FnArgType::Array");
 | 
			
		||||
                hasher.update(ty.compute_digest());
 | 
			
		||||
                match len {
 | 
			
		||||
                    crate::execution::kcl_value::ArrayLen::None => {}
 | 
			
		||||
                    crate::execution::kcl_value::ArrayLen::NonEmpty => hasher.update(usize::MAX.to_ne_bytes()),
 | 
			
		||||
                    crate::execution::kcl_value::ArrayLen::Known(n) => hasher.update(n.to_ne_bytes()),
 | 
			
		||||
                    crate::execution::types::ArrayLen::None => {}
 | 
			
		||||
                    crate::execution::types::ArrayLen::NonEmpty => hasher.update(usize::MAX.to_ne_bytes()),
 | 
			
		||||
                    crate::execution::types::ArrayLen::Known(n) => hasher.update(n.to_ne_bytes()),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Type::Union { tys } => {
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ pub use crate::parsing::ast::types::{
 | 
			
		||||
use crate::{
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    execution::{annotations, kcl_value::ArrayLen, KclValue, Metadata, TagIdentifier},
 | 
			
		||||
    execution::{annotations, types::ArrayLen, KclValue, Metadata, TagIdentifier},
 | 
			
		||||
    parsing::{ast::digest::Digest, token::NumericSuffix, PIPE_OPERATOR},
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
    ModuleId,
 | 
			
		||||
@ -3994,7 +3994,7 @@ startSketchOn('XY')"#;
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            meta_settings.default_length_units,
 | 
			
		||||
            crate::execution::kcl_value::UnitLen::Inches
 | 
			
		||||
            crate::execution::types::UnitLen::Inches
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -4010,13 +4010,13 @@ startSketchOn('XY')"#;
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            meta_settings.default_length_units,
 | 
			
		||||
            crate::execution::kcl_value::UnitLen::Inches
 | 
			
		||||
            crate::execution::types::UnitLen::Inches
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Edit the ast.
 | 
			
		||||
        let new_program = program
 | 
			
		||||
            .change_meta_settings(crate::execution::MetaSettings {
 | 
			
		||||
                default_length_units: crate::execution::kcl_value::UnitLen::Mm,
 | 
			
		||||
                default_length_units: crate::execution::types::UnitLen::Mm,
 | 
			
		||||
                ..Default::default()
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap();
 | 
			
		||||
@ -4025,10 +4025,7 @@ startSketchOn('XY')"#;
 | 
			
		||||
        assert!(result.is_some());
 | 
			
		||||
        let meta_settings = result.unwrap();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            meta_settings.default_length_units,
 | 
			
		||||
            crate::execution::kcl_value::UnitLen::Mm
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(meta_settings.default_length_units, crate::execution::types::UnitLen::Mm);
 | 
			
		||||
 | 
			
		||||
        let formatted = new_program.recast(&Default::default(), 0);
 | 
			
		||||
 | 
			
		||||
@ -4051,7 +4048,7 @@ startSketchOn('XY')
 | 
			
		||||
        // Edit the ast.
 | 
			
		||||
        let new_program = program
 | 
			
		||||
            .change_meta_settings(crate::execution::MetaSettings {
 | 
			
		||||
                default_length_units: crate::execution::kcl_value::UnitLen::Mm,
 | 
			
		||||
                default_length_units: crate::execution::types::UnitLen::Mm,
 | 
			
		||||
                ..Default::default()
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap();
 | 
			
		||||
@ -4060,10 +4057,7 @@ startSketchOn('XY')
 | 
			
		||||
        assert!(result.is_some());
 | 
			
		||||
        let meta_settings = result.unwrap();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            meta_settings.default_length_units,
 | 
			
		||||
            crate::execution::kcl_value::UnitLen::Mm
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(meta_settings.default_length_units, crate::execution::types::UnitLen::Mm);
 | 
			
		||||
 | 
			
		||||
        let formatted = new_program.recast(&Default::default(), 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ use super::{
 | 
			
		||||
use crate::{
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::{CompilationError, Severity, Tag},
 | 
			
		||||
    execution::kcl_value::ArrayLen,
 | 
			
		||||
    execution::types::ArrayLen,
 | 
			
		||||
    parsing::{
 | 
			
		||||
        ast::types::{
 | 
			
		||||
            Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ use validator::Validate;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{kcl_value::RuntimeType, ExecState, KclValue, Solid},
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, KclValue, Solid},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,9 +12,10 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{FunctionSource, NumericType, RuntimeType},
 | 
			
		||||
        ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, PrimitiveType, Sketch,
 | 
			
		||||
        SketchSurface, Solid, TagIdentifier,
 | 
			
		||||
        kcl_value::FunctionSource,
 | 
			
		||||
        types::{NumericType, PrimitiveType, RuntimeType},
 | 
			
		||||
        ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, Sketch, SketchSurface,
 | 
			
		||||
        Solid, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
 | 
			
		||||
@ -8,8 +8,8 @@ use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::RuntimeType, ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, PrimitiveType,
 | 
			
		||||
        Solid,
 | 
			
		||||
        types::{PrimitiveType, RuntimeType},
 | 
			
		||||
        ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, Solid,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    std::{fillet::EdgeReference, Args},
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ use uuid::Uuid;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::RuntimeType, ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, Sketch, SketchSurface,
 | 
			
		||||
        types::RuntimeType, ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, Sketch, SketchSurface,
 | 
			
		||||
        Solid,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
 | 
			
		||||
@ -11,8 +11,8 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::RuntimeType, EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, PrimitiveType,
 | 
			
		||||
        Solid, TagIdentifier,
 | 
			
		||||
        types::{PrimitiveType, RuntimeType},
 | 
			
		||||
        EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    settings::types::UnitLength,
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{kcl_value::RuntimeType, ExecState, KclValue, Sketch, Solid},
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, KclValue, Sketch, Solid},
 | 
			
		||||
    std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    execution::{ExecState, KclValue},
 | 
			
		||||
    execution::{types::PrimitiveType, ExecState, KclValue},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub type StdFn = fn(
 | 
			
		||||
@ -207,25 +207,13 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn std_ty(path: &str, fn_name: &str) -> (crate::execution::PrimitiveType, StdFnProps) {
 | 
			
		||||
pub(crate) fn std_ty(path: &str, fn_name: &str) -> (PrimitiveType, StdFnProps) {
 | 
			
		||||
    match (path, fn_name) {
 | 
			
		||||
        ("prelude", "Sketch") => (
 | 
			
		||||
            crate::execution::PrimitiveType::Sketch,
 | 
			
		||||
            StdFnProps::default("std::Sketch"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("prelude", "Solid") => (
 | 
			
		||||
            crate::execution::PrimitiveType::Solid,
 | 
			
		||||
            StdFnProps::default("std::Solid"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("prelude", "Plane") => (
 | 
			
		||||
            crate::execution::PrimitiveType::Plane,
 | 
			
		||||
            StdFnProps::default("std::Plane"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("prelude", "Face") => (crate::execution::PrimitiveType::Face, StdFnProps::default("std::Face")),
 | 
			
		||||
        ("prelude", "Helix") => (
 | 
			
		||||
            crate::execution::PrimitiveType::Helix,
 | 
			
		||||
            StdFnProps::default("std::Helix"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("prelude", "Sketch") => (PrimitiveType::Sketch, StdFnProps::default("std::Sketch")),
 | 
			
		||||
        ("prelude", "Solid") => (PrimitiveType::Solid, StdFnProps::default("std::Solid")),
 | 
			
		||||
        ("prelude", "Plane") => (PrimitiveType::Plane, StdFnProps::default("std::Plane")),
 | 
			
		||||
        ("prelude", "Face") => (PrimitiveType::Face, StdFnProps::default("std::Face")),
 | 
			
		||||
        ("prelude", "Helix") => (PrimitiveType::Helix, StdFnProps::default("std::Helix")),
 | 
			
		||||
        _ => unreachable!(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,8 @@ use super::args::Arg;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{FunctionSource, NumericType, RuntimeType},
 | 
			
		||||
        kcl_value::FunctionSource,
 | 
			
		||||
        types::{NumericType, RuntimeType},
 | 
			
		||||
        ExecState, Geometries, Geometry, KclObjectFields, KclValue, Point2d, Point3d, Sketch, Solid,
 | 
			
		||||
    },
 | 
			
		||||
    std::Args,
 | 
			
		||||
@ -656,7 +657,7 @@ impl GeometryTrait for Solid {
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::execution::kcl_value::NumericType;
 | 
			
		||||
    use crate::execution::types::NumericType;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_array_to_point3d() {
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,10 @@ use kittycad_modeling_cmds::shared::Angle;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{kcl_value::RuntimeType, ExecState, KclValue, Point2d, PrimitiveType, Sketch, TagIdentifier},
 | 
			
		||||
    execution::{
 | 
			
		||||
        types::{PrimitiveType, RuntimeType},
 | 
			
		||||
        ExecState, KclValue, Point2d, Sketch, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    std::{utils::between, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{kcl_value::RuntimeType, ExecState, KclValue, Solid},
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, KclValue, Solid},
 | 
			
		||||
    std::{sketch::FaceTag, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,9 +14,9 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::RuntimeType, Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path,
 | 
			
		||||
        Plane, Point2d, Point3d, PrimitiveType, Sketch, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane,
 | 
			
		||||
        TagEngineInfo, TagIdentifier,
 | 
			
		||||
        types::{PrimitiveType, RuntimeType},
 | 
			
		||||
        Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d,
 | 
			
		||||
        Sketch, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane, TagEngineInfo, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    std::{
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    execution::{kcl_value::RuntimeType, ExecState, Helix, KclValue, Sketch, Solid},
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, Helix, KclValue, Sketch, Solid},
 | 
			
		||||
    std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{kcl_value::RuntimeType, ExecState, KclValue, SolidOrSketchOrImportedGeometry},
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, KclValue, SolidOrSketchOrImportedGeometry},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ use kcl_derive_docs::stdlib;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    execution::{ExecState, KclValue, UnitLen},
 | 
			
		||||
    execution::{types::UnitLen, ExecState, KclValue},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user