1use std::collections::HashMap;
2
3use anyhow::Result;
4use indexmap::IndexMap;
5use kittycad_modeling_cmds::units::UnitLength;
6use serde::Serialize;
7
8use crate::CompilationIssue;
9use crate::KclError;
10use crate::ModuleId;
11use crate::SourceRange;
12use crate::errors::KclErrorDetails;
13use crate::execution::AbstractSegment;
14use crate::execution::BoundedEdge;
15use crate::execution::EnvironmentRef;
16use crate::execution::ExecState;
17use crate::execution::Face;
18use crate::execution::GdtAnnotation;
19use crate::execution::Geometry;
20use crate::execution::GeometryWithImportedGeometry;
21use crate::execution::Helix;
22use crate::execution::ImportedGeometry;
23use crate::execution::Metadata;
24use crate::execution::Plane;
25use crate::execution::Segment;
26use crate::execution::SegmentRepr;
27use crate::execution::Sketch;
28use crate::execution::SketchConstraint;
29use crate::execution::SketchVar;
30use crate::execution::SketchVarId;
31use crate::execution::Solid;
32use crate::execution::TagIdentifier;
33use crate::execution::UnsolvedExpr;
34use crate::execution::annotations::FnAttrs;
35use crate::execution::annotations::SETTINGS;
36use crate::execution::annotations::SETTINGS_UNIT_LENGTH;
37use crate::execution::annotations::VersionConstraint;
38use crate::execution::annotations::{self};
39use crate::execution::types::NumericType;
40use crate::execution::types::PrimitiveType;
41use crate::execution::types::RuntimeType;
42use crate::parsing::ast::types::DefaultParamVal;
43use crate::parsing::ast::types::FunctionExpression;
44use crate::parsing::ast::types::KclNone;
45use crate::parsing::ast::types::Literal;
46use crate::parsing::ast::types::LiteralValue;
47use crate::parsing::ast::types::Node;
48use crate::parsing::ast::types::NumericLiteral;
49use crate::parsing::ast::types::TagDeclarator;
50use crate::parsing::ast::types::TagNode;
51use crate::parsing::ast::types::Type;
52use crate::std::StdFnProps;
53use crate::std::args::TyF64;
54
55pub type KclObjectFields = HashMap<String, KclValue>;
56
57#[derive(Debug, Clone, Default, PartialEq, Serialize)]
58pub enum KclObjectKind {
59 #[default]
60 Default,
61 SketchTags {
62 #[serde(default, skip_serializing_if = "Vec::is_empty")]
63 deprecated_solid_tag_names: Vec<String>,
64 },
65}
66
67impl KclObjectKind {
68 pub(crate) fn is_default(&self) -> bool {
69 match self {
70 KclObjectKind::Default => true,
71 KclObjectKind::SketchTags { .. } => false,
72 }
73 }
74
75 pub(crate) fn deprecated_solid_tag_names(&self) -> &[String] {
76 match self {
77 Self::Default => &[],
78 Self::SketchTags {
79 deprecated_solid_tag_names,
80 } => deprecated_solid_tag_names,
81 }
82 }
83}
84
85#[derive(Debug, Clone, Serialize, PartialEq)]
87#[serde(tag = "type")]
88pub enum KclValue {
89 Uuid {
90 value: ::uuid::Uuid,
91 #[serde(skip)]
92 meta: Vec<Metadata>,
93 },
94 Bool {
95 value: bool,
96 #[serde(skip)]
97 meta: Vec<Metadata>,
98 },
99 Number {
100 value: f64,
101 ty: NumericType,
102 #[serde(skip)]
103 meta: Vec<Metadata>,
104 },
105 String {
106 value: String,
107 #[serde(skip)]
108 meta: Vec<Metadata>,
109 },
110 SketchVar {
111 value: Box<SketchVar>,
112 },
113 SketchConstraint {
114 value: Box<SketchConstraint>,
115 },
116 Tuple {
117 value: Vec<KclValue>,
118 #[serde(skip)]
119 meta: Vec<Metadata>,
120 },
121 HomArray {
123 value: Vec<KclValue>,
124 #[serde(skip)]
126 ty: RuntimeType,
127 },
128 Object {
129 value: KclObjectFields,
130 constrainable: bool,
131 #[serde(default, skip_serializing_if = "KclObjectKind::is_default")]
132 object_kind: KclObjectKind,
133 #[serde(skip)]
134 meta: Vec<Metadata>,
135 },
136 TagIdentifier(Box<TagIdentifier>),
137 TagDeclarator(crate::parsing::ast::types::BoxNode<TagDeclarator>),
138 GdtAnnotation {
139 value: Box<GdtAnnotation>,
140 },
141 Plane {
142 value: Box<Plane>,
143 },
144 Face {
145 value: Box<Face>,
146 },
147 BoundedEdge {
148 value: BoundedEdge,
149 meta: Vec<Metadata>,
150 },
151 Segment {
152 value: Box<AbstractSegment>,
153 },
154 Sketch {
155 value: Box<Sketch>,
156 },
157 Solid {
158 value: Box<Solid>,
159 },
160 Helix {
161 value: Box<Helix>,
162 },
163 ImportedGeometry(ImportedGeometry),
164 Function {
165 #[serde(serialize_with = "function_value_stub")]
166 value: Box<FunctionSource>,
167 #[serde(skip)]
168 meta: Vec<Metadata>,
169 },
170 Module {
171 value: ModuleId,
172 #[serde(skip)]
173 meta: Vec<Metadata>,
174 },
175 Type {
176 #[serde(skip)]
177 value: TypeDef,
178 experimental: bool,
179 #[serde(skip)]
180 meta: Vec<Metadata>,
181 },
182 KclNone {
183 value: KclNone,
184 #[serde(skip)]
185 meta: Vec<Metadata>,
186 },
187}
188
189fn function_value_stub<S>(_value: &FunctionSource, serializer: S) -> Result<S::Ok, S::Error>
190where
191 S: serde::Serializer,
192{
193 serializer.serialize_unit()
194}
195
196#[derive(Debug, Clone, PartialEq)]
197pub struct NamedParam {
198 pub experimental: bool,
199 pub deprecated: bool,
201 pub deprecated_since: Option<VersionConstraint>,
203 pub default_value: Option<DefaultParamVal>,
204 pub ty: Option<Type>,
205}
206
207#[derive(Debug, Clone, PartialEq)]
208pub struct FunctionSource {
209 pub input_arg: Option<(String, Option<Type>)>,
210 pub named_args: IndexMap<String, NamedParam>,
211 pub return_type: Option<Node<Type>>,
212 pub deprecated: bool,
213 pub deprecated_since: Option<VersionConstraint>,
217 pub experimental: bool,
218 pub include_in_feature_tree: bool,
219 pub std_props: Option<StdFnProps>,
220 pub body: FunctionBody,
221 pub ast: crate::parsing::ast::types::BoxNode<FunctionExpression>,
222}
223
224pub struct KclFunctionSourceParams {
225 pub std_props: Option<StdFnProps>,
226 pub experimental: bool,
227 pub include_in_feature_tree: bool,
228}
229
230impl FunctionSource {
231 pub fn rust(
232 func: crate::std::StdFn,
233 ast: Box<Node<FunctionExpression>>,
234 props: StdFnProps,
235 attrs: FnAttrs,
236 ) -> Self {
237 let (input_arg, named_args) = Self::args_from_ast(&ast);
238
239 FunctionSource {
240 input_arg,
241 named_args,
242 return_type: ast.return_type.clone(),
243 deprecated: attrs.deprecated,
244 deprecated_since: attrs.deprecated_since,
245 experimental: attrs.experimental,
246 include_in_feature_tree: attrs.include_in_feature_tree,
247 std_props: Some(props),
248 body: FunctionBody::Rust(func),
249 ast,
250 }
251 }
252
253 pub fn kcl(ast: Box<Node<FunctionExpression>>, memory: EnvironmentRef, params: KclFunctionSourceParams) -> Self {
254 let KclFunctionSourceParams {
255 std_props,
256 experimental,
257 include_in_feature_tree,
258 } = params;
259 let (input_arg, named_args) = Self::args_from_ast(&ast);
260 FunctionSource {
261 input_arg,
262 named_args,
263 return_type: ast.return_type.clone(),
264 deprecated: false,
265 deprecated_since: None,
266 experimental,
267 include_in_feature_tree,
268 std_props,
269 body: FunctionBody::Kcl(memory),
270 ast,
271 }
272 }
273
274 #[expect(clippy::type_complexity)]
275 fn args_from_ast(ast: &FunctionExpression) -> (Option<(String, Option<Type>)>, IndexMap<String, NamedParam>) {
276 let mut input_arg = None;
277 let mut named_args = IndexMap::new();
278 for p in &ast.params {
279 if !p.labeled {
280 input_arg = Some((
281 p.identifier.name.clone(),
282 p.param_type.as_ref().map(|t| t.inner.clone()),
283 ));
284 continue;
285 }
286
287 named_args.insert(
288 p.identifier.name.clone(),
289 NamedParam {
290 experimental: p.experimental,
291 deprecated: p.deprecated,
292 deprecated_since: p.deprecated_since.clone(),
293 default_value: p.default_value.clone(),
294 ty: p.param_type.as_ref().map(|t| t.inner.clone()),
295 },
296 );
297 }
298
299 (input_arg, named_args)
300 }
301
302 pub(crate) fn is_std(&self) -> bool {
303 self.std_props.is_some()
304 }
305}
306
307#[derive(Debug, Clone, PartialEq)]
308#[allow(unpredictable_function_pointer_comparisons)]
311pub enum FunctionBody {
312 Rust(crate::std::StdFn),
313 Kcl(EnvironmentRef),
314}
315
316#[derive(Debug, Clone, PartialEq)]
317pub enum TypeDef {
318 RustRepr(PrimitiveType, StdFnProps),
319 Alias(RuntimeType),
320}
321
322impl From<Vec<GdtAnnotation>> for KclValue {
323 fn from(mut values: Vec<GdtAnnotation>) -> Self {
324 if values.len() == 1 {
325 let value = values.pop().expect("Just checked len == 1");
326 KclValue::GdtAnnotation { value: Box::new(value) }
327 } else {
328 KclValue::HomArray {
329 value: values
330 .into_iter()
331 .map(|s| KclValue::GdtAnnotation { value: Box::new(s) })
332 .collect(),
333 ty: RuntimeType::Primitive(PrimitiveType::GdtAnnotation),
334 }
335 }
336 }
337}
338
339impl From<Vec<Sketch>> for KclValue {
340 fn from(mut eg: Vec<Sketch>) -> Self {
341 if eg.len() == 1
342 && let Some(s) = eg.pop()
343 {
344 KclValue::Sketch { value: Box::new(s) }
345 } else {
346 KclValue::HomArray {
347 value: eg
348 .into_iter()
349 .map(|s| KclValue::Sketch { value: Box::new(s) })
350 .collect(),
351 ty: RuntimeType::Primitive(PrimitiveType::Sketch),
352 }
353 }
354 }
355}
356
357impl From<Vec<Solid>> for KclValue {
358 fn from(mut eg: Vec<Solid>) -> Self {
359 if eg.len() == 1
360 && let Some(s) = eg.pop()
361 {
362 KclValue::Solid { value: Box::new(s) }
363 } else {
364 KclValue::HomArray {
365 value: eg.into_iter().map(|s| KclValue::Solid { value: Box::new(s) }).collect(),
366 ty: RuntimeType::Primitive(PrimitiveType::Solid),
367 }
368 }
369 }
370}
371
372impl From<KclValue> for Vec<SourceRange> {
373 fn from(item: KclValue) -> Self {
374 match item {
375 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
376 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
377 KclValue::GdtAnnotation { value } => to_vec_sr(&value.meta),
378 KclValue::Solid { value } => to_vec_sr(&value.meta),
379 KclValue::Sketch { value } => to_vec_sr(&value.meta),
380 KclValue::Helix { value } => to_vec_sr(&value.meta),
381 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
382 KclValue::Function { meta, .. } => to_vec_sr(&meta),
383 KclValue::Plane { value } => to_vec_sr(&value.meta),
384 KclValue::Face { value } => to_vec_sr(&value.meta),
385 KclValue::Segment { value } => to_vec_sr(&value.meta),
386 KclValue::Bool { meta, .. } => to_vec_sr(&meta),
387 KclValue::Number { meta, .. } => to_vec_sr(&meta),
388 KclValue::String { meta, .. } => to_vec_sr(&meta),
389 KclValue::SketchVar { value, .. } => to_vec_sr(&value.meta),
390 KclValue::SketchConstraint { value, .. } => to_vec_sr(&value.meta),
391 KclValue::Tuple { meta, .. } => to_vec_sr(&meta),
392 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
393 KclValue::Object { meta, .. } => to_vec_sr(&meta),
394 KclValue::Module { meta, .. } => to_vec_sr(&meta),
395 KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
396 KclValue::Type { meta, .. } => to_vec_sr(&meta),
397 KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
398 KclValue::BoundedEdge { meta, .. } => to_vec_sr(&meta),
399 }
400 }
401}
402
403fn to_vec_sr(meta: &[Metadata]) -> Vec<SourceRange> {
404 meta.iter().map(|m| m.source_range).collect()
405}
406
407impl From<&KclValue> for Vec<SourceRange> {
408 fn from(item: &KclValue) -> Self {
409 match item {
410 KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
411 KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
412 KclValue::GdtAnnotation { value } => to_vec_sr(&value.meta),
413 KclValue::Solid { value } => to_vec_sr(&value.meta),
414 KclValue::Sketch { value } => to_vec_sr(&value.meta),
415 KclValue::Helix { value } => to_vec_sr(&value.meta),
416 KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
417 KclValue::Function { meta, .. } => to_vec_sr(meta),
418 KclValue::Plane { value } => to_vec_sr(&value.meta),
419 KclValue::Face { value } => to_vec_sr(&value.meta),
420 KclValue::Segment { value } => to_vec_sr(&value.meta),
421 KclValue::Bool { meta, .. } => to_vec_sr(meta),
422 KclValue::Number { meta, .. } => to_vec_sr(meta),
423 KclValue::String { meta, .. } => to_vec_sr(meta),
424 KclValue::SketchVar { value, .. } => to_vec_sr(&value.meta),
425 KclValue::SketchConstraint { value, .. } => to_vec_sr(&value.meta),
426 KclValue::Uuid { meta, .. } => to_vec_sr(meta),
427 KclValue::Tuple { meta, .. } => to_vec_sr(meta),
428 KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
429 KclValue::Object { meta, .. } => to_vec_sr(meta),
430 KclValue::Module { meta, .. } => to_vec_sr(meta),
431 KclValue::KclNone { meta, .. } => to_vec_sr(meta),
432 KclValue::Type { meta, .. } => to_vec_sr(meta),
433 KclValue::BoundedEdge { meta, .. } => to_vec_sr(meta),
434 }
435 }
436}
437
438impl From<&KclValue> for SourceRange {
439 fn from(item: &KclValue) -> Self {
440 let v: Vec<_> = item.into();
441 v.into_iter().next().unwrap_or_default()
442 }
443}
444
445impl KclValue {
446 pub(crate) fn metadata(&self) -> Vec<Metadata> {
447 match self {
448 KclValue::Uuid { value: _, meta } => meta.clone(),
449 KclValue::Bool { value: _, meta } => meta.clone(),
450 KclValue::Number { meta, .. } => meta.clone(),
451 KclValue::String { value: _, meta } => meta.clone(),
452 KclValue::SketchVar { value, .. } => value.meta.clone(),
453 KclValue::SketchConstraint { value, .. } => value.meta.clone(),
454 KclValue::Tuple { value: _, meta } => meta.clone(),
455 KclValue::HomArray { value, .. } => value.iter().flat_map(|v| v.metadata()).collect(),
456 KclValue::Object { meta, .. } => meta.clone(),
457 KclValue::TagIdentifier(x) => x.meta.clone(),
458 KclValue::TagDeclarator(x) => vec![x.metadata()],
459 KclValue::GdtAnnotation { value } => value.meta.clone(),
460 KclValue::Plane { value } => value.meta.clone(),
461 KclValue::Face { value } => value.meta.clone(),
462 KclValue::Segment { value } => value.meta.clone(),
463 KclValue::Sketch { value } => value.meta.clone(),
464 KclValue::Solid { value } => value.meta.clone(),
465 KclValue::Helix { value } => value.meta.clone(),
466 KclValue::ImportedGeometry(x) => x.meta.clone(),
467 KclValue::Function { meta, .. } => meta.clone(),
468 KclValue::Module { meta, .. } => meta.clone(),
469 KclValue::KclNone { meta, .. } => meta.clone(),
470 KclValue::Type { meta, .. } => meta.clone(),
471 KclValue::BoundedEdge { meta, .. } => meta.clone(),
472 }
473 }
474
475 #[allow(unused)]
476 pub(crate) fn none() -> Self {
477 Self::KclNone {
478 value: Default::default(),
479 meta: Default::default(),
480 }
481 }
482
483 pub(crate) fn show_variable_in_feature_tree(&self) -> bool {
487 match self {
488 KclValue::Uuid { .. } => false,
489 KclValue::Bool { .. } | KclValue::Number { .. } | KclValue::String { .. } => true,
490 KclValue::SketchVar { .. }
491 | KclValue::SketchConstraint { .. }
492 | KclValue::Tuple { .. }
493 | KclValue::HomArray { .. }
494 | KclValue::Object { .. }
495 | KclValue::TagIdentifier(_)
496 | KclValue::TagDeclarator(_)
497 | KclValue::GdtAnnotation { .. }
498 | KclValue::Plane { .. }
499 | KclValue::Face { .. }
500 | KclValue::Segment { .. }
501 | KclValue::Sketch { .. }
502 | KclValue::Solid { .. }
503 | KclValue::Helix { .. }
504 | KclValue::ImportedGeometry(_)
505 | KclValue::Function { .. }
506 | KclValue::Module { .. }
507 | KclValue::Type { .. }
508 | KclValue::BoundedEdge { .. }
509 | KclValue::KclNone { .. } => false,
510 }
511 }
512
513 pub(crate) fn human_friendly_type(&self) -> String {
516 match self {
517 KclValue::Uuid { .. } => "a unique ID (uuid)".to_owned(),
518 KclValue::TagDeclarator(_) => "a tag declarator".to_owned(),
519 KclValue::TagIdentifier(_) => "a tag identifier".to_owned(),
520 KclValue::GdtAnnotation { .. } => "an annotation".to_owned(),
521 KclValue::Solid { .. } => "a solid".to_owned(),
522 KclValue::Sketch { .. } => "a sketch".to_owned(),
523 KclValue::Helix { .. } => "a helix".to_owned(),
524 KclValue::ImportedGeometry(_) => "an imported geometry".to_owned(),
525 KclValue::Function { .. } => "a function".to_owned(),
526 KclValue::Plane { .. } => "a plane".to_owned(),
527 KclValue::Face { .. } => "a face".to_owned(),
528 KclValue::Segment { .. } => "a segment".to_owned(),
529 KclValue::Bool { .. } => "a boolean (`true` or `false`)".to_owned(),
530 KclValue::Number {
531 ty: NumericType::Unknown,
532 ..
533 } => "a number with unknown units".to_owned(),
534 KclValue::Number {
535 ty: NumericType::Known(units),
536 ..
537 } => format!("a number ({units})"),
538 KclValue::Number { .. } => "a number".to_owned(),
539 KclValue::String { .. } => "a string".to_owned(),
540 KclValue::SketchVar { .. } => "a sketch variable".to_owned(),
541 KclValue::SketchConstraint { .. } => "a sketch constraint".to_owned(),
542 KclValue::Object { .. } => "an object".to_owned(),
543 KclValue::Module { .. } => "a module".to_owned(),
544 KclValue::Type { .. } => "a type".to_owned(),
545 KclValue::KclNone { .. } => "none".to_owned(),
546 KclValue::BoundedEdge { .. } => "a bounded edge".to_owned(),
547 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
548 if value.is_empty() {
549 "an empty array".to_owned()
550 } else {
551 const MAX: usize = 3;
553
554 let len = value.len();
555 let element_tys = value
556 .iter()
557 .take(MAX)
558 .map(|elem| elem.principal_type_string())
559 .collect::<Vec<_>>()
560 .join(", ");
561 let mut result = format!("an array of {element_tys}");
562 if len > MAX {
563 result.push_str(&format!(", ... with {len} values"));
564 }
565 if len == 1 {
566 result.push_str(" with 1 value");
567 }
568 result
569 }
570 }
571 }
572 }
573
574 pub(crate) fn from_sketch_var_literal(
575 literal: &Node<NumericLiteral>,
576 id: SketchVarId,
577 node_path: Option<crate::NodePath>,
578 exec_state: &ExecState,
579 ) -> Self {
580 let meta = vec![literal.metadata()];
581 let ty = NumericType::from_parsed(literal.suffix, &exec_state.mod_local.settings);
582 KclValue::SketchVar {
583 value: Box::new(SketchVar {
584 id,
585 initial_value: literal.value,
586 node_path,
587 meta,
588 ty,
589 }),
590 }
591 }
592
593 pub(crate) fn from_literal(literal: Node<Literal>, exec_state: &mut ExecState) -> Self {
594 let meta = vec![literal.metadata()];
595 match literal.inner.value {
596 LiteralValue::Number { value, suffix } => {
597 let ty = NumericType::from_parsed(suffix, &exec_state.mod_local.settings);
598 if let NumericType::Default { len, .. } = &ty
599 && !exec_state.mod_local.explicit_length_units
600 && *len != UnitLength::Millimeters
601 {
602 exec_state.warn(
603 CompilationIssue::err(
604 literal.as_source_range(),
605 "Project-wide units are deprecated. Prefer to use per-file default units.",
606 )
607 .with_suggestion(
608 "Fix by adding per-file settings",
609 format!("@{SETTINGS}({SETTINGS_UNIT_LENGTH} = {len})\n"),
610 Some(SourceRange::new(0, 0, literal.module_id)),
612 crate::errors::Tag::Deprecated,
613 ),
614 annotations::WARN_DEPRECATED,
615 );
616 }
617 KclValue::Number { value, meta, ty }
618 }
619 LiteralValue::String(value) => KclValue::String { value, meta },
620 LiteralValue::Bool(value) => KclValue::Bool { value, meta },
621 }
622 }
623
624 pub(crate) fn from_default_param(param: DefaultParamVal, exec_state: &mut ExecState) -> Self {
625 match param {
626 DefaultParamVal::Literal(lit) => Self::from_literal(lit, exec_state),
627 DefaultParamVal::KclNone(value) => KclValue::KclNone {
628 value,
629 meta: Default::default(),
630 },
631 }
632 }
633
634 pub(crate) fn map_env_ref(&self, old_env: EnvironmentRef, new_env: EnvironmentRef) -> Self {
635 let mut result = self.clone();
636 if let KclValue::Function { ref mut value, .. } = result
637 && let FunctionSource {
638 body: FunctionBody::Kcl(memory),
639 ..
640 } = &mut **value
641 {
642 memory.replace_env(old_env, new_env);
643 }
644
645 result
646 }
647
648 pub(crate) fn map_env_ref_and_epoch(&self, old_env: EnvironmentRef, new_env: EnvironmentRef) -> Self {
649 let mut result = self.clone();
650 if let KclValue::Function { ref mut value, .. } = result
651 && let FunctionSource {
652 body: FunctionBody::Kcl(memory),
653 ..
654 } = &mut **value
655 {
656 memory.replace_env_and_epoch(old_env, new_env);
657 }
658
659 result
660 }
661
662 pub const fn from_number_with_type(f: f64, ty: NumericType, meta: Vec<Metadata>) -> Self {
663 Self::Number { value: f, meta, ty }
664 }
665
666 pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
668 let [x, y] = p;
669 Self::Tuple {
670 value: vec![
671 Self::Number {
672 value: x,
673 meta: meta.clone(),
674 ty,
675 },
676 Self::Number {
677 value: y,
678 meta: meta.clone(),
679 ty,
680 },
681 ],
682 meta,
683 }
684 }
685
686 pub fn from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
688 let [x, y, z] = p;
689 Self::Tuple {
690 value: vec![
691 Self::Number {
692 value: x,
693 meta: meta.clone(),
694 ty,
695 },
696 Self::Number {
697 value: y,
698 meta: meta.clone(),
699 ty,
700 },
701 Self::Number {
702 value: z,
703 meta: meta.clone(),
704 ty,
705 },
706 ],
707 meta,
708 }
709 }
710
711 pub(crate) fn array_from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
713 let [x, y] = p;
714 Self::HomArray {
715 value: vec![
716 Self::Number {
717 value: x,
718 meta: meta.clone(),
719 ty,
720 },
721 Self::Number { value: y, meta, ty },
722 ],
723 ty: ty.into(),
724 }
725 }
726
727 pub fn array_from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
729 let [x, y, z] = p;
730 Self::HomArray {
731 value: vec![
732 Self::Number {
733 value: x,
734 meta: meta.clone(),
735 ty,
736 },
737 Self::Number {
738 value: y,
739 meta: meta.clone(),
740 ty,
741 },
742 Self::Number { value: z, meta, ty },
743 ],
744 ty: ty.into(),
745 }
746 }
747
748 pub(crate) fn from_unsolved_expr(expr: UnsolvedExpr, meta: Vec<Metadata>) -> Self {
749 match expr {
750 UnsolvedExpr::Known(v) => crate::execution::KclValue::Number {
751 value: v.n,
752 ty: v.ty,
753 meta,
754 },
755 UnsolvedExpr::Unknown(var_id) => crate::execution::KclValue::SketchVar {
759 value: Box::new(SketchVar {
760 id: var_id,
761 initial_value: Default::default(),
762 ty: Default::default(),
764 node_path: None,
765 meta,
766 }),
767 },
768 }
769 }
770
771 pub(crate) fn as_usize(&self) -> Option<usize> {
772 match self {
773 KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
774 _ => None,
775 }
776 }
777
778 pub fn as_int(&self) -> Option<i64> {
779 match self {
780 KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
781 _ => None,
782 }
783 }
784
785 pub fn as_int_with_ty(&self) -> Option<(i64, NumericType)> {
786 match self {
787 KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, *ty)),
788 _ => None,
789 }
790 }
791
792 pub fn as_object(&self) -> Option<&KclObjectFields> {
793 match self {
794 KclValue::Object { value, .. } => Some(value),
795 _ => None,
796 }
797 }
798
799 pub fn into_object(self) -> Option<KclObjectFields> {
800 match self {
801 KclValue::Object { value, .. } => Some(value),
802 _ => None,
803 }
804 }
805
806 pub fn as_unsolved_expr(&self) -> Option<UnsolvedExpr> {
807 match self {
808 KclValue::Number { value, ty, .. } => Some(UnsolvedExpr::Known(TyF64::new(*value, *ty))),
809 KclValue::SketchVar { value, .. } => Some(UnsolvedExpr::Unknown(value.id)),
810 _ => None,
811 }
812 }
813
814 pub fn to_sketch_expr(&self) -> Option<crate::front::Expr> {
815 match self {
816 KclValue::Number { value, ty, .. } => Some(crate::front::Expr::Number(crate::front::Number {
817 value: *value,
818 units: (*ty).try_into().ok()?,
819 })),
820 KclValue::SketchVar { value, .. } => Some(crate::front::Expr::Var(crate::front::Number {
821 value: value.initial_value,
822 units: value.ty.try_into().ok()?,
823 })),
824 _ => None,
825 }
826 }
827
828 pub fn as_str(&self) -> Option<&str> {
829 match self {
830 KclValue::String { value, .. } => Some(value),
831 _ => None,
832 }
833 }
834
835 pub fn into_array(self) -> Vec<KclValue> {
836 match self {
837 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
838 _ => vec![self],
839 }
840 }
841
842 pub fn as_slice(&self) -> Option<&[KclValue]> {
843 match self {
844 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => Some(value),
845 _ => None,
846 }
847 }
848
849 pub fn as_point2d(&self) -> Option<[TyF64; 2]> {
850 let value = match self {
851 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
852 _ => return None,
853 };
854
855 let [x, y] = value.as_slice() else {
856 return None;
857 };
858 let x = x.as_ty_f64()?;
859 let y = y.as_ty_f64()?;
860 Some([x, y])
861 }
862
863 pub fn as_point3d(&self) -> Option<[TyF64; 3]> {
864 let value = match self {
865 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
866 _ => return None,
867 };
868
869 let [x, y, z] = value.as_slice() else {
870 return None;
871 };
872 let x = x.as_ty_f64()?;
873 let y = y.as_ty_f64()?;
874 let z = z.as_ty_f64()?;
875 Some([x, y, z])
876 }
877
878 pub fn as_uuid(&self) -> Option<uuid::Uuid> {
879 match self {
880 KclValue::Uuid { value, .. } => Some(*value),
881 _ => None,
882 }
883 }
884
885 pub fn as_plane(&self) -> Option<&Plane> {
886 match self {
887 KclValue::Plane { value, .. } => Some(value),
888 _ => None,
889 }
890 }
891
892 pub fn as_solid(&self) -> Option<&Solid> {
893 match self {
894 KclValue::Solid { value, .. } => Some(value),
895 _ => None,
896 }
897 }
898
899 pub fn as_sketch(&self) -> Option<&Sketch> {
900 match self {
901 KclValue::Sketch { value, .. } => Some(value),
902 _ => None,
903 }
904 }
905
906 pub fn as_mut_sketch(&mut self) -> Option<&mut Sketch> {
907 match self {
908 KclValue::Sketch { value } => Some(value),
909 _ => None,
910 }
911 }
912
913 pub fn as_sketch_var(&self) -> Option<&SketchVar> {
914 match self {
915 KclValue::SketchVar { value, .. } => Some(value),
916 _ => None,
917 }
918 }
919
920 pub fn as_segment(&self) -> Option<&Segment> {
922 match self {
923 KclValue::Segment { value, .. } => match &value.repr {
924 SegmentRepr::Solved { segment } => Some(segment),
925 _ => None,
926 },
927 _ => None,
928 }
929 }
930
931 pub fn into_segment(self) -> Option<Segment> {
933 match self {
934 KclValue::Segment { value, .. } => match value.repr {
935 SegmentRepr::Solved { segment } => Some(*segment),
936 _ => None,
937 },
938 _ => None,
939 }
940 }
941
942 pub fn as_mut_tag(&mut self) -> Option<&mut TagIdentifier> {
943 match self {
944 KclValue::TagIdentifier(value) => Some(value),
945 _ => None,
946 }
947 }
948
949 #[cfg(test)]
950 pub fn as_f64(&self) -> Option<f64> {
951 match self {
952 KclValue::Number { value, .. } => Some(*value),
953 _ => None,
954 }
955 }
956
957 pub fn as_ty_f64(&self) -> Option<TyF64> {
958 match self {
959 KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)),
960 _ => None,
961 }
962 }
963
964 pub fn as_bool(&self) -> Option<bool> {
965 match self {
966 KclValue::Bool { value, .. } => Some(*value),
967 _ => None,
968 }
969 }
970
971 pub fn as_function(&self) -> Option<&FunctionSource> {
973 match self {
974 KclValue::Function { value, .. } => Some(value),
975 _ => None,
976 }
977 }
978
979 pub fn get_tag_identifier(&self) -> Result<TagIdentifier, KclError> {
981 match self {
982 KclValue::TagIdentifier(t) => Ok(*t.clone()),
983 _ => Err(KclError::new_semantic(KclErrorDetails::new(
984 format!("Not a tag identifier: {self:?}"),
985 self.clone().into(),
986 ))),
987 }
988 }
989
990 pub fn get_tag_declarator(&self) -> Result<TagNode, KclError> {
992 match self {
993 KclValue::TagDeclarator(t) => Ok((**t).clone()),
994 _ => Err(KclError::new_semantic(KclErrorDetails::new(
995 format!("Not a tag declarator: {self:?}"),
996 self.clone().into(),
997 ))),
998 }
999 }
1000
1001 pub fn get_bool(&self) -> Result<bool, KclError> {
1003 self.as_bool().ok_or_else(|| {
1004 KclError::new_type(KclErrorDetails::new(
1005 format!("Expected bool, found {}", self.human_friendly_type()),
1006 self.into(),
1007 ))
1008 })
1009 }
1010
1011 pub fn is_unknown_number(&self) -> bool {
1012 match self {
1013 KclValue::Number { ty, .. } => !ty.is_fully_specified(),
1014 _ => false,
1015 }
1016 }
1017
1018 pub fn value_str(&self) -> Option<String> {
1019 match self {
1020 KclValue::Bool { value, .. } => Some(format!("{value}")),
1021 KclValue::Number { value, .. } => Some(format!("{value}")),
1023 KclValue::String { value, .. } => Some(format!("'{value}'")),
1024 KclValue::SketchVar { value, .. } => Some(format!("var {}", value.initial_value)),
1026 KclValue::Uuid { value, .. } => Some(format!("{value}")),
1027 KclValue::TagDeclarator(tag) => Some(format!("${}", tag.name)),
1028 KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
1029 KclValue::Tuple { .. } => Some("[...]".to_owned()),
1031 KclValue::HomArray { .. } => Some("[...]".to_owned()),
1032 KclValue::Object { .. } => Some("{ ... }".to_owned()),
1033 KclValue::Module { .. }
1034 | KclValue::GdtAnnotation { .. }
1035 | KclValue::SketchConstraint { .. }
1036 | KclValue::Solid { .. }
1037 | KclValue::Sketch { .. }
1038 | KclValue::Helix { .. }
1039 | KclValue::ImportedGeometry(_)
1040 | KclValue::Function { .. }
1041 | KclValue::Plane { .. }
1042 | KclValue::Face { .. }
1043 | KclValue::Segment { .. }
1044 | KclValue::KclNone { .. }
1045 | KclValue::BoundedEdge { .. }
1046 | KclValue::Type { .. } => None,
1047 }
1048 }
1049}
1050
1051impl From<Geometry> for KclValue {
1052 fn from(value: Geometry) -> Self {
1053 match value {
1054 Geometry::Sketch(x) => Self::Sketch { value: Box::new(x) },
1055 Geometry::Solid(x) => Self::Solid { value: Box::new(x) },
1056 }
1057 }
1058}
1059
1060impl From<GeometryWithImportedGeometry> for KclValue {
1061 fn from(value: GeometryWithImportedGeometry) -> Self {
1062 match value {
1063 GeometryWithImportedGeometry::Sketch(x) => Self::Sketch { value: Box::new(x) },
1064 GeometryWithImportedGeometry::Solid(x) => Self::Solid { value: Box::new(x) },
1065 GeometryWithImportedGeometry::ImportedGeometry(x) => Self::ImportedGeometry(*x),
1066 }
1067 }
1068}
1069
1070impl From<Vec<GeometryWithImportedGeometry>> for KclValue {
1071 fn from(mut values: Vec<GeometryWithImportedGeometry>) -> Self {
1072 if values.len() == 1
1073 && let Some(v) = values.pop()
1074 {
1075 KclValue::from(v)
1076 } else {
1077 KclValue::HomArray {
1078 value: values.into_iter().map(KclValue::from).collect(),
1079 ty: RuntimeType::Union(vec![
1080 RuntimeType::Primitive(PrimitiveType::Sketch),
1081 RuntimeType::Primitive(PrimitiveType::Solid),
1082 RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
1083 ]),
1084 }
1085 }
1086 }
1087}
1088
1089#[cfg(test)]
1090mod tests {
1091 use super::*;
1092 use crate::exec::UnitType;
1093
1094 #[test]
1095 fn test_human_friendly_type() {
1096 let len = KclValue::Number {
1097 value: 1.0,
1098 ty: NumericType::Known(UnitType::GenericLength),
1099 meta: vec![],
1100 };
1101 assert_eq!(len.human_friendly_type(), "a number (Length)".to_string());
1102
1103 let unknown = KclValue::Number {
1104 value: 1.0,
1105 ty: NumericType::Unknown,
1106 meta: vec![],
1107 };
1108 assert_eq!(unknown.human_friendly_type(), "a number with unknown units".to_string());
1109
1110 let mm = KclValue::Number {
1111 value: 1.0,
1112 ty: NumericType::Known(UnitType::Length(UnitLength::Millimeters)),
1113 meta: vec![],
1114 };
1115 assert_eq!(mm.human_friendly_type(), "a number (mm)".to_string());
1116
1117 let array1_mm = KclValue::HomArray {
1118 value: vec![mm.clone()],
1119 ty: RuntimeType::any(),
1120 };
1121 assert_eq!(
1122 array1_mm.human_friendly_type(),
1123 "an array of `number(mm)` with 1 value".to_string()
1124 );
1125
1126 let array2_mm = KclValue::HomArray {
1127 value: vec![mm.clone(), mm.clone()],
1128 ty: RuntimeType::any(),
1129 };
1130 assert_eq!(
1131 array2_mm.human_friendly_type(),
1132 "an array of `number(mm)`, `number(mm)`".to_string()
1133 );
1134
1135 let array3_mm = KclValue::HomArray {
1136 value: vec![mm.clone(), mm.clone(), mm.clone()],
1137 ty: RuntimeType::any(),
1138 };
1139 assert_eq!(
1140 array3_mm.human_friendly_type(),
1141 "an array of `number(mm)`, `number(mm)`, `number(mm)`".to_string()
1142 );
1143
1144 let inches = KclValue::Number {
1145 value: 1.0,
1146 ty: NumericType::Known(UnitType::Length(UnitLength::Inches)),
1147 meta: vec![],
1148 };
1149 let array4 = KclValue::HomArray {
1150 value: vec![mm.clone(), mm.clone(), inches, mm],
1151 ty: RuntimeType::any(),
1152 };
1153 assert_eq!(
1154 array4.human_friendly_type(),
1155 "an array of `number(mm)`, `number(mm)`, `number(in)`, ... with 4 values".to_string()
1156 );
1157
1158 let empty_array = KclValue::HomArray {
1159 value: vec![],
1160 ty: RuntimeType::any(),
1161 };
1162 assert_eq!(empty_array.human_friendly_type(), "an empty array".to_string());
1163
1164 let array_nested = KclValue::HomArray {
1165 value: vec![array2_mm],
1166 ty: RuntimeType::any(),
1167 };
1168 assert_eq!(
1169 array_nested.human_friendly_type(),
1170 "an array of `[any; 2]` with 1 value".to_string()
1171 );
1172 }
1173}