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