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,
10};
11use crate::{
12 errors::KclErrorDetails,
13 execution::{
14 ExecState, ExecutorContext, Face, Helix, ImportedGeometry, Metadata, Plane, Sketch, SketchSet, Solid, SolidSet,
15 TagIdentifier,
16 },
17 parsing::{
18 ast::types::{
19 DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node,
20 PrimitiveType as AstPrimitiveType, TagDeclarator, TagNode, Type,
21 },
22 token::NumericSuffix,
23 },
24 std::{args::Arg, StdFnProps},
25 CompilationError, KclError, ModuleId, SourceRange,
26};
27
28pub type KclObjectFields = HashMap<String, KclValue>;
29
30#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
32#[ts(export)]
33#[serde(tag = "type")]
34pub enum KclValue {
35 Uuid {
36 value: ::uuid::Uuid,
37 #[serde(rename = "__meta")]
38 meta: Vec<Metadata>,
39 },
40 Bool {
41 value: bool,
42 #[serde(rename = "__meta")]
43 meta: Vec<Metadata>,
44 },
45 Number {
46 value: f64,
47 ty: NumericType,
48 #[serde(rename = "__meta")]
49 meta: Vec<Metadata>,
50 },
51 String {
52 value: String,
53 #[serde(rename = "__meta")]
54 meta: Vec<Metadata>,
55 },
56 MixedArray {
57 value: Vec<KclValue>,
58 #[serde(rename = "__meta")]
59 meta: Vec<Metadata>,
60 },
61 Object {
62 value: KclObjectFields,
63 #[serde(rename = "__meta")]
64 meta: Vec<Metadata>,
65 },
66 TagIdentifier(Box<TagIdentifier>),
67 TagDeclarator(crate::parsing::ast::types::BoxNode<TagDeclarator>),
68 Plane {
69 value: Box<Plane>,
70 },
71 Face {
72 value: Box<Face>,
73 },
74 Sketch {
75 value: Box<Sketch>,
76 },
77 Sketches {
78 value: Vec<Box<Sketch>>,
79 },
80 Solid {
81 value: Box<Solid>,
82 },
83 Solids {
84 value: Vec<Box<Solid>>,
85 },
86 Helix {
87 value: Box<Helix>,
88 },
89 ImportedGeometry(ImportedGeometry),
90 #[ts(skip)]
91 Function {
92 #[serde(skip)]
93 value: FunctionSource,
94 #[serde(rename = "__meta")]
95 meta: Vec<Metadata>,
96 },
97 Module {
98 value: ModuleId,
99 #[serde(rename = "__meta")]
100 meta: Vec<Metadata>,
101 },
102 #[ts(skip)]
103 Type {
104 #[serde(skip)]
105 value: Option<(PrimitiveType, StdFnProps)>,
106 #[serde(rename = "__meta")]
107 meta: Vec<Metadata>,
108 },
109 KclNone {
110 value: KclNone,
111 #[serde(rename = "__meta")]
112 meta: Vec<Metadata>,
113 },
114 Tombstone {
116 value: (),
117 #[serde(rename = "__meta")]
118 meta: Vec<Metadata>,
119 },
120}
121
122#[derive(Debug, Clone, PartialEq, Default)]
123pub enum FunctionSource {
124 #[default]
125 None,
126 Std {
127 func: crate::std::StdFn,
128 props: StdFnProps,
129 },
130 User {
131 ast: crate::parsing::ast::types::BoxNode<FunctionExpression>,
132 settings: MetaSettings,
133 memory: EnvironmentRef,
134 },
135}
136
137impl JsonSchema for FunctionSource {
138 fn schema_name() -> String {
139 "FunctionSource".to_owned()
140 }
141
142 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
143 gen.subschema_for::<()>()
145 }
146}
147
148impl From<SketchSet> for KclValue {
149 fn from(sg: SketchSet) -> Self {
150 match sg {
151 SketchSet::Sketch(value) => KclValue::Sketch { value },
152 SketchSet::Sketches(value) => KclValue::Sketches { value },
153 }
154 }
155}
156
157impl From<Vec<Box<Sketch>>> for KclValue {
158 fn from(sg: Vec<Box<Sketch>>) -> Self {
159 KclValue::Sketches { value: sg }
160 }
161}
162
163impl From<SolidSet> for KclValue {
164 fn from(eg: SolidSet) -> Self {
165 match eg {
166 SolidSet::Solid(eg) => KclValue::Solid { value: eg },
167 SolidSet::Solids(egs) => KclValue::Solids { value: egs },
168 }
169 }
170}
171
172impl From<Vec<Box<Solid>>> for KclValue {
173 fn from(eg: Vec<Box<Solid>>) -> Self {
174 if eg.len() == 1 {
175 KclValue::Solid { value: eg[0].clone() }
176 } else {
177 KclValue::Solids { value: eg }
178 }
179 }
180}
181impl From<KclValue> for Vec<SourceRange> {
182 fn from(item: KclValue) -> Self {
183 match item {
184 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
185 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
186 KclValue::Solid { value } => to_vec_sr(&value.meta),
187 KclValue::Solids { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
188 KclValue::Sketch { value } => to_vec_sr(&value.meta),
189 KclValue::Sketches { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
190 KclValue::Helix { value } => to_vec_sr(&value.meta),
191 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
192 KclValue::Function { meta, .. } => to_vec_sr(&meta),
193 KclValue::Plane { value } => to_vec_sr(&value.meta),
194 KclValue::Face { value } => to_vec_sr(&value.meta),
195 KclValue::Bool { meta, .. } => to_vec_sr(&meta),
196 KclValue::Number { meta, .. } => to_vec_sr(&meta),
197 KclValue::String { meta, .. } => to_vec_sr(&meta),
198 KclValue::MixedArray { meta, .. } => to_vec_sr(&meta),
199 KclValue::Object { meta, .. } => to_vec_sr(&meta),
200 KclValue::Module { meta, .. } => to_vec_sr(&meta),
201 KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
202 KclValue::Type { meta, .. } => to_vec_sr(&meta),
203 KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
204 KclValue::Tombstone { .. } => unreachable!("Tombstone SourceRange"),
205 }
206 }
207}
208
209fn to_vec_sr(meta: &[Metadata]) -> Vec<SourceRange> {
210 meta.iter().map(|m| m.source_range).collect()
211}
212
213impl From<&KclValue> for Vec<SourceRange> {
214 fn from(item: &KclValue) -> Self {
215 match item {
216 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
217 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
218 KclValue::Solid { value } => to_vec_sr(&value.meta),
219 KclValue::Solids { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
220 KclValue::Sketch { value } => to_vec_sr(&value.meta),
221 KclValue::Sketches { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
222 KclValue::Helix { value } => to_vec_sr(&value.meta),
223 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
224 KclValue::Function { meta, .. } => to_vec_sr(meta),
225 KclValue::Plane { value } => to_vec_sr(&value.meta),
226 KclValue::Face { value } => to_vec_sr(&value.meta),
227 KclValue::Bool { meta, .. } => to_vec_sr(meta),
228 KclValue::Number { meta, .. } => to_vec_sr(meta),
229 KclValue::String { meta, .. } => to_vec_sr(meta),
230 KclValue::Uuid { meta, .. } => to_vec_sr(meta),
231 KclValue::MixedArray { meta, .. } => to_vec_sr(meta),
232 KclValue::Object { meta, .. } => to_vec_sr(meta),
233 KclValue::Module { meta, .. } => to_vec_sr(meta),
234 KclValue::KclNone { meta, .. } => to_vec_sr(meta),
235 KclValue::Type { meta, .. } => to_vec_sr(meta),
236 KclValue::Tombstone { .. } => unreachable!("Tombstone &SourceRange"),
237 }
238 }
239}
240
241impl From<&KclValue> for SourceRange {
242 fn from(item: &KclValue) -> Self {
243 let v: Vec<_> = item.into();
244 v.into_iter().next().unwrap_or_default()
245 }
246}
247
248impl KclValue {
249 pub(crate) fn metadata(&self) -> Vec<Metadata> {
250 match self {
251 KclValue::Uuid { value: _, meta } => meta.clone(),
252 KclValue::Bool { value: _, meta } => meta.clone(),
253 KclValue::Number { meta, .. } => meta.clone(),
254 KclValue::String { value: _, meta } => meta.clone(),
255 KclValue::MixedArray { value: _, meta } => meta.clone(),
256 KclValue::Object { value: _, meta } => meta.clone(),
257 KclValue::TagIdentifier(x) => x.meta.clone(),
258 KclValue::TagDeclarator(x) => vec![x.metadata()],
259 KclValue::Plane { value } => value.meta.clone(),
260 KclValue::Face { value } => value.meta.clone(),
261 KclValue::Sketch { value } => value.meta.clone(),
262 KclValue::Sketches { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
263 KclValue::Solid { value } => value.meta.clone(),
264 KclValue::Solids { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
265 KclValue::Helix { value } => value.meta.clone(),
266 KclValue::ImportedGeometry(x) => x.meta.clone(),
267 KclValue::Function { meta, .. } => meta.clone(),
268 KclValue::Module { meta, .. } => meta.clone(),
269 KclValue::KclNone { meta, .. } => meta.clone(),
270 KclValue::Type { meta, .. } => meta.clone(),
271 KclValue::Tombstone { .. } => unreachable!("Tombstone Metadata"),
272 }
273 }
274
275 pub(crate) fn function_def_source_range(&self) -> Option<SourceRange> {
276 let KclValue::Function {
277 value: FunctionSource::User { ast, .. },
278 ..
279 } = self
280 else {
281 return None;
282 };
283 Some(ast.as_source_range())
286 }
287
288 pub(crate) fn get_solid_set(&self) -> Result<SolidSet> {
289 match self {
290 KclValue::Solid { value } => Ok(SolidSet::Solid(value.clone())),
291 KclValue::Solids { value } => Ok(SolidSet::Solids(value.clone())),
292 KclValue::MixedArray { value, .. } => {
293 let solids: Vec<_> = value
294 .iter()
295 .enumerate()
296 .map(|(i, v)| {
297 v.as_solid().map(|v| v.to_owned()).map(Box::new).ok_or_else(|| {
298 anyhow::anyhow!(
299 "expected this array to only contain solids, but element {i} was actually {}",
300 v.human_friendly_type()
301 )
302 })
303 })
304 .collect::<Result<_, _>>()?;
305 Ok(SolidSet::Solids(solids))
306 }
307 _ => anyhow::bail!("Not a solid or solids: {:?}", self),
308 }
309 }
310
311 #[allow(unused)]
312 pub(crate) fn none() -> Self {
313 Self::KclNone {
314 value: Default::default(),
315 meta: Default::default(),
316 }
317 }
318
319 pub(crate) fn human_friendly_type(&self) -> &'static str {
322 match self {
323 KclValue::Uuid { .. } => "Unique ID (uuid)",
324 KclValue::TagDeclarator(_) => "TagDeclarator",
325 KclValue::TagIdentifier(_) => "TagIdentifier",
326 KclValue::Solid { .. } => "Solid",
327 KclValue::Solids { .. } => "Solids",
328 KclValue::Sketch { .. } => "Sketch",
329 KclValue::Sketches { .. } => "Sketches",
330 KclValue::Helix { .. } => "Helix",
331 KclValue::ImportedGeometry(_) => "ImportedGeometry",
332 KclValue::Function { .. } => "Function",
333 KclValue::Plane { .. } => "Plane",
334 KclValue::Face { .. } => "Face",
335 KclValue::Bool { .. } => "boolean (true/false value)",
336 KclValue::Number { .. } => "number",
337 KclValue::String { .. } => "string (text)",
338 KclValue::MixedArray { .. } => "array (list)",
339 KclValue::Object { .. } => "object",
340 KclValue::Module { .. } => "module",
341 KclValue::Type { .. } => "type",
342 KclValue::KclNone { .. } => "None",
343 KclValue::Tombstone { .. } => "TOMBSTONE",
344 }
345 }
346
347 pub(crate) fn from_literal(literal: Node<Literal>, settings: &MetaSettings) -> Self {
348 let meta = vec![literal.metadata()];
349 match literal.inner.value {
350 LiteralValue::Number { value, suffix } => KclValue::Number {
351 value,
352 meta,
353 ty: NumericType::from_parsed(suffix, settings),
354 },
355 LiteralValue::String(value) => KclValue::String { value, meta },
356 LiteralValue::Bool(value) => KclValue::Bool { value, meta },
357 }
358 }
359
360 pub(crate) fn from_default_param(param: DefaultParamVal, settings: &MetaSettings) -> Self {
361 match param {
362 DefaultParamVal::Literal(lit) => Self::from_literal(lit, settings),
363 DefaultParamVal::KclNone(none) => KclValue::KclNone {
364 value: none,
365 meta: Default::default(),
366 },
367 }
368 }
369
370 pub(crate) fn map_env_ref(&self, env_map: &HashMap<EnvironmentRef, EnvironmentRef>) -> Self {
371 let mut result = self.clone();
372 if let KclValue::Function {
373 value: FunctionSource::User { ref mut memory, .. },
374 ..
375 } = result
376 {
377 if let Some(new) = env_map.get(memory) {
378 *memory = *new;
379 }
380 }
381 result
382 }
383
384 pub const fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
386 Self::Number {
387 value: f,
388 meta,
389 ty: NumericType::Unknown,
390 }
391 }
392
393 pub const fn from_number_with_type(f: f64, ty: NumericType, meta: Vec<Metadata>) -> Self {
394 Self::Number { value: f, meta, ty }
395 }
396
397 pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
399 Self::MixedArray {
400 value: vec![
401 Self::Number {
402 value: p[0],
403 meta: meta.clone(),
404 ty: ty.clone(),
405 },
406 Self::Number {
407 value: p[1],
408 meta: meta.clone(),
409 ty,
410 },
411 ],
412 meta,
413 }
414 }
415
416 pub(crate) fn as_usize(&self) -> Option<usize> {
417 match self {
418 KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
419 _ => None,
420 }
421 }
422
423 pub fn as_int(&self) -> Option<i64> {
424 match self {
425 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
426 _ => None,
427 }
428 }
429
430 pub fn as_object(&self) -> Option<&KclObjectFields> {
431 if let KclValue::Object { value, meta: _ } = &self {
432 Some(value)
433 } else {
434 None
435 }
436 }
437
438 pub fn into_object(self) -> Option<KclObjectFields> {
439 if let KclValue::Object { value, meta: _ } = self {
440 Some(value)
441 } else {
442 None
443 }
444 }
445
446 pub fn as_str(&self) -> Option<&str> {
447 if let KclValue::String { value, meta: _ } = &self {
448 Some(value)
449 } else {
450 None
451 }
452 }
453
454 pub fn as_array(&self) -> Option<&[KclValue]> {
455 if let KclValue::MixedArray { value, meta: _ } = &self {
456 Some(value)
457 } else {
458 None
459 }
460 }
461
462 pub fn as_point2d(&self) -> Option<[f64; 2]> {
463 let arr = self.as_array()?;
464 if arr.len() != 2 {
465 return None;
466 }
467 let x = arr[0].as_f64()?;
468 let y = arr[1].as_f64()?;
469 Some([x, y])
470 }
471
472 pub fn as_uuid(&self) -> Option<uuid::Uuid> {
473 if let KclValue::Uuid { value, meta: _ } = &self {
474 Some(*value)
475 } else {
476 None
477 }
478 }
479
480 pub fn as_plane(&self) -> Option<&Plane> {
481 if let KclValue::Plane { value } = &self {
482 Some(value)
483 } else {
484 None
485 }
486 }
487
488 pub fn as_solid(&self) -> Option<&Solid> {
489 if let KclValue::Solid { value } = &self {
490 Some(value)
491 } else {
492 None
493 }
494 }
495
496 pub fn as_sketch(&self) -> Option<&Sketch> {
497 if let KclValue::Sketch { value } = self {
498 Some(value)
499 } else {
500 None
501 }
502 }
503
504 pub fn as_f64(&self) -> Option<f64> {
505 if let KclValue::Number { value, .. } = &self {
506 Some(*value)
507 } else {
508 None
509 }
510 }
511
512 pub fn as_bool(&self) -> Option<bool> {
513 if let KclValue::Bool { value, meta: _ } = &self {
514 Some(*value)
515 } else {
516 None
517 }
518 }
519
520 pub fn get_u32(&self, source_ranges: Vec<SourceRange>) -> Result<u32, KclError> {
522 let u = self.as_int().and_then(|n| u64::try_from(n).ok()).ok_or_else(|| {
523 KclError::Semantic(KclErrorDetails {
524 message: "Expected an integer >= 0".to_owned(),
525 source_ranges: source_ranges.clone(),
526 })
527 })?;
528 u32::try_from(u).map_err(|_| {
529 KclError::Semantic(KclErrorDetails {
530 message: "Number was too big".to_owned(),
531 source_ranges,
532 })
533 })
534 }
535
536 pub fn get_function(&self) -> Option<&FunctionSource> {
538 match self {
539 KclValue::Function { value, .. } => Some(value),
540 _ => None,
541 }
542 }
543
544 pub fn get_tag_identifier(&self) -> Result<TagIdentifier, KclError> {
546 match self {
547 KclValue::TagIdentifier(t) => Ok(*t.clone()),
548 _ => Err(KclError::Semantic(KclErrorDetails {
549 message: format!("Not a tag identifier: {:?}", self),
550 source_ranges: self.clone().into(),
551 })),
552 }
553 }
554
555 pub fn get_tag_declarator(&self) -> Result<TagNode, KclError> {
557 match self {
558 KclValue::TagDeclarator(t) => Ok((**t).clone()),
559 _ => Err(KclError::Semantic(KclErrorDetails {
560 message: format!("Not a tag declarator: {:?}", self),
561 source_ranges: self.clone().into(),
562 })),
563 }
564 }
565
566 pub fn get_tag_declarator_opt(&self) -> Result<Option<TagNode>, KclError> {
568 match self {
569 KclValue::TagDeclarator(t) => Ok(Some((**t).clone())),
570 _ => Err(KclError::Semantic(KclErrorDetails {
571 message: format!("Not a tag declarator: {:?}", self),
572 source_ranges: self.clone().into(),
573 })),
574 }
575 }
576
577 pub fn get_bool(&self) -> Result<bool, KclError> {
579 let Self::Bool { value: b, .. } = self else {
580 return Err(KclError::Type(KclErrorDetails {
581 source_ranges: self.into(),
582 message: format!("Expected bool, found {}", self.human_friendly_type()),
583 }));
584 };
585 Ok(*b)
586 }
587
588 pub fn has_type(&self, ty: &RuntimeType) -> bool {
590 let Some(self_ty) = self.principal_type() else {
591 return false;
592 };
593
594 self_ty.subtype(ty)
595 }
596
597 pub fn principal_type(&self) -> Option<RuntimeType> {
598 match self {
599 KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
600 KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(ty.clone()))),
601 KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)),
602 KclValue::Object { value, .. } => {
603 let properties = value
604 .iter()
605 .map(|(k, v)| v.principal_type().map(|t| (k.clone(), t)))
606 .collect::<Option<Vec<_>>>()?;
607 Some(RuntimeType::Object(properties))
608 }
609 KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
610 KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
611 KclValue::Sketches { .. } => Some(RuntimeType::Array(PrimitiveType::Sketch)),
612 KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
613 KclValue::Solids { .. } => Some(RuntimeType::Array(PrimitiveType::Solid)),
614 KclValue::MixedArray { value, .. } => Some(RuntimeType::Tuple(
615 value
616 .iter()
617 .map(|v| v.principal_type().and_then(RuntimeType::primitive))
618 .collect::<Option<Vec<_>>>()?,
619 )),
620 KclValue::Face { .. } => None,
621 KclValue::Helix { .. }
622 | KclValue::ImportedGeometry(..)
623 | KclValue::Function { .. }
624 | KclValue::Module { .. }
625 | KclValue::TagIdentifier(_)
626 | KclValue::TagDeclarator(_)
627 | KclValue::KclNone { .. }
628 | KclValue::Type { .. }
629 | KclValue::Uuid { .. }
630 | KclValue::Tombstone { .. } => None,
631 }
632 }
633
634 pub async fn call_fn(
637 &self,
638 args: Vec<Arg>,
639 exec_state: &mut ExecState,
640 ctx: ExecutorContext,
641 source_range: SourceRange,
642 ) -> Result<Option<KclValue>, KclError> {
643 match self {
644 KclValue::Function {
645 value: FunctionSource::Std { func, props },
646 ..
647 } => {
648 if props.deprecated {
649 exec_state.warn(CompilationError::err(
650 source_range,
651 format!(
652 "`{}` is deprecated, see the docs for a recommended replacement",
653 props.name
654 ),
655 ));
656 }
657 exec_state.mut_stack().push_new_env_for_rust_call();
658 let args = crate::std::Args::new(
659 args,
660 source_range,
661 ctx.clone(),
662 exec_state.mod_local.pipe_value.clone().map(Arg::synthetic),
663 );
664 let result = func(exec_state, args).await.map(Some);
665 exec_state.mut_stack().pop_env();
666 result
667 }
668 KclValue::Function {
669 value: FunctionSource::User { ast, memory, .. },
670 ..
671 } => crate::execution::exec_ast::call_user_defined_function(args, *memory, ast, exec_state, &ctx).await,
672 _ => Err(KclError::Semantic(KclErrorDetails {
673 message: "cannot call this because it isn't a function".to_string(),
674 source_ranges: vec![source_range],
675 })),
676 }
677 }
678
679 pub async fn call_fn_kw(
682 &self,
683 args: crate::std::Args,
684 exec_state: &mut ExecState,
685 ctx: ExecutorContext,
686 callsite: SourceRange,
687 ) -> Result<Option<KclValue>, KclError> {
688 match self {
689 KclValue::Function {
690 value: FunctionSource::Std { func: _, props },
691 ..
692 } => {
693 if props.deprecated {
694 exec_state.warn(CompilationError::err(
695 callsite,
696 format!(
697 "`{}` is deprecated, see the docs for a recommended replacement",
698 props.name
699 ),
700 ));
701 }
702 todo!("Implement KCL stdlib fns with keyword args");
703 }
704 KclValue::Function {
705 value: FunctionSource::User { ast, memory, .. },
706 ..
707 } => {
708 crate::execution::exec_ast::call_user_defined_function_kw(args.kw_args, *memory, ast, exec_state, &ctx)
709 .await
710 }
711 _ => Err(KclError::Semantic(KclErrorDetails {
712 message: "cannot call this because it isn't a function".to_string(),
713 source_ranges: vec![callsite],
714 })),
715 }
716 }
717
718 pub fn value_str(&self) -> Option<String> {
719 match self {
720 KclValue::Bool { value, .. } => Some(format!("{value}")),
721 KclValue::Number { value, .. } => Some(format!("{value}")),
722 KclValue::String { value, .. } => Some(format!("'{value}'")),
723 KclValue::Uuid { value, .. } => Some(format!("{value}")),
724 KclValue::TagDeclarator(tag) => Some(format!("${}", tag.name)),
725 KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
726 KclValue::MixedArray { .. } => Some("[...]".to_owned()),
728 KclValue::Object { .. } => Some("{ ... }".to_owned()),
729 KclValue::Module { .. }
730 | KclValue::Solid { .. }
731 | KclValue::Solids { .. }
732 | KclValue::Sketch { .. }
733 | KclValue::Sketches { .. }
734 | KclValue::Helix { .. }
735 | KclValue::ImportedGeometry(_)
736 | KclValue::Function { .. }
737 | KclValue::Plane { .. }
738 | KclValue::Face { .. }
739 | KclValue::KclNone { .. }
740 | KclValue::Type { .. }
741 | KclValue::Tombstone { .. } => None,
742 }
743 }
744}
745
746#[derive(Debug, Clone, PartialEq)]
747pub enum RuntimeType {
748 Primitive(PrimitiveType),
749 Array(PrimitiveType),
750 Tuple(Vec<PrimitiveType>),
751 Object(Vec<(String, RuntimeType)>),
752}
753
754impl RuntimeType {
755 pub fn from_parsed(
756 value: Type,
757 exec_state: &mut ExecState,
758 source_range: SourceRange,
759 ) -> Result<Option<Self>, CompilationError> {
760 Ok(match value {
761 Type::Primitive(pt) => {
762 PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(RuntimeType::Primitive)
763 }
764 Type::Array(pt) => PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(RuntimeType::Array),
765 Type::Object { properties } => properties
766 .into_iter()
767 .map(|p| {
768 let pt = match p.type_ {
769 Some(t) => t,
770 None => return Ok(None),
771 };
772 Ok(RuntimeType::from_parsed(pt.inner, exec_state, source_range)?
773 .map(|ty| (p.identifier.inner.name, ty)))
774 })
775 .collect::<Result<Option<Vec<_>>, CompilationError>>()?
776 .map(RuntimeType::Object),
777 })
778 }
779
780 fn subtype(&self, sup: &RuntimeType) -> bool {
782 use RuntimeType::*;
783
784 match (self, sup) {
785 (Primitive(t1), Primitive(t2)) | (Array(t1), Array(t2)) => t1 == t2,
787 (Tuple(t1), Tuple(t2)) => t1 == t2,
788 (Tuple(t1), Array(t2)) => t1.iter().all(|t| t == t2),
789 (Object(t1), Object(t2)) => t1 == t2,
791 _ => false,
792 }
793 }
794
795 fn primitive(self) -> Option<PrimitiveType> {
796 match self {
797 RuntimeType::Primitive(t) => Some(t),
798 _ => None,
799 }
800 }
801}
802
803impl fmt::Display for RuntimeType {
804 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805 match self {
806 RuntimeType::Primitive(t) => t.fmt(f),
807 RuntimeType::Array(t) => write!(f, "[{t}]"),
808 RuntimeType::Tuple(ts) => write!(
809 f,
810 "[{}]",
811 ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
812 ),
813 RuntimeType::Object(items) => write!(
814 f,
815 "{{ {} }}",
816 items
817 .iter()
818 .map(|(n, t)| format!("{n}: {t}"))
819 .collect::<Vec<_>>()
820 .join(", ")
821 ),
822 }
823 }
824}
825
826#[derive(Debug, Clone, PartialEq)]
827pub enum PrimitiveType {
828 Number(NumericType),
829 String,
830 Boolean,
831 Sketch,
832 Solid,
833 Plane,
834}
835
836impl PrimitiveType {
837 fn from_parsed(
838 value: AstPrimitiveType,
839 exec_state: &mut ExecState,
840 source_range: SourceRange,
841 ) -> Result<Option<Self>, CompilationError> {
842 Ok(match value {
843 AstPrimitiveType::String => Some(PrimitiveType::String),
844 AstPrimitiveType::Boolean => Some(PrimitiveType::Boolean),
845 AstPrimitiveType::Number(suffix) => Some(PrimitiveType::Number(NumericType::from_parsed(
846 suffix,
847 &exec_state.mod_local.settings,
848 ))),
849 AstPrimitiveType::Named(name) => {
850 let ty_val = exec_state
851 .stack()
852 .get(&format!("{}{}", memory::TYPE_PREFIX, name.name), source_range)
853 .map_err(|_| CompilationError::err(source_range, format!("Unknown type: {}", name.name)))?;
854
855 let (ty, _) = match ty_val {
856 KclValue::Type { value: Some(ty), .. } => ty,
857 _ => unreachable!(),
858 };
859
860 Some(ty.clone())
861 }
862 _ => None,
863 })
864 }
865}
866
867impl fmt::Display for PrimitiveType {
868 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
869 match self {
870 PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
871 PrimitiveType::Number(_) => write!(f, "number"),
872 PrimitiveType::String => write!(f, "string"),
873 PrimitiveType::Boolean => write!(f, "bool"),
874 PrimitiveType::Sketch => write!(f, "Sketch"),
875 PrimitiveType::Solid => write!(f, "Solid"),
876 PrimitiveType::Plane => write!(f, "Plane"),
877 }
878 }
879}
880
881#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
882#[ts(export)]
883#[serde(tag = "type")]
884pub enum NumericType {
885 Known(UnitType),
887 Default { len: UnitLen, angle: UnitAngle },
889 Unknown,
891 Any,
893}
894
895impl NumericType {
896 pub fn count() -> Self {
897 NumericType::Known(UnitType::Count)
898 }
899
900 pub fn combine_eq(self, other: &NumericType) -> NumericType {
902 if &self == other {
903 self
904 } else {
905 NumericType::Unknown
906 }
907 }
908
909 pub fn combine_n_eq(tys: &[NumericType]) -> NumericType {
913 let ty0 = tys[0].clone();
914 for t in &tys[1..] {
915 if t != &ty0 {
916 return NumericType::Unknown;
917 }
918 }
919 ty0
920 }
921
922 pub fn combine_add(a: NumericType, b: NumericType) -> NumericType {
924 if a == b {
925 return a;
926 }
927 NumericType::Unknown
928 }
929
930 pub fn combine_mul(a: NumericType, b: NumericType) -> NumericType {
932 if a == NumericType::count() {
933 return b;
934 }
935 if b == NumericType::count() {
936 return a;
937 }
938 NumericType::Unknown
939 }
940
941 pub fn combine_div(a: NumericType, b: NumericType) -> NumericType {
943 if b == NumericType::count() {
944 return a;
945 }
946 NumericType::Unknown
947 }
948
949 pub fn from_parsed(suffix: NumericSuffix, settings: &super::MetaSettings) -> Self {
950 match suffix {
951 NumericSuffix::None => NumericType::Default {
952 len: settings.default_length_units,
953 angle: settings.default_angle_units,
954 },
955 NumericSuffix::Count => NumericType::Known(UnitType::Count),
956 NumericSuffix::Mm => NumericType::Known(UnitType::Length(UnitLen::Mm)),
957 NumericSuffix::Cm => NumericType::Known(UnitType::Length(UnitLen::Cm)),
958 NumericSuffix::M => NumericType::Known(UnitType::Length(UnitLen::M)),
959 NumericSuffix::Inch => NumericType::Known(UnitType::Length(UnitLen::Inches)),
960 NumericSuffix::Ft => NumericType::Known(UnitType::Length(UnitLen::Feet)),
961 NumericSuffix::Yd => NumericType::Known(UnitType::Length(UnitLen::Yards)),
962 NumericSuffix::Deg => NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
963 NumericSuffix::Rad => NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
964 }
965 }
966}
967
968impl From<UnitLen> for NumericType {
969 fn from(value: UnitLen) -> Self {
970 NumericType::Known(UnitType::Length(value))
971 }
972}
973
974impl From<UnitAngle> for NumericType {
975 fn from(value: UnitAngle) -> Self {
976 NumericType::Known(UnitType::Angle(value))
977 }
978}
979
980#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
981#[ts(export)]
982#[serde(tag = "type")]
983pub enum UnitType {
984 Count,
985 Length(UnitLen),
986 Angle(UnitAngle),
987}
988
989impl std::fmt::Display for UnitType {
990 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
991 match self {
992 UnitType::Count => write!(f, "_"),
993 UnitType::Length(l) => l.fmt(f),
994 UnitType::Angle(a) => a.fmt(f),
995 }
996 }
997}
998
999#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
1002#[ts(export)]
1003#[serde(tag = "type")]
1004pub enum UnitLen {
1005 #[default]
1006 Mm,
1007 Cm,
1008 M,
1009 Inches,
1010 Feet,
1011 Yards,
1012}
1013
1014impl std::fmt::Display for UnitLen {
1015 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1016 match self {
1017 UnitLen::Mm => write!(f, "mm"),
1018 UnitLen::Cm => write!(f, "cm"),
1019 UnitLen::M => write!(f, "m"),
1020 UnitLen::Inches => write!(f, "in"),
1021 UnitLen::Feet => write!(f, "ft"),
1022 UnitLen::Yards => write!(f, "yd"),
1023 }
1024 }
1025}
1026
1027impl TryFrom<NumericSuffix> for UnitLen {
1028 type Error = ();
1029
1030 fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
1031 match suffix {
1032 NumericSuffix::Mm => Ok(Self::Mm),
1033 NumericSuffix::Cm => Ok(Self::Cm),
1034 NumericSuffix::M => Ok(Self::M),
1035 NumericSuffix::Inch => Ok(Self::Inches),
1036 NumericSuffix::Ft => Ok(Self::Feet),
1037 NumericSuffix::Yd => Ok(Self::Yards),
1038 _ => Err(()),
1039 }
1040 }
1041}
1042
1043impl From<crate::UnitLength> for UnitLen {
1044 fn from(unit: crate::UnitLength) -> Self {
1045 match unit {
1046 crate::UnitLength::Cm => UnitLen::Cm,
1047 crate::UnitLength::Ft => UnitLen::Feet,
1048 crate::UnitLength::In => UnitLen::Inches,
1049 crate::UnitLength::M => UnitLen::M,
1050 crate::UnitLength::Mm => UnitLen::Mm,
1051 crate::UnitLength::Yd => UnitLen::Yards,
1052 }
1053 }
1054}
1055
1056impl From<UnitLen> for crate::UnitLength {
1057 fn from(unit: UnitLen) -> Self {
1058 match unit {
1059 UnitLen::Cm => crate::UnitLength::Cm,
1060 UnitLen::Feet => crate::UnitLength::Ft,
1061 UnitLen::Inches => crate::UnitLength::In,
1062 UnitLen::M => crate::UnitLength::M,
1063 UnitLen::Mm => crate::UnitLength::Mm,
1064 UnitLen::Yards => crate::UnitLength::Yd,
1065 }
1066 }
1067}
1068
1069impl From<UnitLen> for kittycad_modeling_cmds::units::UnitLength {
1070 fn from(unit: UnitLen) -> Self {
1071 match unit {
1072 UnitLen::Cm => kittycad_modeling_cmds::units::UnitLength::Centimeters,
1073 UnitLen::Feet => kittycad_modeling_cmds::units::UnitLength::Feet,
1074 UnitLen::Inches => kittycad_modeling_cmds::units::UnitLength::Inches,
1075 UnitLen::M => kittycad_modeling_cmds::units::UnitLength::Meters,
1076 UnitLen::Mm => kittycad_modeling_cmds::units::UnitLength::Millimeters,
1077 UnitLen::Yards => kittycad_modeling_cmds::units::UnitLength::Yards,
1078 }
1079 }
1080}
1081
1082#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
1084#[ts(export)]
1085#[serde(tag = "type")]
1086pub enum UnitAngle {
1087 #[default]
1088 Degrees,
1089 Radians,
1090}
1091
1092impl std::fmt::Display for UnitAngle {
1093 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1094 match self {
1095 UnitAngle::Degrees => write!(f, "deg"),
1096 UnitAngle::Radians => write!(f, "rad"),
1097 }
1098 }
1099}
1100
1101impl TryFrom<NumericSuffix> for UnitAngle {
1102 type Error = ();
1103
1104 fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
1105 match suffix {
1106 NumericSuffix::Deg => Ok(Self::Degrees),
1107 NumericSuffix::Rad => Ok(Self::Radians),
1108 _ => Err(()),
1109 }
1110 }
1111}