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