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