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