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 Sketch,
409 Solid,
410 Plane,
411 Helix,
412 Face,
413 Edge,
414 Axis2d,
415 Axis3d,
416 ImportedGeometry,
417 Function,
418}
419
420impl PrimitiveType {
421 fn display_multiple(&self) -> String {
422 match self {
423 PrimitiveType::Any => "any values".to_owned(),
424 PrimitiveType::None => "none values".to_owned(),
425 PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
426 PrimitiveType::Number(_) => "numbers".to_owned(),
427 PrimitiveType::String => "strings".to_owned(),
428 PrimitiveType::Boolean => "bools".to_owned(),
429 PrimitiveType::Sketch => "Sketches".to_owned(),
430 PrimitiveType::Solid => "Solids".to_owned(),
431 PrimitiveType::Plane => "Planes".to_owned(),
432 PrimitiveType::Helix => "Helices".to_owned(),
433 PrimitiveType::Face => "Faces".to_owned(),
434 PrimitiveType::Edge => "Edges".to_owned(),
435 PrimitiveType::Axis2d => "2d axes".to_owned(),
436 PrimitiveType::Axis3d => "3d axes".to_owned(),
437 PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
438 PrimitiveType::Function => "functions".to_owned(),
439 PrimitiveType::TagDecl => "tag declarators".to_owned(),
440 PrimitiveType::TaggedEdge => "tagged edges".to_owned(),
441 PrimitiveType::TaggedFace => "tagged faces".to_owned(),
442 }
443 }
444
445 fn subtype(&self, other: &PrimitiveType) -> bool {
446 match (self, other) {
447 (_, PrimitiveType::Any) => true,
448 (PrimitiveType::Number(n1), PrimitiveType::Number(n2)) => n1.subtype(n2),
449 (PrimitiveType::TaggedEdge, PrimitiveType::TaggedFace)
450 | (PrimitiveType::TaggedEdge, PrimitiveType::Edge) => true,
451 (t1, t2) => t1 == t2,
452 }
453 }
454}
455
456impl fmt::Display for PrimitiveType {
457 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458 match self {
459 PrimitiveType::Any => write!(f, "any"),
460 PrimitiveType::None => write!(f, "none"),
461 PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
462 PrimitiveType::Number(NumericType::Unknown) => write!(f, "number(unknown units)"),
463 PrimitiveType::Number(NumericType::Default { .. }) => write!(f, "number"),
464 PrimitiveType::Number(NumericType::Any) => write!(f, "number(any units)"),
465 PrimitiveType::String => write!(f, "string"),
466 PrimitiveType::Boolean => write!(f, "bool"),
467 PrimitiveType::TagDecl => write!(f, "tag declarator"),
468 PrimitiveType::TaggedEdge => write!(f, "tagged edge"),
469 PrimitiveType::TaggedFace => write!(f, "tagged face"),
470 PrimitiveType::Sketch => write!(f, "Sketch"),
471 PrimitiveType::Solid => write!(f, "Solid"),
472 PrimitiveType::Plane => write!(f, "Plane"),
473 PrimitiveType::Face => write!(f, "Face"),
474 PrimitiveType::Edge => write!(f, "Edge"),
475 PrimitiveType::Axis2d => write!(f, "Axis2d"),
476 PrimitiveType::Axis3d => write!(f, "Axis3d"),
477 PrimitiveType::Helix => write!(f, "Helix"),
478 PrimitiveType::ImportedGeometry => write!(f, "ImportedGeometry"),
479 PrimitiveType::Function => write!(f, "fn"),
480 }
481 }
482}
483
484#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, ts_rs::TS)]
485#[ts(export)]
486#[serde(tag = "type")]
487pub enum NumericType {
488 Known(UnitType),
490 Default { len: UnitLength, angle: UnitAngle },
492 Unknown,
494 Any,
496}
497
498impl Default for NumericType {
499 fn default() -> Self {
500 NumericType::Default {
501 len: UnitLength::Millimeters,
502 angle: UnitAngle::Degrees,
503 }
504 }
505}
506
507impl NumericType {
508 pub const fn count() -> Self {
509 NumericType::Known(UnitType::Count)
510 }
511
512 pub const fn mm() -> Self {
513 NumericType::Known(UnitType::Length(UnitLength::Millimeters))
514 }
515
516 pub const fn radians() -> Self {
517 NumericType::Known(UnitType::Angle(UnitAngle::Radians))
518 }
519
520 pub const fn degrees() -> Self {
521 NumericType::Known(UnitType::Angle(UnitAngle::Degrees))
522 }
523
524 pub fn combine_eq(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
530 use NumericType::*;
531 match (a.ty, b.ty) {
532 (at, bt) if at == bt => (a.n, b.n, at),
533 (at, Any) => (a.n, b.n, at),
534 (Any, bt) => (a.n, b.n, bt),
535
536 (t @ Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => (a.n, adjust_length(l2, b.n, l1).0, t),
537 (t @ Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => (a.n, adjust_angle(a2, b.n, a1).0, t),
538
539 (t @ Known(UnitType::Length(_)), Known(UnitType::GenericLength)) => (a.n, b.n, t),
540 (Known(UnitType::GenericLength), t @ Known(UnitType::Length(_))) => (a.n, b.n, t),
541 (t @ Known(UnitType::Angle(_)), Known(UnitType::GenericAngle)) => (a.n, b.n, t),
542 (Known(UnitType::GenericAngle), t @ Known(UnitType::Angle(_))) => (a.n, b.n, t),
543
544 (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
545 (a.n, b.n, Known(UnitType::Count))
546 }
547 (t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) if l1 == l2 => (a.n, b.n, t),
548 (Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) if l1 == l2 => (a.n, b.n, t),
549 (t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) if a1 == a2 => (a.n, b.n, t),
550 (Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) if a1 == a2 => (a.n, b.n, t),
551
552 _ => (a.n, b.n, Unknown),
553 }
554 }
555
556 pub fn combine_eq_coerce(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
564 use NumericType::*;
565 match (a.ty, b.ty) {
566 (at, bt) if at == bt => (a.n, b.n, at),
567 (at, Any) => (a.n, b.n, at),
568 (Any, bt) => (a.n, b.n, bt),
569
570 (t @ Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => (a.n, adjust_length(l2, b.n, l1).0, t),
572 (t @ Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => (a.n, adjust_angle(a2, b.n, a1).0, t),
573
574 (t @ Known(UnitType::Length(_)), Known(UnitType::GenericLength)) => (a.n, b.n, t),
575 (Known(UnitType::GenericLength), t @ Known(UnitType::Length(_))) => (a.n, b.n, t),
576 (t @ Known(UnitType::Angle(_)), Known(UnitType::GenericAngle)) => (a.n, b.n, t),
577 (Known(UnitType::GenericAngle), t @ Known(UnitType::Angle(_))) => (a.n, b.n, t),
578
579 (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
581 (a.n, b.n, Known(UnitType::Count))
582 }
583
584 (t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) => (a.n, adjust_length(l2, b.n, l1).0, t),
585 (Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) => (adjust_length(l1, a.n, l2).0, b.n, t),
586 (t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) => (a.n, adjust_angle(a2, b.n, a1).0, t),
587 (Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) => (adjust_angle(a1, a.n, a2).0, b.n, t),
588
589 (Default { len: l1, .. }, Known(UnitType::GenericLength)) => (a.n, b.n, l1.into()),
590 (Known(UnitType::GenericLength), Default { len: l2, .. }) => (a.n, b.n, l2.into()),
591 (Default { angle: a1, .. }, Known(UnitType::GenericAngle)) => (a.n, b.n, a1.into()),
592 (Known(UnitType::GenericAngle), Default { angle: a2, .. }) => (a.n, b.n, a2.into()),
593
594 (Known(_), Known(_)) | (Default { .. }, Default { .. }) | (_, Unknown) | (Unknown, _) => {
595 (a.n, b.n, Unknown)
596 }
597 }
598 }
599
600 pub fn combine_eq_array(input: &[TyF64]) -> (Vec<f64>, NumericType) {
601 use NumericType::*;
602 let result = input.iter().map(|t| t.n).collect();
603
604 let mut ty = Any;
605 for i in input {
606 if i.ty == Any || ty == i.ty {
607 continue;
608 }
609
610 match (&ty, &i.ty) {
612 (Any, Default { .. }) if i.n == 0.0 => {}
613 (Any, t) => {
614 ty = *t;
615 }
616 (_, Unknown) | (Default { .. }, Default { .. }) => return (result, Unknown),
617
618 (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
619 ty = Known(UnitType::Count);
620 }
621
622 (Known(UnitType::Length(l1)), Default { len: l2, .. }) if l1 == l2 || i.n == 0.0 => {}
623 (Known(UnitType::Angle(a1)), Default { angle: a2, .. }) if a1 == a2 || i.n == 0.0 => {}
624
625 (Default { len: l1, .. }, Known(UnitType::Length(l2))) if l1 == l2 => {
626 ty = Known(UnitType::Length(*l2));
627 }
628 (Default { angle: a1, .. }, Known(UnitType::Angle(a2))) if a1 == a2 => {
629 ty = Known(UnitType::Angle(*a2));
630 }
631
632 _ => return (result, Unknown),
633 }
634 }
635
636 if ty == Any && !input.is_empty() {
637 ty = input[0].ty;
638 }
639
640 (result, ty)
641 }
642
643 pub fn combine_mul(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
645 use NumericType::*;
646 match (a.ty, b.ty) {
647 (at @ Default { .. }, bt @ Default { .. }) if at == bt => (a.n, b.n, at),
648 (Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
649 (Known(UnitType::Count), bt) => (a.n, b.n, bt),
650 (at, Known(UnitType::Count)) => (a.n, b.n, at),
651 (at @ Known(_), Default { .. }) | (Default { .. }, at @ Known(_)) => (a.n, b.n, at),
652 (Any, Any) => (a.n, b.n, Any),
653 _ => (a.n, b.n, Unknown),
654 }
655 }
656
657 pub fn combine_div(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
659 use NumericType::*;
660 match (a.ty, b.ty) {
661 (at @ Default { .. }, bt @ Default { .. }) if at == bt => (a.n, b.n, at),
662 (at, bt) if at == bt => (a.n, b.n, Known(UnitType::Count)),
663 (Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
664 (at, Known(UnitType::Count) | Any) => (a.n, b.n, at),
665 (at @ Known(_), Default { .. }) => (a.n, b.n, at),
666 (Known(UnitType::Count), _) => (a.n, b.n, Known(UnitType::Count)),
667 _ => (a.n, b.n, Unknown),
668 }
669 }
670
671 pub fn combine_mod(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
673 use NumericType::*;
674 match (a.ty, b.ty) {
675 (at @ Default { .. }, bt @ Default { .. }) if at == bt => (a.n, b.n, at),
676 (at, bt) if at == bt => (a.n, b.n, at),
677 (Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
678 (at, Known(UnitType::Count) | Any) => (a.n, b.n, at),
679 (at @ Known(_), Default { .. }) => (a.n, b.n, at),
680 (Known(UnitType::Count), _) => (a.n, b.n, Known(UnitType::Count)),
681 _ => (a.n, b.n, Unknown),
682 }
683 }
684
685 pub fn from_parsed(suffix: NumericSuffix, settings: &super::MetaSettings) -> Self {
686 match suffix {
687 NumericSuffix::None => NumericType::Default {
688 len: settings.default_length_units,
689 angle: settings.default_angle_units,
690 },
691 NumericSuffix::Count => NumericType::Known(UnitType::Count),
692 NumericSuffix::Length => NumericType::Known(UnitType::GenericLength),
693 NumericSuffix::Angle => NumericType::Known(UnitType::GenericAngle),
694 NumericSuffix::Mm => NumericType::Known(UnitType::Length(UnitLength::Millimeters)),
695 NumericSuffix::Cm => NumericType::Known(UnitType::Length(UnitLength::Centimeters)),
696 NumericSuffix::M => NumericType::Known(UnitType::Length(UnitLength::Meters)),
697 NumericSuffix::Inch => NumericType::Known(UnitType::Length(UnitLength::Inches)),
698 NumericSuffix::Ft => NumericType::Known(UnitType::Length(UnitLength::Feet)),
699 NumericSuffix::Yd => NumericType::Known(UnitType::Length(UnitLength::Yards)),
700 NumericSuffix::Deg => NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
701 NumericSuffix::Rad => NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
702 NumericSuffix::Unknown => NumericType::Unknown,
703 }
704 }
705
706 fn subtype(&self, other: &NumericType) -> bool {
707 use NumericType::*;
708
709 match (self, other) {
710 (_, Any) => true,
711 (a, b) if a == b => true,
712 (
713 NumericType::Known(UnitType::Length(_))
714 | NumericType::Known(UnitType::GenericLength)
715 | NumericType::Default { .. },
716 NumericType::Known(UnitType::GenericLength),
717 )
718 | (
719 NumericType::Known(UnitType::Angle(_))
720 | NumericType::Known(UnitType::GenericAngle)
721 | NumericType::Default { .. },
722 NumericType::Known(UnitType::GenericAngle),
723 ) => true,
724 (Unknown, _) | (_, Unknown) => false,
725 (_, _) => false,
726 }
727 }
728
729 fn is_unknown(&self) -> bool {
730 matches!(
731 self,
732 NumericType::Unknown
733 | NumericType::Known(UnitType::GenericAngle)
734 | NumericType::Known(UnitType::GenericLength)
735 )
736 }
737
738 pub fn is_fully_specified(&self) -> bool {
739 !matches!(
740 self,
741 NumericType::Unknown
742 | NumericType::Known(UnitType::GenericAngle)
743 | NumericType::Known(UnitType::GenericLength)
744 | NumericType::Any
745 | NumericType::Default { .. }
746 )
747 }
748
749 fn example_ty(&self) -> Option<String> {
750 match self {
751 Self::Known(t) if !self.is_unknown() => Some(t.to_string()),
752 Self::Default { len, .. } => Some(len.to_string()),
753 _ => None,
754 }
755 }
756
757 fn coerce(&self, val: &KclValue) -> Result<KclValue, CoercionError> {
758 let KclValue::Number { value, ty, meta } = val else {
759 return Err(val.into());
760 };
761
762 if ty.subtype(self) {
763 return Ok(KclValue::Number {
764 value: *value,
765 ty: *ty,
766 meta: meta.clone(),
767 });
768 }
769
770 use NumericType::*;
772 match (ty, self) {
773 (Unknown, _) => Err(CoercionError::from(val).with_explicit(self.example_ty().unwrap_or("mm".to_owned()))),
775 (_, Unknown) => Err(val.into()),
776
777 (Any, _) => Ok(KclValue::Number {
778 value: *value,
779 ty: *self,
780 meta: meta.clone(),
781 }),
782
783 (_, Default { .. }) => Ok(KclValue::Number {
786 value: *value,
787 ty: *ty,
788 meta: meta.clone(),
789 }),
790
791 (Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => {
793 let (value, ty) = adjust_length(*l1, *value, *l2);
794 Ok(KclValue::Number {
795 value,
796 ty: Known(UnitType::Length(ty)),
797 meta: meta.clone(),
798 })
799 }
800 (Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => {
801 let (value, ty) = adjust_angle(*a1, *value, *a2);
802 Ok(KclValue::Number {
803 value,
804 ty: Known(UnitType::Angle(ty)),
805 meta: meta.clone(),
806 })
807 }
808
809 (Known(_), Known(_)) => Err(val.into()),
811
812 (Default { .. }, Known(UnitType::Count)) => Ok(KclValue::Number {
814 value: *value,
815 ty: Known(UnitType::Count),
816 meta: meta.clone(),
817 }),
818
819 (Default { len: l1, .. }, Known(UnitType::Length(l2))) => {
820 let (value, ty) = adjust_length(*l1, *value, *l2);
821 Ok(KclValue::Number {
822 value,
823 ty: Known(UnitType::Length(ty)),
824 meta: meta.clone(),
825 })
826 }
827
828 (Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => {
829 let (value, ty) = adjust_angle(*a1, *value, *a2);
830 Ok(KclValue::Number {
831 value,
832 ty: Known(UnitType::Angle(ty)),
833 meta: meta.clone(),
834 })
835 }
836
837 (_, _) => unreachable!(),
838 }
839 }
840
841 pub fn as_length(&self) -> Option<UnitLength> {
842 match self {
843 Self::Known(UnitType::Length(len)) | Self::Default { len, .. } => Some(*len),
844 _ => None,
845 }
846 }
847}
848
849impl From<NumericType> for RuntimeType {
850 fn from(t: NumericType) -> RuntimeType {
851 RuntimeType::Primitive(PrimitiveType::Number(t))
852 }
853}
854
855impl From<UnitLength> for NumericType {
856 fn from(value: UnitLength) -> Self {
857 NumericType::Known(UnitType::Length(value))
858 }
859}
860
861impl From<Option<UnitLength>> for NumericType {
862 fn from(value: Option<UnitLength>) -> Self {
863 match value {
864 Some(v) => v.into(),
865 None => NumericType::Unknown,
866 }
867 }
868}
869
870impl From<UnitAngle> for NumericType {
871 fn from(value: UnitAngle) -> Self {
872 NumericType::Known(UnitType::Angle(value))
873 }
874}
875
876#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS)]
877#[ts(export)]
878#[serde(tag = "type")]
879pub enum UnitType {
880 Count,
881 Length(UnitLength),
882 GenericLength,
883 Angle(UnitAngle),
884 GenericAngle,
885}
886
887impl UnitType {
888 pub(crate) fn to_suffix(self) -> Option<String> {
889 match self {
890 UnitType::Count => Some("_".to_owned()),
891 UnitType::GenericLength | UnitType::GenericAngle => None,
892 UnitType::Length(l) => Some(l.to_string()),
893 UnitType::Angle(a) => Some(a.to_string()),
894 }
895 }
896}
897
898impl std::fmt::Display for UnitType {
899 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
900 match self {
901 UnitType::Count => write!(f, "Count"),
902 UnitType::Length(l) => l.fmt(f),
903 UnitType::GenericLength => write!(f, "Length"),
904 UnitType::Angle(a) => a.fmt(f),
905 UnitType::GenericAngle => write!(f, "Angle"),
906 }
907 }
908}
909
910pub fn adjust_length(from: UnitLength, value: f64, to: UnitLength) -> (f64, UnitLength) {
911 use UnitLength::*;
912
913 if from == to {
914 return (value, to);
915 }
916
917 let (base, base_unit) = match from {
918 Millimeters => (value, Millimeters),
919 Centimeters => (value * 10.0, Millimeters),
920 Meters => (value * 1000.0, Millimeters),
921 Inches => (value, Inches),
922 Feet => (value * 12.0, Inches),
923 Yards => (value * 36.0, Inches),
924 };
925 let (base, base_unit) = match (base_unit, to) {
926 (Millimeters, Inches) | (Millimeters, Feet) | (Millimeters, Yards) => (base / 25.4, Inches),
927 (Inches, Millimeters) | (Inches, Centimeters) | (Inches, Meters) => (base * 25.4, Millimeters),
928 _ => (base, base_unit),
929 };
930
931 let value = match (base_unit, to) {
932 (Millimeters, Millimeters) => base,
933 (Millimeters, Centimeters) => base / 10.0,
934 (Millimeters, Meters) => base / 1000.0,
935 (Inches, Inches) => base,
936 (Inches, Feet) => base / 12.0,
937 (Inches, Yards) => base / 36.0,
938 _ => unreachable!(),
939 };
940
941 (value, to)
942}
943
944pub fn adjust_angle(from: UnitAngle, value: f64, to: UnitAngle) -> (f64, UnitAngle) {
945 use std::f64::consts::PI;
946
947 use UnitAngle::*;
948
949 let value = match (from, to) {
950 (Degrees, Degrees) => value,
951 (Degrees, Radians) => (value / 180.0) * PI,
952 (Radians, Degrees) => 180.0 * value / PI,
953 (Radians, Radians) => value,
954 };
955
956 (value, to)
957}
958
959pub(super) fn length_from_str(s: &str, source_range: SourceRange) -> Result<UnitLength, KclError> {
960 match s {
962 "mm" => Ok(UnitLength::Millimeters),
963 "cm" => Ok(UnitLength::Centimeters),
964 "m" => Ok(UnitLength::Meters),
965 "inch" | "in" => Ok(UnitLength::Inches),
966 "ft" => Ok(UnitLength::Feet),
967 "yd" => Ok(UnitLength::Yards),
968 value => Err(KclError::new_semantic(KclErrorDetails::new(
969 format!("Unexpected value for length units: `{value}`; expected one of `mm`, `cm`, `m`, `in`, `ft`, `yd`"),
970 vec![source_range],
971 ))),
972 }
973}
974
975pub(super) fn angle_from_str(s: &str, source_range: SourceRange) -> Result<UnitAngle, KclError> {
976 UnitAngle::from_str(s).map_err(|_| {
977 KclError::new_semantic(KclErrorDetails::new(
978 format!("Unexpected value for angle units: `{s}`; expected one of `deg`, `rad`"),
979 vec![source_range],
980 ))
981 })
982}
983
984#[derive(Debug, Clone)]
985pub struct CoercionError {
986 pub found: Option<RuntimeType>,
987 pub explicit_coercion: Option<String>,
988}
989
990impl CoercionError {
991 fn with_explicit(mut self, c: String) -> Self {
992 self.explicit_coercion = Some(c);
993 self
994 }
995}
996
997impl From<&'_ KclValue> for CoercionError {
998 fn from(value: &'_ KclValue) -> Self {
999 CoercionError {
1000 found: value.principal_type(),
1001 explicit_coercion: None,
1002 }
1003 }
1004}
1005
1006impl KclValue {
1007 pub fn has_type(&self, ty: &RuntimeType) -> bool {
1009 let Some(self_ty) = self.principal_type() else {
1010 return false;
1011 };
1012
1013 self_ty.subtype(ty)
1014 }
1015
1016 pub fn coerce(
1023 &self,
1024 ty: &RuntimeType,
1025 convert_units: bool,
1026 exec_state: &mut ExecState,
1027 ) -> Result<KclValue, CoercionError> {
1028 match self {
1029 KclValue::Tuple { value, .. }
1030 if value.len() == 1
1031 && !matches!(ty, RuntimeType::Primitive(PrimitiveType::Any) | RuntimeType::Tuple(..)) =>
1032 {
1033 if let Ok(coerced) = value[0].coerce(ty, convert_units, exec_state) {
1034 return Ok(coerced);
1035 }
1036 }
1037 KclValue::HomArray { value, .. }
1038 if value.len() == 1
1039 && !matches!(ty, RuntimeType::Primitive(PrimitiveType::Any) | RuntimeType::Array(..)) =>
1040 {
1041 if let Ok(coerced) = value[0].coerce(ty, convert_units, exec_state) {
1042 return Ok(coerced);
1043 }
1044 }
1045 _ => {}
1046 }
1047
1048 match ty {
1049 RuntimeType::Primitive(ty) => self.coerce_to_primitive_type(ty, convert_units, exec_state),
1050 RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, convert_units, *len, exec_state, false),
1051 RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, convert_units, exec_state),
1052 RuntimeType::Union(tys) => self.coerce_to_union_type(tys, convert_units, exec_state),
1053 RuntimeType::Object(tys, constrainable) => {
1054 self.coerce_to_object_type(tys, *constrainable, convert_units, exec_state)
1055 }
1056 }
1057 }
1058
1059 fn coerce_to_primitive_type(
1060 &self,
1061 ty: &PrimitiveType,
1062 convert_units: bool,
1063 exec_state: &mut ExecState,
1064 ) -> Result<KclValue, CoercionError> {
1065 match ty {
1066 PrimitiveType::Any => Ok(self.clone()),
1067 PrimitiveType::None => match self {
1068 KclValue::KclNone { .. } => Ok(self.clone()),
1069 _ => Err(self.into()),
1070 },
1071 PrimitiveType::Number(ty) => {
1072 if convert_units {
1073 return ty.coerce(self);
1074 }
1075
1076 if let KclValue::Number { value: n, meta, .. } = &self
1083 && ty.is_fully_specified()
1084 {
1085 let value = KclValue::Number {
1086 ty: NumericType::Any,
1087 value: *n,
1088 meta: meta.clone(),
1089 };
1090 return ty.coerce(&value);
1091 }
1092 ty.coerce(self)
1093 }
1094 PrimitiveType::String => match self {
1095 KclValue::String { .. } => Ok(self.clone()),
1096 _ => Err(self.into()),
1097 },
1098 PrimitiveType::Boolean => match self {
1099 KclValue::Bool { .. } => Ok(self.clone()),
1100 _ => Err(self.into()),
1101 },
1102 PrimitiveType::Sketch => match self {
1103 KclValue::Sketch { .. } => Ok(self.clone()),
1104 _ => Err(self.into()),
1105 },
1106 PrimitiveType::Solid => match self {
1107 KclValue::Solid { .. } => Ok(self.clone()),
1108 _ => Err(self.into()),
1109 },
1110 PrimitiveType::Plane => {
1111 match self {
1112 KclValue::String { value: s, .. }
1113 if [
1114 "xy", "xz", "yz", "-xy", "-xz", "-yz", "XY", "XZ", "YZ", "-XY", "-XZ", "-YZ",
1115 ]
1116 .contains(&&**s) =>
1117 {
1118 Ok(self.clone())
1119 }
1120 KclValue::Plane { .. } => Ok(self.clone()),
1121 KclValue::Object { value, meta, .. } => {
1122 let origin = value
1123 .get("origin")
1124 .and_then(Point3d::from_kcl_val)
1125 .ok_or(CoercionError::from(self))?;
1126 let x_axis = value
1127 .get("xAxis")
1128 .and_then(Point3d::from_kcl_val)
1129 .ok_or(CoercionError::from(self))?;
1130 let y_axis = value
1131 .get("yAxis")
1132 .and_then(Point3d::from_kcl_val)
1133 .ok_or(CoercionError::from(self))?;
1134 let z_axis = x_axis.axes_cross_product(&y_axis);
1135
1136 if value.get("zAxis").is_some() {
1137 exec_state.warn(CompilationError::err(
1138 self.into(),
1139 "Object with a zAxis field is being coerced into a plane, but the zAxis is ignored.",
1140 ), annotations::WARN_IGNORED_Z_AXIS);
1141 }
1142
1143 let id = exec_state.mod_local.id_generator.next_uuid();
1144 let plane = Plane {
1145 id,
1146 artifact_id: id.into(),
1147 info: PlaneInfo {
1148 origin,
1149 x_axis: x_axis.normalize(),
1150 y_axis: y_axis.normalize(),
1151 z_axis: z_axis.normalize(),
1152 },
1153 value: super::PlaneType::Uninit,
1154 meta: meta.clone(),
1155 };
1156
1157 Ok(KclValue::Plane { value: Box::new(plane) })
1158 }
1159 _ => Err(self.into()),
1160 }
1161 }
1162 PrimitiveType::Face => match self {
1163 KclValue::Face { .. } => Ok(self.clone()),
1164 _ => Err(self.into()),
1165 },
1166 PrimitiveType::Helix => match self {
1167 KclValue::Helix { .. } => Ok(self.clone()),
1168 _ => Err(self.into()),
1169 },
1170 PrimitiveType::Edge => match self {
1171 KclValue::Uuid { .. } => Ok(self.clone()),
1172 KclValue::TagIdentifier { .. } => Ok(self.clone()),
1173 _ => Err(self.into()),
1174 },
1175 PrimitiveType::TaggedEdge => match self {
1176 KclValue::TagIdentifier { .. } => Ok(self.clone()),
1177 _ => Err(self.into()),
1178 },
1179 PrimitiveType::TaggedFace => match self {
1180 KclValue::TagIdentifier { .. } => Ok(self.clone()),
1181 s @ KclValue::String { value, .. } if ["start", "end", "START", "END"].contains(&&**value) => {
1182 Ok(s.clone())
1183 }
1184 _ => Err(self.into()),
1185 },
1186 PrimitiveType::Axis2d => match self {
1187 KclValue::Object {
1188 value: values, meta, ..
1189 } => {
1190 if values
1191 .get("origin")
1192 .ok_or(CoercionError::from(self))?
1193 .has_type(&RuntimeType::point2d())
1194 && values
1195 .get("direction")
1196 .ok_or(CoercionError::from(self))?
1197 .has_type(&RuntimeType::point2d())
1198 {
1199 return Ok(self.clone());
1200 }
1201
1202 let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
1203 p.coerce_to_array_type(
1204 &RuntimeType::length(),
1205 convert_units,
1206 ArrayLen::Known(2),
1207 exec_state,
1208 true,
1209 )
1210 })?;
1211 let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
1212 p.coerce_to_array_type(
1213 &RuntimeType::length(),
1214 convert_units,
1215 ArrayLen::Known(2),
1216 exec_state,
1217 true,
1218 )
1219 })?;
1220
1221 Ok(KclValue::Object {
1222 value: [("origin".to_owned(), origin), ("direction".to_owned(), direction)].into(),
1223 meta: meta.clone(),
1224 constrainable: false,
1225 })
1226 }
1227 _ => Err(self.into()),
1228 },
1229 PrimitiveType::Axis3d => match self {
1230 KclValue::Object {
1231 value: values, meta, ..
1232 } => {
1233 if values
1234 .get("origin")
1235 .ok_or(CoercionError::from(self))?
1236 .has_type(&RuntimeType::point3d())
1237 && values
1238 .get("direction")
1239 .ok_or(CoercionError::from(self))?
1240 .has_type(&RuntimeType::point3d())
1241 {
1242 return Ok(self.clone());
1243 }
1244
1245 let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
1246 p.coerce_to_array_type(
1247 &RuntimeType::length(),
1248 convert_units,
1249 ArrayLen::Known(3),
1250 exec_state,
1251 true,
1252 )
1253 })?;
1254 let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
1255 p.coerce_to_array_type(
1256 &RuntimeType::length(),
1257 convert_units,
1258 ArrayLen::Known(3),
1259 exec_state,
1260 true,
1261 )
1262 })?;
1263
1264 Ok(KclValue::Object {
1265 value: [("origin".to_owned(), origin), ("direction".to_owned(), direction)].into(),
1266 meta: meta.clone(),
1267 constrainable: false,
1268 })
1269 }
1270 _ => Err(self.into()),
1271 },
1272 PrimitiveType::ImportedGeometry => match self {
1273 KclValue::ImportedGeometry { .. } => Ok(self.clone()),
1274 _ => Err(self.into()),
1275 },
1276 PrimitiveType::Function => match self {
1277 KclValue::Function { .. } => Ok(self.clone()),
1278 _ => Err(self.into()),
1279 },
1280 PrimitiveType::TagDecl => match self {
1281 KclValue::TagDeclarator { .. } => Ok(self.clone()),
1282 _ => Err(self.into()),
1283 },
1284 }
1285 }
1286
1287 fn coerce_to_array_type(
1288 &self,
1289 ty: &RuntimeType,
1290 convert_units: bool,
1291 len: ArrayLen,
1292 exec_state: &mut ExecState,
1293 allow_shrink: bool,
1294 ) -> Result<KclValue, CoercionError> {
1295 match self {
1296 KclValue::HomArray { value, ty: aty, .. } => {
1297 let satisfied_len = len.satisfied(value.len(), allow_shrink);
1298
1299 if aty.subtype(ty) {
1300 return satisfied_len
1307 .map(|len| KclValue::HomArray {
1308 value: value[..len].to_vec(),
1309 ty: aty.clone(),
1310 })
1311 .ok_or(self.into());
1312 }
1313
1314 if let Some(satisfied_len) = satisfied_len {
1316 let value_result = value
1317 .iter()
1318 .take(satisfied_len)
1319 .map(|v| v.coerce(ty, convert_units, exec_state))
1320 .collect::<Result<Vec<_>, _>>();
1321
1322 if let Ok(value) = value_result {
1323 return Ok(KclValue::HomArray { value, ty: ty.clone() });
1325 }
1326 }
1327
1328 let mut values = Vec::new();
1330 for item in value {
1331 if let KclValue::HomArray { value: inner_value, .. } = item {
1332 for item in inner_value {
1334 values.push(item.coerce(ty, convert_units, exec_state)?);
1335 }
1336 } else {
1337 values.push(item.coerce(ty, convert_units, exec_state)?);
1338 }
1339 }
1340
1341 let len = len
1342 .satisfied(values.len(), allow_shrink)
1343 .ok_or(CoercionError::from(self))?;
1344
1345 if len > values.len() {
1346 let message = format!(
1347 "Internal: Expected coerced array length {len} to be less than or equal to original length {}",
1348 values.len()
1349 );
1350 exec_state.err(CompilationError::err(self.into(), message.clone()));
1351 #[cfg(debug_assertions)]
1352 panic!("{message}");
1353 }
1354 values.truncate(len);
1355
1356 Ok(KclValue::HomArray {
1357 value: values,
1358 ty: ty.clone(),
1359 })
1360 }
1361 KclValue::Tuple { value, .. } => {
1362 let len = len
1363 .satisfied(value.len(), allow_shrink)
1364 .ok_or(CoercionError::from(self))?;
1365 let value = value
1366 .iter()
1367 .map(|item| item.coerce(ty, convert_units, exec_state))
1368 .take(len)
1369 .collect::<Result<Vec<_>, _>>()?;
1370
1371 Ok(KclValue::HomArray { value, ty: ty.clone() })
1372 }
1373 KclValue::KclNone { .. } if len.satisfied(0, false).is_some() => Ok(KclValue::HomArray {
1374 value: Vec::new(),
1375 ty: ty.clone(),
1376 }),
1377 _ if len.satisfied(1, false).is_some() => self.coerce(ty, convert_units, exec_state),
1378 _ => Err(self.into()),
1379 }
1380 }
1381
1382 fn coerce_to_tuple_type(
1383 &self,
1384 tys: &[RuntimeType],
1385 convert_units: bool,
1386 exec_state: &mut ExecState,
1387 ) -> Result<KclValue, CoercionError> {
1388 match self {
1389 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } if value.len() == tys.len() => {
1390 let mut result = Vec::new();
1391 for (i, t) in tys.iter().enumerate() {
1392 result.push(value[i].coerce(t, convert_units, exec_state)?);
1393 }
1394
1395 Ok(KclValue::Tuple {
1396 value: result,
1397 meta: Vec::new(),
1398 })
1399 }
1400 KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::Tuple {
1401 value: Vec::new(),
1402 meta: meta.clone(),
1403 }),
1404 _ if tys.len() == 1 => self.coerce(&tys[0], convert_units, exec_state),
1405 _ => Err(self.into()),
1406 }
1407 }
1408
1409 fn coerce_to_union_type(
1410 &self,
1411 tys: &[RuntimeType],
1412 convert_units: bool,
1413 exec_state: &mut ExecState,
1414 ) -> Result<KclValue, CoercionError> {
1415 for t in tys {
1416 if let Ok(v) = self.coerce(t, convert_units, exec_state) {
1417 return Ok(v);
1418 }
1419 }
1420
1421 Err(self.into())
1422 }
1423
1424 fn coerce_to_object_type(
1425 &self,
1426 tys: &[(String, RuntimeType)],
1427 constrainable: bool,
1428 _convert_units: bool,
1429 _exec_state: &mut ExecState,
1430 ) -> Result<KclValue, CoercionError> {
1431 match self {
1432 KclValue::Object { value, meta, .. } => {
1433 for (s, t) in tys {
1434 if !value.get(s).ok_or(CoercionError::from(self))?.has_type(t) {
1436 return Err(self.into());
1437 }
1438 }
1439 Ok(KclValue::Object {
1441 value: value.clone(),
1442 meta: meta.clone(),
1443 constrainable,
1446 })
1447 }
1448 KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::Object {
1449 value: HashMap::new(),
1450 meta: meta.clone(),
1451 constrainable,
1452 }),
1453 _ => Err(self.into()),
1454 }
1455 }
1456
1457 pub fn principal_type(&self) -> Option<RuntimeType> {
1458 match self {
1459 KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
1460 KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(*ty))),
1461 KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)),
1462 KclValue::Object {
1463 value, constrainable, ..
1464 } => {
1465 let properties = value
1466 .iter()
1467 .map(|(k, v)| v.principal_type().map(|t| (k.clone(), t)))
1468 .collect::<Option<Vec<_>>>()?;
1469 Some(RuntimeType::Object(properties, *constrainable))
1470 }
1471 KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
1472 KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
1473 KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
1474 KclValue::Face { .. } => Some(RuntimeType::Primitive(PrimitiveType::Face)),
1475 KclValue::Helix { .. } => Some(RuntimeType::Primitive(PrimitiveType::Helix)),
1476 KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
1477 KclValue::Tuple { value, .. } => Some(RuntimeType::Tuple(
1478 value.iter().map(|v| v.principal_type()).collect::<Option<Vec<_>>>()?,
1479 )),
1480 KclValue::HomArray { ty, value, .. } => {
1481 Some(RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(value.len())))
1482 }
1483 KclValue::TagIdentifier(_) => Some(RuntimeType::Primitive(PrimitiveType::TaggedEdge)),
1484 KclValue::TagDeclarator(_) => Some(RuntimeType::Primitive(PrimitiveType::TagDecl)),
1485 KclValue::Uuid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Edge)),
1486 KclValue::Function { .. } => Some(RuntimeType::Primitive(PrimitiveType::Function)),
1487 KclValue::KclNone { .. } => Some(RuntimeType::Primitive(PrimitiveType::None)),
1488 KclValue::Module { .. } | KclValue::Type { .. } => None,
1489 }
1490 }
1491
1492 pub fn principal_type_string(&self) -> String {
1493 if let Some(ty) = self.principal_type() {
1494 return format!("`{ty}`");
1495 }
1496
1497 match self {
1498 KclValue::Module { .. } => "module",
1499 KclValue::KclNone { .. } => "none",
1500 KclValue::Type { .. } => "type",
1501 _ => {
1502 debug_assert!(false);
1503 "<unexpected type>"
1504 }
1505 }
1506 .to_owned()
1507 }
1508}
1509
1510#[cfg(test)]
1511mod test {
1512 use super::*;
1513 use crate::execution::{ExecTestResults, parse_execute};
1514
1515 fn values(exec_state: &mut ExecState) -> Vec<KclValue> {
1516 vec![
1517 KclValue::Bool {
1518 value: true,
1519 meta: Vec::new(),
1520 },
1521 KclValue::Number {
1522 value: 1.0,
1523 ty: NumericType::count(),
1524 meta: Vec::new(),
1525 },
1526 KclValue::String {
1527 value: "hello".to_owned(),
1528 meta: Vec::new(),
1529 },
1530 KclValue::Tuple {
1531 value: Vec::new(),
1532 meta: Vec::new(),
1533 },
1534 KclValue::HomArray {
1535 value: Vec::new(),
1536 ty: RuntimeType::solid(),
1537 },
1538 KclValue::Object {
1539 value: crate::execution::KclObjectFields::new(),
1540 meta: Vec::new(),
1541 constrainable: false,
1542 },
1543 KclValue::TagIdentifier(Box::new("foo".parse().unwrap())),
1544 KclValue::TagDeclarator(Box::new(crate::parsing::ast::types::TagDeclarator::new("foo"))),
1545 KclValue::Plane {
1546 value: Box::new(Plane::from_plane_data(crate::std::sketch::PlaneData::XY, exec_state).unwrap()),
1547 },
1548 KclValue::ImportedGeometry(crate::execution::ImportedGeometry::new(
1550 uuid::Uuid::nil(),
1551 Vec::new(),
1552 Vec::new(),
1553 )),
1554 ]
1556 }
1557
1558 #[track_caller]
1559 fn assert_coerce_results(
1560 value: &KclValue,
1561 super_type: &RuntimeType,
1562 expected_value: &KclValue,
1563 exec_state: &mut ExecState,
1564 ) {
1565 let is_subtype = value == expected_value;
1566 let actual = value.coerce(super_type, true, exec_state).unwrap();
1567 assert_eq!(&actual, expected_value);
1568 assert_eq!(
1569 is_subtype,
1570 value.principal_type().is_some() && value.principal_type().unwrap().subtype(super_type),
1571 "{:?} <: {super_type:?} should be {is_subtype}",
1572 value.principal_type().unwrap()
1573 );
1574 assert!(
1575 expected_value.principal_type().unwrap().subtype(super_type),
1576 "{} <: {super_type}",
1577 expected_value.principal_type().unwrap()
1578 )
1579 }
1580
1581 #[tokio::test(flavor = "multi_thread")]
1582 async fn coerce_idempotent() {
1583 let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1584 let values = values(&mut exec_state);
1585 for v in &values {
1586 let ty = v.principal_type().unwrap();
1588 assert_coerce_results(v, &ty, v, &mut exec_state);
1589
1590 let uty1 = RuntimeType::Union(vec![ty.clone()]);
1592 let uty2 = RuntimeType::Union(vec![ty.clone(), RuntimeType::Primitive(PrimitiveType::Boolean)]);
1593 assert_coerce_results(v, &uty1, v, &mut exec_state);
1594 assert_coerce_results(v, &uty2, v, &mut exec_state);
1595
1596 let aty = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::None);
1598 let aty1 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(1));
1599 let aty0 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Minimum(1));
1600
1601 match v {
1602 KclValue::HomArray { .. } => {
1603 assert_coerce_results(
1605 v,
1606 &aty,
1607 &KclValue::HomArray {
1608 value: vec![],
1609 ty: ty.clone(),
1610 },
1611 &mut exec_state,
1612 );
1613 v.coerce(&aty1, true, &mut exec_state).unwrap_err();
1616 v.coerce(&aty0, true, &mut exec_state).unwrap_err();
1619 }
1620 KclValue::Tuple { .. } => {}
1621 _ => {
1622 assert_coerce_results(v, &aty, v, &mut exec_state);
1623 assert_coerce_results(v, &aty1, v, &mut exec_state);
1624 assert_coerce_results(v, &aty0, v, &mut exec_state);
1625
1626 let tty = RuntimeType::Tuple(vec![ty.clone()]);
1628 assert_coerce_results(v, &tty, v, &mut exec_state);
1629 }
1630 }
1631 }
1632
1633 for v in &values[1..] {
1634 v.coerce(&RuntimeType::Primitive(PrimitiveType::Boolean), true, &mut exec_state)
1636 .unwrap_err();
1637 }
1638 }
1639
1640 #[tokio::test(flavor = "multi_thread")]
1641 async fn coerce_none() {
1642 let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1643 let none = KclValue::KclNone {
1644 value: crate::parsing::ast::types::KclNone::new(),
1645 meta: Vec::new(),
1646 };
1647
1648 let aty = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::None);
1649 let aty0 = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::Known(0));
1650 let aty1 = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::Known(1));
1651 let aty1p = RuntimeType::Array(Box::new(RuntimeType::solid()), ArrayLen::Minimum(1));
1652 assert_coerce_results(
1653 &none,
1654 &aty,
1655 &KclValue::HomArray {
1656 value: Vec::new(),
1657 ty: RuntimeType::solid(),
1658 },
1659 &mut exec_state,
1660 );
1661 assert_coerce_results(
1662 &none,
1663 &aty0,
1664 &KclValue::HomArray {
1665 value: Vec::new(),
1666 ty: RuntimeType::solid(),
1667 },
1668 &mut exec_state,
1669 );
1670 none.coerce(&aty1, true, &mut exec_state).unwrap_err();
1671 none.coerce(&aty1p, true, &mut exec_state).unwrap_err();
1672
1673 let tty = RuntimeType::Tuple(vec![]);
1674 let tty1 = RuntimeType::Tuple(vec![RuntimeType::solid()]);
1675 assert_coerce_results(
1676 &none,
1677 &tty,
1678 &KclValue::Tuple {
1679 value: Vec::new(),
1680 meta: Vec::new(),
1681 },
1682 &mut exec_state,
1683 );
1684 none.coerce(&tty1, true, &mut exec_state).unwrap_err();
1685
1686 let oty = RuntimeType::Object(vec![], false);
1687 assert_coerce_results(
1688 &none,
1689 &oty,
1690 &KclValue::Object {
1691 value: HashMap::new(),
1692 meta: Vec::new(),
1693 constrainable: false,
1694 },
1695 &mut exec_state,
1696 );
1697 }
1698
1699 #[tokio::test(flavor = "multi_thread")]
1700 async fn coerce_record() {
1701 let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1702
1703 let obj0 = KclValue::Object {
1704 value: HashMap::new(),
1705 meta: Vec::new(),
1706 constrainable: false,
1707 };
1708 let obj1 = KclValue::Object {
1709 value: [(
1710 "foo".to_owned(),
1711 KclValue::Bool {
1712 value: true,
1713 meta: Vec::new(),
1714 },
1715 )]
1716 .into(),
1717 meta: Vec::new(),
1718 constrainable: false,
1719 };
1720 let obj2 = KclValue::Object {
1721 value: [
1722 (
1723 "foo".to_owned(),
1724 KclValue::Bool {
1725 value: true,
1726 meta: Vec::new(),
1727 },
1728 ),
1729 (
1730 "bar".to_owned(),
1731 KclValue::Number {
1732 value: 0.0,
1733 ty: NumericType::count(),
1734 meta: Vec::new(),
1735 },
1736 ),
1737 (
1738 "baz".to_owned(),
1739 KclValue::Number {
1740 value: 42.0,
1741 ty: NumericType::count(),
1742 meta: Vec::new(),
1743 },
1744 ),
1745 ]
1746 .into(),
1747 meta: Vec::new(),
1748 constrainable: false,
1749 };
1750
1751 let ty0 = RuntimeType::Object(vec![], false);
1752 assert_coerce_results(&obj0, &ty0, &obj0, &mut exec_state);
1753 assert_coerce_results(&obj1, &ty0, &obj1, &mut exec_state);
1754 assert_coerce_results(&obj2, &ty0, &obj2, &mut exec_state);
1755
1756 let ty1 = RuntimeType::Object(
1757 vec![("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))],
1758 false,
1759 );
1760 obj0.coerce(&ty1, true, &mut exec_state).unwrap_err();
1761 assert_coerce_results(&obj1, &ty1, &obj1, &mut exec_state);
1762 assert_coerce_results(&obj2, &ty1, &obj2, &mut exec_state);
1763
1764 let ty2 = RuntimeType::Object(
1766 vec![
1767 (
1768 "bar".to_owned(),
1769 RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1770 ),
1771 ("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean)),
1772 ],
1773 false,
1774 );
1775 obj0.coerce(&ty2, true, &mut exec_state).unwrap_err();
1776 obj1.coerce(&ty2, true, &mut exec_state).unwrap_err();
1777 assert_coerce_results(&obj2, &ty2, &obj2, &mut exec_state);
1778
1779 let tyq = RuntimeType::Object(
1781 vec![("qux".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))],
1782 false,
1783 );
1784 obj0.coerce(&tyq, true, &mut exec_state).unwrap_err();
1785 obj1.coerce(&tyq, true, &mut exec_state).unwrap_err();
1786 obj2.coerce(&tyq, true, &mut exec_state).unwrap_err();
1787
1788 let ty1 = RuntimeType::Object(
1790 vec![("bar".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))],
1791 false,
1792 );
1793 obj2.coerce(&ty1, true, &mut exec_state).unwrap_err();
1794 }
1795
1796 #[tokio::test(flavor = "multi_thread")]
1797 async fn coerce_array() {
1798 let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1799
1800 let hom_arr = KclValue::HomArray {
1801 value: vec![
1802 KclValue::Number {
1803 value: 0.0,
1804 ty: NumericType::count(),
1805 meta: Vec::new(),
1806 },
1807 KclValue::Number {
1808 value: 1.0,
1809 ty: NumericType::count(),
1810 meta: Vec::new(),
1811 },
1812 KclValue::Number {
1813 value: 2.0,
1814 ty: NumericType::count(),
1815 meta: Vec::new(),
1816 },
1817 KclValue::Number {
1818 value: 3.0,
1819 ty: NumericType::count(),
1820 meta: Vec::new(),
1821 },
1822 ],
1823 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1824 };
1825 let mixed1 = KclValue::Tuple {
1826 value: vec![
1827 KclValue::Number {
1828 value: 0.0,
1829 ty: NumericType::count(),
1830 meta: Vec::new(),
1831 },
1832 KclValue::Number {
1833 value: 1.0,
1834 ty: NumericType::count(),
1835 meta: Vec::new(),
1836 },
1837 ],
1838 meta: Vec::new(),
1839 };
1840 let mixed2 = KclValue::Tuple {
1841 value: vec![
1842 KclValue::Number {
1843 value: 0.0,
1844 ty: NumericType::count(),
1845 meta: Vec::new(),
1846 },
1847 KclValue::Bool {
1848 value: true,
1849 meta: Vec::new(),
1850 },
1851 ],
1852 meta: Vec::new(),
1853 };
1854
1855 let tyh = RuntimeType::Array(
1857 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
1858 ArrayLen::Known(4),
1859 );
1860 let tym1 = RuntimeType::Tuple(vec![
1861 RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1862 RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1863 ]);
1864 let tym2 = RuntimeType::Tuple(vec![
1865 RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1866 RuntimeType::Primitive(PrimitiveType::Boolean),
1867 ]);
1868 assert_coerce_results(&hom_arr, &tyh, &hom_arr, &mut exec_state);
1869 assert_coerce_results(&mixed1, &tym1, &mixed1, &mut exec_state);
1870 assert_coerce_results(&mixed2, &tym2, &mixed2, &mut exec_state);
1871 mixed1.coerce(&tym2, true, &mut exec_state).unwrap_err();
1872 mixed2.coerce(&tym1, true, &mut exec_state).unwrap_err();
1873
1874 let tyhn = RuntimeType::Array(
1876 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
1877 ArrayLen::None,
1878 );
1879 let tyh1 = RuntimeType::Array(
1880 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
1881 ArrayLen::Minimum(1),
1882 );
1883 let tyh3 = RuntimeType::Array(
1884 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
1885 ArrayLen::Known(3),
1886 );
1887 let tyhm3 = RuntimeType::Array(
1888 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
1889 ArrayLen::Minimum(3),
1890 );
1891 let tyhm5 = RuntimeType::Array(
1892 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
1893 ArrayLen::Minimum(5),
1894 );
1895 assert_coerce_results(&hom_arr, &tyhn, &hom_arr, &mut exec_state);
1896 assert_coerce_results(&hom_arr, &tyh1, &hom_arr, &mut exec_state);
1897 hom_arr.coerce(&tyh3, true, &mut exec_state).unwrap_err();
1898 assert_coerce_results(&hom_arr, &tyhm3, &hom_arr, &mut exec_state);
1899 hom_arr.coerce(&tyhm5, true, &mut exec_state).unwrap_err();
1900
1901 let hom_arr0 = KclValue::HomArray {
1902 value: vec![],
1903 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1904 };
1905 assert_coerce_results(&hom_arr0, &tyhn, &hom_arr0, &mut exec_state);
1906 hom_arr0.coerce(&tyh1, true, &mut exec_state).unwrap_err();
1907 hom_arr0.coerce(&tyh3, true, &mut exec_state).unwrap_err();
1908
1909 let tym1 = RuntimeType::Tuple(vec![
1912 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1913 RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1914 ]);
1915 let tym2 = RuntimeType::Tuple(vec![
1916 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1917 RuntimeType::Primitive(PrimitiveType::Boolean),
1918 ]);
1919 assert_coerce_results(&mixed1, &tym1, &mixed1, &mut exec_state);
1922 assert_coerce_results(&mixed2, &tym2, &mixed2, &mut exec_state);
1923
1924 let hom_arr_2 = KclValue::HomArray {
1926 value: vec![
1927 KclValue::Number {
1928 value: 0.0,
1929 ty: NumericType::count(),
1930 meta: Vec::new(),
1931 },
1932 KclValue::Number {
1933 value: 1.0,
1934 ty: NumericType::count(),
1935 meta: Vec::new(),
1936 },
1937 ],
1938 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
1939 };
1940 let mixed0 = KclValue::Tuple {
1941 value: vec![],
1942 meta: Vec::new(),
1943 };
1944 assert_coerce_results(&mixed1, &tyhn, &hom_arr_2, &mut exec_state);
1945 assert_coerce_results(&mixed1, &tyh1, &hom_arr_2, &mut exec_state);
1946 assert_coerce_results(&mixed0, &tyhn, &hom_arr0, &mut exec_state);
1947 mixed0.coerce(&tyh, true, &mut exec_state).unwrap_err();
1948 mixed0.coerce(&tyh1, true, &mut exec_state).unwrap_err();
1949
1950 assert_coerce_results(&hom_arr_2, &tym1, &mixed1, &mut exec_state);
1952 hom_arr.coerce(&tym1, true, &mut exec_state).unwrap_err();
1953 hom_arr_2.coerce(&tym2, true, &mut exec_state).unwrap_err();
1954
1955 mixed0.coerce(&tym1, true, &mut exec_state).unwrap_err();
1956 mixed0.coerce(&tym2, true, &mut exec_state).unwrap_err();
1957 }
1958
1959 #[tokio::test(flavor = "multi_thread")]
1960 async fn coerce_union() {
1961 let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
1962
1963 assert!(RuntimeType::Union(vec![]).subtype(&RuntimeType::Union(vec![
1965 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1966 RuntimeType::Primitive(PrimitiveType::Boolean)
1967 ])));
1968 assert!(
1969 RuntimeType::Union(vec![RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))]).subtype(
1970 &RuntimeType::Union(vec![
1971 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1972 RuntimeType::Primitive(PrimitiveType::Boolean)
1973 ])
1974 )
1975 );
1976 assert!(
1977 RuntimeType::Union(vec![
1978 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1979 RuntimeType::Primitive(PrimitiveType::Boolean)
1980 ])
1981 .subtype(&RuntimeType::Union(vec![
1982 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1983 RuntimeType::Primitive(PrimitiveType::Boolean)
1984 ]))
1985 );
1986
1987 let count = KclValue::Number {
1989 value: 1.0,
1990 ty: NumericType::count(),
1991 meta: Vec::new(),
1992 };
1993
1994 let tya = RuntimeType::Union(vec![RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))]);
1995 let tya2 = RuntimeType::Union(vec![
1996 RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any)),
1997 RuntimeType::Primitive(PrimitiveType::Boolean),
1998 ]);
1999 assert_coerce_results(&count, &tya, &count, &mut exec_state);
2000 assert_coerce_results(&count, &tya2, &count, &mut exec_state);
2001
2002 let tyb = RuntimeType::Union(vec![RuntimeType::Primitive(PrimitiveType::Boolean)]);
2004 let tyb2 = RuntimeType::Union(vec![
2005 RuntimeType::Primitive(PrimitiveType::Boolean),
2006 RuntimeType::Primitive(PrimitiveType::String),
2007 ]);
2008 count.coerce(&tyb, true, &mut exec_state).unwrap_err();
2009 count.coerce(&tyb2, true, &mut exec_state).unwrap_err();
2010 }
2011
2012 #[tokio::test(flavor = "multi_thread")]
2013 async fn coerce_axes() {
2014 let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
2015
2016 assert!(RuntimeType::Primitive(PrimitiveType::Axis2d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis2d)));
2018 assert!(RuntimeType::Primitive(PrimitiveType::Axis3d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis3d)));
2019 assert!(!RuntimeType::Primitive(PrimitiveType::Axis3d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis2d)));
2020 assert!(!RuntimeType::Primitive(PrimitiveType::Axis2d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis3d)));
2021
2022 let a2d = KclValue::Object {
2024 value: [
2025 (
2026 "origin".to_owned(),
2027 KclValue::HomArray {
2028 value: vec![
2029 KclValue::Number {
2030 value: 0.0,
2031 ty: NumericType::mm(),
2032 meta: Vec::new(),
2033 },
2034 KclValue::Number {
2035 value: 0.0,
2036 ty: NumericType::mm(),
2037 meta: Vec::new(),
2038 },
2039 ],
2040 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
2041 },
2042 ),
2043 (
2044 "direction".to_owned(),
2045 KclValue::HomArray {
2046 value: vec![
2047 KclValue::Number {
2048 value: 1.0,
2049 ty: NumericType::mm(),
2050 meta: Vec::new(),
2051 },
2052 KclValue::Number {
2053 value: 0.0,
2054 ty: NumericType::mm(),
2055 meta: Vec::new(),
2056 },
2057 ],
2058 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
2059 },
2060 ),
2061 ]
2062 .into(),
2063 meta: Vec::new(),
2064 constrainable: false,
2065 };
2066 let a3d = KclValue::Object {
2067 value: [
2068 (
2069 "origin".to_owned(),
2070 KclValue::HomArray {
2071 value: vec![
2072 KclValue::Number {
2073 value: 0.0,
2074 ty: NumericType::mm(),
2075 meta: Vec::new(),
2076 },
2077 KclValue::Number {
2078 value: 0.0,
2079 ty: NumericType::mm(),
2080 meta: Vec::new(),
2081 },
2082 KclValue::Number {
2083 value: 0.0,
2084 ty: NumericType::mm(),
2085 meta: Vec::new(),
2086 },
2087 ],
2088 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
2089 },
2090 ),
2091 (
2092 "direction".to_owned(),
2093 KclValue::HomArray {
2094 value: vec![
2095 KclValue::Number {
2096 value: 1.0,
2097 ty: NumericType::mm(),
2098 meta: Vec::new(),
2099 },
2100 KclValue::Number {
2101 value: 0.0,
2102 ty: NumericType::mm(),
2103 meta: Vec::new(),
2104 },
2105 KclValue::Number {
2106 value: 1.0,
2107 ty: NumericType::mm(),
2108 meta: Vec::new(),
2109 },
2110 ],
2111 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
2112 },
2113 ),
2114 ]
2115 .into(),
2116 meta: Vec::new(),
2117 constrainable: false,
2118 };
2119
2120 let ty2d = RuntimeType::Primitive(PrimitiveType::Axis2d);
2121 let ty3d = RuntimeType::Primitive(PrimitiveType::Axis3d);
2122
2123 assert_coerce_results(&a2d, &ty2d, &a2d, &mut exec_state);
2124 assert_coerce_results(&a3d, &ty3d, &a3d, &mut exec_state);
2125 assert_coerce_results(&a3d, &ty2d, &a2d, &mut exec_state);
2126 a2d.coerce(&ty3d, true, &mut exec_state).unwrap_err();
2127 }
2128
2129 #[tokio::test(flavor = "multi_thread")]
2130 async fn coerce_numeric() {
2131 let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
2132
2133 let count = KclValue::Number {
2134 value: 1.0,
2135 ty: NumericType::count(),
2136 meta: Vec::new(),
2137 };
2138 let mm = KclValue::Number {
2139 value: 1.0,
2140 ty: NumericType::mm(),
2141 meta: Vec::new(),
2142 };
2143 let inches = KclValue::Number {
2144 value: 1.0,
2145 ty: NumericType::Known(UnitType::Length(UnitLength::Inches)),
2146 meta: Vec::new(),
2147 };
2148 let rads = KclValue::Number {
2149 value: 1.0,
2150 ty: NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
2151 meta: Vec::new(),
2152 };
2153 let default = KclValue::Number {
2154 value: 1.0,
2155 ty: NumericType::default(),
2156 meta: Vec::new(),
2157 };
2158 let any = KclValue::Number {
2159 value: 1.0,
2160 ty: NumericType::Any,
2161 meta: Vec::new(),
2162 };
2163 let unknown = KclValue::Number {
2164 value: 1.0,
2165 ty: NumericType::Unknown,
2166 meta: Vec::new(),
2167 };
2168
2169 assert_coerce_results(&count, &NumericType::count().into(), &count, &mut exec_state);
2171 assert_coerce_results(&mm, &NumericType::mm().into(), &mm, &mut exec_state);
2172 assert_coerce_results(&any, &NumericType::Any.into(), &any, &mut exec_state);
2173 assert_coerce_results(&unknown, &NumericType::Unknown.into(), &unknown, &mut exec_state);
2174 assert_coerce_results(&default, &NumericType::default().into(), &default, &mut exec_state);
2175
2176 assert_coerce_results(&count, &NumericType::Any.into(), &count, &mut exec_state);
2177 assert_coerce_results(&mm, &NumericType::Any.into(), &mm, &mut exec_state);
2178 assert_coerce_results(&unknown, &NumericType::Any.into(), &unknown, &mut exec_state);
2179 assert_coerce_results(&default, &NumericType::Any.into(), &default, &mut exec_state);
2180
2181 assert_eq!(
2182 default
2183 .coerce(
2184 &NumericType::Default {
2185 len: UnitLength::Yards,
2186 angle: UnitAngle::Degrees,
2187 }
2188 .into(),
2189 true,
2190 &mut exec_state
2191 )
2192 .unwrap(),
2193 default
2194 );
2195
2196 count
2198 .coerce(&NumericType::mm().into(), true, &mut exec_state)
2199 .unwrap_err();
2200 mm.coerce(&NumericType::count().into(), true, &mut exec_state)
2201 .unwrap_err();
2202 unknown
2203 .coerce(&NumericType::mm().into(), true, &mut exec_state)
2204 .unwrap_err();
2205 unknown
2206 .coerce(&NumericType::default().into(), true, &mut exec_state)
2207 .unwrap_err();
2208
2209 count
2210 .coerce(&NumericType::Unknown.into(), true, &mut exec_state)
2211 .unwrap_err();
2212 mm.coerce(&NumericType::Unknown.into(), true, &mut exec_state)
2213 .unwrap_err();
2214 default
2215 .coerce(&NumericType::Unknown.into(), true, &mut exec_state)
2216 .unwrap_err();
2217
2218 assert_eq!(
2219 inches
2220 .coerce(&NumericType::mm().into(), true, &mut exec_state)
2221 .unwrap()
2222 .as_f64()
2223 .unwrap()
2224 .round(),
2225 25.0
2226 );
2227 assert_eq!(
2228 rads.coerce(
2229 &NumericType::Known(UnitType::Angle(UnitAngle::Degrees)).into(),
2230 true,
2231 &mut exec_state
2232 )
2233 .unwrap()
2234 .as_f64()
2235 .unwrap()
2236 .round(),
2237 57.0
2238 );
2239 assert_eq!(
2240 inches
2241 .coerce(&NumericType::default().into(), true, &mut exec_state)
2242 .unwrap()
2243 .as_f64()
2244 .unwrap()
2245 .round(),
2246 1.0
2247 );
2248 assert_eq!(
2249 rads.coerce(&NumericType::default().into(), true, &mut exec_state)
2250 .unwrap()
2251 .as_f64()
2252 .unwrap()
2253 .round(),
2254 1.0
2255 );
2256 }
2257
2258 #[track_caller]
2259 fn assert_value_and_type(name: &str, result: &ExecTestResults, expected: f64, expected_ty: NumericType) {
2260 let mem = result.exec_state.stack();
2261 match mem
2262 .memory
2263 .get_from(name, result.mem_env, SourceRange::default(), 0)
2264 .unwrap()
2265 {
2266 KclValue::Number { value, ty, .. } => {
2267 assert_eq!(value.round(), expected);
2268 assert_eq!(*ty, expected_ty);
2269 }
2270 _ => unreachable!(),
2271 }
2272 }
2273
2274 #[tokio::test(flavor = "multi_thread")]
2275 async fn combine_numeric() {
2276 let program = r#"a = 5 + 4
2277b = 5 - 2
2278c = 5mm - 2mm + 10mm
2279d = 5mm - 2 + 10
2280e = 5 - 2mm + 10
2281f = 30mm - 1inch
2282
2283g = 2 * 10
2284h = 2 * 10mm
2285i = 2mm * 10mm
2286j = 2_ * 10
2287k = 2_ * 3mm * 3mm
2288
2289l = 1 / 10
2290m = 2mm / 1mm
2291n = 10inch / 2mm
2292o = 3mm / 3
2293p = 3_ / 4
2294q = 4inch / 2_
2295
2296r = min([0, 3, 42])
2297s = min([0, 3mm, -42])
2298t = min([100, 3in, 142mm])
2299u = min([3rad, 4in])
2300"#;
2301
2302 let result = parse_execute(program).await.unwrap();
2303 assert_eq!(
2304 result.exec_state.errors().len(),
2305 5,
2306 "errors: {:?}",
2307 result.exec_state.errors()
2308 );
2309
2310 assert_value_and_type("a", &result, 9.0, NumericType::default());
2311 assert_value_and_type("b", &result, 3.0, NumericType::default());
2312 assert_value_and_type("c", &result, 13.0, NumericType::mm());
2313 assert_value_and_type("d", &result, 13.0, NumericType::mm());
2314 assert_value_and_type("e", &result, 13.0, NumericType::mm());
2315 assert_value_and_type("f", &result, 5.0, NumericType::mm());
2316
2317 assert_value_and_type("g", &result, 20.0, NumericType::default());
2318 assert_value_and_type("h", &result, 20.0, NumericType::mm());
2319 assert_value_and_type("i", &result, 20.0, NumericType::Unknown);
2320 assert_value_and_type("j", &result, 20.0, NumericType::default());
2321 assert_value_and_type("k", &result, 18.0, NumericType::Unknown);
2322
2323 assert_value_and_type("l", &result, 0.0, NumericType::default());
2324 assert_value_and_type("m", &result, 2.0, NumericType::count());
2325 assert_value_and_type("n", &result, 5.0, NumericType::Unknown);
2326 assert_value_and_type("o", &result, 1.0, NumericType::mm());
2327 assert_value_and_type("p", &result, 1.0, NumericType::count());
2328 assert_value_and_type(
2329 "q",
2330 &result,
2331 2.0,
2332 NumericType::Known(UnitType::Length(UnitLength::Inches)),
2333 );
2334
2335 assert_value_and_type("r", &result, 0.0, NumericType::default());
2336 assert_value_and_type("s", &result, -42.0, NumericType::mm());
2337 assert_value_and_type("t", &result, 3.0, NumericType::Unknown);
2338 assert_value_and_type("u", &result, 3.0, NumericType::Unknown);
2339 }
2340
2341 #[tokio::test(flavor = "multi_thread")]
2342 async fn bad_typed_arithmetic() {
2343 let program = r#"
2344a = 1rad
2345b = 180 / PI * a + 360
2346"#;
2347
2348 let result = parse_execute(program).await.unwrap();
2349
2350 assert_value_and_type("a", &result, 1.0, NumericType::radians());
2351 assert_value_and_type("b", &result, 417.0, NumericType::Unknown);
2352 }
2353
2354 #[tokio::test(flavor = "multi_thread")]
2355 async fn cos_coercions() {
2356 let program = r#"
2357a = cos(units::toRadians(30))
2358b = 3 / a
2359c = cos(30deg)
2360d = cos(30)
2361"#;
2362
2363 let result = parse_execute(program).await.unwrap();
2364 assert!(result.exec_state.errors().is_empty());
2365
2366 assert_value_and_type("a", &result, 1.0, NumericType::default());
2367 assert_value_and_type("b", &result, 3.0, NumericType::default());
2368 assert_value_and_type("c", &result, 1.0, NumericType::default());
2369 assert_value_and_type("d", &result, 1.0, NumericType::default());
2370 }
2371
2372 #[tokio::test(flavor = "multi_thread")]
2373 async fn coerce_nested_array() {
2374 let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock(None).await);
2375
2376 let mixed1 = KclValue::HomArray {
2377 value: vec![
2378 KclValue::Number {
2379 value: 0.0,
2380 ty: NumericType::count(),
2381 meta: Vec::new(),
2382 },
2383 KclValue::Number {
2384 value: 1.0,
2385 ty: NumericType::count(),
2386 meta: Vec::new(),
2387 },
2388 KclValue::HomArray {
2389 value: vec![
2390 KclValue::Number {
2391 value: 2.0,
2392 ty: NumericType::count(),
2393 meta: Vec::new(),
2394 },
2395 KclValue::Number {
2396 value: 3.0,
2397 ty: NumericType::count(),
2398 meta: Vec::new(),
2399 },
2400 ],
2401 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2402 },
2403 ],
2404 ty: RuntimeType::any(),
2405 };
2406
2407 let tym1 = RuntimeType::Array(
2409 Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()))),
2410 ArrayLen::Minimum(1),
2411 );
2412
2413 let result = KclValue::HomArray {
2414 value: vec![
2415 KclValue::Number {
2416 value: 0.0,
2417 ty: NumericType::count(),
2418 meta: Vec::new(),
2419 },
2420 KclValue::Number {
2421 value: 1.0,
2422 ty: NumericType::count(),
2423 meta: Vec::new(),
2424 },
2425 KclValue::Number {
2426 value: 2.0,
2427 ty: NumericType::count(),
2428 meta: Vec::new(),
2429 },
2430 KclValue::Number {
2431 value: 3.0,
2432 ty: NumericType::count(),
2433 meta: Vec::new(),
2434 },
2435 ],
2436 ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
2437 };
2438 assert_coerce_results(&mixed1, &tym1, &result, &mut exec_state);
2439 }
2440}