1use std::{collections::HashMap, fmt};
2
3use anyhow::Result;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use crate::{
8    execution::{
9        kcl_value::{KclValue, TypeDef},
10        memory::{self},
11        ExecState, Plane, PlaneInfo, Point3d,
12    },
13    parsing::{
14        ast::types::{PrimitiveType as AstPrimitiveType, Type},
15        token::NumericSuffix,
16    },
17    std::args::{FromKclValue, TyF64},
18    CompilationError, SourceRange,
19};
20
21#[derive(Debug, Clone, PartialEq)]
22pub enum RuntimeType {
23    Primitive(PrimitiveType),
24    Array(Box<RuntimeType>, ArrayLen),
25    Union(Vec<RuntimeType>),
26    Tuple(Vec<RuntimeType>),
27    Object(Vec<(String, RuntimeType)>),
28}
29
30impl RuntimeType {
31    pub fn any() -> Self {
32        RuntimeType::Primitive(PrimitiveType::Any)
33    }
34
35    pub fn edge() -> Self {
36        RuntimeType::Primitive(PrimitiveType::Edge)
37    }
38
39    pub fn sketch() -> Self {
40        RuntimeType::Primitive(PrimitiveType::Sketch)
41    }
42
43    pub fn sketches() -> Self {
45        RuntimeType::Array(
46            Box::new(RuntimeType::Primitive(PrimitiveType::Sketch)),
47            ArrayLen::NonEmpty,
48        )
49    }
50
51    pub fn solids() -> Self {
53        RuntimeType::Array(
54            Box::new(RuntimeType::Primitive(PrimitiveType::Solid)),
55            ArrayLen::NonEmpty,
56        )
57    }
58
59    pub fn solid() -> Self {
60        RuntimeType::Primitive(PrimitiveType::Solid)
61    }
62
63    pub fn helix() -> Self {
64        RuntimeType::Primitive(PrimitiveType::Helix)
65    }
66
67    pub fn plane() -> Self {
68        RuntimeType::Primitive(PrimitiveType::Plane)
69    }
70
71    pub fn face() -> Self {
72        RuntimeType::Primitive(PrimitiveType::Face)
73    }
74
75    pub fn tag() -> Self {
76        RuntimeType::Primitive(PrimitiveType::Tag)
77    }
78
79    pub fn tag_identifier() -> Self {
80        RuntimeType::Primitive(PrimitiveType::TagId)
81    }
82
83    pub fn bool() -> Self {
84        RuntimeType::Primitive(PrimitiveType::Boolean)
85    }
86
87    pub fn string() -> Self {
88        RuntimeType::Primitive(PrimitiveType::String)
89    }
90
91    pub fn imported() -> Self {
92        RuntimeType::Primitive(PrimitiveType::ImportedGeometry)
93    }
94
95    pub fn point2d() -> Self {
97        RuntimeType::Array(Box::new(RuntimeType::length()), ArrayLen::Known(2))
98    }
99
100    pub fn point3d() -> Self {
102        RuntimeType::Array(Box::new(RuntimeType::length()), ArrayLen::Known(3))
103    }
104
105    pub fn length() -> Self {
106        RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Length(
107            UnitLen::Unknown,
108        ))))
109    }
110
111    pub fn known_length(len: UnitLen) -> Self {
112        RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Length(len))))
113    }
114
115    pub fn angle() -> Self {
116        RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Angle(
117            UnitAngle::Unknown,
118        ))))
119    }
120
121    pub fn radians() -> Self {
122        RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Angle(
123            UnitAngle::Radians,
124        ))))
125    }
126
127    pub fn degrees() -> Self {
128        RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Angle(
129            UnitAngle::Degrees,
130        ))))
131    }
132
133    pub fn count() -> Self {
134        RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Count)))
135    }
136
137    pub fn num_any() -> Self {
138        RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))
139    }
140
141    pub fn from_parsed(
142        value: Type,
143        exec_state: &mut ExecState,
144        source_range: SourceRange,
145    ) -> Result<Self, CompilationError> {
146        match value {
147            Type::Primitive(pt) => Self::from_parsed_primitive(pt, exec_state, source_range),
148            Type::Array { ty, len } => {
149                Self::from_parsed(*ty, exec_state, source_range).map(|t| RuntimeType::Array(Box::new(t), len))
150            }
151            Type::Union { tys } => tys
152                .into_iter()
153                .map(|t| Self::from_parsed_primitive(t.inner, exec_state, source_range))
154                .collect::<Result<Vec<_>, CompilationError>>()
155                .map(RuntimeType::Union),
156            Type::Object { properties } => properties
157                .into_iter()
158                .map(|p| {
159                    RuntimeType::from_parsed(p.type_.unwrap().inner, exec_state, source_range)
160                        .map(|ty| (p.identifier.inner.name, ty))
161                })
162                .collect::<Result<Vec<_>, CompilationError>>()
163                .map(RuntimeType::Object),
164        }
165    }
166
167    fn from_parsed_primitive(
168        value: AstPrimitiveType,
169        exec_state: &mut ExecState,
170        source_range: SourceRange,
171    ) -> Result<Self, CompilationError> {
172        Ok(match value {
173            AstPrimitiveType::Any => RuntimeType::Primitive(PrimitiveType::Any),
174            AstPrimitiveType::String => RuntimeType::Primitive(PrimitiveType::String),
175            AstPrimitiveType::Boolean => RuntimeType::Primitive(PrimitiveType::Boolean),
176            AstPrimitiveType::Number(suffix) => {
177                let ty = match suffix {
178                    NumericSuffix::None => NumericType::Any,
179                    _ => NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
180                };
181                RuntimeType::Primitive(PrimitiveType::Number(ty))
182            }
183            AstPrimitiveType::Named(name) => Self::from_alias(&name.name, exec_state, source_range)?,
184            AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
185            AstPrimitiveType::ImportedGeometry => RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
186            AstPrimitiveType::Function(_) => RuntimeType::Primitive(PrimitiveType::Function),
187        })
188    }
189
190    pub fn from_alias(
191        alias: &str,
192        exec_state: &mut ExecState,
193        source_range: SourceRange,
194    ) -> Result<Self, CompilationError> {
195        let ty_val = exec_state
196            .stack()
197            .get(&format!("{}{}", memory::TYPE_PREFIX, alias), source_range)
198            .map_err(|_| CompilationError::err(source_range, format!("Unknown type: {}", alias)))?;
199
200        Ok(match ty_val {
201            KclValue::Type { value, .. } => match value {
202                TypeDef::RustRepr(ty, _) => RuntimeType::Primitive(ty.clone()),
203                TypeDef::Alias(ty) => ty.clone(),
204            },
205            _ => unreachable!(),
206        })
207    }
208
209    pub fn human_friendly_type(&self) -> String {
210        match self {
211            RuntimeType::Primitive(ty) => ty.to_string(),
212            RuntimeType::Array(ty, ArrayLen::None) => format!("an array of {}", ty.display_multiple()),
213            RuntimeType::Array(ty, ArrayLen::NonEmpty) => format!("one or more {}", ty.display_multiple()),
214            RuntimeType::Array(ty, ArrayLen::Known(n)) => format!("an array of {n} {}", ty.display_multiple()),
215            RuntimeType::Union(tys) => tys
216                .iter()
217                .map(Self::human_friendly_type)
218                .collect::<Vec<_>>()
219                .join(" or "),
220            RuntimeType::Tuple(tys) => format!(
221                "a tuple with values of types ({})",
222                tys.iter().map(Self::human_friendly_type).collect::<Vec<_>>().join(", ")
223            ),
224            RuntimeType::Object(_) => format!("an object with fields {}", self),
225        }
226    }
227
228    fn subtype(&self, sup: &RuntimeType) -> bool {
230        use RuntimeType::*;
231
232        match (self, sup) {
233            (_, Primitive(PrimitiveType::Any)) => true,
234            (Primitive(t1), Primitive(t2)) => t1.subtype(t2),
235            (Array(t1, l1), Array(t2, l2)) => t1.subtype(t2) && l1.subtype(*l2),
236            (Tuple(t1), Tuple(t2)) => t1.len() == t2.len() && t1.iter().zip(t2).all(|(t1, t2)| t1.subtype(t2)),
237            (Union(ts1), Union(ts2)) => ts1.iter().all(|t| ts2.contains(t)),
238            (t1, Union(ts2)) => ts2.iter().any(|t| t1.subtype(t)),
239            (Object(t1), Object(t2)) => t2
240                .iter()
241                .all(|(f, t)| t1.iter().any(|(ff, tt)| f == ff && tt.subtype(t))),
242            (Object(t1), Primitive(PrimitiveType::Axis2d)) => {
244                t1.iter()
245                    .any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point2d()))
246                    && t1
247                        .iter()
248                        .any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point2d()))
249            }
250            (Object(t1), Primitive(PrimitiveType::Axis3d)) => {
251                t1.iter()
252                    .any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point3d()))
253                    && t1
254                        .iter()
255                        .any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point3d()))
256            }
257            (Primitive(PrimitiveType::Axis2d), Object(t2)) => {
258                t2.iter()
259                    .any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point2d()))
260                    && t2
261                        .iter()
262                        .any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point2d()))
263            }
264            (Primitive(PrimitiveType::Axis3d), Object(t2)) => {
265                t2.iter()
266                    .any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point3d()))
267                    && t2
268                        .iter()
269                        .any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point3d()))
270            }
271            _ => false,
272        }
273    }
274
275    fn display_multiple(&self) -> String {
276        match self {
277            RuntimeType::Primitive(ty) => ty.display_multiple(),
278            RuntimeType::Array(..) => "arrays".to_owned(),
279            RuntimeType::Union(tys) => tys
280                .iter()
281                .map(|t| t.display_multiple())
282                .collect::<Vec<_>>()
283                .join(" or "),
284            RuntimeType::Tuple(_) => "tuples".to_owned(),
285            RuntimeType::Object(_) => format!("objects with fields {self}"),
286        }
287    }
288}
289
290impl fmt::Display for RuntimeType {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        match self {
293            RuntimeType::Primitive(t) => t.fmt(f),
294            RuntimeType::Array(t, l) => match l {
295                ArrayLen::None => write!(f, "[{t}]"),
296                ArrayLen::NonEmpty => write!(f, "[{t}; 1+]"),
297                ArrayLen::Known(n) => write!(f, "[{t}; {n}]"),
298            },
299            RuntimeType::Tuple(ts) => write!(
300                f,
301                "({})",
302                ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
303            ),
304            RuntimeType::Union(ts) => write!(
305                f,
306                "{}",
307                ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(" | ")
308            ),
309            RuntimeType::Object(items) => write!(
310                f,
311                "{{ {} }}",
312                items
313                    .iter()
314                    .map(|(n, t)| format!("{n}: {t}"))
315                    .collect::<Vec<_>>()
316                    .join(", ")
317            ),
318        }
319    }
320}
321
322#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, ts_rs::TS, JsonSchema)]
323pub enum ArrayLen {
324    None,
325    NonEmpty,
326    Known(usize),
327}
328
329impl ArrayLen {
330    pub fn subtype(self, other: ArrayLen) -> bool {
331        match (self, other) {
332            (_, ArrayLen::None) => true,
333            (ArrayLen::NonEmpty, ArrayLen::NonEmpty) => true,
334            (ArrayLen::Known(size), ArrayLen::NonEmpty) if size > 0 => true,
335            (ArrayLen::Known(s1), ArrayLen::Known(s2)) if s1 == s2 => true,
336            _ => false,
337        }
338    }
339
340    fn satisfied(self, len: usize, allow_shrink: bool) -> Option<usize> {
342        match self {
343            ArrayLen::None => Some(len),
344            ArrayLen::NonEmpty => (len > 0).then_some(len),
345            ArrayLen::Known(s) => (if allow_shrink { len >= s } else { len == s }).then_some(s),
346        }
347    }
348}
349
350#[derive(Debug, Clone, PartialEq)]
351pub enum PrimitiveType {
352    Any,
353    Number(NumericType),
354    String,
355    Boolean,
356    Tag,
357    TagId,
359    Sketch,
360    Solid,
361    Plane,
362    Helix,
363    Face,
364    Edge,
365    Axis2d,
366    Axis3d,
367    ImportedGeometry,
368    Function,
369}
370
371impl PrimitiveType {
372    fn display_multiple(&self) -> String {
373        match self {
374            PrimitiveType::Any => "any values".to_owned(),
375            PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
376            PrimitiveType::Number(_) => "numbers".to_owned(),
377            PrimitiveType::String => "strings".to_owned(),
378            PrimitiveType::Boolean => "bools".to_owned(),
379            PrimitiveType::Sketch => "Sketches".to_owned(),
380            PrimitiveType::Solid => "Solids".to_owned(),
381            PrimitiveType::Plane => "Planes".to_owned(),
382            PrimitiveType::Helix => "Helices".to_owned(),
383            PrimitiveType::Face => "Faces".to_owned(),
384            PrimitiveType::Edge => "Edges".to_owned(),
385            PrimitiveType::Axis2d => "2d axes".to_owned(),
386            PrimitiveType::Axis3d => "3d axes".to_owned(),
387            PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
388            PrimitiveType::Function => "functions".to_owned(),
389            PrimitiveType::Tag => "tags".to_owned(),
390            PrimitiveType::TagId => "tag identifiers".to_owned(),
391        }
392    }
393
394    fn subtype(&self, other: &PrimitiveType) -> bool {
395        match (self, other) {
396            (_, PrimitiveType::Any) => true,
397            (PrimitiveType::Number(n1), PrimitiveType::Number(n2)) => n1.subtype(n2),
398            (PrimitiveType::TagId, PrimitiveType::Tag) => true,
399            (t1, t2) => t1 == t2,
400        }
401    }
402}
403
404impl fmt::Display for PrimitiveType {
405    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406        match self {
407            PrimitiveType::Any => write!(f, "any"),
408            PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
409            PrimitiveType::Number(NumericType::Unknown) => write!(f, "number(unknown units)"),
410            PrimitiveType::Number(NumericType::Default { .. }) => write!(f, "number(default units)"),
411            PrimitiveType::Number(NumericType::Any) => write!(f, "number(any units)"),
412            PrimitiveType::String => write!(f, "string"),
413            PrimitiveType::Boolean => write!(f, "bool"),
414            PrimitiveType::Tag => write!(f, "tag"),
415            PrimitiveType::TagId => write!(f, "tag identifier"),
416            PrimitiveType::Sketch => write!(f, "Sketch"),
417            PrimitiveType::Solid => write!(f, "Solid"),
418            PrimitiveType::Plane => write!(f, "Plane"),
419            PrimitiveType::Face => write!(f, "Face"),
420            PrimitiveType::Edge => write!(f, "Edge"),
421            PrimitiveType::Axis2d => write!(f, "Axis2d"),
422            PrimitiveType::Axis3d => write!(f, "Axis3d"),
423            PrimitiveType::Helix => write!(f, "Helix"),
424            PrimitiveType::ImportedGeometry => write!(f, "imported geometry"),
425            PrimitiveType::Function => write!(f, "function"),
426        }
427    }
428}
429
430#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
431#[ts(export)]
432#[serde(tag = "type")]
433pub enum NumericType {
434    Known(UnitType),
436    Default { len: UnitLen, angle: UnitAngle },
438    Unknown,
440    Any,
442}
443
444impl Default for NumericType {
445    fn default() -> Self {
446        NumericType::Default {
447            len: UnitLen::default(),
448            angle: UnitAngle::default(),
449        }
450    }
451}
452
453impl NumericType {
454    pub const fn count() -> Self {
455        NumericType::Known(UnitType::Count)
456    }
457
458    pub const fn mm() -> Self {
459        NumericType::Known(UnitType::Length(UnitLen::Mm))
460    }
461
462    pub const fn radians() -> Self {
463        NumericType::Known(UnitType::Angle(UnitAngle::Radians))
464    }
465
466    pub const fn degrees() -> Self {
467        NumericType::Known(UnitType::Angle(UnitAngle::Degrees))
468    }
469
470    pub fn expect_default_length(&self) -> Self {
471        match self {
472            NumericType::Default { len, .. } => NumericType::Known(UnitType::Length(*len)),
473            _ => unreachable!(),
474        }
475    }
476
477    pub fn expect_default_angle(&self) -> Self {
478        match self {
479            NumericType::Default { angle, .. } => NumericType::Known(UnitType::Angle(*angle)),
480            _ => unreachable!(),
481        }
482    }
483
484    pub fn combine_eq(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
490        use NumericType::*;
491        match (a.ty, b.ty) {
492            (at, bt) if at == bt => (a.n, b.n, at),
493            (at, Any) => (a.n, b.n, at),
494            (Any, bt) => (a.n, b.n, bt),
495
496            (t @ Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => (a.n, l2.adjust_to(b.n, l1).0, t),
497            (t @ Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => (a.n, a2.adjust_to(b.n, a1).0, t),
498
499            (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
500                (a.n, b.n, Known(UnitType::Count))
501            }
502            (t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) if l1 == l2 => (a.n, b.n, t),
503            (Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) if l1 == l2 => (a.n, b.n, t),
504            (t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) if a1 == a2 => (a.n, b.n, t),
505            (Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) if a1 == a2 => (a.n, b.n, t),
506
507            _ => (a.n, b.n, Unknown),
508        }
509    }
510
511    pub fn combine_eq_coerce(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
519        use NumericType::*;
520        match (a.ty, b.ty) {
521            (at, bt) if at == bt => (a.n, b.n, at),
522            (at, Any) => (a.n, b.n, at),
523            (Any, bt) => (a.n, b.n, bt),
524
525            (Default { .. }, Default { .. }) | (_, Unknown) | (Unknown, _) => (a.n, b.n, Unknown),
526
527            (t @ Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => (a.n, l2.adjust_to(b.n, l1).0, t),
529            (t @ Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => (a.n, a2.adjust_to(b.n, a1).0, t),
530
531            (Known(_), Known(_)) => (a.n, b.n, Unknown),
533
534            (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
536                (a.n, b.n, Known(UnitType::Count))
537            }
538
539            (t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) => (a.n, l2.adjust_to(b.n, l1).0, t),
540            (Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) => (l1.adjust_to(a.n, l2).0, b.n, t),
541
542            (t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) => (a.n, a2.adjust_to(b.n, a1).0, t),
543            (Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) => (a1.adjust_to(a.n, a2).0, b.n, t),
544        }
545    }
546
547    pub fn combine_eq_array(input: &[TyF64]) -> (Vec<f64>, NumericType) {
548        use NumericType::*;
549        let result = input.iter().map(|t| t.n).collect();
550
551        let mut ty = Any;
552        for i in input {
553            if i.ty == Any || ty == i.ty {
554                continue;
555            }
556
557            match (&ty, &i.ty) {
559                (Any, Default { .. }) if i.n == 0.0 => {}
560                (Any, t) => {
561                    ty = t.clone();
562                }
563                (_, Unknown) | (Default { .. }, Default { .. }) => return (result, Unknown),
564
565                (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
566                    ty = Known(UnitType::Count);
567                }
568
569                (Known(UnitType::Length(l1)), Default { len: l2, .. }) if l1 == l2 || i.n == 0.0 => {}
570                (Known(UnitType::Angle(a1)), Default { angle: a2, .. }) if a1 == a2 || i.n == 0.0 => {}
571
572                (Default { len: l1, .. }, Known(UnitType::Length(l2))) if l1 == l2 => {
573                    ty = Known(UnitType::Length(*l2));
574                }
575                (Default { angle: a1, .. }, Known(UnitType::Angle(a2))) if a1 == a2 => {
576                    ty = Known(UnitType::Angle(*a2));
577                }
578
579                _ => return (result, Unknown),
580            }
581        }
582
583        if ty == Any && !input.is_empty() {
584            ty = input[0].ty.clone();
585        }
586
587        (result, ty)
588    }
589
590    pub fn combine_mul(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
592        use NumericType::*;
593        match (a.ty, b.ty) {
594            (at @ Default { .. }, bt @ Default { .. }) if at == bt => (a.n, b.n, at),
595            (Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
596            (Known(UnitType::Count), bt) => (a.n, b.n, bt),
597            (at, Known(UnitType::Count)) => (a.n, b.n, at),
598            (at @ Known(_), Default { .. }) | (Default { .. }, at @ Known(_)) => (a.n, b.n, at),
599            (Any, Any) => (a.n, b.n, Any),
600            _ => (a.n, b.n, Unknown),
601        }
602    }
603
604    pub fn combine_div(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
606        use NumericType::*;
607        match (a.ty, b.ty) {
608            (at @ Default { .. }, bt @ Default { .. }) if at == bt => (a.n, b.n, at),
609            (at, bt) if at == bt => (a.n, b.n, Known(UnitType::Count)),
610            (Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
611            (at, Known(UnitType::Count) | Any) => (a.n, b.n, at),
612            (at @ Known(_), Default { .. }) => (a.n, b.n, at),
613            (Known(UnitType::Count), _) => (a.n, b.n, Known(UnitType::Count)),
614            _ => (a.n, b.n, Unknown),
615        }
616    }
617
618    pub fn from_parsed(suffix: NumericSuffix, settings: &super::MetaSettings) -> Self {
619        match suffix {
620            NumericSuffix::None => NumericType::Default {
621                len: settings.default_length_units,
622                angle: settings.default_angle_units,
623            },
624            NumericSuffix::Count => NumericType::Known(UnitType::Count),
625            NumericSuffix::Length => NumericType::Known(UnitType::Length(UnitLen::Unknown)),
626            NumericSuffix::Angle => NumericType::Known(UnitType::Angle(UnitAngle::Unknown)),
627            NumericSuffix::Mm => NumericType::Known(UnitType::Length(UnitLen::Mm)),
628            NumericSuffix::Cm => NumericType::Known(UnitType::Length(UnitLen::Cm)),
629            NumericSuffix::M => NumericType::Known(UnitType::Length(UnitLen::M)),
630            NumericSuffix::Inch => NumericType::Known(UnitType::Length(UnitLen::Inches)),
631            NumericSuffix::Ft => NumericType::Known(UnitType::Length(UnitLen::Feet)),
632            NumericSuffix::Yd => NumericType::Known(UnitType::Length(UnitLen::Yards)),
633            NumericSuffix::Deg => NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
634            NumericSuffix::Rad => NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
635            NumericSuffix::Unknown => NumericType::Unknown,
636        }
637    }
638
639    fn subtype(&self, other: &NumericType) -> bool {
640        use NumericType::*;
641
642        match (self, other) {
643            (_, Any) => true,
644            (a, b) if a == b => true,
645            (
646                NumericType::Known(UnitType::Length(_)) | NumericType::Default { .. },
647                NumericType::Known(UnitType::Length(UnitLen::Unknown)),
648            )
649            | (
650                NumericType::Known(UnitType::Angle(_)) | NumericType::Default { .. },
651                NumericType::Known(UnitType::Angle(UnitAngle::Unknown)),
652            ) => true,
653            (Unknown, _) | (_, Unknown) => false,
654            (_, _) => false,
655        }
656    }
657
658    fn is_unknown(&self) -> bool {
659        matches!(
660            self,
661            NumericType::Unknown
662                | NumericType::Known(UnitType::Angle(UnitAngle::Unknown))
663                | NumericType::Known(UnitType::Length(UnitLen::Unknown))
664        )
665    }
666
667    pub fn is_fully_specified(&self) -> bool {
668        !matches!(
669            self,
670            NumericType::Unknown
671                | NumericType::Known(UnitType::Angle(UnitAngle::Unknown))
672                | NumericType::Known(UnitType::Length(UnitLen::Unknown))
673                | NumericType::Any
674                | NumericType::Default { .. }
675        )
676    }
677
678    fn example_ty(&self) -> Option<String> {
679        match self {
680            Self::Known(t) if !self.is_unknown() => Some(t.to_string()),
681            Self::Default { len, .. } => Some(len.to_string()),
682            _ => None,
683        }
684    }
685
686    fn coerce(&self, val: &KclValue) -> Result<KclValue, CoercionError> {
687        let KclValue::Number { value, ty, meta } = val else {
688            return Err(val.into());
689        };
690
691        if ty.subtype(self) {
692            return Ok(KclValue::Number {
693                value: *value,
694                ty: ty.clone(),
695                meta: meta.clone(),
696            });
697        }
698
699        use NumericType::*;
701        match (ty, self) {
702            (Unknown, _) => Err(CoercionError::from(val).with_explicit(self.example_ty().unwrap_or("mm".to_owned()))),
704            (_, Unknown) => Err(val.into()),
705
706            (Any, _) => Ok(KclValue::Number {
707                value: *value,
708                ty: self.clone(),
709                meta: meta.clone(),
710            }),
711
712            (_, Default { .. }) => Ok(KclValue::Number {
715                value: *value,
716                ty: ty.clone(),
717                meta: meta.clone(),
718            }),
719
720            (Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => {
722                let (value, ty) = l1.adjust_to(*value, *l2);
723                Ok(KclValue::Number {
724                    value,
725                    ty: Known(UnitType::Length(ty)),
726                    meta: meta.clone(),
727                })
728            }
729            (Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => {
730                let (value, ty) = a1.adjust_to(*value, *a2);
731                Ok(KclValue::Number {
732                    value,
733                    ty: Known(UnitType::Angle(ty)),
734                    meta: meta.clone(),
735                })
736            }
737
738            (Known(_), Known(_)) => Err(val.into()),
740
741            (Default { .. }, Known(UnitType::Count)) => Ok(KclValue::Number {
743                value: *value,
744                ty: Known(UnitType::Count),
745                meta: meta.clone(),
746            }),
747
748            (Default { len: l1, .. }, Known(UnitType::Length(l2))) => {
749                let (value, ty) = l1.adjust_to(*value, *l2);
750                Ok(KclValue::Number {
751                    value,
752                    ty: Known(UnitType::Length(ty)),
753                    meta: meta.clone(),
754                })
755            }
756
757            (Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => {
758                let (value, ty) = a1.adjust_to(*value, *a2);
759                Ok(KclValue::Number {
760                    value,
761                    ty: Known(UnitType::Angle(ty)),
762                    meta: meta.clone(),
763                })
764            }
765
766            (_, _) => unreachable!(),
767        }
768    }
769
770    pub fn expect_length(&self) -> UnitLen {
771        match self {
772            Self::Known(UnitType::Length(len)) | Self::Default { len, .. } => *len,
773            _ => unreachable!("Found {self:?}"),
774        }
775    }
776
777    pub fn as_length(&self) -> Option<UnitLen> {
778        match self {
779            Self::Known(UnitType::Length(len)) | Self::Default { len, .. } => Some(*len),
780            _ => None,
781        }
782    }
783}
784
785impl From<NumericType> for RuntimeType {
786    fn from(t: NumericType) -> RuntimeType {
787        RuntimeType::Primitive(PrimitiveType::Number(t))
788    }
789}
790
791impl From<UnitLen> for NumericType {
792    fn from(value: UnitLen) -> Self {
793        NumericType::Known(UnitType::Length(value))
794    }
795}
796
797impl From<UnitAngle> for NumericType {
798    fn from(value: UnitAngle) -> Self {
799        NumericType::Known(UnitType::Angle(value))
800    }
801}
802
803#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
804#[ts(export)]
805#[serde(tag = "type")]
806pub enum UnitType {
807    Count,
808    Length(UnitLen),
809    Angle(UnitAngle),
810}
811
812impl std::fmt::Display for UnitType {
813    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
814        match self {
815            UnitType::Count => write!(f, "Count"),
816            UnitType::Length(l) => l.fmt(f),
817            UnitType::Angle(a) => a.fmt(f),
818        }
819    }
820}
821
822#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
825#[ts(export)]
826#[serde(tag = "type")]
827pub enum UnitLen {
828    #[default]
829    Mm,
830    Cm,
831    M,
832    Inches,
833    Feet,
834    Yards,
835    Unknown,
836}
837
838impl UnitLen {
839    pub fn adjust_to(self, value: f64, to: UnitLen) -> (f64, UnitLen) {
840        use UnitLen::*;
841
842        if self == to {
843            return (value, to);
844        }
845
846        if to == Unknown {
847            return (value, self);
848        }
849
850        let (base, base_unit) = match self {
851            Mm => (value, Mm),
852            Cm => (value * 10.0, Mm),
853            M => (value * 1000.0, Mm),
854            Inches => (value, Inches),
855            Feet => (value * 12.0, Inches),
856            Yards => (value * 36.0, Inches),
857            Unknown => unreachable!(),
858        };
859        let (base, base_unit) = match (base_unit, to) {
860            (Mm, Inches) | (Mm, Feet) | (Mm, Yards) => (base / 25.4, Inches),
861            (Inches, Mm) | (Inches, Cm) | (Inches, M) => (base * 25.4, Mm),
862            _ => (base, base_unit),
863        };
864
865        let value = match (base_unit, to) {
866            (Mm, Mm) => base,
867            (Mm, Cm) => base / 10.0,
868            (Mm, M) => base / 1000.0,
869            (Inches, Inches) => base,
870            (Inches, Feet) => base / 12.0,
871            (Inches, Yards) => base / 36.0,
872            _ => unreachable!(),
873        };
874
875        (value, to)
876    }
877}
878
879impl std::fmt::Display for UnitLen {
880    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
881        match self {
882            UnitLen::Mm => write!(f, "mm"),
883            UnitLen::Cm => write!(f, "cm"),
884            UnitLen::M => write!(f, "m"),
885            UnitLen::Inches => write!(f, "in"),
886            UnitLen::Feet => write!(f, "ft"),
887            UnitLen::Yards => write!(f, "yd"),
888            UnitLen::Unknown => write!(f, "Length"),
889        }
890    }
891}
892
893impl TryFrom<NumericSuffix> for UnitLen {
894    type Error = ();
895
896    fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
897        match suffix {
898            NumericSuffix::Mm => Ok(Self::Mm),
899            NumericSuffix::Cm => Ok(Self::Cm),
900            NumericSuffix::M => Ok(Self::M),
901            NumericSuffix::Inch => Ok(Self::Inches),
902            NumericSuffix::Ft => Ok(Self::Feet),
903            NumericSuffix::Yd => Ok(Self::Yards),
904            _ => Err(()),
905        }
906    }
907}
908
909impl From<crate::UnitLength> for UnitLen {
910    fn from(unit: crate::UnitLength) -> Self {
911        match unit {
912            crate::UnitLength::Cm => UnitLen::Cm,
913            crate::UnitLength::Ft => UnitLen::Feet,
914            crate::UnitLength::In => UnitLen::Inches,
915            crate::UnitLength::M => UnitLen::M,
916            crate::UnitLength::Mm => UnitLen::Mm,
917            crate::UnitLength::Yd => UnitLen::Yards,
918        }
919    }
920}
921
922impl From<UnitLen> for crate::UnitLength {
923    fn from(unit: UnitLen) -> Self {
924        match unit {
925            UnitLen::Cm => crate::UnitLength::Cm,
926            UnitLen::Feet => crate::UnitLength::Ft,
927            UnitLen::Inches => crate::UnitLength::In,
928            UnitLen::M => crate::UnitLength::M,
929            UnitLen::Mm => crate::UnitLength::Mm,
930            UnitLen::Yards => crate::UnitLength::Yd,
931            UnitLen::Unknown => unreachable!(),
932        }
933    }
934}
935
936impl From<UnitLen> for kittycad_modeling_cmds::units::UnitLength {
937    fn from(unit: UnitLen) -> Self {
938        match unit {
939            UnitLen::Cm => kittycad_modeling_cmds::units::UnitLength::Centimeters,
940            UnitLen::Feet => kittycad_modeling_cmds::units::UnitLength::Feet,
941            UnitLen::Inches => kittycad_modeling_cmds::units::UnitLength::Inches,
942            UnitLen::M => kittycad_modeling_cmds::units::UnitLength::Meters,
943            UnitLen::Mm => kittycad_modeling_cmds::units::UnitLength::Millimeters,
944            UnitLen::Yards => kittycad_modeling_cmds::units::UnitLength::Yards,
945            UnitLen::Unknown => unreachable!(),
946        }
947    }
948}
949
950#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
952#[ts(export)]
953#[serde(tag = "type")]
954pub enum UnitAngle {
955    #[default]
956    Degrees,
957    Radians,
958    Unknown,
959}
960
961impl UnitAngle {
962    pub fn adjust_to(self, value: f64, to: UnitAngle) -> (f64, UnitAngle) {
963        use std::f64::consts::PI;
964
965        use UnitAngle::*;
966
967        if to == Unknown {
968            return (value, self);
969        }
970
971        let value = match (self, to) {
972            (Degrees, Degrees) => value,
973            (Degrees, Radians) => (value / 180.0) * PI,
974            (Radians, Degrees) => 180.0 * value / PI,
975            (Radians, Radians) => value,
976            (Unknown, _) | (_, Unknown) => unreachable!(),
977        };
978
979        (value, to)
980    }
981}
982
983impl std::fmt::Display for UnitAngle {
984    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
985        match self {
986            UnitAngle::Degrees => write!(f, "deg"),
987            UnitAngle::Radians => write!(f, "rad"),
988            UnitAngle::Unknown => write!(f, "Angle"),
989        }
990    }
991}
992
993impl TryFrom<NumericSuffix> for UnitAngle {
994    type Error = ();
995
996    fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
997        match suffix {
998            NumericSuffix::Deg => Ok(Self::Degrees),
999            NumericSuffix::Rad => Ok(Self::Radians),
1000            _ => Err(()),
1001        }
1002    }
1003}
1004
1005#[derive(Debug, Clone)]
1006pub struct CoercionError {
1007    pub found: Option<RuntimeType>,
1008    pub explicit_coercion: Option<String>,
1009}
1010
1011impl CoercionError {
1012    fn with_explicit(mut self, c: String) -> Self {
1013        self.explicit_coercion = Some(c);
1014        self
1015    }
1016}
1017
1018impl From<&'_ KclValue> for CoercionError {
1019    fn from(value: &'_ KclValue) -> Self {
1020        CoercionError {
1021            found: value.principal_type(),
1022            explicit_coercion: None,
1023        }
1024    }
1025}
1026
1027impl KclValue {
1028    pub fn has_type(&self, ty: &RuntimeType) -> bool {
1030        let Some(self_ty) = self.principal_type() else {
1031            return false;
1032        };
1033
1034        self_ty.subtype(ty)
1035    }
1036
1037    pub fn coerce(&self, ty: &RuntimeType, exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
1044        match ty {
1045            RuntimeType::Primitive(ty) => self.coerce_to_primitive_type(ty, exec_state),
1046            RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, *len, exec_state, false),
1047            RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, exec_state),
1048            RuntimeType::Union(tys) => self.coerce_to_union_type(tys, exec_state),
1049            RuntimeType::Object(tys) => self.coerce_to_object_type(tys, exec_state),
1050        }
1051    }
1052
1053    fn coerce_to_primitive_type(
1054        &self,
1055        ty: &PrimitiveType,
1056        exec_state: &mut ExecState,
1057    ) -> Result<KclValue, CoercionError> {
1058        let value = match self {
1059            KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } if value.len() == 1 => &value[0],
1060            _ => self,
1061        };
1062        match ty {
1063            PrimitiveType::Any => Ok(value.clone()),
1064            PrimitiveType::Number(ty) => ty.coerce(value),
1065            PrimitiveType::String => match value {
1066                KclValue::String { .. } => Ok(value.clone()),
1067                _ => Err(self.into()),
1068            },
1069            PrimitiveType::Boolean => match value {
1070                KclValue::Bool { .. } => Ok(value.clone()),
1071                _ => Err(self.into()),
1072            },
1073            PrimitiveType::Sketch => match value {
1074                KclValue::Sketch { .. } => Ok(value.clone()),
1075                _ => Err(self.into()),
1076            },
1077            PrimitiveType::Solid => match value {
1078                KclValue::Solid { .. } => Ok(value.clone()),
1079                _ => Err(self.into()),
1080            },
1081            PrimitiveType::Plane => match value {
1082                KclValue::String { value: s, .. }
1083                    if [
1084                        "xy", "xz", "yz", "-xy", "-xz", "-yz", "XY", "XZ", "YZ", "-XY", "-XZ", "-YZ",
1085                    ]
1086                    .contains(&&**s) =>
1087                {
1088                    Ok(value.clone())
1089                }
1090                KclValue::Plane { .. } => Ok(value.clone()),
1091                KclValue::Object { value, meta } => {
1092                    let origin = value
1093                        .get("origin")
1094                        .and_then(Point3d::from_kcl_val)
1095                        .ok_or(CoercionError::from(self))?;
1096                    let x_axis = value
1097                        .get("xAxis")
1098                        .and_then(Point3d::from_kcl_val)
1099                        .ok_or(CoercionError::from(self))?;
1100                    let y_axis = value
1101                        .get("yAxis")
1102                        .and_then(Point3d::from_kcl_val)
1103                        .ok_or(CoercionError::from(self))?;
1104
1105                    if value.get("zAxis").is_some() {
1106                        exec_state.warn(CompilationError::err(
1107                            self.into(),
1108                            "Object with a zAxis field is being coerced into a plane, but the zAxis is ignored.",
1109                        ));
1110                    }
1111
1112                    let id = exec_state.mod_local.id_generator.next_uuid();
1113                    let plane = Plane {
1114                        id,
1115                        #[cfg(feature = "artifact-graph")]
1116                        artifact_id: id.into(),
1117                        info: PlaneInfo {
1118                            origin,
1119                            x_axis: x_axis.normalize(),
1120                            y_axis: y_axis.normalize(),
1121                        },
1122                        value: super::PlaneType::Uninit,
1123                        meta: meta.clone(),
1124                    };
1125
1126                    Ok(KclValue::Plane { value: Box::new(plane) })
1127                }
1128                _ => Err(self.into()),
1129            },
1130            PrimitiveType::Face => match value {
1131                KclValue::Face { .. } => Ok(value.clone()),
1132                _ => Err(self.into()),
1133            },
1134            PrimitiveType::Helix => match value {
1135                KclValue::Helix { .. } => Ok(value.clone()),
1136                _ => Err(self.into()),
1137            },
1138            PrimitiveType::Edge => match value {
1139                KclValue::Uuid { .. } => Ok(value.clone()),
1140                KclValue::TagIdentifier { .. } => Ok(value.clone()),
1141                _ => Err(self.into()),
1142            },
1143            PrimitiveType::Axis2d => match value {
1144                KclValue::Object { value: values, meta } => {
1145                    if values
1146                        .get("origin")
1147                        .ok_or(CoercionError::from(self))?
1148                        .has_type(&RuntimeType::point2d())
1149                        && values
1150                            .get("direction")
1151                            .ok_or(CoercionError::from(self))?
1152                            .has_type(&RuntimeType::point2d())
1153                    {
1154                        return Ok(value.clone());
1155                    }
1156
1157                    let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
1158                        p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(2), exec_state, true)
1159                    })?;
1160                    let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
1161                        p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(2), exec_state, true)
1162                    })?;
1163
1164                    Ok(KclValue::Object {
1165                        value: [("origin".to_owned(), origin), ("direction".to_owned(), direction)].into(),
1166                        meta: meta.clone(),
1167                    })
1168                }
1169                _ => Err(self.into()),
1170            },
1171            PrimitiveType::Axis3d => match value {
1172                KclValue::Object { value: values, meta } => {
1173                    if values
1174                        .get("origin")
1175                        .ok_or(CoercionError::from(self))?
1176                        .has_type(&RuntimeType::point3d())
1177                        && values
1178                            .get("direction")
1179                            .ok_or(CoercionError::from(self))?
1180                            .has_type(&RuntimeType::point3d())
1181                    {
1182                        return Ok(value.clone());
1183                    }
1184
1185                    let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
1186                        p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(3), exec_state, true)
1187                    })?;
1188                    let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
1189                        p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(3), exec_state, true)
1190                    })?;
1191
1192                    Ok(KclValue::Object {
1193                        value: [("origin".to_owned(), origin), ("direction".to_owned(), direction)].into(),
1194                        meta: meta.clone(),
1195                    })
1196                }
1197                _ => Err(self.into()),
1198            },
1199            PrimitiveType::ImportedGeometry => match value {
1200                KclValue::ImportedGeometry { .. } => Ok(value.clone()),
1201                _ => Err(self.into()),
1202            },
1203            PrimitiveType::Function => match value {
1204                KclValue::Function { .. } => Ok(value.clone()),
1205                _ => Err(self.into()),
1206            },
1207            PrimitiveType::TagId => match value {
1208                KclValue::TagIdentifier { .. } => Ok(value.clone()),
1209                _ => Err(self.into()),
1210            },
1211            PrimitiveType::Tag => match value {
1212                KclValue::TagDeclarator { .. } | KclValue::TagIdentifier { .. } | KclValue::Uuid { .. } => {
1213                    Ok(value.clone())
1214                }
1215                s @ KclValue::String { value, .. } if ["start", "end", "START", "END"].contains(&&**value) => {
1216                    Ok(s.clone())
1217                }
1218                _ => Err(self.into()),
1219            },
1220        }
1221    }
1222
1223    fn coerce_to_array_type(
1224        &self,
1225        ty: &RuntimeType,
1226        len: ArrayLen,
1227        exec_state: &mut ExecState,
1228        allow_shrink: bool,
1229    ) -> Result<KclValue, CoercionError> {
1230        match self {
1231            KclValue::HomArray { value, ty: aty, .. } => {
1232                let satisfied_len = len.satisfied(value.len(), allow_shrink);
1233
1234                if aty.subtype(ty) {
1235                    return satisfied_len
1242                        .map(|len| KclValue::HomArray {
1243                            value: value[..len].to_vec(),
1244                            ty: aty.clone(),
1245                        })
1246                        .ok_or(self.into());
1247                }
1248
1249                if let Some(satisfied_len) = satisfied_len {
1251                    let value_result = value
1252                        .iter()
1253                        .take(satisfied_len)
1254                        .map(|v| v.coerce(ty, exec_state))
1255                        .collect::<Result<Vec<_>, _>>();
1256
1257                    if let Ok(value) = value_result {
1258                        return Ok(KclValue::HomArray { value, ty: ty.clone() });
1260                    }
1261                }
1262
1263                let mut values = Vec::new();
1265                for item in value {
1266                    if let KclValue::HomArray { value: inner_value, .. } = item {
1267                        for item in inner_value {
1269                            values.push(item.coerce(ty, exec_state)?);
1270                        }
1271                    } else {
1272                        values.push(item.coerce(ty, exec_state)?);
1273                    }
1274                }
1275
1276                let len = len
1277                    .satisfied(values.len(), allow_shrink)
1278                    .ok_or(CoercionError::from(self))?;
1279
1280                if len > values.len() {
1281                    let message = format!(
1282                        "Internal: Expected coerced array length {len} to be less than or equal to original length {}",
1283                        values.len()
1284                    );
1285                    exec_state.err(CompilationError::err(self.into(), message.clone()));
1286                    #[cfg(debug_assertions)]
1287                    panic!("{message}");
1288                }
1289                values.truncate(len);
1290
1291                Ok(KclValue::HomArray {
1292                    value: values,
1293                    ty: ty.clone(),
1294                })
1295            }
1296            KclValue::Tuple { value, .. } => {
1297                let len = len
1298                    .satisfied(value.len(), allow_shrink)
1299                    .ok_or(CoercionError::from(self))?;
1300                let value = value
1301                    .iter()
1302                    .map(|item| item.coerce(ty, exec_state))
1303                    .take(len)
1304                    .collect::<Result<Vec<_>, _>>()?;
1305
1306                Ok(KclValue::HomArray { value, ty: ty.clone() })
1307            }
1308            KclValue::KclNone { .. } if len.satisfied(0, false).is_some() => Ok(KclValue::HomArray {
1309                value: Vec::new(),
1310                ty: ty.clone(),
1311            }),
1312            _ if len.satisfied(1, false).is_some() => Ok(KclValue::HomArray {
1313                value: vec![self.coerce(ty, exec_state)?],
1314                ty: ty.clone(),
1315            }),
1316            _ => Err(self.into()),
1317        }
1318    }
1319
1320    fn coerce_to_tuple_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
1321        match self {
1322            KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } if value.len() == tys.len() => {
1323                let mut result = Vec::new();
1324                for (i, t) in tys.iter().enumerate() {
1325                    result.push(value[i].coerce(t, exec_state)?);
1326                }
1327
1328                Ok(KclValue::Tuple {
1329                    value: result,
1330                    meta: Vec::new(),
1331                })
1332            }
1333            KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::Tuple {
1334                value: Vec::new(),
1335                meta: meta.clone(),
1336            }),
1337            value if tys.len() == 1 && value.has_type(&tys[0]) => Ok(KclValue::Tuple {
1338                value: vec![value.clone()],
1339                meta: Vec::new(),
1340            }),
1341            _ => Err(self.into()),
1342        }
1343    }
1344
1345    fn coerce_to_union_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
1346        for t in tys {
1347            if let Ok(v) = self.coerce(t, exec_state) {
1348                return Ok(v);
1349            }
1350        }
1351
1352        Err(self.into())
1353    }
1354
1355    fn coerce_to_object_type(
1356        &self,
1357        tys: &[(String, RuntimeType)],
1358        _exec_state: &mut ExecState,
1359    ) -> Result<KclValue, CoercionError> {
1360        match self {
1361            KclValue::Object { value, .. } => {
1362                for (s, t) in tys {
1363                    if !value.get(s).ok_or(CoercionError::from(self))?.has_type(t) {
1365                        return Err(self.into());
1366                    }
1367                }
1368                Ok(self.clone())
1370            }
1371            KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::Object {
1372                value: HashMap::new(),
1373                meta: meta.clone(),
1374            }),
1375            _ => Err(self.into()),
1376        }
1377    }
1378
1379    pub fn principal_type(&self) -> Option<RuntimeType> {
1380        match self {
1381            KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
1382            KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(ty.clone()))),
1383            KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)),
1384            KclValue::Object { value, .. } => {
1385                let properties = value
1386                    .iter()
1387                    .map(|(k, v)| v.principal_type().map(|t| (k.clone(), t)))
1388                    .collect::<Option<Vec<_>>>()?;
1389                Some(RuntimeType::Object(properties))
1390            }
1391            KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
1392            KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
1393            KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
1394            KclValue::Face { .. } => Some(RuntimeType::Primitive(PrimitiveType::Face)),
1395            KclValue::Helix { .. } => Some(RuntimeType::Primitive(PrimitiveType::Helix)),
1396            KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
1397            KclValue::Tuple { value, .. } => Some(RuntimeType::Tuple(
1398                value.iter().map(|v| v.principal_type()).collect::<Option<Vec<_>>>()?,
1399            )),
1400            KclValue::HomArray { ty, value, .. } => {
1401                Some(RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(value.len())))
1402            }
1403            KclValue::TagIdentifier(_) => Some(RuntimeType::Primitive(PrimitiveType::TagId)),
1404            KclValue::TagDeclarator(_) | KclValue::Uuid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Tag)),
1405            KclValue::Function { .. } => Some(RuntimeType::Primitive(PrimitiveType::Function)),
1406            KclValue::Module { .. } | KclValue::KclNone { .. } | KclValue::Type { .. } => None,
1407        }
1408    }
1409}
1410
1411#[cfg(test)]
1412mod test {
1413    use super::*;
1414    use crate::execution::{parse_execute, ExecTestResults};
1415
1416    fn values(exec_state: &mut ExecState) -> Vec<KclValue> {
1417        vec![
1418            KclValue::Bool {
1419                value: true,
1420                meta: Vec::new(),
1421            },
1422            KclValue::Number {
1423                value: 1.0,
1424                ty: NumericType::count(),
1425                meta: Vec::new(),
1426            },
1427            KclValue::String {
1428                value: "hello".to_owned(),
1429                meta: Vec::new(),
1430            },
1431            KclValue::Tuple {
1432                value: Vec::new(),
1433                meta: Vec::new(),
1434            },
1435            KclValue::HomArray {
1436                value: Vec::new(),
1437                ty: RuntimeType::solid(),
1438            },
1439            KclValue::Object {
1440                value: crate::execution::KclObjectFields::new(),
1441                meta: Vec::new(),
1442            },
1443            KclValue::TagIdentifier(Box::new("foo".parse().unwrap())),
1444            KclValue::TagDeclarator(Box::new(crate::parsing::ast::types::TagDeclarator::new("foo"))),
1445            KclValue::Plane {
1446                value: Box::new(Plane::from_plane_data(crate::std::sketch::PlaneData::XY, exec_state).unwrap()),
1447            },
1448            KclValue::ImportedGeometry(crate::execution::ImportedGeometry::new(
1450                uuid::Uuid::nil(),
1451                Vec::new(),
1452                Vec::new(),
1453            )),
1454            ]
1456    }
1457
1458    #[track_caller]
1459    fn assert_coerce_results(
1460        value: &KclValue,
1461        super_type: &RuntimeType,
1462        expected_value: &KclValue,
1463        exec_state: &mut ExecState,
1464    ) {
1465        let is_subtype = value == expected_value;
1466        assert_eq!(&value.coerce(super_type, exec_state).unwrap(), expected_value);
1467        assert_eq!(
1468            is_subtype,
1469            value.principal_type().is_some() && value.principal_type().unwrap().subtype(super_type),
1470            "{:?} <: {super_type:?} should be {is_subtype}",
1471            value.principal_type().unwrap()
1472        );
1473        assert!(
1474            expected_value.principal_type().unwrap().subtype(super_type),
1475            "{} <: {super_type}",
1476            expected_value.principal_type().unwrap()
1477        )
1478    }
1479
1480    #[tokio::test(flavor = "multi_thread")]
1481    async fn coerce_idempotent() {
1482        let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1483        let values = values(&mut exec_state);
1484        for v in &values {
1485            let ty = v.principal_type().unwrap();
1487            assert_coerce_results(v, &ty, v, &mut exec_state);
1488
1489            let uty1 = RuntimeType::Union(vec![ty.clone()]);
1491            let uty2 = RuntimeType::Union(vec![ty.clone(), RuntimeType::Primitive(PrimitiveType::Boolean)]);
1492            assert_coerce_results(v, &uty1, v, &mut exec_state);
1493            assert_coerce_results(v, &uty2, v, &mut exec_state);
1494
1495            let aty = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::None);
1497            let aty1 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(1));
1498            let aty0 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::NonEmpty);
1499
1500            match v {
1501                KclValue::Tuple { .. } | KclValue::HomArray { .. } => {
1502                    assert_coerce_results(
1504                        v,
1505                        &aty,
1506                        &KclValue::HomArray {
1507                            value: vec![],
1508                            ty: ty.clone(),
1509                        },
1510                        &mut exec_state,
1511                    );
1512                    v.coerce(&aty1, &mut exec_state).unwrap_err();
1515                    v.coerce(&aty0, &mut exec_state).unwrap_err();
1518                }
1519                _ => {
1520                    assert_coerce_results(
1521                        v,
1522                        &aty,
1523                        &KclValue::HomArray {
1524                            value: vec![v.clone()],
1525                            ty: ty.clone(),
1526                        },
1527                        &mut exec_state,
1528                    );
1529                    assert_coerce_results(
1530                        v,
1531                        &aty1,
1532                        &KclValue::HomArray {
1533                            value: vec![v.clone()],
1534                            ty: ty.clone(),
1535                        },
1536                        &mut exec_state,
1537                    );
1538                    assert_coerce_results(
1539                        v,
1540                        &aty0,
1541                        &KclValue::HomArray {
1542                            value: vec![v.clone()],
1543                            ty: ty.clone(),
1544                        },
1545                        &mut exec_state,
1546                    );
1547
1548                    let tty = RuntimeType::Tuple(vec![ty.clone()]);
1550                    assert_coerce_results(
1551                        v,
1552                        &tty,
1553                        &KclValue::Tuple {
1554                            value: vec![v.clone()],
1555                            meta: Vec::new(),
1556                        },
1557                        &mut exec_state,
1558                    );
1559                }
1560            }
1561        }
1562
1563        for v in &values[1..] {
1564            v.coerce(&RuntimeType::Primitive(PrimitiveType::Boolean), &mut exec_state)
1566                .unwrap_err();
1567        }
1568    }
1569
1570    #[tokio::test(flavor = "multi_thread")]
1571    async fn coerce_none() {
1572        let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1573        let none = KclValue::KclNone {
1574            value: crate::parsing::ast::types::KclNone::new(),
1575            meta: Vec::new(),
1576        };
1577
1578        let aty = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::None);
1579        let aty0 = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::Known(0));
1580        let aty1 = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::Known(1));
1581        let aty1p = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::NonEmpty);
1582        assert_coerce_results(
1583            &none,
1584            &aty,
1585            &KclValue::HomArray {
1586                value: Vec::new(),
1587                ty: RuntimeType::solid(),
1588            },
1589            &mut exec_state,
1590        );
1591        assert_coerce_results(
1592            &none,
1593            &aty0,
1594            &KclValue::HomArray {
1595                value: Vec::new(),
1596                ty: RuntimeType::solid(),
1597            },
1598            &mut exec_state,
1599        );
1600        none.coerce(&aty1, &mut exec_state).unwrap_err();
1601        none.coerce(&aty1p, &mut exec_state).unwrap_err();
1602
1603        let tty = RuntimeType::Tuple(vec![]);
1604        let tty1 = RuntimeType::Tuple(vec![RuntimeType::solid()]);
1605        assert_coerce_results(
1606            &none,
1607            &tty,
1608            &KclValue::Tuple {
1609                value: Vec::new(),
1610                meta: Vec::new(),
1611            },
1612            &mut exec_state,
1613        );
1614        none.coerce(&tty1, &mut exec_state).unwrap_err();
1615
1616        let oty = RuntimeType::Object(vec![]);
1617        assert_coerce_results(
1618            &none,
1619            &oty,
1620            &KclValue::Object {
1621                value: HashMap::new(),
1622                meta: Vec::new(),
1623            },
1624            &mut exec_state,
1625        );
1626    }
1627
1628    #[tokio::test(flavor = "multi_thread")]
1629    async fn coerce_record() {
1630        let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1631
1632        let obj0 = KclValue::Object {
1633            value: HashMap::new(),
1634            meta: Vec::new(),
1635        };
1636        let obj1 = KclValue::Object {
1637            value: [(
1638                "foo".to_owned(),
1639                KclValue::Bool {
1640                    value: true,
1641                    meta: Vec::new(),
1642                },
1643            )]
1644            .into(),
1645            meta: Vec::new(),
1646        };
1647        let obj2 = KclValue::Object {
1648            value: [
1649                (
1650                    "foo".to_owned(),
1651                    KclValue::Bool {
1652                        value: true,
1653                        meta: Vec::new(),
1654                    },
1655                ),
1656                (
1657                    "bar".to_owned(),
1658                    KclValue::Number {
1659                        value: 0.0,
1660                        ty: NumericType::count(),
1661                        meta: Vec::new(),
1662                    },
1663                ),
1664                (
1665                    "baz".to_owned(),
1666                    KclValue::Number {
1667                        value: 42.0,
1668                        ty: NumericType::count(),
1669                        meta: Vec::new(),
1670                    },
1671                ),
1672            ]
1673            .into(),
1674            meta: Vec::new(),
1675        };
1676
1677        let ty0 = RuntimeType::Object(vec![]);
1678        assert_coerce_results(&obj0, &ty0, &obj0, &mut exec_state);
1679        assert_coerce_results(&obj1, &ty0, &obj1, &mut exec_state);
1680        assert_coerce_results(&obj2, &ty0, &obj2, &mut exec_state);
1681
1682        let ty1 = RuntimeType::Object(vec![("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
1683        obj0.coerce(&ty1, &mut exec_state).unwrap_err();
1684        assert_coerce_results(&obj1, &ty1, &obj1, &mut exec_state);
1685        assert_coerce_results(&obj2, &ty1, &obj2, &mut exec_state);
1686
1687        let ty2 = RuntimeType::Object(vec![
1689            (
1690                "bar".to_owned(),
1691                RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1692            ),
1693            ("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean)),
1694        ]);
1695        obj0.coerce(&ty2, &mut exec_state).unwrap_err();
1696        obj1.coerce(&ty2, &mut exec_state).unwrap_err();
1697        assert_coerce_results(&obj2, &ty2, &obj2, &mut exec_state);
1698
1699        let tyq = RuntimeType::Object(vec![("qux".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
1701        obj0.coerce(&tyq, &mut exec_state).unwrap_err();
1702        obj1.coerce(&tyq, &mut exec_state).unwrap_err();
1703        obj2.coerce(&tyq, &mut exec_state).unwrap_err();
1704
1705        let ty1 = RuntimeType::Object(vec![("bar".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
1707        obj2.coerce(&ty1, &mut exec_state).unwrap_err();
1708    }
1709
1710    #[tokio::test(flavor = "multi_thread")]
1711    async fn coerce_array() {
1712        let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1713
1714        let hom_arr = KclValue::HomArray {
1715            value: vec![
1716                KclValue::Number {
1717                    value: 0.0,
1718                    ty: NumericType::count(),
1719                    meta: Vec::new(),
1720                },
1721                KclValue::Number {
1722                    value: 1.0,
1723                    ty: NumericType::count(),
1724                    meta: Vec::new(),
1725                },
1726                KclValue::Number {
1727                    value: 2.0,
1728                    ty: NumericType::count(),
1729                    meta: Vec::new(),
1730                },
1731                KclValue::Number {
1732                    value: 3.0,
1733                    ty: NumericType::count(),
1734                    meta: Vec::new(),
1735                },
1736            ],
1737            ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1738        };
1739        let mixed1 = KclValue::Tuple {
1740            value: vec![
1741                KclValue::Number {
1742                    value: 0.0,
1743                    ty: NumericType::count(),
1744                    meta: Vec::new(),
1745                },
1746                KclValue::Number {
1747                    value: 1.0,
1748                    ty: NumericType::count(),
1749                    meta: Vec::new(),
1750                },
1751            ],
1752            meta: Vec::new(),
1753        };
1754        let mixed2 = KclValue::Tuple {
1755            value: vec![
1756                KclValue::Number {
1757                    value: 0.0,
1758                    ty: NumericType::count(),
1759                    meta: Vec::new(),
1760                },
1761                KclValue::Bool {
1762                    value: true,
1763                    meta: Vec::new(),
1764                },
1765            ],
1766            meta: Vec::new(),
1767        };
1768
1769        let tyh = RuntimeType::Array(
1771            Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
1772            ArrayLen::Known(4),
1773        );
1774        let tym1 = RuntimeType::Tuple(vec![
1775            RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1776            RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1777        ]);
1778        let tym2 = RuntimeType::Tuple(vec![
1779            RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1780            RuntimeType::Primitive(PrimitiveType::Boolean),
1781        ]);
1782        assert_coerce_results(&hom_arr, &tyh, &hom_arr, &mut exec_state);
1783        assert_coerce_results(&mixed1, &tym1, &mixed1, &mut exec_state);
1784        assert_coerce_results(&mixed2, &tym2, &mixed2, &mut exec_state);
1785        mixed1.coerce(&tym2, &mut exec_state).unwrap_err();
1786        mixed2.coerce(&tym1, &mut exec_state).unwrap_err();
1787
1788        let tyhn = RuntimeType::Array(
1790            Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
1791            ArrayLen::None,
1792        );
1793        let tyh1 = RuntimeType::Array(
1794            Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
1795            ArrayLen::NonEmpty,
1796        );
1797        let tyh3 = RuntimeType::Array(
1798            Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
1799            ArrayLen::Known(3),
1800        );
1801        assert_coerce_results(&hom_arr, &tyhn, &hom_arr, &mut exec_state);
1802        assert_coerce_results(&hom_arr, &tyh1, &hom_arr, &mut exec_state);
1803        hom_arr.coerce(&tyh3, &mut exec_state).unwrap_err();
1804
1805        let hom_arr0 = KclValue::HomArray {
1806            value: vec![],
1807            ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1808        };
1809        assert_coerce_results(&hom_arr0, &tyhn, &hom_arr0, &mut exec_state);
1810        hom_arr0.coerce(&tyh1, &mut exec_state).unwrap_err();
1811        hom_arr0.coerce(&tyh3, &mut exec_state).unwrap_err();
1812
1813        let tym1 = RuntimeType::Tuple(vec![
1816            RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1817            RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1818        ]);
1819        let tym2 = RuntimeType::Tuple(vec![
1820            RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1821            RuntimeType::Primitive(PrimitiveType::Boolean),
1822        ]);
1823        assert_coerce_results(&mixed1, &tym1, &mixed1, &mut exec_state);
1826        assert_coerce_results(&mixed2, &tym2, &mixed2, &mut exec_state);
1827
1828        let hom_arr_2 = KclValue::HomArray {
1830            value: vec![
1831                KclValue::Number {
1832                    value: 0.0,
1833                    ty: NumericType::count(),
1834                    meta: Vec::new(),
1835                },
1836                KclValue::Number {
1837                    value: 1.0,
1838                    ty: NumericType::count(),
1839                    meta: Vec::new(),
1840                },
1841            ],
1842            ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1843        };
1844        let mixed0 = KclValue::Tuple {
1845            value: vec![],
1846            meta: Vec::new(),
1847        };
1848        assert_coerce_results(&mixed1, &tyhn, &hom_arr_2, &mut exec_state);
1849        assert_coerce_results(&mixed1, &tyh1, &hom_arr_2, &mut exec_state);
1850        assert_coerce_results(&mixed0, &tyhn, &hom_arr0, &mut exec_state);
1851        mixed0.coerce(&tyh, &mut exec_state).unwrap_err();
1852        mixed0.coerce(&tyh1, &mut exec_state).unwrap_err();
1853
1854        assert_coerce_results(&hom_arr_2, &tym1, &mixed1, &mut exec_state);
1856        hom_arr.coerce(&tym1, &mut exec_state).unwrap_err();
1857        hom_arr_2.coerce(&tym2, &mut exec_state).unwrap_err();
1858
1859        mixed0.coerce(&tym1, &mut exec_state).unwrap_err();
1860        mixed0.coerce(&tym2, &mut exec_state).unwrap_err();
1861    }
1862
1863    #[tokio::test(flavor = "multi_thread")]
1864    async fn coerce_union() {
1865        let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1866
1867        assert!(RuntimeType::Union(vec![]).subtype(&RuntimeType::Union(vec![
1869            RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1870            RuntimeType::Primitive(PrimitiveType::Boolean)
1871        ])));
1872        assert!(
1873            RuntimeType::Union(vec![RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))]).subtype(
1874                &RuntimeType::Union(vec![
1875                    RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1876                    RuntimeType::Primitive(PrimitiveType::Boolean)
1877                ])
1878            )
1879        );
1880        assert!(RuntimeType::Union(vec![
1881            RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1882            RuntimeType::Primitive(PrimitiveType::Boolean)
1883        ])
1884        .subtype(&RuntimeType::Union(vec![
1885            RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1886            RuntimeType::Primitive(PrimitiveType::Boolean)
1887        ])));
1888
1889        let count = KclValue::Number {
1891            value: 1.0,
1892            ty: NumericType::count(),
1893            meta: Vec::new(),
1894        };
1895
1896        let tya = RuntimeType::Union(vec![RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))]);
1897        let tya2 = RuntimeType::Union(vec![
1898            RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1899            RuntimeType::Primitive(PrimitiveType::Boolean),
1900        ]);
1901        assert_coerce_results(&count, &tya, &count, &mut exec_state);
1902        assert_coerce_results(&count, &tya2, &count, &mut exec_state);
1903
1904        let tyb = RuntimeType::Union(vec![RuntimeType::Primitive(PrimitiveType::Boolean)]);
1906        let tyb2 = RuntimeType::Union(vec![
1907            RuntimeType::Primitive(PrimitiveType::Boolean),
1908            RuntimeType::Primitive(PrimitiveType::String),
1909        ]);
1910        count.coerce(&tyb, &mut exec_state).unwrap_err();
1911        count.coerce(&tyb2, &mut exec_state).unwrap_err();
1912    }
1913
1914    #[tokio::test(flavor = "multi_thread")]
1915    async fn coerce_axes() {
1916        let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1917
1918        assert!(RuntimeType::Primitive(PrimitiveType::Axis2d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis2d)));
1920        assert!(RuntimeType::Primitive(PrimitiveType::Axis3d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis3d)));
1921        assert!(!RuntimeType::Primitive(PrimitiveType::Axis3d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis2d)));
1922        assert!(!RuntimeType::Primitive(PrimitiveType::Axis2d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis3d)));
1923
1924        let a2d = KclValue::Object {
1926            value: [
1927                (
1928                    "origin".to_owned(),
1929                    KclValue::HomArray {
1930                        value: vec![
1931                            KclValue::Number {
1932                                value: 0.0,
1933                                ty: NumericType::mm(),
1934                                meta: Vec::new(),
1935                            },
1936                            KclValue::Number {
1937                                value: 0.0,
1938                                ty: NumericType::mm(),
1939                                meta: Vec::new(),
1940                            },
1941                        ],
1942                        ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
1943                    },
1944                ),
1945                (
1946                    "direction".to_owned(),
1947                    KclValue::HomArray {
1948                        value: vec![
1949                            KclValue::Number {
1950                                value: 1.0,
1951                                ty: NumericType::mm(),
1952                                meta: Vec::new(),
1953                            },
1954                            KclValue::Number {
1955                                value: 0.0,
1956                                ty: NumericType::mm(),
1957                                meta: Vec::new(),
1958                            },
1959                        ],
1960                        ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
1961                    },
1962                ),
1963            ]
1964            .into(),
1965            meta: Vec::new(),
1966        };
1967        let a3d = KclValue::Object {
1968            value: [
1969                (
1970                    "origin".to_owned(),
1971                    KclValue::HomArray {
1972                        value: vec![
1973                            KclValue::Number {
1974                                value: 0.0,
1975                                ty: NumericType::mm(),
1976                                meta: Vec::new(),
1977                            },
1978                            KclValue::Number {
1979                                value: 0.0,
1980                                ty: NumericType::mm(),
1981                                meta: Vec::new(),
1982                            },
1983                            KclValue::Number {
1984                                value: 0.0,
1985                                ty: NumericType::mm(),
1986                                meta: Vec::new(),
1987                            },
1988                        ],
1989                        ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
1990                    },
1991                ),
1992                (
1993                    "direction".to_owned(),
1994                    KclValue::HomArray {
1995                        value: vec![
1996                            KclValue::Number {
1997                                value: 1.0,
1998                                ty: NumericType::mm(),
1999                                meta: Vec::new(),
2000                            },
2001                            KclValue::Number {
2002                                value: 0.0,
2003                                ty: NumericType::mm(),
2004                                meta: Vec::new(),
2005                            },
2006                            KclValue::Number {
2007                                value: 1.0,
2008                                ty: NumericType::mm(),
2009                                meta: Vec::new(),
2010                            },
2011                        ],
2012                        ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
2013                    },
2014                ),
2015            ]
2016            .into(),
2017            meta: Vec::new(),
2018        };
2019
2020        let ty2d = RuntimeType::Primitive(PrimitiveType::Axis2d);
2021        let ty3d = RuntimeType::Primitive(PrimitiveType::Axis3d);
2022
2023        assert_coerce_results(&a2d, &ty2d, &a2d, &mut exec_state);
2024        assert_coerce_results(&a3d, &ty3d, &a3d, &mut exec_state);
2025        assert_coerce_results(&a3d, &ty2d, &a2d, &mut exec_state);
2026        a2d.coerce(&ty3d, &mut exec_state).unwrap_err();
2027    }
2028
2029    #[tokio::test(flavor = "multi_thread")]
2030    async fn coerce_numeric() {
2031        let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
2032
2033        let count = KclValue::Number {
2034            value: 1.0,
2035            ty: NumericType::count(),
2036            meta: Vec::new(),
2037        };
2038        let mm = KclValue::Number {
2039            value: 1.0,
2040            ty: NumericType::mm(),
2041            meta: Vec::new(),
2042        };
2043        let inches = KclValue::Number {
2044            value: 1.0,
2045            ty: NumericType::Known(UnitType::Length(UnitLen::Inches)),
2046            meta: Vec::new(),
2047        };
2048        let rads = KclValue::Number {
2049            value: 1.0,
2050            ty: NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
2051            meta: Vec::new(),
2052        };
2053        let default = KclValue::Number {
2054            value: 1.0,
2055            ty: NumericType::default(),
2056            meta: Vec::new(),
2057        };
2058        let any = KclValue::Number {
2059            value: 1.0,
2060            ty: NumericType::Any,
2061            meta: Vec::new(),
2062        };
2063        let unknown = KclValue::Number {
2064            value: 1.0,
2065            ty: NumericType::Unknown,
2066            meta: Vec::new(),
2067        };
2068
2069        assert_coerce_results(&count, &NumericType::count().into(), &count, &mut exec_state);
2071        assert_coerce_results(&mm, &NumericType::mm().into(), &mm, &mut exec_state);
2072        assert_coerce_results(&any, &NumericType::Any.into(), &any, &mut exec_state);
2073        assert_coerce_results(&unknown, &NumericType::Unknown.into(), &unknown, &mut exec_state);
2074        assert_coerce_results(&default, &NumericType::default().into(), &default, &mut exec_state);
2075
2076        assert_coerce_results(&count, &NumericType::Any.into(), &count, &mut exec_state);
2077        assert_coerce_results(&mm, &NumericType::Any.into(), &mm, &mut exec_state);
2078        assert_coerce_results(&unknown, &NumericType::Any.into(), &unknown, &mut exec_state);
2079        assert_coerce_results(&default, &NumericType::Any.into(), &default, &mut exec_state);
2080
2081        assert_eq!(
2082            default
2083                .coerce(
2084                    &NumericType::Default {
2085                        len: UnitLen::Yards,
2086                        angle: UnitAngle::default()
2087                    }
2088                    .into(),
2089                    &mut exec_state
2090                )
2091                .unwrap(),
2092            default
2093        );
2094
2095        count.coerce(&NumericType::mm().into(), &mut exec_state).unwrap_err();
2097        mm.coerce(&NumericType::count().into(), &mut exec_state).unwrap_err();
2098        unknown.coerce(&NumericType::mm().into(), &mut exec_state).unwrap_err();
2099        unknown
2100            .coerce(&NumericType::default().into(), &mut exec_state)
2101            .unwrap_err();
2102
2103        count.coerce(&NumericType::Unknown.into(), &mut exec_state).unwrap_err();
2104        mm.coerce(&NumericType::Unknown.into(), &mut exec_state).unwrap_err();
2105        default
2106            .coerce(&NumericType::Unknown.into(), &mut exec_state)
2107            .unwrap_err();
2108
2109        assert_eq!(
2110            inches
2111                .coerce(&NumericType::mm().into(), &mut exec_state)
2112                .unwrap()
2113                .as_f64()
2114                .unwrap()
2115                .round(),
2116            25.0
2117        );
2118        assert_eq!(
2119            rads.coerce(
2120                &NumericType::Known(UnitType::Angle(UnitAngle::Degrees)).into(),
2121                &mut exec_state
2122            )
2123            .unwrap()
2124            .as_f64()
2125            .unwrap()
2126            .round(),
2127            57.0
2128        );
2129        assert_eq!(
2130            inches
2131                .coerce(&NumericType::default().into(), &mut exec_state)
2132                .unwrap()
2133                .as_f64()
2134                .unwrap()
2135                .round(),
2136            1.0
2137        );
2138        assert_eq!(
2139            rads.coerce(&NumericType::default().into(), &mut exec_state)
2140                .unwrap()
2141                .as_f64()
2142                .unwrap()
2143                .round(),
2144            1.0
2145        );
2146    }
2147
2148    #[track_caller]
2149    fn assert_value_and_type(name: &str, result: &ExecTestResults, expected: f64, expected_ty: NumericType) {
2150        let mem = result.exec_state.stack();
2151        match mem
2152            .memory
2153            .get_from(name, result.mem_env, SourceRange::default(), 0)
2154            .unwrap()
2155        {
2156            KclValue::Number { value, ty, .. } => {
2157                assert_eq!(value.round(), expected);
2158                assert_eq!(*ty, expected_ty);
2159            }
2160            _ => unreachable!(),
2161        }
2162    }
2163
2164    #[tokio::test(flavor = "multi_thread")]
2165    async fn combine_numeric() {
2166        let program = r#"a = 5 + 4
2167b = 5 - 2
2168c = 5mm - 2mm + 10mm
2169d = 5mm - 2 + 10
2170e = 5 - 2mm + 10
2171f = 30mm - 1inch
2172
2173g = 2 * 10
2174h = 2 * 10mm
2175i = 2mm * 10mm
2176j = 2_ * 10
2177k = 2_ * 3mm * 3mm
2178
2179l = 1 / 10
2180m = 2mm / 1mm
2181n = 10inch / 2mm
2182o = 3mm / 3
2183p = 3_ / 4
2184q = 4inch / 2_
2185
2186r = min([0, 3, 42])
2187s = min([0, 3mm, -42])
2188t = min([100, 3in, 142mm])
2189u = min([3rad, 4in])
2190"#;
2191
2192        let result = parse_execute(program).await.unwrap();
2193        assert_eq!(
2194            result.exec_state.errors().len(),
2195            5,
2196            "errors: {:?}",
2197            result.exec_state.errors()
2198        );
2199
2200        assert_value_and_type("a", &result, 9.0, NumericType::default());
2201        assert_value_and_type("b", &result, 3.0, NumericType::default());
2202        assert_value_and_type("c", &result, 13.0, NumericType::mm());
2203        assert_value_and_type("d", &result, 13.0, NumericType::mm());
2204        assert_value_and_type("e", &result, 13.0, NumericType::mm());
2205        assert_value_and_type("f", &result, 5.0, NumericType::mm());
2206
2207        assert_value_and_type("g", &result, 20.0, NumericType::default());
2208        assert_value_and_type("h", &result, 20.0, NumericType::mm());
2209        assert_value_and_type("i", &result, 20.0, NumericType::Unknown);
2210        assert_value_and_type("j", &result, 20.0, NumericType::default());
2211        assert_value_and_type("k", &result, 18.0, NumericType::Unknown);
2212
2213        assert_value_and_type("l", &result, 0.0, NumericType::default());
2214        assert_value_and_type("m", &result, 2.0, NumericType::count());
2215        assert_value_and_type("n", &result, 5.0, NumericType::Unknown);
2216        assert_value_and_type("o", &result, 1.0, NumericType::mm());
2217        assert_value_and_type("p", &result, 1.0, NumericType::count());
2218        assert_value_and_type("q", &result, 2.0, NumericType::Known(UnitType::Length(UnitLen::Inches)));
2219
2220        assert_value_and_type("r", &result, 0.0, NumericType::default());
2221        assert_value_and_type("s", &result, -42.0, NumericType::mm());
2222        assert_value_and_type("t", &result, 3.0, NumericType::Unknown);
2223        assert_value_and_type("u", &result, 3.0, NumericType::Unknown);
2224    }
2225
2226    #[tokio::test(flavor = "multi_thread")]
2227    async fn bad_typed_arithmetic() {
2228        let program = r#"
2229a = 1rad
2230b = 180 / PI * a + 360
2231"#;
2232
2233        let result = parse_execute(program).await.unwrap();
2234
2235        assert_value_and_type("a", &result, 1.0, NumericType::radians());
2236        assert_value_and_type("b", &result, 417.0, NumericType::Unknown);
2237    }
2238
2239    #[tokio::test(flavor = "multi_thread")]
2240    async fn cos_coercions() {
2241        let program = r#"
2242a = cos(units::toRadians(30))
2243b = 3 / a
2244c = cos(30deg)
2245d = cos(30)
2246"#;
2247
2248        let result = parse_execute(program).await.unwrap();
2249        assert!(result.exec_state.errors().is_empty());
2250
2251        assert_value_and_type("a", &result, 1.0, NumericType::count());
2252        assert_value_and_type("b", &result, 3.0, NumericType::default());
2253        assert_value_and_type("c", &result, 1.0, NumericType::count());
2254        assert_value_and_type("d", &result, 1.0, NumericType::count());
2255    }
2256
2257    #[tokio::test(flavor = "multi_thread")]
2258    async fn coerce_nested_array() {
2259        let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
2260
2261        let mixed1 = KclValue::HomArray {
2262            value: vec![
2263                KclValue::Number {
2264                    value: 0.0,
2265                    ty: NumericType::count(),
2266                    meta: Vec::new(),
2267                },
2268                KclValue::Number {
2269                    value: 1.0,
2270                    ty: NumericType::count(),
2271                    meta: Vec::new(),
2272                },
2273                KclValue::HomArray {
2274                    value: vec![
2275                        KclValue::Number {
2276                            value: 2.0,
2277                            ty: NumericType::count(),
2278                            meta: Vec::new(),
2279                        },
2280                        KclValue::Number {
2281                            value: 3.0,
2282                            ty: NumericType::count(),
2283                            meta: Vec::new(),
2284                        },
2285                    ],
2286                    ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2287                },
2288            ],
2289            ty: RuntimeType::any(),
2290        };
2291
2292        let tym1 = RuntimeType::Array(
2294            Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
2295            ArrayLen::NonEmpty,
2296        );
2297
2298        let result = KclValue::HomArray {
2299            value: vec![
2300                KclValue::Number {
2301                    value: 0.0,
2302                    ty: NumericType::count(),
2303                    meta: Vec::new(),
2304                },
2305                KclValue::Number {
2306                    value: 1.0,
2307                    ty: NumericType::count(),
2308                    meta: Vec::new(),
2309                },
2310                KclValue::Number {
2311                    value: 2.0,
2312                    ty: NumericType::count(),
2313                    meta: Vec::new(),
2314                },
2315                KclValue::Number {
2316                    value: 3.0,
2317                    ty: NumericType::count(),
2318                    meta: Vec::new(),
2319                },
2320            ],
2321            ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2322        };
2323        assert_coerce_results(&mixed1, &tym1, &result, &mut exec_state);
2324    }
2325}