1use std::{collections::HashMap, fmt};
2
3use anyhow::Result;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use super::{
8 memory::{self, EnvironmentRef},
9 MetaSettings, Point3d,
10};
11use crate::{
12 errors::KclErrorDetails,
13 execution::{
14 ExecState, ExecutorContext, Face, Helix, ImportedGeometry, Metadata, Plane, Sketch, Solid, TagIdentifier,
15 },
16 parsing::{
17 ast::types::{
18 DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node,
19 PrimitiveType as AstPrimitiveType, TagDeclarator, TagNode, Type,
20 },
21 token::NumericSuffix,
22 },
23 std::{
24 args::{Arg, FromKclValue},
25 StdFnProps,
26 },
27 CompilationError, KclError, ModuleId, SourceRange,
28};
29
30pub type KclObjectFields = HashMap<String, KclValue>;
31
32#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
34#[ts(export)]
35#[serde(tag = "type")]
36pub enum KclValue {
37 Uuid {
38 value: ::uuid::Uuid,
39 #[serde(skip)]
40 meta: Vec<Metadata>,
41 },
42 Bool {
43 value: bool,
44 #[serde(skip)]
45 meta: Vec<Metadata>,
46 },
47 Number {
48 value: f64,
49 ty: NumericType,
50 #[serde(skip)]
51 meta: Vec<Metadata>,
52 },
53 String {
54 value: String,
55 #[serde(skip)]
56 meta: Vec<Metadata>,
57 },
58 MixedArray {
59 value: Vec<KclValue>,
60 #[serde(skip)]
61 meta: Vec<Metadata>,
62 },
63 HomArray {
65 value: Vec<KclValue>,
66 #[serde(skip)]
68 ty: PrimitiveType,
69 },
70 Object {
71 value: KclObjectFields,
72 #[serde(skip)]
73 meta: Vec<Metadata>,
74 },
75 TagIdentifier(Box<TagIdentifier>),
76 TagDeclarator(crate::parsing::ast::types::BoxNode<TagDeclarator>),
77 Plane {
78 value: Box<Plane>,
79 },
80 Face {
81 value: Box<Face>,
82 },
83 Sketch {
84 value: Box<Sketch>,
85 },
86 Solid {
87 value: Box<Solid>,
88 },
89 Helix {
90 value: Box<Helix>,
91 },
92 ImportedGeometry(ImportedGeometry),
93 #[ts(skip)]
94 Function {
95 #[serde(skip)]
96 value: FunctionSource,
97 #[serde(skip)]
98 meta: Vec<Metadata>,
99 },
100 Module {
101 value: ModuleId,
102 #[serde(skip)]
103 meta: Vec<Metadata>,
104 },
105 #[ts(skip)]
106 Type {
107 #[serde(skip)]
108 value: Option<(PrimitiveType, StdFnProps)>,
109 #[serde(skip)]
110 meta: Vec<Metadata>,
111 },
112 KclNone {
113 value: KclNone,
114 #[serde(skip)]
115 meta: Vec<Metadata>,
116 },
117}
118
119#[derive(Debug, Clone, PartialEq, Default)]
120pub enum FunctionSource {
121 #[default]
122 None,
123 Std {
124 func: crate::std::StdFn,
125 props: StdFnProps,
126 },
127 User {
128 ast: crate::parsing::ast::types::BoxNode<FunctionExpression>,
129 settings: MetaSettings,
130 memory: EnvironmentRef,
131 },
132}
133
134impl JsonSchema for FunctionSource {
135 fn schema_name() -> String {
136 "FunctionSource".to_owned()
137 }
138
139 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
140 gen.subschema_for::<()>()
142 }
143}
144
145impl From<Vec<Sketch>> for KclValue {
146 fn from(mut eg: Vec<Sketch>) -> Self {
147 if eg.len() == 1 {
148 KclValue::Sketch {
149 value: Box::new(eg.pop().unwrap()),
150 }
151 } else {
152 KclValue::HomArray {
153 value: eg
154 .into_iter()
155 .map(|s| KclValue::Sketch { value: Box::new(s) })
156 .collect(),
157 ty: crate::execution::PrimitiveType::Sketch,
158 }
159 }
160 }
161}
162
163impl From<Vec<Solid>> for KclValue {
164 fn from(mut eg: Vec<Solid>) -> Self {
165 if eg.len() == 1 {
166 KclValue::Solid {
167 value: Box::new(eg.pop().unwrap()),
168 }
169 } else {
170 KclValue::HomArray {
171 value: eg.into_iter().map(|s| KclValue::Solid { value: Box::new(s) }).collect(),
172 ty: crate::execution::PrimitiveType::Solid,
173 }
174 }
175 }
176}
177
178impl From<KclValue> for Vec<SourceRange> {
179 fn from(item: KclValue) -> Self {
180 match item {
181 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
182 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
183 KclValue::Solid { value } => to_vec_sr(&value.meta),
184 KclValue::Sketch { value } => to_vec_sr(&value.meta),
185 KclValue::Helix { value } => to_vec_sr(&value.meta),
186 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
187 KclValue::Function { meta, .. } => to_vec_sr(&meta),
188 KclValue::Plane { value } => to_vec_sr(&value.meta),
189 KclValue::Face { value } => to_vec_sr(&value.meta),
190 KclValue::Bool { meta, .. } => to_vec_sr(&meta),
191 KclValue::Number { meta, .. } => to_vec_sr(&meta),
192 KclValue::String { meta, .. } => to_vec_sr(&meta),
193 KclValue::MixedArray { meta, .. } => to_vec_sr(&meta),
194 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
195 KclValue::Object { meta, .. } => to_vec_sr(&meta),
196 KclValue::Module { meta, .. } => to_vec_sr(&meta),
197 KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
198 KclValue::Type { meta, .. } => to_vec_sr(&meta),
199 KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
200 }
201 }
202}
203
204fn to_vec_sr(meta: &[Metadata]) -> Vec<SourceRange> {
205 meta.iter().map(|m| m.source_range).collect()
206}
207
208impl From<&KclValue> for Vec<SourceRange> {
209 fn from(item: &KclValue) -> Self {
210 match item {
211 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
212 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
213 KclValue::Solid { value } => to_vec_sr(&value.meta),
214 KclValue::Sketch { value } => to_vec_sr(&value.meta),
215 KclValue::Helix { value } => to_vec_sr(&value.meta),
216 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
217 KclValue::Function { meta, .. } => to_vec_sr(meta),
218 KclValue::Plane { value } => to_vec_sr(&value.meta),
219 KclValue::Face { value } => to_vec_sr(&value.meta),
220 KclValue::Bool { meta, .. } => to_vec_sr(meta),
221 KclValue::Number { meta, .. } => to_vec_sr(meta),
222 KclValue::String { meta, .. } => to_vec_sr(meta),
223 KclValue::Uuid { meta, .. } => to_vec_sr(meta),
224 KclValue::MixedArray { meta, .. } => to_vec_sr(meta),
225 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
226 KclValue::Object { meta, .. } => to_vec_sr(meta),
227 KclValue::Module { meta, .. } => to_vec_sr(meta),
228 KclValue::KclNone { meta, .. } => to_vec_sr(meta),
229 KclValue::Type { meta, .. } => to_vec_sr(meta),
230 }
231 }
232}
233
234impl From<&KclValue> for SourceRange {
235 fn from(item: &KclValue) -> Self {
236 let v: Vec<_> = item.into();
237 v.into_iter().next().unwrap_or_default()
238 }
239}
240
241impl KclValue {
242 pub(crate) fn metadata(&self) -> Vec<Metadata> {
243 match self {
244 KclValue::Uuid { value: _, meta } => meta.clone(),
245 KclValue::Bool { value: _, meta } => meta.clone(),
246 KclValue::Number { meta, .. } => meta.clone(),
247 KclValue::String { value: _, meta } => meta.clone(),
248 KclValue::MixedArray { value: _, meta } => meta.clone(),
249 KclValue::HomArray { value, .. } => value.iter().flat_map(|v| v.metadata()).collect(),
250 KclValue::Object { value: _, meta } => meta.clone(),
251 KclValue::TagIdentifier(x) => x.meta.clone(),
252 KclValue::TagDeclarator(x) => vec![x.metadata()],
253 KclValue::Plane { value } => value.meta.clone(),
254 KclValue::Face { value } => value.meta.clone(),
255 KclValue::Sketch { value } => value.meta.clone(),
256 KclValue::Solid { value } => value.meta.clone(),
257 KclValue::Helix { value } => value.meta.clone(),
258 KclValue::ImportedGeometry(x) => x.meta.clone(),
259 KclValue::Function { meta, .. } => meta.clone(),
260 KclValue::Module { meta, .. } => meta.clone(),
261 KclValue::KclNone { meta, .. } => meta.clone(),
262 KclValue::Type { meta, .. } => meta.clone(),
263 }
264 }
265
266 pub(crate) fn function_def_source_range(&self) -> Option<SourceRange> {
267 let KclValue::Function {
268 value: FunctionSource::User { ast, .. },
269 ..
270 } = self
271 else {
272 return None;
273 };
274 Some(ast.as_source_range())
277 }
278
279 #[allow(unused)]
280 pub(crate) fn none() -> Self {
281 Self::KclNone {
282 value: Default::default(),
283 meta: Default::default(),
284 }
285 }
286
287 pub(crate) fn human_friendly_type(&self) -> &'static str {
290 match self {
291 KclValue::Uuid { .. } => "Unique ID (uuid)",
292 KclValue::TagDeclarator(_) => "TagDeclarator",
293 KclValue::TagIdentifier(_) => "TagIdentifier",
294 KclValue::Solid { .. } => "Solid",
295 KclValue::Sketch { .. } => "Sketch",
296 KclValue::Helix { .. } => "Helix",
297 KclValue::ImportedGeometry(_) => "ImportedGeometry",
298 KclValue::Function { .. } => "Function",
299 KclValue::Plane { .. } => "Plane",
300 KclValue::Face { .. } => "Face",
301 KclValue::Bool { .. } => "boolean (true/false value)",
302 KclValue::Number { .. } => "number",
303 KclValue::String { .. } => "string (text)",
304 KclValue::MixedArray { .. } => "array (list)",
305 KclValue::HomArray { .. } => "array (list)",
306 KclValue::Object { .. } => "object",
307 KclValue::Module { .. } => "module",
308 KclValue::Type { .. } => "type",
309 KclValue::KclNone { .. } => "None",
310 }
311 }
312
313 pub(crate) fn from_literal(literal: Node<Literal>, settings: &MetaSettings) -> Self {
314 let meta = vec![literal.metadata()];
315 match literal.inner.value {
316 LiteralValue::Number { value, suffix } => KclValue::Number {
317 value,
318 meta,
319 ty: NumericType::from_parsed(suffix, settings),
320 },
321 LiteralValue::String(value) => KclValue::String { value, meta },
322 LiteralValue::Bool(value) => KclValue::Bool { value, meta },
323 }
324 }
325
326 pub(crate) fn from_default_param(param: DefaultParamVal, settings: &MetaSettings) -> Self {
327 match param {
328 DefaultParamVal::Literal(lit) => Self::from_literal(lit, settings),
329 DefaultParamVal::KclNone(none) => KclValue::KclNone {
330 value: none,
331 meta: Default::default(),
332 },
333 }
334 }
335
336 pub(crate) fn map_env_ref(&self, old_env: usize, new_env: usize) -> Self {
337 let mut result = self.clone();
338 if let KclValue::Function {
339 value: FunctionSource::User { ref mut memory, .. },
340 ..
341 } = result
342 {
343 memory.replace_env(old_env, new_env);
344 }
345 result
346 }
347
348 pub const fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
350 Self::Number {
351 value: f,
352 meta,
353 ty: NumericType::Unknown,
354 }
355 }
356
357 pub const fn from_number_with_type(f: f64, ty: NumericType, meta: Vec<Metadata>) -> Self {
358 Self::Number { value: f, meta, ty }
359 }
360
361 pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
363 Self::MixedArray {
364 value: vec![
365 Self::Number {
366 value: p[0],
367 meta: meta.clone(),
368 ty: ty.clone(),
369 },
370 Self::Number {
371 value: p[1],
372 meta: meta.clone(),
373 ty,
374 },
375 ],
376 meta,
377 }
378 }
379
380 pub(crate) fn as_usize(&self) -> Option<usize> {
381 match self {
382 KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
383 _ => None,
384 }
385 }
386
387 pub fn as_int(&self) -> Option<i64> {
388 match self {
389 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
390 _ => None,
391 }
392 }
393
394 pub fn as_object(&self) -> Option<&KclObjectFields> {
395 if let KclValue::Object { value, meta: _ } = &self {
396 Some(value)
397 } else {
398 None
399 }
400 }
401
402 pub fn into_object(self) -> Option<KclObjectFields> {
403 if let KclValue::Object { value, meta: _ } = self {
404 Some(value)
405 } else {
406 None
407 }
408 }
409
410 pub fn as_str(&self) -> Option<&str> {
411 if let KclValue::String { value, meta: _ } = &self {
412 Some(value)
413 } else {
414 None
415 }
416 }
417
418 pub fn as_array(&self) -> Option<&[KclValue]> {
419 if let KclValue::MixedArray { value, meta: _ } = &self {
420 Some(value)
421 } else {
422 None
423 }
424 }
425
426 pub fn as_point2d(&self) -> Option<[f64; 2]> {
427 let arr = self.as_array()?;
428 if arr.len() != 2 {
429 return None;
430 }
431 let x = arr[0].as_f64()?;
432 let y = arr[1].as_f64()?;
433 Some([x, y])
434 }
435
436 pub fn as_uuid(&self) -> Option<uuid::Uuid> {
437 if let KclValue::Uuid { value, meta: _ } = &self {
438 Some(*value)
439 } else {
440 None
441 }
442 }
443
444 pub fn as_plane(&self) -> Option<&Plane> {
445 if let KclValue::Plane { value } = &self {
446 Some(value)
447 } else {
448 None
449 }
450 }
451
452 pub fn as_solid(&self) -> Option<&Solid> {
453 if let KclValue::Solid { value } = &self {
454 Some(value)
455 } else {
456 None
457 }
458 }
459
460 pub fn as_sketch(&self) -> Option<&Sketch> {
461 if let KclValue::Sketch { value } = self {
462 Some(value)
463 } else {
464 None
465 }
466 }
467
468 pub fn as_mut_sketch(&mut self) -> Option<&mut Sketch> {
469 if let KclValue::Sketch { value } = self {
470 Some(value)
471 } else {
472 None
473 }
474 }
475
476 pub fn as_mut_tag(&mut self) -> Option<&mut TagIdentifier> {
477 if let KclValue::TagIdentifier(value) = self {
478 Some(value)
479 } else {
480 None
481 }
482 }
483 pub fn as_f64(&self) -> Option<f64> {
484 if let KclValue::Number { value, .. } = &self {
485 Some(*value)
486 } else {
487 None
488 }
489 }
490
491 pub fn as_bool(&self) -> Option<bool> {
492 if let KclValue::Bool { value, meta: _ } = &self {
493 Some(*value)
494 } else {
495 None
496 }
497 }
498
499 pub fn get_u32(&self, source_ranges: Vec<SourceRange>) -> Result<u32, KclError> {
501 let u = self.as_int().and_then(|n| u64::try_from(n).ok()).ok_or_else(|| {
502 KclError::Semantic(KclErrorDetails {
503 message: "Expected an integer >= 0".to_owned(),
504 source_ranges: source_ranges.clone(),
505 })
506 })?;
507 u32::try_from(u).map_err(|_| {
508 KclError::Semantic(KclErrorDetails {
509 message: "Number was too big".to_owned(),
510 source_ranges,
511 })
512 })
513 }
514
515 pub fn get_function(&self) -> Option<&FunctionSource> {
517 match self {
518 KclValue::Function { value, .. } => Some(value),
519 _ => None,
520 }
521 }
522
523 pub fn get_tag_identifier(&self) -> Result<TagIdentifier, KclError> {
525 match self {
526 KclValue::TagIdentifier(t) => Ok(*t.clone()),
527 _ => Err(KclError::Semantic(KclErrorDetails {
528 message: format!("Not a tag identifier: {:?}", self),
529 source_ranges: self.clone().into(),
530 })),
531 }
532 }
533
534 pub fn get_tag_declarator(&self) -> Result<TagNode, KclError> {
536 match self {
537 KclValue::TagDeclarator(t) => Ok((**t).clone()),
538 _ => Err(KclError::Semantic(KclErrorDetails {
539 message: format!("Not a tag declarator: {:?}", self),
540 source_ranges: self.clone().into(),
541 })),
542 }
543 }
544
545 pub fn get_bool(&self) -> Result<bool, KclError> {
547 let Self::Bool { value: b, .. } = self else {
548 return Err(KclError::Type(KclErrorDetails {
549 source_ranges: self.into(),
550 message: format!("Expected bool, found {}", self.human_friendly_type()),
551 }));
552 };
553 Ok(*b)
554 }
555
556 pub fn has_type(&self, ty: &RuntimeType) -> bool {
558 let Some(self_ty) = self.principal_type() else {
559 return false;
560 };
561
562 self_ty.subtype(ty)
563 }
564
565 pub fn coerce(&self, ty: &RuntimeType, exec_state: &mut ExecState) -> Option<KclValue> {
572 match ty {
573 RuntimeType::Primitive(ty) => self.coerce_to_primitive_type(ty, exec_state),
574 RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, *len, exec_state),
575 RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, exec_state),
576 RuntimeType::Union(tys) => self.coerce_to_union_type(tys, exec_state),
577 RuntimeType::Object(tys) => self.coerce_to_object_type(tys, exec_state),
578 }
579 }
580
581 fn coerce_to_primitive_type(&self, ty: &PrimitiveType, exec_state: &mut ExecState) -> Option<KclValue> {
582 let value = match self {
583 KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } if value.len() == 1 => &value[0],
584 _ => self,
585 };
586 match ty {
587 PrimitiveType::Number(_ty) => match value {
589 KclValue::Number { .. } => Some(value.clone()),
590 _ => None,
591 },
592 PrimitiveType::String => match value {
593 KclValue::String { .. } => Some(value.clone()),
594 _ => None,
595 },
596 PrimitiveType::Boolean => match value {
597 KclValue::Bool { .. } => Some(value.clone()),
598 _ => None,
599 },
600 PrimitiveType::Sketch => match value {
601 KclValue::Sketch { .. } => Some(value.clone()),
602 _ => None,
603 },
604 PrimitiveType::Solid => match value {
605 KclValue::Solid { .. } => Some(value.clone()),
606 _ => None,
607 },
608 PrimitiveType::Plane => match value {
609 KclValue::Plane { .. } => Some(value.clone()),
610 KclValue::Object { value, meta } => {
611 let origin = value.get("origin").and_then(Point3d::from_kcl_val)?;
612 let x_axis = value.get("xAxis").and_then(Point3d::from_kcl_val)?;
613 let y_axis = value.get("yAxis").and_then(Point3d::from_kcl_val)?;
614 let z_axis = value.get("zAxis").and_then(Point3d::from_kcl_val)?;
615
616 let id = exec_state.mod_local.id_generator.next_uuid();
617 let plane = Plane {
618 id,
619 artifact_id: id.into(),
620 origin,
621 x_axis,
622 y_axis,
623 z_axis,
624 value: super::PlaneType::Uninit,
625 units: exec_state.length_unit(),
627 meta: meta.clone(),
628 };
629
630 Some(KclValue::Plane { value: Box::new(plane) })
631 }
632 _ => None,
633 },
634 PrimitiveType::ImportedGeometry => match value {
635 KclValue::ImportedGeometry { .. } => Some(value.clone()),
636 _ => None,
637 },
638 }
639 }
640
641 fn coerce_to_array_type(&self, ty: &PrimitiveType, len: ArrayLen, exec_state: &mut ExecState) -> Option<KclValue> {
642 match self {
643 KclValue::HomArray { value, ty: aty } => {
644 if aty != ty {
646 return None;
647 }
648
649 let value = match len {
650 ArrayLen::None => value.clone(),
651 ArrayLen::NonEmpty => {
652 if value.is_empty() {
653 return None;
654 }
655
656 value.clone()
657 }
658 ArrayLen::Known(n) => {
659 if n != value.len() {
660 return None;
661 }
662
663 value[..n].to_vec()
664 }
665 };
666
667 Some(KclValue::HomArray { value, ty: ty.clone() })
668 }
669 KclValue::MixedArray { value, .. } => {
670 let value = match len {
671 ArrayLen::None => value.clone(),
672 ArrayLen::NonEmpty => {
673 if value.is_empty() {
674 return None;
675 }
676
677 value.clone()
678 }
679 ArrayLen::Known(n) => {
680 if n != value.len() {
681 return None;
682 }
683
684 value[..n].to_vec()
685 }
686 };
687
688 let rt = RuntimeType::Primitive(ty.clone());
689 let value = value
690 .iter()
691 .map(|v| v.coerce(&rt, exec_state))
692 .collect::<Option<Vec<_>>>()?;
693
694 Some(KclValue::HomArray { value, ty: ty.clone() })
695 }
696 KclValue::KclNone { .. } if len.satisfied(0) => Some(KclValue::HomArray {
697 value: Vec::new(),
698 ty: ty.clone(),
699 }),
700 value if len.satisfied(1) => {
701 if value.has_type(&RuntimeType::Primitive(ty.clone())) {
702 Some(KclValue::HomArray {
703 value: vec![value.clone()],
704 ty: ty.clone(),
705 })
706 } else {
707 None
708 }
709 }
710 _ => None,
711 }
712 }
713
714 fn coerce_to_tuple_type(&self, tys: &[PrimitiveType], exec_state: &mut ExecState) -> Option<KclValue> {
715 match self {
716 KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
717 if value.len() < tys.len() {
718 return None;
719 }
720 let mut result = Vec::new();
721 for (i, t) in tys.iter().enumerate() {
722 result.push(value[i].coerce_to_primitive_type(t, exec_state)?);
723 }
724
725 Some(KclValue::MixedArray {
726 value: result,
727 meta: Vec::new(),
728 })
729 }
730 KclValue::KclNone { meta, .. } if tys.is_empty() => Some(KclValue::MixedArray {
731 value: Vec::new(),
732 meta: meta.clone(),
733 }),
734 value if tys.len() == 1 => {
735 if value.has_type(&RuntimeType::Primitive(tys[0].clone())) {
736 Some(KclValue::MixedArray {
737 value: vec![value.clone()],
738 meta: Vec::new(),
739 })
740 } else {
741 None
742 }
743 }
744 _ => None,
745 }
746 }
747
748 fn coerce_to_union_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Option<KclValue> {
749 for t in tys {
750 if let Some(v) = self.coerce(t, exec_state) {
751 return Some(v);
752 }
753 }
754
755 None
756 }
757
758 fn coerce_to_object_type(&self, tys: &[(String, RuntimeType)], _exec_state: &mut ExecState) -> Option<KclValue> {
759 match self {
760 KclValue::Object { value, .. } => {
761 for (s, t) in tys {
762 if !value.get(s)?.has_type(t) {
764 return None;
765 }
766 }
767 Some(self.clone())
769 }
770 _ => None,
771 }
772 }
773
774 pub fn principal_type(&self) -> Option<RuntimeType> {
775 match self {
776 KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
777 KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(ty.clone()))),
778 KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)),
779 KclValue::Object { value, .. } => {
780 let properties = value
781 .iter()
782 .map(|(k, v)| v.principal_type().map(|t| (k.clone(), t)))
783 .collect::<Option<Vec<_>>>()?;
784 Some(RuntimeType::Object(properties))
785 }
786 KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
787 KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
788 KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
789 KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
790 KclValue::MixedArray { value, .. } => Some(RuntimeType::Tuple(
791 value
792 .iter()
793 .map(|v| v.principal_type().and_then(RuntimeType::primitive))
794 .collect::<Option<Vec<_>>>()?,
795 )),
796 KclValue::HomArray { ty, value, .. } => Some(RuntimeType::Array(ty.clone(), ArrayLen::Known(value.len()))),
797 KclValue::Face { .. } => None,
798 KclValue::Helix { .. }
799 | KclValue::Function { .. }
800 | KclValue::Module { .. }
801 | KclValue::TagIdentifier(_)
802 | KclValue::TagDeclarator(_)
803 | KclValue::KclNone { .. }
804 | KclValue::Type { .. }
805 | KclValue::Uuid { .. } => None,
806 }
807 }
808
809 pub async fn call_fn(
812 &self,
813 args: Vec<Arg>,
814 exec_state: &mut ExecState,
815 ctx: ExecutorContext,
816 source_range: SourceRange,
817 ) -> Result<Option<KclValue>, KclError> {
818 match self {
819 KclValue::Function {
820 value: FunctionSource::Std { func, props },
821 ..
822 } => {
823 if props.deprecated {
824 exec_state.warn(CompilationError::err(
825 source_range,
826 format!(
827 "`{}` is deprecated, see the docs for a recommended replacement",
828 props.name
829 ),
830 ));
831 }
832 exec_state.mut_stack().push_new_env_for_rust_call();
833 let args = crate::std::Args::new(
834 args,
835 source_range,
836 ctx.clone(),
837 exec_state
838 .mod_local
839 .pipe_value
840 .clone()
841 .map(|v| Arg::new(v, source_range)),
842 );
843 let result = func(exec_state, args).await.map(Some);
844 exec_state.mut_stack().pop_env();
845 result
846 }
847 KclValue::Function {
848 value: FunctionSource::User { ast, memory, .. },
849 ..
850 } => crate::execution::exec_ast::call_user_defined_function(args, *memory, ast, exec_state, &ctx).await,
851 _ => Err(KclError::Semantic(KclErrorDetails {
852 message: "cannot call this because it isn't a function".to_string(),
853 source_ranges: vec![source_range],
854 })),
855 }
856 }
857
858 pub async fn call_fn_kw(
861 &self,
862 args: crate::std::Args,
863 exec_state: &mut ExecState,
864 ctx: ExecutorContext,
865 callsite: SourceRange,
866 ) -> Result<Option<KclValue>, KclError> {
867 match self {
868 KclValue::Function {
869 value: FunctionSource::Std { func: _, props },
870 ..
871 } => {
872 if props.deprecated {
873 exec_state.warn(CompilationError::err(
874 callsite,
875 format!(
876 "`{}` is deprecated, see the docs for a recommended replacement",
877 props.name
878 ),
879 ));
880 }
881 todo!("Implement KCL stdlib fns with keyword args");
882 }
883 KclValue::Function {
884 value: FunctionSource::User { ast, memory, .. },
885 ..
886 } => {
887 crate::execution::exec_ast::call_user_defined_function_kw(args.kw_args, *memory, ast, exec_state, &ctx)
888 .await
889 }
890 _ => Err(KclError::Semantic(KclErrorDetails {
891 message: "cannot call this because it isn't a function".to_string(),
892 source_ranges: vec![callsite],
893 })),
894 }
895 }
896
897 pub fn value_str(&self) -> Option<String> {
898 match self {
899 KclValue::Bool { value, .. } => Some(format!("{value}")),
900 KclValue::Number { value, .. } => Some(format!("{value}")),
901 KclValue::String { value, .. } => Some(format!("'{value}'")),
902 KclValue::Uuid { value, .. } => Some(format!("{value}")),
903 KclValue::TagDeclarator(tag) => Some(format!("${}", tag.name)),
904 KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
905 KclValue::MixedArray { .. } => Some("[...]".to_owned()),
907 KclValue::HomArray { .. } => Some("[...]".to_owned()),
908 KclValue::Object { .. } => Some("{ ... }".to_owned()),
909 KclValue::Module { .. }
910 | KclValue::Solid { .. }
911 | KclValue::Sketch { .. }
912 | KclValue::Helix { .. }
913 | KclValue::ImportedGeometry(_)
914 | KclValue::Function { .. }
915 | KclValue::Plane { .. }
916 | KclValue::Face { .. }
917 | KclValue::KclNone { .. }
918 | KclValue::Type { .. } => None,
919 }
920 }
921}
922
923#[derive(Debug, Clone, PartialEq)]
924pub enum RuntimeType {
925 Primitive(PrimitiveType),
926 Array(PrimitiveType, ArrayLen),
927 Union(Vec<RuntimeType>),
928 Tuple(Vec<PrimitiveType>),
929 Object(Vec<(String, RuntimeType)>),
930}
931
932impl RuntimeType {
933 pub fn from_parsed(
934 value: Type,
935 exec_state: &mut ExecState,
936 source_range: SourceRange,
937 ) -> Result<Option<Self>, CompilationError> {
938 Ok(match value {
939 Type::Primitive(pt) => {
940 PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(RuntimeType::Primitive)
941 }
942 Type::Array(pt) => {
943 PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(|t| RuntimeType::Array(t, ArrayLen::None))
944 }
945 Type::Object { properties } => properties
946 .into_iter()
947 .map(|p| {
948 let pt = match p.type_ {
949 Some(t) => t,
950 None => return Ok(None),
951 };
952 Ok(RuntimeType::from_parsed(pt.inner, exec_state, source_range)?
953 .map(|ty| (p.identifier.inner.name, ty)))
954 })
955 .collect::<Result<Option<Vec<_>>, CompilationError>>()?
956 .map(RuntimeType::Object),
957 })
958 }
959
960 pub fn human_friendly_type(&self) -> String {
961 match self {
962 RuntimeType::Primitive(ty) => ty.to_string(),
963 RuntimeType::Array(ty, ArrayLen::None) => format!("an array of {}", ty.display_multiple()),
964 RuntimeType::Array(ty, ArrayLen::NonEmpty) => format!("one or more {}", ty.display_multiple()),
965 RuntimeType::Array(ty, ArrayLen::Known(n)) => format!("an array of {n} {}", ty.display_multiple()),
966 RuntimeType::Union(tys) => tys
967 .iter()
968 .map(Self::human_friendly_type)
969 .collect::<Vec<_>>()
970 .join(" or "),
971 RuntimeType::Tuple(tys) => format!(
972 "an array with values of types ({})",
973 tys.iter().map(PrimitiveType::to_string).collect::<Vec<_>>().join(", ")
974 ),
975 RuntimeType::Object(_) => format!("an object with fields {}", self),
976 }
977 }
978
979 fn subtype(&self, sup: &RuntimeType) -> bool {
981 use RuntimeType::*;
982
983 match (self, sup) {
984 (Primitive(t1), Primitive(t2)) => t1 == t2,
985 (Array(t1, l1), Array(t2, l2)) => t1 == t2 && l1.subtype(*l2),
987 (Tuple(t1), Tuple(t2)) => t1 == t2,
988 (Tuple(t1), Array(t2, l2)) => (l2.satisfied(t1.len())) && t1.iter().all(|t| t == t2),
989 (Union(ts1), Union(ts2)) => ts1.iter().all(|t| ts2.contains(t)),
990 (t1, Union(ts2)) => ts2.contains(t1),
991 (Object(t1), Object(t2)) => t1 == t2,
993 _ => false,
994 }
995 }
996
997 fn primitive(self) -> Option<PrimitiveType> {
998 match self {
999 RuntimeType::Primitive(t) => Some(t),
1000 _ => None,
1001 }
1002 }
1003}
1004
1005impl fmt::Display for RuntimeType {
1006 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1007 match self {
1008 RuntimeType::Primitive(t) => t.fmt(f),
1009 RuntimeType::Array(t, l) => match l {
1010 ArrayLen::None => write!(f, "[{t}]"),
1011 ArrayLen::NonEmpty => write!(f, "[{t}; 1+]"),
1012 ArrayLen::Known(n) => write!(f, "[{t}; {n}]"),
1013 },
1014 RuntimeType::Tuple(ts) => write!(
1015 f,
1016 "[{}]",
1017 ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
1018 ),
1019 RuntimeType::Union(ts) => write!(
1020 f,
1021 "{}",
1022 ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(" | ")
1023 ),
1024 RuntimeType::Object(items) => write!(
1025 f,
1026 "{{ {} }}",
1027 items
1028 .iter()
1029 .map(|(n, t)| format!("{n}: {t}"))
1030 .collect::<Vec<_>>()
1031 .join(", ")
1032 ),
1033 }
1034 }
1035}
1036
1037#[derive(Debug, Clone, Copy, PartialEq)]
1038pub enum ArrayLen {
1039 None,
1040 NonEmpty,
1041 Known(usize),
1042}
1043
1044impl ArrayLen {
1045 pub fn subtype(self, other: ArrayLen) -> bool {
1046 match (self, other) {
1047 (_, ArrayLen::None) => true,
1048 (ArrayLen::NonEmpty, ArrayLen::NonEmpty) => true,
1049 (ArrayLen::Known(size), ArrayLen::NonEmpty) if size > 0 => true,
1050 (ArrayLen::Known(s1), ArrayLen::Known(s2)) if s1 == s2 => true,
1051 _ => false,
1052 }
1053 }
1054
1055 fn satisfied(self, len: usize) -> bool {
1057 match self {
1058 ArrayLen::None => true,
1059 ArrayLen::NonEmpty => len > 0,
1060 ArrayLen::Known(s) => len == s,
1061 }
1062 }
1063}
1064
1065#[derive(Debug, Clone, PartialEq)]
1066pub enum PrimitiveType {
1067 Number(NumericType),
1068 String,
1069 Boolean,
1070 Sketch,
1071 Solid,
1072 Plane,
1073 ImportedGeometry,
1074}
1075
1076impl PrimitiveType {
1077 fn from_parsed(
1078 value: AstPrimitiveType,
1079 exec_state: &mut ExecState,
1080 source_range: SourceRange,
1081 ) -> Result<Option<Self>, CompilationError> {
1082 Ok(match value {
1083 AstPrimitiveType::String => Some(PrimitiveType::String),
1084 AstPrimitiveType::Boolean => Some(PrimitiveType::Boolean),
1085 AstPrimitiveType::Number(suffix) => Some(PrimitiveType::Number(NumericType::from_parsed(
1086 suffix,
1087 &exec_state.mod_local.settings,
1088 ))),
1089 AstPrimitiveType::Named(name) => {
1090 let ty_val = exec_state
1091 .stack()
1092 .get(&format!("{}{}", memory::TYPE_PREFIX, name.name), source_range)
1093 .map_err(|_| CompilationError::err(source_range, format!("Unknown type: {}", name.name)))?;
1094
1095 let (ty, _) = match ty_val {
1096 KclValue::Type { value: Some(ty), .. } => ty,
1097 _ => unreachable!(),
1098 };
1099
1100 Some(ty.clone())
1101 }
1102 _ => None,
1103 })
1104 }
1105
1106 fn display_multiple(&self) -> String {
1107 match self {
1108 PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
1109 PrimitiveType::Number(_) => "numbers".to_owned(),
1110 PrimitiveType::String => "strings".to_owned(),
1111 PrimitiveType::Boolean => "bools".to_owned(),
1112 PrimitiveType::Sketch => "Sketches".to_owned(),
1113 PrimitiveType::Solid => "Solids".to_owned(),
1114 PrimitiveType::Plane => "Planes".to_owned(),
1115 PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
1116 }
1117 }
1118}
1119
1120impl fmt::Display for PrimitiveType {
1121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1122 match self {
1123 PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
1124 PrimitiveType::Number(_) => write!(f, "number"),
1125 PrimitiveType::String => write!(f, "string"),
1126 PrimitiveType::Boolean => write!(f, "bool"),
1127 PrimitiveType::Sketch => write!(f, "Sketch"),
1128 PrimitiveType::Solid => write!(f, "Solid"),
1129 PrimitiveType::Plane => write!(f, "Plane"),
1130 PrimitiveType::ImportedGeometry => write!(f, "imported geometry"),
1131 }
1132 }
1133}
1134
1135#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
1136#[ts(export)]
1137#[serde(tag = "type")]
1138pub enum NumericType {
1139 Known(UnitType),
1141 Default { len: UnitLen, angle: UnitAngle },
1143 Unknown,
1145 Any,
1147}
1148
1149impl NumericType {
1150 pub fn count() -> Self {
1151 NumericType::Known(UnitType::Count)
1152 }
1153
1154 pub fn combine_eq(self, other: &NumericType) -> NumericType {
1156 if &self == other {
1157 self
1158 } else {
1159 NumericType::Unknown
1160 }
1161 }
1162
1163 pub fn combine_n_eq(tys: &[NumericType]) -> NumericType {
1167 let ty0 = tys[0].clone();
1168 for t in &tys[1..] {
1169 if t != &ty0 {
1170 return NumericType::Unknown;
1171 }
1172 }
1173 ty0
1174 }
1175
1176 pub fn combine_add(a: NumericType, b: NumericType) -> NumericType {
1178 if a == b {
1179 return a;
1180 }
1181 NumericType::Unknown
1182 }
1183
1184 pub fn combine_mul(a: NumericType, b: NumericType) -> NumericType {
1186 if a == NumericType::count() {
1187 return b;
1188 }
1189 if b == NumericType::count() {
1190 return a;
1191 }
1192 NumericType::Unknown
1193 }
1194
1195 pub fn combine_div(a: NumericType, b: NumericType) -> NumericType {
1197 if b == NumericType::count() {
1198 return a;
1199 }
1200 NumericType::Unknown
1201 }
1202
1203 pub fn from_parsed(suffix: NumericSuffix, settings: &super::MetaSettings) -> Self {
1204 match suffix {
1205 NumericSuffix::None => NumericType::Default {
1206 len: settings.default_length_units,
1207 angle: settings.default_angle_units,
1208 },
1209 NumericSuffix::Count => NumericType::Known(UnitType::Count),
1210 NumericSuffix::Mm => NumericType::Known(UnitType::Length(UnitLen::Mm)),
1211 NumericSuffix::Cm => NumericType::Known(UnitType::Length(UnitLen::Cm)),
1212 NumericSuffix::M => NumericType::Known(UnitType::Length(UnitLen::M)),
1213 NumericSuffix::Inch => NumericType::Known(UnitType::Length(UnitLen::Inches)),
1214 NumericSuffix::Ft => NumericType::Known(UnitType::Length(UnitLen::Feet)),
1215 NumericSuffix::Yd => NumericType::Known(UnitType::Length(UnitLen::Yards)),
1216 NumericSuffix::Deg => NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
1217 NumericSuffix::Rad => NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
1218 }
1219 }
1220}
1221
1222impl From<UnitLen> for NumericType {
1223 fn from(value: UnitLen) -> Self {
1224 NumericType::Known(UnitType::Length(value))
1225 }
1226}
1227
1228impl From<UnitAngle> for NumericType {
1229 fn from(value: UnitAngle) -> Self {
1230 NumericType::Known(UnitType::Angle(value))
1231 }
1232}
1233
1234#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
1235#[ts(export)]
1236#[serde(tag = "type")]
1237pub enum UnitType {
1238 Count,
1239 Length(UnitLen),
1240 Angle(UnitAngle),
1241}
1242
1243impl std::fmt::Display for UnitType {
1244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1245 match self {
1246 UnitType::Count => write!(f, "_"),
1247 UnitType::Length(l) => l.fmt(f),
1248 UnitType::Angle(a) => a.fmt(f),
1249 }
1250 }
1251}
1252
1253#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
1256#[ts(export)]
1257#[serde(tag = "type")]
1258pub enum UnitLen {
1259 #[default]
1260 Mm,
1261 Cm,
1262 M,
1263 Inches,
1264 Feet,
1265 Yards,
1266}
1267
1268impl std::fmt::Display for UnitLen {
1269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1270 match self {
1271 UnitLen::Mm => write!(f, "mm"),
1272 UnitLen::Cm => write!(f, "cm"),
1273 UnitLen::M => write!(f, "m"),
1274 UnitLen::Inches => write!(f, "in"),
1275 UnitLen::Feet => write!(f, "ft"),
1276 UnitLen::Yards => write!(f, "yd"),
1277 }
1278 }
1279}
1280
1281impl TryFrom<NumericSuffix> for UnitLen {
1282 type Error = ();
1283
1284 fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
1285 match suffix {
1286 NumericSuffix::Mm => Ok(Self::Mm),
1287 NumericSuffix::Cm => Ok(Self::Cm),
1288 NumericSuffix::M => Ok(Self::M),
1289 NumericSuffix::Inch => Ok(Self::Inches),
1290 NumericSuffix::Ft => Ok(Self::Feet),
1291 NumericSuffix::Yd => Ok(Self::Yards),
1292 _ => Err(()),
1293 }
1294 }
1295}
1296
1297impl From<crate::UnitLength> for UnitLen {
1298 fn from(unit: crate::UnitLength) -> Self {
1299 match unit {
1300 crate::UnitLength::Cm => UnitLen::Cm,
1301 crate::UnitLength::Ft => UnitLen::Feet,
1302 crate::UnitLength::In => UnitLen::Inches,
1303 crate::UnitLength::M => UnitLen::M,
1304 crate::UnitLength::Mm => UnitLen::Mm,
1305 crate::UnitLength::Yd => UnitLen::Yards,
1306 }
1307 }
1308}
1309
1310impl From<UnitLen> for crate::UnitLength {
1311 fn from(unit: UnitLen) -> Self {
1312 match unit {
1313 UnitLen::Cm => crate::UnitLength::Cm,
1314 UnitLen::Feet => crate::UnitLength::Ft,
1315 UnitLen::Inches => crate::UnitLength::In,
1316 UnitLen::M => crate::UnitLength::M,
1317 UnitLen::Mm => crate::UnitLength::Mm,
1318 UnitLen::Yards => crate::UnitLength::Yd,
1319 }
1320 }
1321}
1322
1323impl From<UnitLen> for kittycad_modeling_cmds::units::UnitLength {
1324 fn from(unit: UnitLen) -> Self {
1325 match unit {
1326 UnitLen::Cm => kittycad_modeling_cmds::units::UnitLength::Centimeters,
1327 UnitLen::Feet => kittycad_modeling_cmds::units::UnitLength::Feet,
1328 UnitLen::Inches => kittycad_modeling_cmds::units::UnitLength::Inches,
1329 UnitLen::M => kittycad_modeling_cmds::units::UnitLength::Meters,
1330 UnitLen::Mm => kittycad_modeling_cmds::units::UnitLength::Millimeters,
1331 UnitLen::Yards => kittycad_modeling_cmds::units::UnitLength::Yards,
1332 }
1333 }
1334}
1335
1336#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
1338#[ts(export)]
1339#[serde(tag = "type")]
1340pub enum UnitAngle {
1341 #[default]
1342 Degrees,
1343 Radians,
1344}
1345
1346impl std::fmt::Display for UnitAngle {
1347 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1348 match self {
1349 UnitAngle::Degrees => write!(f, "deg"),
1350 UnitAngle::Radians => write!(f, "rad"),
1351 }
1352 }
1353}
1354
1355impl TryFrom<NumericSuffix> for UnitAngle {
1356 type Error = ();
1357
1358 fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
1359 match suffix {
1360 NumericSuffix::Deg => Ok(Self::Degrees),
1361 NumericSuffix::Rad => Ok(Self::Radians),
1362 _ => Err(()),
1363 }
1364 }
1365}