1use std::collections::HashMap;
2use std::str::FromStr;
3
4use anyhow::Result;
5pub use kcl_api::NumericType;
6use kcl_api::UnitAngle;
7use kcl_api::UnitLength;
8pub use kcl_api::UnitType;
9use serde::Deserialize;
10use serde::Serialize;
11
12use crate::CompilationIssue;
13use crate::KclError;
14use crate::SourceRange;
15use crate::errors::KclErrorDetails;
16use crate::exec::PlaneKind;
17use crate::execution::ExecState;
18use crate::execution::Plane;
19use crate::execution::PlaneInfo;
20use crate::execution::Point3d;
21use crate::execution::SKETCH_OBJECT_META;
22use crate::execution::SKETCH_OBJECT_META_SKETCH;
23use crate::execution::annotations;
24use crate::execution::kcl_value::KclValue;
25use crate::execution::kcl_value::TypeDef;
26use crate::execution::memory::{self};
27use crate::fmt;
28use crate::parsing::ast::types::PrimitiveType as AstPrimitiveType;
29use crate::parsing::ast::types::Type;
30use crate::parsing::token::NumericSuffix;
31use crate::std::args::FromKclValue;
32use crate::std::args::TyF64;
33
34#[derive(Debug, Clone, PartialEq)]
35pub enum RuntimeType {
36 Primitive(PrimitiveType),
37 Array(Box<RuntimeType>, ArrayLen),
38 Union(Vec<RuntimeType>),
39 Tuple(Vec<RuntimeType>),
40 Object(Vec<(String, RuntimeType)>, bool),
41}
42
43impl RuntimeType {
44 pub fn any() -> Self {
45 RuntimeType::Primitive(PrimitiveType::Any)
46 }
47
48 pub fn any_array() -> Self {
49 RuntimeType::Array(Box::new(RuntimeType::Primitive(PrimitiveType::Any)), ArrayLen::None)
50 }
51
52 pub fn edge() -> Self {
53 RuntimeType::Primitive(PrimitiveType::Edge)
54 }
55
56 pub fn function() -> Self {
57 RuntimeType::Primitive(PrimitiveType::Function)
58 }
59
60 pub fn segment() -> Self {
61 RuntimeType::Primitive(PrimitiveType::Segment)
62 }
63
64 pub fn segments() -> Self {
66 RuntimeType::Array(Box::new(Self::segment()), ArrayLen::Minimum(1))
67 }
68
69 pub fn sketch() -> Self {
70 RuntimeType::Primitive(PrimitiveType::Sketch)
71 }
72
73 pub fn sketch_or_surface() -> Self {
74 RuntimeType::Union(vec![Self::sketch(), Self::plane(), Self::face()])
75 }
76
77 pub fn sketches() -> Self {
79 RuntimeType::Array(
80 Box::new(RuntimeType::Primitive(PrimitiveType::Sketch)),
81 ArrayLen::Minimum(1),
82 )
83 }
84
85 pub fn faces() -> Self {
87 RuntimeType::Array(
88 Box::new(RuntimeType::Primitive(PrimitiveType::Face)),
89 ArrayLen::Minimum(1),
90 )
91 }
92
93 pub fn tagged_faces() -> Self {
95 RuntimeType::Array(
96 Box::new(RuntimeType::Primitive(PrimitiveType::TaggedFace)),
97 ArrayLen::Minimum(1),
98 )
99 }
100
101 pub fn solids() -> Self {
103 RuntimeType::Array(
104 Box::new(RuntimeType::Primitive(PrimitiveType::Solid)),
105 ArrayLen::Minimum(1),
106 )
107 }
108
109 pub fn solid() -> Self {
110 RuntimeType::Primitive(PrimitiveType::Solid)
111 }
112
113 pub fn gdt() -> Self {
114 RuntimeType::Primitive(PrimitiveType::GdtAnnotation)
115 }
116
117 pub fn gdts() -> Self {
119 RuntimeType::Array(
120 Box::new(RuntimeType::Primitive(PrimitiveType::GdtAnnotation)),
121 ArrayLen::Minimum(1),
122 )
123 }
124
125 pub fn helices() -> Self {
127 RuntimeType::Array(
128 Box::new(RuntimeType::Primitive(PrimitiveType::Helix)),
129 ArrayLen::Minimum(1),
130 )
131 }
132 pub fn helix() -> Self {
133 RuntimeType::Primitive(PrimitiveType::Helix)
134 }
135
136 pub fn plane() -> Self {
137 RuntimeType::Primitive(PrimitiveType::Plane)
138 }
139
140 pub fn planes() -> Self {
142 RuntimeType::Array(
143 Box::new(RuntimeType::Primitive(PrimitiveType::Plane)),
144 ArrayLen::Minimum(1),
145 )
146 }
147
148 pub fn face() -> Self {
149 RuntimeType::Primitive(PrimitiveType::Face)
150 }
151
152 pub fn tag_decl() -> Self {
153 RuntimeType::Primitive(PrimitiveType::TagDecl)
154 }
155
156 pub fn tagged_face() -> Self {
157 RuntimeType::Primitive(PrimitiveType::TaggedFace)
158 }
159
160 pub fn tagged_face_or_segment() -> Self {
161 RuntimeType::Union(vec![
162 RuntimeType::Primitive(PrimitiveType::TaggedFace),
163 RuntimeType::Primitive(PrimitiveType::Segment),
164 ])
165 }
166
167 pub fn tagged_edge() -> Self {
168 RuntimeType::Primitive(PrimitiveType::TaggedEdge)
169 }
170
171 pub fn bool() -> Self {
172 RuntimeType::Primitive(PrimitiveType::Boolean)
173 }
174
175 pub fn string() -> Self {
176 RuntimeType::Primitive(PrimitiveType::String)
177 }
178
179 pub fn imported() -> Self {
180 RuntimeType::Primitive(PrimitiveType::ImportedGeometry)
181 }
182
183 pub fn point2d() -> Self {
185 RuntimeType::Array(Box::new(RuntimeType::length()), ArrayLen::Known(2))
186 }
187
188 pub fn point3d() -> Self {
190 RuntimeType::Array(Box::new(RuntimeType::length()), ArrayLen::Known(3))
191 }
192
193 pub fn length() -> Self {
194 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::GenericLength)))
195 }
196
197 pub fn known_length(len: UnitLength) -> Self {
198 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Length(len))))
199 }
200
201 pub fn angle() -> Self {
202 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::GenericAngle)))
203 }
204
205 pub fn radians() -> Self {
206 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Angle(
207 UnitAngle::Radians,
208 ))))
209 }
210
211 pub fn degrees() -> Self {
212 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Angle(
213 UnitAngle::Degrees,
214 ))))
215 }
216
217 pub fn count() -> Self {
218 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Count)))
219 }
220
221 pub fn num_any() -> Self {
222 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))
223 }
224
225 pub fn from_parsed(
226 value: Type,
227 exec_state: &mut ExecState,
228 source_range: SourceRange,
229 constrainable: bool,
230 suppress_warnings: bool,
231 ) -> Result<Self, CompilationIssue> {
232 match value {
233 Type::Primitive(pt) => Self::from_parsed_primitive(pt, exec_state, source_range, suppress_warnings),
234 Type::Array { ty, len } => {
235 Self::from_parsed(*ty, exec_state, source_range, constrainable, suppress_warnings)
236 .map(|t| RuntimeType::Array(Box::new(t), len))
237 }
238 Type::Union { tys } => tys
239 .into_iter()
240 .map(|t| Self::from_parsed(t.inner, exec_state, source_range, constrainable, suppress_warnings))
241 .collect::<Result<Vec<_>, CompilationIssue>>()
242 .map(RuntimeType::Union),
243 Type::Object { properties } => properties
244 .into_iter()
245 .map(|(id, ty)| {
246 RuntimeType::from_parsed(ty.inner, exec_state, source_range, constrainable, suppress_warnings)
247 .map(|ty| (id.name.clone(), ty))
248 })
249 .collect::<Result<Vec<_>, CompilationIssue>>()
250 .map(|values| RuntimeType::Object(values, constrainable)),
251 }
252 }
253
254 fn from_parsed_primitive(
255 value: AstPrimitiveType,
256 exec_state: &mut ExecState,
257 source_range: SourceRange,
258 suppress_warnings: bool,
259 ) -> Result<Self, CompilationIssue> {
260 Ok(match value {
261 AstPrimitiveType::Any => RuntimeType::Primitive(PrimitiveType::Any),
262 AstPrimitiveType::None => RuntimeType::Primitive(PrimitiveType::None),
263 AstPrimitiveType::String => RuntimeType::Primitive(PrimitiveType::String),
264 AstPrimitiveType::Boolean => RuntimeType::Primitive(PrimitiveType::Boolean),
265 AstPrimitiveType::Number(suffix) => {
266 let ty = match suffix {
267 NumericSuffix::None => NumericType::Any,
268 _ => NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
269 };
270 RuntimeType::Primitive(PrimitiveType::Number(ty))
271 }
272 AstPrimitiveType::Named { id } => Self::from_alias(&id.name, exec_state, source_range, suppress_warnings)?,
273 AstPrimitiveType::TagDecl => RuntimeType::Primitive(PrimitiveType::TagDecl),
274 AstPrimitiveType::ImportedGeometry => RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
275 AstPrimitiveType::Function(_) => RuntimeType::Primitive(PrimitiveType::Function),
276 })
277 }
278
279 pub fn from_alias(
280 alias: &str,
281 exec_state: &mut ExecState,
282 source_range: SourceRange,
283 suppress_warnings: bool,
284 ) -> Result<Self, CompilationIssue> {
285 let ty_val = exec_state
286 .stack()
287 .get(&format!("{}{}", memory::TYPE_PREFIX, alias), source_range)
288 .map_err(|_| CompilationIssue::err(source_range, format!("Unknown type: {alias}")))?;
289
290 Ok(match ty_val {
291 KclValue::Type {
292 value, experimental, ..
293 } => {
294 let result = match value {
295 TypeDef::RustRepr(ty, _) => RuntimeType::Primitive(ty),
296 TypeDef::Alias(ty) => ty,
297 };
298 if experimental && !suppress_warnings {
299 exec_state.warn_experimental(&format!("the type `{alias}`"), source_range);
300 }
301 result
302 }
303 _ => unreachable!(),
304 })
305 }
306
307 pub fn human_friendly_type(&self) -> String {
308 match self {
309 RuntimeType::Primitive(ty) => ty.to_string(),
310 RuntimeType::Array(ty, ArrayLen::None | ArrayLen::Minimum(0)) => {
311 format!("an array of {}", ty.display_multiple())
312 }
313 RuntimeType::Array(ty, ArrayLen::Minimum(1)) => format!("one or more {}", ty.display_multiple()),
314 RuntimeType::Array(ty, ArrayLen::Minimum(n)) => {
315 format!("an array of {n} or more {}", ty.display_multiple())
316 }
317 RuntimeType::Array(ty, ArrayLen::Known(n)) => format!("an array of {n} {}", ty.display_multiple()),
318 RuntimeType::Union(tys) => tys
319 .iter()
320 .map(Self::human_friendly_type)
321 .collect::<Vec<_>>()
322 .join(" or "),
323 RuntimeType::Tuple(tys) => format!(
324 "a tuple with values of types ({})",
325 tys.iter().map(Self::human_friendly_type).collect::<Vec<_>>().join(", ")
326 ),
327 RuntimeType::Object(..) => format!("an object with fields {self}"),
328 }
329 }
330
331 pub(crate) fn subtype(&self, sup: &RuntimeType) -> bool {
333 use RuntimeType::*;
334
335 match (self, sup) {
336 (_, Primitive(PrimitiveType::Any)) => true,
337 (Primitive(t1), Primitive(t2)) => t1.subtype(t2),
338 (Array(t1, l1), Array(t2, l2)) => t1.subtype(t2) && l1.subtype(*l2),
339 (Tuple(t1), Tuple(t2)) => t1.len() == t2.len() && t1.iter().zip(t2).all(|(t1, t2)| t1.subtype(t2)),
340
341 (Union(ts1), Union(ts2)) => ts1.iter().all(|t| ts2.contains(t)),
342 (t1, Union(ts2)) => ts2.iter().any(|t| t1.subtype(t)),
343
344 (Object(t1, _), Object(t2, _)) => t2
345 .iter()
346 .all(|(f, t)| t1.iter().any(|(ff, tt)| f == ff && tt.subtype(t))),
347
348 (t1, RuntimeType::Array(t2, l)) if t1.subtype(t2) && ArrayLen::Known(1).subtype(*l) => true,
350 (RuntimeType::Array(t1, ArrayLen::Known(1)), t2) if t1.subtype(t2) => true,
351 (t1, RuntimeType::Tuple(t2)) if !t2.is_empty() && t1.subtype(&t2[0]) => true,
352 (RuntimeType::Tuple(t1), t2) if t1.len() == 1 && t1[0].subtype(t2) => true,
353
354 (Object(t1, _), Primitive(PrimitiveType::Axis2d)) => {
356 t1.iter()
357 .any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point2d()))
358 && t1
359 .iter()
360 .any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point2d()))
361 }
362 (Object(t1, _), Primitive(PrimitiveType::Axis3d)) => {
363 t1.iter()
364 .any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point3d()))
365 && t1
366 .iter()
367 .any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point3d()))
368 }
369 (Primitive(PrimitiveType::Axis2d), Object(t2, _)) => {
370 t2.iter()
371 .any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point2d()))
372 && t2
373 .iter()
374 .any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point2d()))
375 }
376 (Primitive(PrimitiveType::Axis3d), Object(t2, _)) => {
377 t2.iter()
378 .any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point3d()))
379 && t2
380 .iter()
381 .any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point3d()))
382 }
383 _ => false,
384 }
385 }
386
387 fn display_multiple(&self) -> String {
388 match self {
389 RuntimeType::Primitive(ty) => ty.display_multiple(),
390 RuntimeType::Array(..) => "arrays".to_owned(),
391 RuntimeType::Union(tys) => tys
392 .iter()
393 .map(|t| t.display_multiple())
394 .collect::<Vec<_>>()
395 .join(" or "),
396 RuntimeType::Tuple(_) => "tuples".to_owned(),
397 RuntimeType::Object(..) => format!("objects with fields {self}"),
398 }
399 }
400}
401
402impl std::fmt::Display for RuntimeType {
403 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
404 match self {
405 RuntimeType::Primitive(t) => t.fmt(f),
406 RuntimeType::Array(t, l) => match l {
407 ArrayLen::None => write!(f, "[{t}]"),
408 ArrayLen::Minimum(n) => write!(f, "[{t}; {n}+]"),
409 ArrayLen::Known(n) => write!(f, "[{t}; {n}]"),
410 },
411 RuntimeType::Tuple(ts) => write!(
412 f,
413 "({})",
414 ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
415 ),
416 RuntimeType::Union(ts) => write!(
417 f,
418 "{}",
419 ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(" | ")
420 ),
421 RuntimeType::Object(items, _) => write!(
422 f,
423 "{{ {} }}",
424 items
425 .iter()
426 .map(|(n, t)| format!("{n}: {t}"))
427 .collect::<Vec<_>>()
428 .join(", ")
429 ),
430 }
431 }
432}
433
434#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, ts_rs::TS)]
435pub enum ArrayLen {
436 None,
437 Minimum(usize),
438 Known(usize),
439}
440
441impl ArrayLen {
442 pub fn subtype(self, other: ArrayLen) -> bool {
443 match (self, other) {
444 (_, ArrayLen::None) => true,
445 (ArrayLen::Minimum(s1), ArrayLen::Minimum(s2)) if s1 >= s2 => true,
446 (ArrayLen::Known(s1), ArrayLen::Minimum(s2)) if s1 >= s2 => true,
447 (ArrayLen::None, ArrayLen::Minimum(0)) => true,
448 (ArrayLen::Known(s1), ArrayLen::Known(s2)) if s1 == s2 => true,
449 _ => false,
450 }
451 }
452
453 pub fn satisfied(self, len: usize, allow_shrink: bool) -> Option<usize> {
455 match self {
456 ArrayLen::None => Some(len),
457 ArrayLen::Minimum(s) => (len >= s).then_some(len),
458 ArrayLen::Known(s) => (if allow_shrink { len >= s } else { len == s }).then_some(s),
459 }
460 }
461
462 pub fn human_friendly_type(self) -> String {
463 match self {
464 ArrayLen::None | ArrayLen::Minimum(0) => "any number of elements".to_owned(),
465 ArrayLen::Minimum(1) => "at least 1 element".to_owned(),
466 ArrayLen::Minimum(n) => format!("at least {n} elements"),
467 ArrayLen::Known(0) => "no elements".to_owned(),
468 ArrayLen::Known(1) => "exactly 1 element".to_owned(),
469 ArrayLen::Known(n) => format!("exactly {n} elements"),
470 }
471 }
472}
473
474#[derive(Debug, Clone, PartialEq)]
475pub enum PrimitiveType {
476 Any,
477 None,
478 Number(NumericType),
479 String,
480 Boolean,
481 TaggedEdge,
482 TaggedFace,
483 TagDecl,
484 GdtAnnotation,
485 Segment,
486 Sketch,
487 Constraint,
488 Solid,
489 Plane,
490 Helix,
491 Face,
492 Edge,
493 BoundedEdge,
494 Axis2d,
495 Axis3d,
496 ImportedGeometry,
497 Function,
498}
499
500impl PrimitiveType {
501 fn display_multiple(&self) -> String {
502 match self {
503 PrimitiveType::Any => "any values".to_owned(),
504 PrimitiveType::None => "none values".to_owned(),
505 PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
506 PrimitiveType::Number(_) => "numbers".to_owned(),
507 PrimitiveType::String => "strings".to_owned(),
508 PrimitiveType::Boolean => "bools".to_owned(),
509 PrimitiveType::GdtAnnotation => "GD&T Annotations".to_owned(),
510 PrimitiveType::Segment => "Segments".to_owned(),
511 PrimitiveType::Sketch => "Sketches".to_owned(),
512 PrimitiveType::Constraint => "Constraints".to_owned(),
513 PrimitiveType::Solid => "Solids".to_owned(),
514 PrimitiveType::Plane => "Planes".to_owned(),
515 PrimitiveType::Helix => "Helices".to_owned(),
516 PrimitiveType::Face => "Faces".to_owned(),
517 PrimitiveType::Edge => "Edges".to_owned(),
518 PrimitiveType::BoundedEdge => "BoundedEdges".to_owned(),
519 PrimitiveType::Axis2d => "2d axes".to_owned(),
520 PrimitiveType::Axis3d => "3d axes".to_owned(),
521 PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
522 PrimitiveType::Function => "functions".to_owned(),
523 PrimitiveType::TagDecl => "tag declarators".to_owned(),
524 PrimitiveType::TaggedEdge => "tagged edges".to_owned(),
525 PrimitiveType::TaggedFace => "tagged faces".to_owned(),
526 }
527 }
528
529 fn subtype(&self, other: &PrimitiveType) -> bool {
530 match (self, other) {
531 (_, PrimitiveType::Any) => true,
532 (PrimitiveType::Number(n1), PrimitiveType::Number(n2)) => n1.subtype(n2),
533 (PrimitiveType::TaggedEdge, PrimitiveType::TaggedFace)
534 | (PrimitiveType::TaggedEdge, PrimitiveType::Edge) => true,
535 (t1, t2) => t1 == t2,
536 }
537 }
538}
539
540impl std::fmt::Display for PrimitiveType {
541 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
542 match self {
543 PrimitiveType::Any => write!(f, "any"),
544 PrimitiveType::None => write!(f, "none"),
545 PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
546 PrimitiveType::Number(NumericType::Unknown) => write!(f, "number(unknown units)"),
547 PrimitiveType::Number(NumericType::Default { .. }) => write!(f, "number"),
548 PrimitiveType::Number(NumericType::Any) => write!(f, "number(any units)"),
549 PrimitiveType::String => write!(f, "string"),
550 PrimitiveType::Boolean => write!(f, "bool"),
551 PrimitiveType::TagDecl => write!(f, "tag declarator"),
552 PrimitiveType::TaggedEdge => write!(f, "tagged edge"),
553 PrimitiveType::TaggedFace => write!(f, "tagged face"),
554 PrimitiveType::GdtAnnotation => write!(f, "GD&T Annotation"),
555 PrimitiveType::Segment => write!(f, "Segment"),
556 PrimitiveType::Sketch => write!(f, "Sketch"),
557 PrimitiveType::Constraint => write!(f, "Constraint"),
558 PrimitiveType::Solid => write!(f, "Solid"),
559 PrimitiveType::Plane => write!(f, "Plane"),
560 PrimitiveType::Face => write!(f, "Face"),
561 PrimitiveType::Edge => write!(f, "Edge"),
562 PrimitiveType::BoundedEdge => write!(f, "BoundedEdge"),
563 PrimitiveType::Axis2d => write!(f, "Axis2d"),
564 PrimitiveType::Axis3d => write!(f, "Axis3d"),
565 PrimitiveType::Helix => write!(f, "Helix"),
566 PrimitiveType::ImportedGeometry => write!(f, "ImportedGeometry"),
567 PrimitiveType::Function => write!(f, "fn"),
568 }
569 }
570}
571
572pub trait NumericTypeExt {
573 fn count() -> Self;
574
575 fn mm() -> Self;
576
577 fn radians() -> Self;
578
579 fn degrees() -> Self;
580
581 fn length(unit: UnitLength) -> Self;
582
583 fn optional_length(unit: Option<UnitLength>) -> Self;
584
585 fn angle(unit: UnitAngle) -> Self;
586
587 fn combine_eq(a: TyF64, b: TyF64, exec_state: &mut ExecState, source_range: SourceRange)
593 -> (f64, f64, NumericType);
594
595 fn combine_eq_coerce(
603 a: TyF64,
604 b: TyF64,
605 for_errs: Option<(&mut ExecState, SourceRange)>,
606 ) -> (f64, f64, NumericType);
607
608 fn combine_eq_array(input: &[TyF64]) -> (Vec<f64>, NumericType);
609
610 fn combine_mul(a: TyF64, b: TyF64) -> (f64, f64, NumericType);
612
613 fn combine_div(a: TyF64, b: TyF64) -> (f64, f64, NumericType);
615
616 fn combine_mod(a: TyF64, b: TyF64) -> (f64, f64, NumericType);
618
619 fn combine_range(
625 a: TyF64,
626 b: TyF64,
627 exec_state: &mut ExecState,
628 source_range: SourceRange,
629 ) -> Result<(f64, f64, NumericType), KclError>;
630
631 fn from_parsed(suffix: NumericSuffix, settings: &super::MetaSettings) -> Self;
632
633 fn subtype(&self, other: &NumericType) -> bool;
634
635 fn is_unknown(&self) -> bool;
636
637 fn is_fully_specified(&self) -> bool;
638
639 fn example_ty(&self) -> Option<String>;
640
641 fn coerce(&self, val: &KclValue) -> Result<KclValue, CoercionError>;
642
643 fn as_length(&self) -> Option<UnitLength>;
644}
645
646impl NumericTypeExt for NumericType {
647 fn count() -> Self {
648 NumericType::Known(UnitType::Count)
649 }
650
651 fn mm() -> Self {
652 NumericType::Known(UnitType::Length(UnitLength::Millimeters))
653 }
654
655 fn radians() -> Self {
656 NumericType::Known(UnitType::Angle(UnitAngle::Radians))
657 }
658
659 fn degrees() -> Self {
660 NumericType::Known(UnitType::Angle(UnitAngle::Degrees))
661 }
662
663 fn length(unit: UnitLength) -> Self {
664 NumericType::Known(UnitType::Length(unit))
665 }
666
667 fn optional_length(unit: Option<UnitLength>) -> Self {
668 match unit {
669 Some(unit) => Self::length(unit),
670 None => NumericType::Unknown,
671 }
672 }
673
674 fn angle(unit: UnitAngle) -> Self {
675 NumericType::Known(UnitType::Angle(unit))
676 }
677
678 fn combine_eq(
684 a: TyF64,
685 b: TyF64,
686 exec_state: &mut ExecState,
687 source_range: SourceRange,
688 ) -> (f64, f64, NumericType) {
689 use NumericType::*;
690 match (a.ty, b.ty) {
691 (at, bt) if at == bt => (a.n, b.n, at),
692 (at, Any) => (a.n, b.n, at),
693 (Any, bt) => (a.n, b.n, bt),
694
695 (t @ Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => (a.n, adjust_length(l2, b.n, l1).0, t),
696 (t @ Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => (a.n, adjust_angle(a2, b.n, a1).0, t),
697
698 (t @ Known(UnitType::Length(_)), Known(UnitType::GenericLength)) => (a.n, b.n, t),
699 (Known(UnitType::GenericLength), t @ Known(UnitType::Length(_))) => (a.n, b.n, t),
700 (t @ Known(UnitType::Angle(_)), Known(UnitType::GenericAngle)) => (a.n, b.n, t),
701 (Known(UnitType::GenericAngle), t @ Known(UnitType::Angle(_))) => (a.n, b.n, t),
702
703 (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
704 (a.n, b.n, Known(UnitType::Count))
705 }
706 (t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) if l1 == l2 => (a.n, b.n, t),
707 (Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) if l1 == l2 => (a.n, b.n, t),
708 (t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) if a1 == a2 => {
709 if b.n != 0.0 {
710 exec_state.warn(
711 CompilationIssue::err(source_range, "Prefer to use explicit units for angles"),
712 annotations::WARN_ANGLE_UNITS,
713 );
714 }
715 (a.n, b.n, t)
716 }
717 (Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) if a1 == a2 => {
718 if a.n != 0.0 {
719 exec_state.warn(
720 CompilationIssue::err(source_range, "Prefer to use explicit units for angles"),
721 annotations::WARN_ANGLE_UNITS,
722 );
723 }
724 (a.n, b.n, t)
725 }
726
727 _ => (a.n, b.n, Unknown),
728 }
729 }
730
731 fn combine_eq_coerce(
739 a: TyF64,
740 b: TyF64,
741 for_errs: Option<(&mut ExecState, SourceRange)>,
742 ) -> (f64, f64, NumericType) {
743 use NumericType::*;
744 match (a.ty, b.ty) {
745 (at, bt) if at == bt => (a.n, b.n, at),
746 (at, Any) => (a.n, b.n, at),
747 (Any, bt) => (a.n, b.n, bt),
748
749 (t @ Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => (a.n, adjust_length(l2, b.n, l1).0, t),
751 (t @ Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => (a.n, adjust_angle(a2, b.n, a1).0, t),
752
753 (t @ Known(UnitType::Length(_)), Known(UnitType::GenericLength)) => (a.n, b.n, t),
754 (Known(UnitType::GenericLength), t @ Known(UnitType::Length(_))) => (a.n, b.n, t),
755 (t @ Known(UnitType::Angle(_)), Known(UnitType::GenericAngle)) => (a.n, b.n, t),
756 (Known(UnitType::GenericAngle), t @ Known(UnitType::Angle(_))) => (a.n, b.n, t),
757
758 (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
760 (a.n, b.n, Known(UnitType::Count))
761 }
762
763 (t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) => (a.n, adjust_length(l2, b.n, l1).0, t),
764 (Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) => (adjust_length(l1, a.n, l2).0, b.n, t),
765 (t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) => {
766 if let Some((exec_state, source_range)) = for_errs
767 && b.n != 0.0
768 {
769 exec_state.warn(
770 CompilationIssue::err(source_range, "Prefer to use explicit units for angles"),
771 annotations::WARN_ANGLE_UNITS,
772 );
773 }
774 (a.n, adjust_angle(a2, b.n, a1).0, t)
775 }
776 (Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) => {
777 if let Some((exec_state, source_range)) = for_errs
778 && a.n != 0.0
779 {
780 exec_state.warn(
781 CompilationIssue::err(source_range, "Prefer to use explicit units for angles"),
782 annotations::WARN_ANGLE_UNITS,
783 );
784 }
785 (adjust_angle(a1, a.n, a2).0, b.n, t)
786 }
787
788 (Default { len: l1, .. }, Known(UnitType::GenericLength)) => (a.n, b.n, Self::length(l1)),
789 (Known(UnitType::GenericLength), Default { len: l2, .. }) => (a.n, b.n, Self::length(l2)),
790 (Default { angle: a1, .. }, Known(UnitType::GenericAngle)) => {
791 if let Some((exec_state, source_range)) = for_errs
792 && b.n != 0.0
793 {
794 exec_state.warn(
795 CompilationIssue::err(source_range, "Prefer to use explicit units for angles"),
796 annotations::WARN_ANGLE_UNITS,
797 );
798 }
799 (a.n, b.n, Self::angle(a1))
800 }
801 (Known(UnitType::GenericAngle), Default { angle: a2, .. }) => {
802 if let Some((exec_state, source_range)) = for_errs
803 && a.n != 0.0
804 {
805 exec_state.warn(
806 CompilationIssue::err(source_range, "Prefer to use explicit units for angles"),
807 annotations::WARN_ANGLE_UNITS,
808 );
809 }
810 (a.n, b.n, Self::angle(a2))
811 }
812
813 (Known(_), Known(_)) | (Default { .. }, Default { .. }) | (_, Unknown) | (Unknown, _) => {
814 (a.n, b.n, Unknown)
815 }
816 }
817 }
818
819 fn combine_eq_array(input: &[TyF64]) -> (Vec<f64>, NumericType) {
820 use NumericType::*;
821 let result = input.iter().map(|t| t.n).collect();
822
823 let mut ty = Any;
824 for i in input {
825 if i.ty == Any || ty == i.ty {
826 continue;
827 }
828
829 match (&ty, &i.ty) {
831 (Any, Default { .. }) if i.n == 0.0 => {}
832 (Any, t) => {
833 ty = *t;
834 }
835 (_, Unknown) | (Default { .. }, Default { .. }) => return (result, Unknown),
836
837 (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
838 ty = Known(UnitType::Count);
839 }
840
841 (Known(UnitType::Length(l1)), Default { len: l2, .. }) if l1 == l2 || i.n == 0.0 => {}
842 (Known(UnitType::Angle(a1)), Default { angle: a2, .. }) if a1 == a2 || i.n == 0.0 => {}
843
844 (Default { len: l1, .. }, Known(UnitType::Length(l2))) if l1 == l2 => {
845 ty = Known(UnitType::Length(*l2));
846 }
847 (Default { angle: a1, .. }, Known(UnitType::Angle(a2))) if a1 == a2 => {
848 ty = Known(UnitType::Angle(*a2));
849 }
850
851 _ => return (result, Unknown),
852 }
853 }
854
855 if ty == Any && !input.is_empty() {
856 ty = input[0].ty;
857 }
858
859 (result, ty)
860 }
861
862 fn combine_mul(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
864 use NumericType::*;
865 match (a.ty, b.ty) {
866 (at @ Default { .. }, bt @ Default { .. }) if at == bt => (a.n, b.n, at),
867 (Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
868 (Known(UnitType::Count), bt) => (a.n, b.n, bt),
869 (at, Known(UnitType::Count)) => (a.n, b.n, at),
870 (at @ Known(_), Default { .. }) | (Default { .. }, at @ Known(_)) => (a.n, b.n, at),
871 (Any, Any) => (a.n, b.n, Any),
872 _ => (a.n, b.n, Unknown),
873 }
874 }
875
876 fn combine_div(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
878 use NumericType::*;
879 match (a.ty, b.ty) {
880 (at @ Default { .. }, bt @ Default { .. }) if at == bt => (a.n, b.n, at),
881 (at, bt) if at == bt => (a.n, b.n, Known(UnitType::Count)),
882 (Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
883 (at, Known(UnitType::Count) | Any) => (a.n, b.n, at),
884 (at @ Known(_), Default { .. }) => (a.n, b.n, at),
885 (Known(UnitType::Count), _) => (a.n, b.n, Known(UnitType::Count)),
886 _ => (a.n, b.n, Unknown),
887 }
888 }
889
890 fn combine_mod(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
892 use NumericType::*;
893 match (a.ty, b.ty) {
894 (at @ Default { .. }, bt @ Default { .. }) if at == bt => (a.n, b.n, at),
895 (at, bt) if at == bt => (a.n, b.n, at),
896 (Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
897 (at, Known(UnitType::Count) | Any) => (a.n, b.n, at),
898 (at @ Known(_), Default { .. }) => (a.n, b.n, at),
899 (Known(UnitType::Count), _) => (a.n, b.n, Known(UnitType::Count)),
900 _ => (a.n, b.n, Unknown),
901 }
902 }
903
904 fn combine_range(
910 a: TyF64,
911 b: TyF64,
912 exec_state: &mut ExecState,
913 source_range: SourceRange,
914 ) -> Result<(f64, f64, NumericType), KclError> {
915 use NumericType::*;
916 match (a.ty, b.ty) {
917 (at, bt) if at == bt => Ok((a.n, b.n, at)),
918 (at, Any) => Ok((a.n, b.n, at)),
919 (Any, bt) => Ok((a.n, b.n, bt)),
920
921 (Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => {
922 Err(KclError::new_semantic(KclErrorDetails::new(
923 format!("Range start and range end have incompatible units: {l1} and {l2}"),
924 vec![source_range],
925 )))
926 }
927 (Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => {
928 Err(KclError::new_semantic(KclErrorDetails::new(
929 format!("Range start and range end have incompatible units: {a1} and {a2}"),
930 vec![source_range],
931 )))
932 }
933
934 (t @ Known(UnitType::Length(_)), Known(UnitType::GenericLength)) => Ok((a.n, b.n, t)),
935 (Known(UnitType::GenericLength), t @ Known(UnitType::Length(_))) => Ok((a.n, b.n, t)),
936 (t @ Known(UnitType::Angle(_)), Known(UnitType::GenericAngle)) => Ok((a.n, b.n, t)),
937 (Known(UnitType::GenericAngle), t @ Known(UnitType::Angle(_))) => Ok((a.n, b.n, t)),
938
939 (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
940 Ok((a.n, b.n, Known(UnitType::Count)))
941 }
942 (t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) if l1 == l2 => Ok((a.n, b.n, t)),
943 (Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) if l1 == l2 => Ok((a.n, b.n, t)),
944 (t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) if a1 == a2 => {
945 if b.n != 0.0 {
946 exec_state.warn(
947 CompilationIssue::err(source_range, "Prefer to use explicit units for angles"),
948 annotations::WARN_ANGLE_UNITS,
949 );
950 }
951 Ok((a.n, b.n, t))
952 }
953 (Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) if a1 == a2 => {
954 if a.n != 0.0 {
955 exec_state.warn(
956 CompilationIssue::err(source_range, "Prefer to use explicit units for angles"),
957 annotations::WARN_ANGLE_UNITS,
958 );
959 }
960 Ok((a.n, b.n, t))
961 }
962
963 _ => {
964 let a = fmt::human_display_number(a.n, a.ty);
965 let b = fmt::human_display_number(b.n, b.ty);
966 Err(KclError::new_semantic(KclErrorDetails::new(
967 format!(
968 "Range start and range end must be of the same type and have compatible units, but found {a} and {b}",
969 ),
970 vec![source_range],
971 )))
972 }
973 }
974 }
975
976 fn from_parsed(suffix: NumericSuffix, settings: &super::MetaSettings) -> Self {
977 match suffix {
978 NumericSuffix::None => NumericType::Default {
979 len: settings.default_length_units,
980 angle: settings.default_angle_units,
981 },
982 NumericSuffix::Count => NumericType::Known(UnitType::Count),
983 NumericSuffix::Length => NumericType::Known(UnitType::GenericLength),
984 NumericSuffix::Angle => NumericType::Known(UnitType::GenericAngle),
985 NumericSuffix::Mm => NumericType::Known(UnitType::Length(UnitLength::Millimeters)),
986 NumericSuffix::Cm => NumericType::Known(UnitType::Length(UnitLength::Centimeters)),
987 NumericSuffix::M => NumericType::Known(UnitType::Length(UnitLength::Meters)),
988 NumericSuffix::Inch => NumericType::Known(UnitType::Length(UnitLength::Inches)),
989 NumericSuffix::Ft => NumericType::Known(UnitType::Length(UnitLength::Feet)),
990 NumericSuffix::Yd => NumericType::Known(UnitType::Length(UnitLength::Yards)),
991 NumericSuffix::Deg => NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
992 NumericSuffix::Rad => NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
993 NumericSuffix::Unknown => NumericType::Unknown,
994 }
995 }
996
997 fn subtype(&self, other: &NumericType) -> bool {
998 use NumericType::*;
999
1000 match (self, other) {
1001 (_, Any) => true,
1002 (a, b) if a == b => true,
1003 (
1004 NumericType::Known(UnitType::Length(_))
1005 | NumericType::Known(UnitType::GenericLength)
1006 | NumericType::Default { .. },
1007 NumericType::Known(UnitType::GenericLength),
1008 )
1009 | (
1010 NumericType::Known(UnitType::Angle(_))
1011 | NumericType::Known(UnitType::GenericAngle)
1012 | NumericType::Default { .. },
1013 NumericType::Known(UnitType::GenericAngle),
1014 ) => true,
1015 (Unknown, _) | (_, Unknown) => false,
1016 (_, _) => false,
1017 }
1018 }
1019
1020 fn is_unknown(&self) -> bool {
1021 matches!(
1022 self,
1023 NumericType::Unknown
1024 | NumericType::Known(UnitType::GenericAngle)
1025 | NumericType::Known(UnitType::GenericLength)
1026 )
1027 }
1028
1029 fn is_fully_specified(&self) -> bool {
1030 !matches!(
1031 self,
1032 NumericType::Unknown
1033 | NumericType::Known(UnitType::GenericAngle)
1034 | NumericType::Known(UnitType::GenericLength)
1035 | NumericType::Any
1036 | NumericType::Default { .. }
1037 )
1038 }
1039
1040 fn example_ty(&self) -> Option<String> {
1041 match self {
1042 Self::Known(t) if !self.is_unknown() => Some(t.to_string()),
1043 Self::Default { len, .. } => Some(len.to_string()),
1044 _ => None,
1045 }
1046 }
1047
1048 fn coerce(&self, val: &KclValue) -> Result<KclValue, CoercionError> {
1049 let (value, ty, meta) = match val {
1050 KclValue::Number { value, ty, meta } => (value, ty, meta),
1051 KclValue::SketchVar { .. } => return Ok(val.clone()),
1055 _ => return Err(val.into()),
1056 };
1057
1058 if ty.subtype(self) {
1059 return Ok(KclValue::Number {
1060 value: *value,
1061 ty: *ty,
1062 meta: meta.clone(),
1063 });
1064 }
1065
1066 use NumericType::*;
1068 match (ty, self) {
1069 (Unknown, _) => Err(CoercionError::from(val).with_explicit(self.example_ty().unwrap_or("mm".to_owned()))),
1071 (_, Unknown) => Err(val.into()),
1072
1073 (Any, _) => Ok(KclValue::Number {
1074 value: *value,
1075 ty: *self,
1076 meta: meta.clone(),
1077 }),
1078
1079 (_, Default { .. }) => Ok(KclValue::Number {
1082 value: *value,
1083 ty: *ty,
1084 meta: meta.clone(),
1085 }),
1086
1087 (Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => {
1089 let (value, ty) = adjust_length(*l1, *value, *l2);
1090 Ok(KclValue::Number {
1091 value,
1092 ty: Known(UnitType::Length(ty)),
1093 meta: meta.clone(),
1094 })
1095 }
1096 (Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => {
1097 let (value, ty) = adjust_angle(*a1, *value, *a2);
1098 Ok(KclValue::Number {
1099 value,
1100 ty: Known(UnitType::Angle(ty)),
1101 meta: meta.clone(),
1102 })
1103 }
1104
1105 (Known(_), Known(_)) => Err(val.into()),
1107
1108 (Default { .. }, Known(UnitType::Count)) => Ok(KclValue::Number {
1110 value: *value,
1111 ty: Known(UnitType::Count),
1112 meta: meta.clone(),
1113 }),
1114
1115 (Default { len: l1, .. }, Known(UnitType::Length(l2))) => {
1116 let (value, ty) = adjust_length(*l1, *value, *l2);
1117 Ok(KclValue::Number {
1118 value,
1119 ty: Known(UnitType::Length(ty)),
1120 meta: meta.clone(),
1121 })
1122 }
1123
1124 (Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => {
1125 let (value, ty) = adjust_angle(*a1, *value, *a2);
1126 Ok(KclValue::Number {
1127 value,
1128 ty: Known(UnitType::Angle(ty)),
1129 meta: meta.clone(),
1130 })
1131 }
1132
1133 (_, _) => unreachable!(),
1134 }
1135 }
1136
1137 fn as_length(&self) -> Option<UnitLength> {
1138 match self {
1139 Self::Known(UnitType::Length(len)) | Self::Default { len, .. } => Some(*len),
1140 _ => None,
1141 }
1142 }
1143}
1144
1145impl From<NumericType> for RuntimeType {
1146 fn from(t: NumericType) -> RuntimeType {
1147 RuntimeType::Primitive(PrimitiveType::Number(t))
1148 }
1149}
1150
1151impl From<UnitLength> for NumericSuffix {
1152 fn from(value: UnitLength) -> Self {
1153 match value {
1154 UnitLength::Millimeters => NumericSuffix::Mm,
1155 UnitLength::Centimeters => NumericSuffix::Cm,
1156 UnitLength::Meters => NumericSuffix::M,
1157 UnitLength::Inches => NumericSuffix::Inch,
1158 UnitLength::Feet => NumericSuffix::Ft,
1159 UnitLength::Yards => NumericSuffix::Yd,
1160 }
1161 }
1162}
1163
1164#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS)]
1165pub struct NumericSuffixTypeConvertError;
1166
1167impl TryFrom<NumericType> for NumericSuffix {
1168 type Error = NumericSuffixTypeConvertError;
1169
1170 fn try_from(value: NumericType) -> Result<Self, Self::Error> {
1171 match value {
1172 NumericType::Known(UnitType::Count) => Ok(NumericSuffix::Count),
1173 NumericType::Known(UnitType::Length(unit_length)) => Ok(NumericSuffix::from(unit_length)),
1174 NumericType::Known(UnitType::GenericLength) => Ok(NumericSuffix::Length),
1175 NumericType::Known(UnitType::Angle(UnitAngle::Degrees)) => Ok(NumericSuffix::Deg),
1176 NumericType::Known(UnitType::Angle(UnitAngle::Radians)) => Ok(NumericSuffix::Rad),
1177 NumericType::Known(UnitType::GenericAngle) => Ok(NumericSuffix::Angle),
1178 NumericType::Default { .. } => Ok(NumericSuffix::None),
1179 NumericType::Unknown => Ok(NumericSuffix::Unknown),
1180 NumericType::Any => Err(NumericSuffixTypeConvertError),
1181 }
1182 }
1183}
1184
1185pub fn adjust_length(from: UnitLength, value: f64, to: UnitLength) -> (f64, UnitLength) {
1186 use UnitLength::*;
1187
1188 if from == to {
1189 return (value, to);
1190 }
1191
1192 let (base, base_unit) = match from {
1193 Millimeters => (value, Millimeters),
1194 Centimeters => (value * 10.0, Millimeters),
1195 Meters => (value * 1000.0, Millimeters),
1196 Inches => (value, Inches),
1197 Feet => (value * 12.0, Inches),
1198 Yards => (value * 36.0, Inches),
1199 };
1200 let (base, base_unit) = match (base_unit, to) {
1201 (Millimeters, Inches) | (Millimeters, Feet) | (Millimeters, Yards) => (base / 25.4, Inches),
1202 (Inches, Millimeters) | (Inches, Centimeters) | (Inches, Meters) => (base * 25.4, Millimeters),
1203 _ => (base, base_unit),
1204 };
1205
1206 let value = match (base_unit, to) {
1207 (Millimeters, Millimeters) => base,
1208 (Millimeters, Centimeters) => base / 10.0,
1209 (Millimeters, Meters) => base / 1000.0,
1210 (Inches, Inches) => base,
1211 (Inches, Feet) => base / 12.0,
1212 (Inches, Yards) => base / 36.0,
1213 _ => unreachable!(),
1214 };
1215
1216 (value, to)
1217}
1218
1219pub fn adjust_angle(from: UnitAngle, value: f64, to: UnitAngle) -> (f64, UnitAngle) {
1220 use std::f64::consts::PI;
1221
1222 use UnitAngle::*;
1223
1224 let value = match (from, to) {
1225 (Degrees, Degrees) => value,
1226 (Degrees, Radians) => (value / 180.0) * PI,
1227 (Radians, Degrees) => 180.0 * value / PI,
1228 (Radians, Radians) => value,
1229 };
1230
1231 (value, to)
1232}
1233
1234pub(super) fn length_from_str(s: &str, source_range: SourceRange) -> Result<UnitLength, KclError> {
1235 match s {
1237 "mm" => Ok(UnitLength::Millimeters),
1238 "cm" => Ok(UnitLength::Centimeters),
1239 "m" => Ok(UnitLength::Meters),
1240 "inch" | "in" => Ok(UnitLength::Inches),
1241 "ft" => Ok(UnitLength::Feet),
1242 "yd" => Ok(UnitLength::Yards),
1243 value => Err(KclError::new_semantic(KclErrorDetails::new(
1244 format!("Unexpected value for length units: `{value}`; expected one of `mm`, `cm`, `m`, `in`, `ft`, `yd`"),
1245 vec![source_range],
1246 ))),
1247 }
1248}
1249
1250pub(super) fn angle_from_str(s: &str, source_range: SourceRange) -> Result<UnitAngle, KclError> {
1251 UnitAngle::from_str(s).map_err(|_| {
1252 KclError::new_semantic(KclErrorDetails::new(
1253 format!("Unexpected value for angle units: `{s}`; expected one of `deg`, `rad`"),
1254 vec![source_range],
1255 ))
1256 })
1257}
1258
1259#[derive(Debug, Clone)]
1260pub struct CoercionError {
1261 pub found: Option<RuntimeType>,
1262 pub explicit_coercion: Option<String>,
1263}
1264
1265impl CoercionError {
1266 fn with_explicit(mut self, c: String) -> Self {
1267 self.explicit_coercion = Some(c);
1268 self
1269 }
1270}
1271
1272impl From<&'_ KclValue> for CoercionError {
1273 fn from(value: &'_ KclValue) -> Self {
1274 CoercionError {
1275 found: value.principal_type(),
1276 explicit_coercion: None,
1277 }
1278 }
1279}
1280
1281impl KclValue {
1282 pub fn has_type(&self, ty: &RuntimeType) -> bool {
1284 let Some(self_ty) = self.principal_type() else {
1285 return false;
1286 };
1287
1288 self_ty.subtype(ty)
1289 }
1290
1291 pub fn coerce(
1298 &self,
1299 ty: &RuntimeType,
1300 convert_units: bool,
1301 exec_state: &mut ExecState,
1302 ) -> Result<KclValue, CoercionError> {
1303 match self {
1304 KclValue::Tuple { value, .. }
1305 if value.len() == 1
1306 && !matches!(ty, RuntimeType::Primitive(PrimitiveType::Any) | RuntimeType::Tuple(..)) =>
1307 {
1308 if let Ok(coerced) = value[0].coerce(ty, convert_units, exec_state) {
1309 return Ok(coerced);
1310 }
1311 }
1312 KclValue::HomArray { value, .. }
1313 if value.len() == 1
1314 && !matches!(ty, RuntimeType::Primitive(PrimitiveType::Any) | RuntimeType::Array(..)) =>
1315 {
1316 if let Ok(coerced) = value[0].coerce(ty, convert_units, exec_state) {
1317 return Ok(coerced);
1318 }
1319 }
1320 _ => {}
1321 }
1322
1323 match ty {
1324 RuntimeType::Primitive(ty) => self.coerce_to_primitive_type(ty, convert_units, exec_state),
1325 RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, convert_units, *len, exec_state, false),
1326 RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, convert_units, exec_state),
1327 RuntimeType::Union(tys) => self.coerce_to_union_type(tys, convert_units, exec_state),
1328 RuntimeType::Object(tys, constrainable) => {
1329 self.coerce_to_object_type(tys, *constrainable, convert_units, exec_state)
1330 }
1331 }
1332 }
1333
1334 fn coerce_to_primitive_type(
1335 &self,
1336 ty: &PrimitiveType,
1337 convert_units: bool,
1338 exec_state: &mut ExecState,
1339 ) -> Result<KclValue, CoercionError> {
1340 match ty {
1341 PrimitiveType::Any => Ok(self.clone()),
1342 PrimitiveType::None => match self {
1343 KclValue::KclNone { .. } => Ok(self.clone()),
1344 _ => Err(self.into()),
1345 },
1346 PrimitiveType::Number(ty) => {
1347 if convert_units {
1348 return ty.coerce(self);
1349 }
1350
1351 if let KclValue::Number { value: n, meta, .. } = &self
1358 && ty.is_fully_specified()
1359 {
1360 let value = KclValue::Number {
1361 ty: NumericType::Any,
1362 value: *n,
1363 meta: meta.clone(),
1364 };
1365 return ty.coerce(&value);
1366 }
1367 ty.coerce(self)
1368 }
1369 PrimitiveType::String => match self {
1370 KclValue::String { .. } => Ok(self.clone()),
1371 _ => Err(self.into()),
1372 },
1373 PrimitiveType::Boolean => match self {
1374 KclValue::Bool { .. } => Ok(self.clone()),
1375 _ => Err(self.into()),
1376 },
1377 PrimitiveType::GdtAnnotation => match self {
1378 KclValue::GdtAnnotation { .. } => Ok(self.clone()),
1379 _ => Err(self.into()),
1380 },
1381 PrimitiveType::Segment => match self {
1382 KclValue::Segment { .. } => Ok(self.clone()),
1383 _ => Err(self.into()),
1384 },
1385 PrimitiveType::Sketch => match self {
1386 KclValue::Sketch { .. } => Ok(self.clone()),
1387 KclValue::Object { value, .. } => {
1388 let Some(meta) = value.get(SKETCH_OBJECT_META) else {
1389 return Err(self.into());
1390 };
1391 let KclValue::Object { value: meta_map, .. } = meta else {
1392 return Err(self.into());
1393 };
1394 let Some(sketch) = meta_map.get(SKETCH_OBJECT_META_SKETCH).and_then(KclValue::as_sketch) else {
1395 return Err(self.into());
1396 };
1397
1398 Ok(KclValue::Sketch {
1399 value: Box::new(sketch.clone()),
1400 })
1401 }
1402 _ => Err(self.into()),
1403 },
1404 PrimitiveType::Constraint => match self {
1405 KclValue::SketchConstraint { .. } => Ok(self.clone()),
1406 _ => Err(self.into()),
1407 },
1408 PrimitiveType::Solid => match self {
1409 KclValue::Solid { .. } => Ok(self.clone()),
1410 _ => Err(self.into()),
1411 },
1412 PrimitiveType::Plane => {
1413 match self {
1414 KclValue::String { value: s, .. }
1415 if [
1416 "xy", "xz", "yz", "-xy", "-xz", "-yz", "XY", "XZ", "YZ", "-XY", "-XZ", "-YZ",
1417 ]
1418 .contains(&&**s) =>
1419 {
1420 Ok(self.clone())
1421 }
1422 KclValue::Plane { .. } => Ok(self.clone()),
1423 KclValue::Object { value, meta, .. } => {
1424 let origin = value
1425 .get("origin")
1426 .and_then(Point3d::from_kcl_val)
1427 .ok_or(CoercionError::from(self))?;
1428 let x_axis = value
1429 .get("xAxis")
1430 .and_then(Point3d::from_kcl_val)
1431 .ok_or(CoercionError::from(self))?;
1432 let y_axis = value
1433 .get("yAxis")
1434 .and_then(Point3d::from_kcl_val)
1435 .ok_or(CoercionError::from(self))?;
1436 let z_axis = x_axis.axes_cross_product(&y_axis);
1437
1438 if value.get("zAxis").is_some() {
1439 exec_state.warn(CompilationIssue::err(
1440 self.into(),
1441 "Object with a zAxis field is being coerced into a plane, but the zAxis is ignored.",
1442 ), annotations::WARN_IGNORED_Z_AXIS);
1443 }
1444
1445 let id = exec_state.mod_local.id_generator.next_uuid();
1446 let info = PlaneInfo {
1447 origin,
1448 x_axis: x_axis.normalize(),
1449 y_axis: y_axis.normalize(),
1450 z_axis: z_axis.normalize(),
1451 };
1452 let plane = Plane {
1453 id,
1454 artifact_id: id.into(),
1455 object_id: None,
1456 kind: PlaneKind::from(&info),
1457 info,
1458 meta: meta.clone(),
1459 };
1460
1461 Ok(KclValue::Plane { value: Box::new(plane) })
1462 }
1463 _ => Err(self.into()),
1464 }
1465 }
1466 PrimitiveType::Face => match self {
1467 KclValue::Face { .. } => Ok(self.clone()),
1468 _ => Err(self.into()),
1469 },
1470 PrimitiveType::Helix => match self {
1471 KclValue::Helix { .. } => Ok(self.clone()),
1472 _ => Err(self.into()),
1473 },
1474 PrimitiveType::Edge => match self {
1475 KclValue::Uuid { .. } => Ok(self.clone()),
1476 KclValue::TagIdentifier { .. } => Ok(self.clone()),
1477 _ => Err(self.into()),
1478 },
1479 PrimitiveType::BoundedEdge => match self {
1480 KclValue::BoundedEdge { .. } => Ok(self.clone()),
1481 _ => Err(self.into()),
1482 },
1483 PrimitiveType::TaggedEdge => match self {
1484 KclValue::TagIdentifier { .. } => Ok(self.clone()),
1485 _ => Err(self.into()),
1486 },
1487 PrimitiveType::TaggedFace => match self {
1488 KclValue::TagIdentifier { .. } => Ok(self.clone()),
1489 s @ KclValue::String { value, .. } if ["start", "end", "START", "END"].contains(&&**value) => {
1490 Ok(s.clone())
1491 }
1492 _ => Err(self.into()),
1493 },
1494 PrimitiveType::Axis2d => match self {
1495 KclValue::Object {
1496 value: values, meta, ..
1497 } => {
1498 if values
1499 .get("origin")
1500 .ok_or(CoercionError::from(self))?
1501 .has_type(&RuntimeType::point2d())
1502 && values
1503 .get("direction")
1504 .ok_or(CoercionError::from(self))?
1505 .has_type(&RuntimeType::point2d())
1506 {
1507 return Ok(self.clone());
1508 }
1509
1510 let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
1511 p.coerce_to_array_type(
1512 &RuntimeType::length(),
1513 convert_units,
1514 ArrayLen::Known(2),
1515 exec_state,
1516 true,
1517 )
1518 })?;
1519 let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
1520 p.coerce_to_array_type(
1521 &RuntimeType::length(),
1522 convert_units,
1523 ArrayLen::Known(2),
1524 exec_state,
1525 true,
1526 )
1527 })?;
1528
1529 Ok(KclValue::Object {
1530 value: [("origin".to_owned(), origin), ("direction".to_owned(), direction)].into(),
1531 meta: meta.clone(),
1532 constrainable: false,
1533 object_kind: Default::default(),
1534 })
1535 }
1536 _ => Err(self.into()),
1537 },
1538 PrimitiveType::Axis3d => match self {
1539 KclValue::Object {
1540 value: values, meta, ..
1541 } => {
1542 if values
1543 .get("origin")
1544 .ok_or(CoercionError::from(self))?
1545 .has_type(&RuntimeType::point3d())
1546 && values
1547 .get("direction")
1548 .ok_or(CoercionError::from(self))?
1549 .has_type(&RuntimeType::point3d())
1550 {
1551 return Ok(self.clone());
1552 }
1553
1554 let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
1555 p.coerce_to_array_type(
1556 &RuntimeType::length(),
1557 convert_units,
1558 ArrayLen::Known(3),
1559 exec_state,
1560 true,
1561 )
1562 })?;
1563 let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
1564 p.coerce_to_array_type(
1565 &RuntimeType::length(),
1566 convert_units,
1567 ArrayLen::Known(3),
1568 exec_state,
1569 true,
1570 )
1571 })?;
1572
1573 Ok(KclValue::Object {
1574 value: [("origin".to_owned(), origin), ("direction".to_owned(), direction)].into(),
1575 meta: meta.clone(),
1576 constrainable: false,
1577 object_kind: Default::default(),
1578 })
1579 }
1580 _ => Err(self.into()),
1581 },
1582 PrimitiveType::ImportedGeometry => match self {
1583 KclValue::ImportedGeometry { .. } => Ok(self.clone()),
1584 _ => Err(self.into()),
1585 },
1586 PrimitiveType::Function => match self {
1587 KclValue::Function { .. } => Ok(self.clone()),
1588 _ => Err(self.into()),
1589 },
1590 PrimitiveType::TagDecl => match self {
1591 KclValue::TagDeclarator { .. } => Ok(self.clone()),
1592 _ => Err(self.into()),
1593 },
1594 }
1595 }
1596
1597 fn coerce_to_array_type(
1598 &self,
1599 ty: &RuntimeType,
1600 convert_units: bool,
1601 len: ArrayLen,
1602 exec_state: &mut ExecState,
1603 allow_shrink: bool,
1604 ) -> Result<KclValue, CoercionError> {
1605 match self {
1606 KclValue::HomArray { value, ty: aty, .. } => {
1607 let satisfied_len = len.satisfied(value.len(), allow_shrink);
1608
1609 if aty.subtype(ty) {
1610 return satisfied_len
1617 .map(|len| KclValue::HomArray {
1618 value: value[..len].to_vec(),
1619 ty: aty.clone(),
1620 })
1621 .ok_or(self.into());
1622 }
1623
1624 if let Some(satisfied_len) = satisfied_len {
1626 let value_result = value
1627 .iter()
1628 .take(satisfied_len)
1629 .map(|v| v.coerce(ty, convert_units, exec_state))
1630 .collect::<Result<Vec<_>, _>>();
1631
1632 if let Ok(value) = value_result {
1633 return Ok(KclValue::HomArray { value, ty: ty.clone() });
1635 }
1636 }
1637
1638 let mut values = Vec::new();
1640 for item in value {
1641 if let KclValue::HomArray { value: inner_value, .. } = item {
1642 for item in inner_value {
1644 values.push(item.coerce(ty, convert_units, exec_state)?);
1645 }
1646 } else {
1647 values.push(item.coerce(ty, convert_units, exec_state)?);
1648 }
1649 }
1650
1651 let len = len
1652 .satisfied(values.len(), allow_shrink)
1653 .ok_or(CoercionError::from(self))?;
1654
1655 if len > values.len() {
1656 let message = format!(
1657 "Internal: Expected coerced array length {len} to be less than or equal to original length {}",
1658 values.len()
1659 );
1660 exec_state.err(CompilationIssue::err(self.into(), message.clone()));
1661 #[cfg(debug_assertions)]
1662 panic!("{message}");
1663 }
1664 values.truncate(len);
1665
1666 Ok(KclValue::HomArray {
1667 value: values,
1668 ty: ty.clone(),
1669 })
1670 }
1671 KclValue::Tuple { value, .. } => {
1672 let len = len
1673 .satisfied(value.len(), allow_shrink)
1674 .ok_or(CoercionError::from(self))?;
1675 let value = value
1676 .iter()
1677 .map(|item| item.coerce(ty, convert_units, exec_state))
1678 .take(len)
1679 .collect::<Result<Vec<_>, _>>()?;
1680
1681 Ok(KclValue::HomArray { value, ty: ty.clone() })
1682 }
1683 KclValue::KclNone { .. } if len.satisfied(0, false).is_some() => Ok(KclValue::HomArray {
1684 value: Vec::new(),
1685 ty: ty.clone(),
1686 }),
1687 _ if len.satisfied(1, false).is_some() => self.coerce(ty, convert_units, exec_state),
1688 _ => Err(self.into()),
1689 }
1690 }
1691
1692 fn coerce_to_tuple_type(
1693 &self,
1694 tys: &[RuntimeType],
1695 convert_units: bool,
1696 exec_state: &mut ExecState,
1697 ) -> Result<KclValue, CoercionError> {
1698 match self {
1699 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } if value.len() == tys.len() => {
1700 let mut result = Vec::new();
1701 for (i, t) in tys.iter().enumerate() {
1702 result.push(value[i].coerce(t, convert_units, exec_state)?);
1703 }
1704
1705 Ok(KclValue::Tuple {
1706 value: result,
1707 meta: Vec::new(),
1708 })
1709 }
1710 KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::Tuple {
1711 value: Vec::new(),
1712 meta: meta.clone(),
1713 }),
1714 _ if tys.len() == 1 => self.coerce(&tys[0], convert_units, exec_state),
1715 _ => Err(self.into()),
1716 }
1717 }
1718
1719 fn coerce_to_union_type(
1720 &self,
1721 tys: &[RuntimeType],
1722 convert_units: bool,
1723 exec_state: &mut ExecState,
1724 ) -> Result<KclValue, CoercionError> {
1725 for t in tys {
1726 if let Ok(v) = self.coerce(t, convert_units, exec_state) {
1727 return Ok(v);
1728 }
1729 }
1730
1731 Err(self.into())
1732 }
1733
1734 fn coerce_to_object_type(
1735 &self,
1736 tys: &[(String, RuntimeType)],
1737 constrainable: bool,
1738 _convert_units: bool,
1739 _exec_state: &mut ExecState,
1740 ) -> Result<KclValue, CoercionError> {
1741 match self {
1742 KclValue::Object { value, meta, .. } => {
1743 for (s, t) in tys {
1744 if !value.get(s).ok_or(CoercionError::from(self))?.has_type(t) {
1746 return Err(self.into());
1747 }
1748 }
1749 Ok(KclValue::Object {
1751 value: value.clone(),
1752 meta: meta.clone(),
1753 constrainable,
1756 object_kind: Default::default(),
1757 })
1758 }
1759 KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::Object {
1760 value: HashMap::new(),
1761 meta: meta.clone(),
1762 constrainable,
1763 object_kind: Default::default(),
1764 }),
1765 _ => Err(self.into()),
1766 }
1767 }
1768
1769 pub fn principal_type(&self) -> Option<RuntimeType> {
1770 match self {
1771 KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
1772 KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(*ty))),
1773 KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)),
1774 KclValue::SketchVar { value, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(value.ty))),
1775 KclValue::SketchConstraint { .. } => Some(RuntimeType::Primitive(PrimitiveType::Constraint)),
1776 KclValue::Object {
1777 value, constrainable, ..
1778 } => {
1779 let properties = value
1780 .iter()
1781 .map(|(k, v)| v.principal_type().map(|t| (k.clone(), t)))
1782 .collect::<Option<Vec<_>>>()?;
1783 Some(RuntimeType::Object(properties, *constrainable))
1784 }
1785 KclValue::GdtAnnotation { .. } => Some(RuntimeType::Primitive(PrimitiveType::GdtAnnotation)),
1786 KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
1787 KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
1788 KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
1789 KclValue::Face { .. } => Some(RuntimeType::Primitive(PrimitiveType::Face)),
1790 KclValue::Segment { .. } => Some(RuntimeType::Primitive(PrimitiveType::Segment)),
1791 KclValue::Helix { .. } => Some(RuntimeType::Primitive(PrimitiveType::Helix)),
1792 KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
1793 KclValue::Tuple { value, .. } => Some(RuntimeType::Tuple(
1794 value.iter().map(|v| v.principal_type()).collect::<Option<Vec<_>>>()?,
1795 )),
1796 KclValue::HomArray { ty, value, .. } => {
1797 Some(RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(value.len())))
1798 }
1799 KclValue::TagIdentifier(_) => Some(RuntimeType::Primitive(PrimitiveType::TaggedEdge)),
1800 KclValue::TagDeclarator(_) => Some(RuntimeType::Primitive(PrimitiveType::TagDecl)),
1801 KclValue::Uuid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Edge)),
1802 KclValue::Function { .. } => Some(RuntimeType::Primitive(PrimitiveType::Function)),
1803 KclValue::KclNone { .. } => Some(RuntimeType::Primitive(PrimitiveType::None)),
1804 KclValue::Module { .. } | KclValue::Type { .. } => None,
1805 KclValue::BoundedEdge { .. } => Some(RuntimeType::Primitive(PrimitiveType::BoundedEdge)),
1806 }
1807 }
1808
1809 pub fn principal_type_string(&self) -> String {
1810 if let Some(ty) = self.principal_type() {
1811 return format!("`{ty}`");
1812 }
1813
1814 match self {
1815 KclValue::Module { .. } => "module",
1816 KclValue::KclNone { .. } => "none",
1817 KclValue::Type { .. } => "type",
1818 _ => {
1819 debug_assert!(false);
1820 "<unexpected type>"
1821 }
1822 }
1823 .to_owned()
1824 }
1825}
1826
1827#[cfg(test)]
1828mod test {
1829 use super::*;
1830 use crate::execution::ExecTestResults;
1831 use crate::execution::parse_execute;
1832
1833 async fn new_exec_state() -> (crate::ExecutorContext, ExecState) {
1834 let ctx = crate::ExecutorContext::new_mock(None).await;
1835 let exec_state = ExecState::new(&ctx);
1836 (ctx, exec_state)
1837 }
1838
1839 fn values(exec_state: &mut ExecState) -> Vec<KclValue> {
1840 vec![
1841 KclValue::Bool {
1842 value: true,
1843 meta: Vec::new(),
1844 },
1845 KclValue::Number {
1846 value: 1.0,
1847 ty: NumericType::count(),
1848 meta: Vec::new(),
1849 },
1850 KclValue::String {
1851 value: "hello".to_owned(),
1852 meta: Vec::new(),
1853 },
1854 KclValue::Tuple {
1855 value: Vec::new(),
1856 meta: Vec::new(),
1857 },
1858 KclValue::HomArray {
1859 value: Vec::new(),
1860 ty: RuntimeType::solid(),
1861 },
1862 KclValue::Object {
1863 value: crate::execution::KclObjectFields::new(),
1864 meta: Vec::new(),
1865 constrainable: false,
1866 object_kind: Default::default(),
1867 },
1868 KclValue::TagIdentifier(Box::new("foo".parse().unwrap())),
1869 KclValue::TagDeclarator(Box::new(crate::parsing::ast::types::TagDeclarator::new("foo"))),
1870 KclValue::Plane {
1871 value: Box::new(
1872 Plane::from_plane_data_skipping_engine(crate::std::sketch::PlaneData::XY, exec_state).unwrap(),
1873 ),
1874 },
1875 KclValue::ImportedGeometry(crate::execution::ImportedGeometry::new(
1877 uuid::Uuid::nil(),
1878 Vec::new(),
1879 Vec::new(),
1880 )),
1881 ]
1883 }
1884
1885 #[track_caller]
1886 fn assert_coerce_results(
1887 value: &KclValue,
1888 super_type: &RuntimeType,
1889 expected_value: &KclValue,
1890 exec_state: &mut ExecState,
1891 ) {
1892 let is_subtype = value == expected_value;
1893 let actual = value.coerce(super_type, true, exec_state).unwrap();
1894 assert_eq!(&actual, expected_value);
1895 assert_eq!(
1896 is_subtype,
1897 value.principal_type().is_some() && value.principal_type().unwrap().subtype(super_type),
1898 "{:?} <: {super_type:?} should be {is_subtype}",
1899 value.principal_type().unwrap()
1900 );
1901 assert!(
1902 expected_value.principal_type().unwrap().subtype(super_type),
1903 "{} <: {super_type}",
1904 expected_value.principal_type().unwrap()
1905 )
1906 }
1907
1908 #[tokio::test(flavor = "multi_thread")]
1909 async fn coerce_idempotent() {
1910 let (ctx, mut exec_state) = new_exec_state().await;
1911 let values = values(&mut exec_state);
1912 for v in &values {
1913 let ty = v.principal_type().unwrap();
1915 assert_coerce_results(v, &ty, v, &mut exec_state);
1916
1917 let uty1 = RuntimeType::Union(vec![ty.clone()]);
1919 let uty2 = RuntimeType::Union(vec![ty.clone(), RuntimeType::Primitive(PrimitiveType::Boolean)]);
1920 assert_coerce_results(v, &uty1, v, &mut exec_state);
1921 assert_coerce_results(v, &uty2, v, &mut exec_state);
1922
1923 let aty = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::None);
1925 let aty1 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(1));
1926 let aty0 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Minimum(1));
1927
1928 match v {
1929 KclValue::HomArray { .. } => {
1930 assert_coerce_results(
1932 v,
1933 &aty,
1934 &KclValue::HomArray {
1935 value: vec![],
1936 ty: ty.clone(),
1937 },
1938 &mut exec_state,
1939 );
1940 v.coerce(&aty1, true, &mut exec_state).unwrap_err();
1943 v.coerce(&aty0, true, &mut exec_state).unwrap_err();
1946 }
1947 KclValue::Tuple { .. } => {}
1948 _ => {
1949 assert_coerce_results(v, &aty, v, &mut exec_state);
1950 assert_coerce_results(v, &aty1, v, &mut exec_state);
1951 assert_coerce_results(v, &aty0, v, &mut exec_state);
1952
1953 let tty = RuntimeType::Tuple(vec![ty.clone()]);
1955 assert_coerce_results(v, &tty, v, &mut exec_state);
1956 }
1957 }
1958 }
1959
1960 for v in &values[1..] {
1961 v.coerce(&RuntimeType::Primitive(PrimitiveType::Boolean), true, &mut exec_state)
1963 .unwrap_err();
1964 }
1965 ctx.close().await;
1966 }
1967
1968 #[tokio::test(flavor = "multi_thread")]
1969 async fn coerce_none() {
1970 let (ctx, mut exec_state) = new_exec_state().await;
1971 let none = KclValue::KclNone {
1972 value: crate::parsing::ast::types::KclNone::new(),
1973 meta: Vec::new(),
1974 };
1975
1976 let aty = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::None);
1977 let aty0 = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::Known(0));
1978 let aty1 = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::Known(1));
1979 let aty1p = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::Minimum(1));
1980 assert_coerce_results(
1981 &none,
1982 &aty,
1983 &KclValue::HomArray {
1984 value: Vec::new(),
1985 ty: RuntimeType::solid(),
1986 },
1987 &mut exec_state,
1988 );
1989 assert_coerce_results(
1990 &none,
1991 &aty0,
1992 &KclValue::HomArray {
1993 value: Vec::new(),
1994 ty: RuntimeType::solid(),
1995 },
1996 &mut exec_state,
1997 );
1998 none.coerce(&aty1, true, &mut exec_state).unwrap_err();
1999 none.coerce(&aty1p, true, &mut exec_state).unwrap_err();
2000
2001 let tty = RuntimeType::Tuple(vec![]);
2002 let tty1 = RuntimeType::Tuple(vec![RuntimeType::solid()]);
2003 assert_coerce_results(
2004 &none,
2005 &tty,
2006 &KclValue::Tuple {
2007 value: Vec::new(),
2008 meta: Vec::new(),
2009 },
2010 &mut exec_state,
2011 );
2012 none.coerce(&tty1, true, &mut exec_state).unwrap_err();
2013
2014 let oty = RuntimeType::Object(vec![], false);
2015 assert_coerce_results(
2016 &none,
2017 &oty,
2018 &KclValue::Object {
2019 value: HashMap::new(),
2020 meta: Vec::new(),
2021 constrainable: false,
2022 object_kind: Default::default(),
2023 },
2024 &mut exec_state,
2025 );
2026 ctx.close().await;
2027 }
2028
2029 #[tokio::test(flavor = "multi_thread")]
2030 async fn coerce_record() {
2031 let (ctx, mut exec_state) = new_exec_state().await;
2032
2033 let obj0 = KclValue::Object {
2034 value: HashMap::new(),
2035 meta: Vec::new(),
2036 constrainable: false,
2037 object_kind: Default::default(),
2038 };
2039 let obj1 = KclValue::Object {
2040 value: [(
2041 "foo".to_owned(),
2042 KclValue::Bool {
2043 value: true,
2044 meta: Vec::new(),
2045 },
2046 )]
2047 .into(),
2048 meta: Vec::new(),
2049 constrainable: false,
2050 object_kind: Default::default(),
2051 };
2052 let obj2 = KclValue::Object {
2053 value: [
2054 (
2055 "foo".to_owned(),
2056 KclValue::Bool {
2057 value: true,
2058 meta: Vec::new(),
2059 },
2060 ),
2061 (
2062 "bar".to_owned(),
2063 KclValue::Number {
2064 value: 0.0,
2065 ty: NumericType::count(),
2066 meta: Vec::new(),
2067 },
2068 ),
2069 (
2070 "baz".to_owned(),
2071 KclValue::Number {
2072 value: 42.0,
2073 ty: NumericType::count(),
2074 meta: Vec::new(),
2075 },
2076 ),
2077 ]
2078 .into(),
2079 meta: Vec::new(),
2080 constrainable: false,
2081 object_kind: Default::default(),
2082 };
2083
2084 let ty0 = RuntimeType::Object(vec![], false);
2085 assert_coerce_results(&obj0, &ty0, &obj0, &mut exec_state);
2086 assert_coerce_results(&obj1, &ty0, &obj1, &mut exec_state);
2087 assert_coerce_results(&obj2, &ty0, &obj2, &mut exec_state);
2088
2089 let ty1 = RuntimeType::Object(
2090 vec![("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))],
2091 false,
2092 );
2093 obj0.coerce(&ty1, true, &mut exec_state).unwrap_err();
2094 assert_coerce_results(&obj1, &ty1, &obj1, &mut exec_state);
2095 assert_coerce_results(&obj2, &ty1, &obj2, &mut exec_state);
2096
2097 let ty2 = RuntimeType::Object(
2099 vec![
2100 (
2101 "bar".to_owned(),
2102 RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2103 ),
2104 ("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean)),
2105 ],
2106 false,
2107 );
2108 obj0.coerce(&ty2, true, &mut exec_state).unwrap_err();
2109 obj1.coerce(&ty2, true, &mut exec_state).unwrap_err();
2110 assert_coerce_results(&obj2, &ty2, &obj2, &mut exec_state);
2111
2112 let tyq = RuntimeType::Object(
2114 vec![("qux".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))],
2115 false,
2116 );
2117 obj0.coerce(&tyq, true, &mut exec_state).unwrap_err();
2118 obj1.coerce(&tyq, true, &mut exec_state).unwrap_err();
2119 obj2.coerce(&tyq, true, &mut exec_state).unwrap_err();
2120
2121 let ty1 = RuntimeType::Object(
2123 vec![("bar".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))],
2124 false,
2125 );
2126 obj2.coerce(&ty1, true, &mut exec_state).unwrap_err();
2127 ctx.close().await;
2128 }
2129
2130 #[tokio::test(flavor = "multi_thread")]
2131 async fn coerce_array() {
2132 let (ctx, mut exec_state) = new_exec_state().await;
2133
2134 let hom_arr = KclValue::HomArray {
2135 value: vec![
2136 KclValue::Number {
2137 value: 0.0,
2138 ty: NumericType::count(),
2139 meta: Vec::new(),
2140 },
2141 KclValue::Number {
2142 value: 1.0,
2143 ty: NumericType::count(),
2144 meta: Vec::new(),
2145 },
2146 KclValue::Number {
2147 value: 2.0,
2148 ty: NumericType::count(),
2149 meta: Vec::new(),
2150 },
2151 KclValue::Number {
2152 value: 3.0,
2153 ty: NumericType::count(),
2154 meta: Vec::new(),
2155 },
2156 ],
2157 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2158 };
2159 let mixed1 = KclValue::Tuple {
2160 value: vec![
2161 KclValue::Number {
2162 value: 0.0,
2163 ty: NumericType::count(),
2164 meta: Vec::new(),
2165 },
2166 KclValue::Number {
2167 value: 1.0,
2168 ty: NumericType::count(),
2169 meta: Vec::new(),
2170 },
2171 ],
2172 meta: Vec::new(),
2173 };
2174 let mixed2 = KclValue::Tuple {
2175 value: vec![
2176 KclValue::Number {
2177 value: 0.0,
2178 ty: NumericType::count(),
2179 meta: Vec::new(),
2180 },
2181 KclValue::Bool {
2182 value: true,
2183 meta: Vec::new(),
2184 },
2185 ],
2186 meta: Vec::new(),
2187 };
2188
2189 let tyh = RuntimeType::Array(
2191 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
2192 ArrayLen::Known(4),
2193 );
2194 let tym1 = RuntimeType::Tuple(vec![
2195 RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2196 RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2197 ]);
2198 let tym2 = RuntimeType::Tuple(vec![
2199 RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2200 RuntimeType::Primitive(PrimitiveType::Boolean),
2201 ]);
2202 assert_coerce_results(&hom_arr, &tyh, &hom_arr, &mut exec_state);
2203 assert_coerce_results(&mixed1, &tym1, &mixed1, &mut exec_state);
2204 assert_coerce_results(&mixed2, &tym2, &mixed2, &mut exec_state);
2205 mixed1.coerce(&tym2, true, &mut exec_state).unwrap_err();
2206 mixed2.coerce(&tym1, true, &mut exec_state).unwrap_err();
2207
2208 let tyhn = RuntimeType::Array(
2210 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
2211 ArrayLen::None,
2212 );
2213 let tyh1 = RuntimeType::Array(
2214 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
2215 ArrayLen::Minimum(1),
2216 );
2217 let tyh3 = RuntimeType::Array(
2218 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
2219 ArrayLen::Known(3),
2220 );
2221 let tyhm3 = RuntimeType::Array(
2222 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
2223 ArrayLen::Minimum(3),
2224 );
2225 let tyhm5 = RuntimeType::Array(
2226 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
2227 ArrayLen::Minimum(5),
2228 );
2229 assert_coerce_results(&hom_arr, &tyhn, &hom_arr, &mut exec_state);
2230 assert_coerce_results(&hom_arr, &tyh1, &hom_arr, &mut exec_state);
2231 hom_arr.coerce(&tyh3, true, &mut exec_state).unwrap_err();
2232 assert_coerce_results(&hom_arr, &tyhm3, &hom_arr, &mut exec_state);
2233 hom_arr.coerce(&tyhm5, true, &mut exec_state).unwrap_err();
2234
2235 let hom_arr0 = KclValue::HomArray {
2236 value: vec![],
2237 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2238 };
2239 assert_coerce_results(&hom_arr0, &tyhn, &hom_arr0, &mut exec_state);
2240 hom_arr0.coerce(&tyh1, true, &mut exec_state).unwrap_err();
2241 hom_arr0.coerce(&tyh3, true, &mut exec_state).unwrap_err();
2242
2243 let tym1 = RuntimeType::Tuple(vec![
2246 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
2247 RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2248 ]);
2249 let tym2 = RuntimeType::Tuple(vec![
2250 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
2251 RuntimeType::Primitive(PrimitiveType::Boolean),
2252 ]);
2253 assert_coerce_results(&mixed1, &tym1, &mixed1, &mut exec_state);
2256 assert_coerce_results(&mixed2, &tym2, &mixed2, &mut exec_state);
2257
2258 let hom_arr_2 = KclValue::HomArray {
2260 value: vec![
2261 KclValue::Number {
2262 value: 0.0,
2263 ty: NumericType::count(),
2264 meta: Vec::new(),
2265 },
2266 KclValue::Number {
2267 value: 1.0,
2268 ty: NumericType::count(),
2269 meta: Vec::new(),
2270 },
2271 ],
2272 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2273 };
2274 let mixed0 = KclValue::Tuple {
2275 value: vec![],
2276 meta: Vec::new(),
2277 };
2278 assert_coerce_results(&mixed1, &tyhn, &hom_arr_2, &mut exec_state);
2279 assert_coerce_results(&mixed1, &tyh1, &hom_arr_2, &mut exec_state);
2280 assert_coerce_results(&mixed0, &tyhn, &hom_arr0, &mut exec_state);
2281 mixed0.coerce(&tyh, true, &mut exec_state).unwrap_err();
2282 mixed0.coerce(&tyh1, true, &mut exec_state).unwrap_err();
2283
2284 assert_coerce_results(&hom_arr_2, &tym1, &mixed1, &mut exec_state);
2286 hom_arr.coerce(&tym1, true, &mut exec_state).unwrap_err();
2287 hom_arr_2.coerce(&tym2, true, &mut exec_state).unwrap_err();
2288
2289 mixed0.coerce(&tym1, true, &mut exec_state).unwrap_err();
2290 mixed0.coerce(&tym2, true, &mut exec_state).unwrap_err();
2291 ctx.close().await;
2292 }
2293
2294 #[tokio::test(flavor = "multi_thread")]
2295 async fn coerce_union() {
2296 let (ctx, mut exec_state) = new_exec_state().await;
2297
2298 assert!(RuntimeType::Union(vec![]).subtype(&RuntimeType::Union(vec![
2300 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
2301 RuntimeType::Primitive(PrimitiveType::Boolean)
2302 ])));
2303 assert!(
2304 RuntimeType::Union(vec![RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))]).subtype(
2305 &RuntimeType::Union(vec![
2306 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
2307 RuntimeType::Primitive(PrimitiveType::Boolean)
2308 ])
2309 )
2310 );
2311 assert!(
2312 RuntimeType::Union(vec![
2313 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
2314 RuntimeType::Primitive(PrimitiveType::Boolean)
2315 ])
2316 .subtype(&RuntimeType::Union(vec![
2317 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
2318 RuntimeType::Primitive(PrimitiveType::Boolean)
2319 ]))
2320 );
2321
2322 let count = KclValue::Number {
2324 value: 1.0,
2325 ty: NumericType::count(),
2326 meta: Vec::new(),
2327 };
2328
2329 let tya = RuntimeType::Union(vec![RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))]);
2330 let tya2 = RuntimeType::Union(vec![
2331 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
2332 RuntimeType::Primitive(PrimitiveType::Boolean),
2333 ]);
2334 assert_coerce_results(&count, &tya, &count, &mut exec_state);
2335 assert_coerce_results(&count, &tya2, &count, &mut exec_state);
2336
2337 let tyb = RuntimeType::Union(vec![RuntimeType::Primitive(PrimitiveType::Boolean)]);
2339 let tyb2 = RuntimeType::Union(vec![
2340 RuntimeType::Primitive(PrimitiveType::Boolean),
2341 RuntimeType::Primitive(PrimitiveType::String),
2342 ]);
2343 count.coerce(&tyb, true, &mut exec_state).unwrap_err();
2344 count.coerce(&tyb2, true, &mut exec_state).unwrap_err();
2345 ctx.close().await;
2346 }
2347
2348 #[tokio::test(flavor = "multi_thread")]
2349 async fn coerce_axes() {
2350 let (ctx, mut exec_state) = new_exec_state().await;
2351
2352 assert!(RuntimeType::Primitive(PrimitiveType::Axis2d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis2d)));
2354 assert!(RuntimeType::Primitive(PrimitiveType::Axis3d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis3d)));
2355 assert!(!RuntimeType::Primitive(PrimitiveType::Axis3d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis2d)));
2356 assert!(!RuntimeType::Primitive(PrimitiveType::Axis2d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis3d)));
2357
2358 let a2d = KclValue::Object {
2360 value: [
2361 (
2362 "origin".to_owned(),
2363 KclValue::HomArray {
2364 value: vec![
2365 KclValue::Number {
2366 value: 0.0,
2367 ty: NumericType::mm(),
2368 meta: Vec::new(),
2369 },
2370 KclValue::Number {
2371 value: 0.0,
2372 ty: NumericType::mm(),
2373 meta: Vec::new(),
2374 },
2375 ],
2376 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
2377 },
2378 ),
2379 (
2380 "direction".to_owned(),
2381 KclValue::HomArray {
2382 value: vec![
2383 KclValue::Number {
2384 value: 1.0,
2385 ty: NumericType::mm(),
2386 meta: Vec::new(),
2387 },
2388 KclValue::Number {
2389 value: 0.0,
2390 ty: NumericType::mm(),
2391 meta: Vec::new(),
2392 },
2393 ],
2394 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
2395 },
2396 ),
2397 ]
2398 .into(),
2399 meta: Vec::new(),
2400 constrainable: false,
2401 object_kind: Default::default(),
2402 };
2403 let a3d = KclValue::Object {
2404 value: [
2405 (
2406 "origin".to_owned(),
2407 KclValue::HomArray {
2408 value: vec![
2409 KclValue::Number {
2410 value: 0.0,
2411 ty: NumericType::mm(),
2412 meta: Vec::new(),
2413 },
2414 KclValue::Number {
2415 value: 0.0,
2416 ty: NumericType::mm(),
2417 meta: Vec::new(),
2418 },
2419 KclValue::Number {
2420 value: 0.0,
2421 ty: NumericType::mm(),
2422 meta: Vec::new(),
2423 },
2424 ],
2425 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
2426 },
2427 ),
2428 (
2429 "direction".to_owned(),
2430 KclValue::HomArray {
2431 value: vec![
2432 KclValue::Number {
2433 value: 1.0,
2434 ty: NumericType::mm(),
2435 meta: Vec::new(),
2436 },
2437 KclValue::Number {
2438 value: 0.0,
2439 ty: NumericType::mm(),
2440 meta: Vec::new(),
2441 },
2442 KclValue::Number {
2443 value: 1.0,
2444 ty: NumericType::mm(),
2445 meta: Vec::new(),
2446 },
2447 ],
2448 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
2449 },
2450 ),
2451 ]
2452 .into(),
2453 meta: Vec::new(),
2454 constrainable: false,
2455 object_kind: Default::default(),
2456 };
2457
2458 let ty2d = RuntimeType::Primitive(PrimitiveType::Axis2d);
2459 let ty3d = RuntimeType::Primitive(PrimitiveType::Axis3d);
2460
2461 assert_coerce_results(&a2d, &ty2d, &a2d, &mut exec_state);
2462 assert_coerce_results(&a3d, &ty3d, &a3d, &mut exec_state);
2463 assert_coerce_results(&a3d, &ty2d, &a2d, &mut exec_state);
2464 a2d.coerce(&ty3d, true, &mut exec_state).unwrap_err();
2465 ctx.close().await;
2466 }
2467
2468 #[tokio::test(flavor = "multi_thread")]
2469 async fn coerce_numeric() {
2470 let (ctx, mut exec_state) = new_exec_state().await;
2471
2472 let count = KclValue::Number {
2473 value: 1.0,
2474 ty: NumericType::count(),
2475 meta: Vec::new(),
2476 };
2477 let mm = KclValue::Number {
2478 value: 1.0,
2479 ty: NumericType::mm(),
2480 meta: Vec::new(),
2481 };
2482 let inches = KclValue::Number {
2483 value: 1.0,
2484 ty: NumericType::Known(UnitType::Length(UnitLength::Inches)),
2485 meta: Vec::new(),
2486 };
2487 let rads = KclValue::Number {
2488 value: 1.0,
2489 ty: NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
2490 meta: Vec::new(),
2491 };
2492 let default = KclValue::Number {
2493 value: 1.0,
2494 ty: NumericType::default(),
2495 meta: Vec::new(),
2496 };
2497 let any = KclValue::Number {
2498 value: 1.0,
2499 ty: NumericType::Any,
2500 meta: Vec::new(),
2501 };
2502 let unknown = KclValue::Number {
2503 value: 1.0,
2504 ty: NumericType::Unknown,
2505 meta: Vec::new(),
2506 };
2507
2508 assert_coerce_results(&count, &NumericType::count().into(), &count, &mut exec_state);
2510 assert_coerce_results(&mm, &NumericType::mm().into(), &mm, &mut exec_state);
2511 assert_coerce_results(&any, &NumericType::Any.into(), &any, &mut exec_state);
2512 assert_coerce_results(&unknown, &NumericType::Unknown.into(), &unknown, &mut exec_state);
2513 assert_coerce_results(&default, &NumericType::default().into(), &default, &mut exec_state);
2514
2515 assert_coerce_results(&count, &NumericType::Any.into(), &count, &mut exec_state);
2516 assert_coerce_results(&mm, &NumericType::Any.into(), &mm, &mut exec_state);
2517 assert_coerce_results(&unknown, &NumericType::Any.into(), &unknown, &mut exec_state);
2518 assert_coerce_results(&default, &NumericType::Any.into(), &default, &mut exec_state);
2519
2520 assert_eq!(
2521 default
2522 .coerce(
2523 &NumericType::Default {
2524 len: UnitLength::Yards,
2525 angle: UnitAngle::Degrees,
2526 }
2527 .into(),
2528 true,
2529 &mut exec_state
2530 )
2531 .unwrap(),
2532 default
2533 );
2534
2535 count
2537 .coerce(&NumericType::mm().into(), true, &mut exec_state)
2538 .unwrap_err();
2539 mm.coerce(&NumericType::count().into(), true, &mut exec_state)
2540 .unwrap_err();
2541 unknown
2542 .coerce(&NumericType::mm().into(), true, &mut exec_state)
2543 .unwrap_err();
2544 unknown
2545 .coerce(&NumericType::default().into(), true, &mut exec_state)
2546 .unwrap_err();
2547
2548 count
2549 .coerce(&NumericType::Unknown.into(), true, &mut exec_state)
2550 .unwrap_err();
2551 mm.coerce(&NumericType::Unknown.into(), true, &mut exec_state)
2552 .unwrap_err();
2553 default
2554 .coerce(&NumericType::Unknown.into(), true, &mut exec_state)
2555 .unwrap_err();
2556
2557 assert_eq!(
2558 inches
2559 .coerce(&NumericType::mm().into(), true, &mut exec_state)
2560 .unwrap()
2561 .as_f64()
2562 .unwrap()
2563 .round(),
2564 25.0
2565 );
2566 assert_eq!(
2567 rads.coerce(
2568 &NumericType::Known(UnitType::Angle(UnitAngle::Degrees)).into(),
2569 true,
2570 &mut exec_state
2571 )
2572 .unwrap()
2573 .as_f64()
2574 .unwrap()
2575 .round(),
2576 57.0
2577 );
2578 assert_eq!(
2579 inches
2580 .coerce(&NumericType::default().into(), true, &mut exec_state)
2581 .unwrap()
2582 .as_f64()
2583 .unwrap()
2584 .round(),
2585 1.0
2586 );
2587 assert_eq!(
2588 rads.coerce(&NumericType::default().into(), true, &mut exec_state)
2589 .unwrap()
2590 .as_f64()
2591 .unwrap()
2592 .round(),
2593 1.0
2594 );
2595 ctx.close().await;
2596 }
2597
2598 #[track_caller]
2599 fn assert_value_and_type(name: &str, result: &ExecTestResults, expected: f64, expected_ty: NumericType) {
2600 let mem = result.exec_state.stack();
2601 match mem
2602 .memory
2603 .get_from_owned(name, result.mem_env, SourceRange::default(), 0)
2604 .unwrap()
2605 {
2606 KclValue::Number { value, ty, .. } => {
2607 assert_eq!(value.round(), expected);
2608 assert_eq!(ty, expected_ty);
2609 }
2610 _ => unreachable!(),
2611 }
2612 }
2613
2614 #[tokio::test(flavor = "multi_thread")]
2615 async fn combine_numeric() {
2616 let program = r#"a = 5 + 4
2617b = 5 - 2
2618c = 5mm - 2mm + 10mm
2619d = 5mm - 2 + 10
2620e = 5 - 2mm + 10
2621f = 30mm - 1inch
2622
2623g = 2 * 10
2624h = 2 * 10mm
2625i = 2mm * 10mm
2626j = 2_ * 10
2627k = 2_ * 3mm * 3mm
2628
2629l = 1 / 10
2630m = 2mm / 1mm
2631n = 10inch / 2mm
2632o = 3mm / 3
2633p = 3_ / 4
2634q = 4inch / 2_
2635
2636r = min([0, 3, 42])
2637s = min([0, 3mm, -42])
2638t = min([100, 3in, 142mm])
2639u = min([3rad, 4in])
2640"#;
2641
2642 let result = parse_execute(program).await.unwrap();
2643 assert_eq!(
2644 result.exec_state.issues().len(),
2645 5,
2646 "errors: {:?}",
2647 result.exec_state.issues()
2648 );
2649
2650 assert_value_and_type("a", &result, 9.0, NumericType::default());
2651 assert_value_and_type("b", &result, 3.0, NumericType::default());
2652 assert_value_and_type("c", &result, 13.0, NumericType::mm());
2653 assert_value_and_type("d", &result, 13.0, NumericType::mm());
2654 assert_value_and_type("e", &result, 13.0, NumericType::mm());
2655 assert_value_and_type("f", &result, 5.0, NumericType::mm());
2656
2657 assert_value_and_type("g", &result, 20.0, NumericType::default());
2658 assert_value_and_type("h", &result, 20.0, NumericType::mm());
2659 assert_value_and_type("i", &result, 20.0, NumericType::Unknown);
2660 assert_value_and_type("j", &result, 20.0, NumericType::default());
2661 assert_value_and_type("k", &result, 18.0, NumericType::Unknown);
2662
2663 assert_value_and_type("l", &result, 0.0, NumericType::default());
2664 assert_value_and_type("m", &result, 2.0, NumericType::count());
2665 assert_value_and_type("n", &result, 5.0, NumericType::Unknown);
2666 assert_value_and_type("o", &result, 1.0, NumericType::mm());
2667 assert_value_and_type("p", &result, 1.0, NumericType::count());
2668 assert_value_and_type(
2669 "q",
2670 &result,
2671 2.0,
2672 NumericType::Known(UnitType::Length(UnitLength::Inches)),
2673 );
2674
2675 assert_value_and_type("r", &result, 0.0, NumericType::default());
2676 assert_value_and_type("s", &result, -42.0, NumericType::mm());
2677 assert_value_and_type("t", &result, 3.0, NumericType::Unknown);
2678 assert_value_and_type("u", &result, 3.0, NumericType::Unknown);
2679 }
2680
2681 #[tokio::test(flavor = "multi_thread")]
2682 async fn bad_typed_arithmetic() {
2683 let program = r#"
2684a = 1rad
2685b = 180 / PI * a + 360
2686"#;
2687
2688 let result = parse_execute(program).await.unwrap();
2689
2690 assert_value_and_type("a", &result, 1.0, NumericType::radians());
2691 assert_value_and_type("b", &result, 417.0, NumericType::Unknown);
2692 }
2693
2694 #[tokio::test(flavor = "multi_thread")]
2695 async fn cos_coercions() {
2696 let program = r#"
2697a = cos(units::toRadians(30deg))
2698b = 3 / a
2699c = cos(30deg)
2700d = cos(1rad)
2701"#;
2702
2703 let result = parse_execute(program).await.unwrap();
2704 assert!(
2705 result.exec_state.issues().is_empty(),
2706 "{:?}",
2707 result.exec_state.issues()
2708 );
2709
2710 assert_value_and_type("a", &result, 1.0, NumericType::default());
2711 assert_value_and_type("b", &result, 3.0, NumericType::default());
2712 assert_value_and_type("c", &result, 1.0, NumericType::default());
2713 assert_value_and_type("d", &result, 1.0, NumericType::default());
2714 }
2715
2716 #[tokio::test(flavor = "multi_thread")]
2717 async fn coerce_nested_array() {
2718 let (ctx, mut exec_state) = new_exec_state().await;
2719
2720 let mixed1 = KclValue::HomArray {
2721 value: vec![
2722 KclValue::Number {
2723 value: 0.0,
2724 ty: NumericType::count(),
2725 meta: Vec::new(),
2726 },
2727 KclValue::Number {
2728 value: 1.0,
2729 ty: NumericType::count(),
2730 meta: Vec::new(),
2731 },
2732 KclValue::HomArray {
2733 value: vec![
2734 KclValue::Number {
2735 value: 2.0,
2736 ty: NumericType::count(),
2737 meta: Vec::new(),
2738 },
2739 KclValue::Number {
2740 value: 3.0,
2741 ty: NumericType::count(),
2742 meta: Vec::new(),
2743 },
2744 ],
2745 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2746 },
2747 ],
2748 ty: RuntimeType::any(),
2749 };
2750
2751 let tym1 = RuntimeType::Array(
2753 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
2754 ArrayLen::Minimum(1),
2755 );
2756
2757 let result = KclValue::HomArray {
2758 value: vec![
2759 KclValue::Number {
2760 value: 0.0,
2761 ty: NumericType::count(),
2762 meta: Vec::new(),
2763 },
2764 KclValue::Number {
2765 value: 1.0,
2766 ty: NumericType::count(),
2767 meta: Vec::new(),
2768 },
2769 KclValue::Number {
2770 value: 2.0,
2771 ty: NumericType::count(),
2772 meta: Vec::new(),
2773 },
2774 KclValue::Number {
2775 value: 3.0,
2776 ty: NumericType::count(),
2777 meta: Vec::new(),
2778 },
2779 ],
2780 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2781 };
2782 assert_coerce_results(&mixed1, &tym1, &result, &mut exec_state);
2783 ctx.close().await;
2784 }
2785}