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