1use std::collections::HashMap;
2
3use async_recursion::async_recursion;
4use indexmap::IndexMap;
5use kcl_ezpz::{Constraint, NonLinearSystemError};
6
7#[cfg(feature = "artifact-graph")]
8use crate::front::{Object, ObjectKind};
9use crate::{
10 CompilationError, NodePath, SourceRange,
11 errors::{KclError, KclErrorDetails},
12 execution::{
13 AbstractSegment, BodyType, ControlFlowKind, EnvironmentRef, ExecState, ExecutorContext, KclValue,
14 KclValueControlFlow, Metadata, ModelingCmdMeta, ModuleArtifactState, Operation, PreserveMem, Segment,
15 SegmentKind, SegmentRepr, SketchConstraintKind, SketchSurface, StatementKind, TagIdentifier, UnsolvedExpr,
16 UnsolvedSegment, UnsolvedSegmentKind, annotations,
17 cad_op::OpKclValue,
18 control_continue,
19 fn_call::{Arg, Args},
20 kcl_value::{FunctionSource, KclFunctionSourceParams, TypeDef},
21 memory,
22 sketch_solve::{
23 FreedomAnalysis, Solved, create_segment_scene_objects, normalize_to_solver_unit, solver_numeric_type,
24 substitute_sketch_var_in_segment, substitute_sketch_vars,
25 },
26 state::{ModuleState, SketchBlockState},
27 types::{NumericType, PrimitiveType, RuntimeType},
28 },
29 front::PointCtor,
30 modules::{ModuleExecutionOutcome, ModuleId, ModulePath, ModuleRepr},
31 parsing::ast::types::{
32 Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
33 BinaryPart, BodyItem, CodeBlock, Expr, IfExpression, ImportPath, ImportSelector, ItemVisibility,
34 MemberExpression, Name, Node, ObjectExpression, PipeExpression, Program, SketchBlock, SketchVar, TagDeclarator,
35 Type, UnaryExpression, UnaryOperator,
36 },
37 std::{
38 args::TyF64, shapes::SketchOrSurface, sketch::ensure_sketch_plane_in_engine, sketch2::create_segments_in_engine,
39 },
40};
41
42fn internal_err(message: impl Into<String>, range: impl Into<SourceRange>) -> KclError {
43 KclError::new_internal(KclErrorDetails::new(message.into(), vec![range.into()]))
44}
45
46impl<'a> StatementKind<'a> {
47 fn expect_name(&self) -> &'a str {
48 match self {
49 StatementKind::Declaration { name } => name,
50 StatementKind::Expression => unreachable!(),
51 }
52 }
53}
54
55impl ExecutorContext {
56 async fn handle_annotations(
58 &self,
59 annotations: impl Iterator<Item = &Node<Annotation>>,
60 body_type: BodyType,
61 exec_state: &mut ExecState,
62 ) -> Result<bool, KclError> {
63 let mut no_prelude = false;
64 for annotation in annotations {
65 if annotation.name() == Some(annotations::SETTINGS) {
66 if matches!(body_type, BodyType::Root) {
67 let (updated_len, updated_angle) =
68 exec_state.mod_local.settings.update_from_annotation(annotation)?;
69 if updated_len {
70 exec_state.mod_local.explicit_length_units = true;
71 }
72 if updated_angle {
73 exec_state.warn(
74 CompilationError::err(
75 annotation.as_source_range(),
76 "Prefer to use explicit units for angles",
77 ),
78 annotations::WARN_ANGLE_UNITS,
79 );
80 }
81 } else {
82 exec_state.err(CompilationError::err(
83 annotation.as_source_range(),
84 "Settings can only be modified at the top level scope of a file",
85 ));
86 }
87 } else if annotation.name() == Some(annotations::NO_PRELUDE) {
88 if matches!(body_type, BodyType::Root) {
89 no_prelude = true;
90 } else {
91 exec_state.err(CompilationError::err(
92 annotation.as_source_range(),
93 "The standard library can only be skipped at the top level scope of a file",
94 ));
95 }
96 } else if annotation.name() == Some(annotations::WARNINGS) {
97 if matches!(body_type, BodyType::Root) {
99 let props = annotations::expect_properties(annotations::WARNINGS, annotation)?;
100 for p in props {
101 match &*p.inner.key.name {
102 annotations::WARN_ALLOW => {
103 let allowed = annotations::many_of(
104 &p.inner.value,
105 &annotations::WARN_VALUES,
106 annotation.as_source_range(),
107 )?;
108 exec_state.mod_local.allowed_warnings = allowed;
109 }
110 annotations::WARN_DENY => {
111 let denied = annotations::many_of(
112 &p.inner.value,
113 &annotations::WARN_VALUES,
114 annotation.as_source_range(),
115 )?;
116 exec_state.mod_local.denied_warnings = denied;
117 }
118 name => {
119 return Err(KclError::new_semantic(KclErrorDetails::new(
120 format!(
121 "Unexpected warnings key: `{name}`; expected one of `{}`, `{}`",
122 annotations::WARN_ALLOW,
123 annotations::WARN_DENY,
124 ),
125 vec![annotation.as_source_range()],
126 )));
127 }
128 }
129 }
130 } else {
131 exec_state.err(CompilationError::err(
132 annotation.as_source_range(),
133 "Warnings can only be customized at the top level scope of a file",
134 ));
135 }
136 } else {
137 exec_state.warn(
138 CompilationError::err(annotation.as_source_range(), "Unknown annotation"),
139 annotations::WARN_UNKNOWN_ATTR,
140 );
141 }
142 }
143 Ok(no_prelude)
144 }
145
146 pub(super) async fn exec_module_body(
147 &self,
148 program: &Node<Program>,
149 exec_state: &mut ExecState,
150 preserve_mem: PreserveMem,
151 module_id: ModuleId,
152 path: &ModulePath,
153 ) -> Result<ModuleExecutionOutcome, (KclError, Option<EnvironmentRef>, Option<ModuleArtifactState>)> {
154 crate::log::log(format!("enter module {path} {}", exec_state.stack()));
155
156 let mut local_state = ModuleState::new(
165 path.clone(),
166 exec_state.stack().memory.clone(),
167 Some(module_id),
168 exec_state.mod_local.sketch_mode,
169 exec_state.mod_local.freedom_analysis,
170 );
171 match preserve_mem {
172 PreserveMem::Always => {
173 #[cfg(feature = "artifact-graph")]
174 {
175 use crate::id::IncIdGenerator;
176 exec_state
177 .mod_local
178 .artifacts
179 .scene_objects
180 .clone_from(&exec_state.global.root_module_artifacts.scene_objects);
181 exec_state.mod_local.artifacts.object_id_generator =
182 IncIdGenerator::new(exec_state.global.root_module_artifacts.scene_objects.len());
183 }
184 }
185 PreserveMem::Normal => {
186 #[cfg(feature = "artifact-graph")]
187 {
188 local_state
189 .artifacts
190 .scene_objects
191 .clone_from(&exec_state.mod_local.artifacts.scene_objects);
192 }
193 std::mem::swap(&mut exec_state.mod_local, &mut local_state);
194 }
195 }
196
197 let no_prelude = self
198 .handle_annotations(program.inner_attrs.iter(), crate::execution::BodyType::Root, exec_state)
199 .await
200 .map_err(|err| (err, None, None))?;
201
202 if preserve_mem.normal() {
203 exec_state.mut_stack().push_new_root_env(!no_prelude);
204 }
205
206 let result = self
207 .exec_block(program, exec_state, crate::execution::BodyType::Root)
208 .await;
209
210 let env_ref = match preserve_mem {
211 PreserveMem::Always => exec_state.mut_stack().pop_and_preserve_env(),
212 PreserveMem::Normal => exec_state.mut_stack().pop_env(),
213 };
214 let module_artifacts = match preserve_mem {
215 PreserveMem::Always => std::mem::take(&mut exec_state.mod_local.artifacts),
216 PreserveMem::Normal => {
217 std::mem::swap(&mut exec_state.mod_local, &mut local_state);
218 local_state.artifacts
219 }
220 };
221
222 crate::log::log(format!("leave {path}"));
223
224 result
225 .map_err(|err| (err, Some(env_ref), Some(module_artifacts.clone())))
226 .map(|last_expr| ModuleExecutionOutcome {
227 last_expr: last_expr.map(|value_cf| value_cf.into_value()),
228 environment: env_ref,
229 exports: local_state.module_exports,
230 artifacts: module_artifacts,
231 })
232 }
233
234 #[async_recursion]
236 pub(super) async fn exec_block<'a, B>(
237 &'a self,
238 block: &'a B,
239 exec_state: &mut ExecState,
240 body_type: BodyType,
241 ) -> Result<Option<KclValueControlFlow>, KclError>
242 where
243 B: CodeBlock + Sync,
244 {
245 let mut last_expr = None;
246 for statement in block.body() {
248 match statement {
249 BodyItem::ImportStatement(import_stmt) => {
250 if exec_state.sketch_mode() {
251 continue;
252 }
253 if !matches!(body_type, BodyType::Root) {
254 return Err(KclError::new_semantic(KclErrorDetails::new(
255 "Imports are only supported at the top-level of a file.".to_owned(),
256 vec![import_stmt.into()],
257 )));
258 }
259
260 let source_range = SourceRange::from(import_stmt);
261 let attrs = &import_stmt.outer_attrs;
262 let module_path = ModulePath::from_import_path(
263 &import_stmt.path,
264 &self.settings.project_directory,
265 &exec_state.mod_local.path,
266 )?;
267 let module_id = self
268 .open_module(&import_stmt.path, attrs, &module_path, exec_state, source_range)
269 .await?;
270
271 match &import_stmt.selector {
272 ImportSelector::List { items } => {
273 let (env_ref, module_exports) =
274 self.exec_module_for_items(module_id, exec_state, source_range).await?;
275 for import_item in items {
276 let mem = &exec_state.stack().memory;
278 let mut value = mem
279 .get_from(&import_item.name.name, env_ref, import_item.into(), 0)
280 .cloned();
281 let ty_name = format!("{}{}", memory::TYPE_PREFIX, import_item.name.name);
282 let mut ty = mem.get_from(&ty_name, env_ref, import_item.into(), 0).cloned();
283 let mod_name = format!("{}{}", memory::MODULE_PREFIX, import_item.name.name);
284 let mut mod_value = mem.get_from(&mod_name, env_ref, import_item.into(), 0).cloned();
285
286 if value.is_err() && ty.is_err() && mod_value.is_err() {
287 return Err(KclError::new_undefined_value(
288 KclErrorDetails::new(
289 format!("{} is not defined in module", import_item.name.name),
290 vec![SourceRange::from(&import_item.name)],
291 ),
292 None,
293 ));
294 }
295
296 if value.is_ok() && !module_exports.contains(&import_item.name.name) {
298 value = Err(KclError::new_semantic(KclErrorDetails::new(
299 format!(
300 "Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
301 import_item.name.name
302 ),
303 vec![SourceRange::from(&import_item.name)],
304 )));
305 }
306
307 if ty.is_ok() && !module_exports.contains(&ty_name) {
308 ty = Err(KclError::new_semantic(KclErrorDetails::new(
309 format!(
310 "Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
311 import_item.name.name
312 ),
313 vec![SourceRange::from(&import_item.name)],
314 )));
315 }
316
317 if mod_value.is_ok() && !module_exports.contains(&mod_name) {
318 mod_value = Err(KclError::new_semantic(KclErrorDetails::new(
319 format!(
320 "Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
321 import_item.name.name
322 ),
323 vec![SourceRange::from(&import_item.name)],
324 )));
325 }
326
327 if value.is_err() && ty.is_err() && mod_value.is_err() {
328 return value.map(|v| Some(v.continue_()));
329 }
330
331 if let Ok(value) = value {
333 exec_state.mut_stack().add(
334 import_item.identifier().to_owned(),
335 value,
336 SourceRange::from(&import_item.name),
337 )?;
338
339 if let ItemVisibility::Export = import_stmt.visibility {
340 exec_state
341 .mod_local
342 .module_exports
343 .push(import_item.identifier().to_owned());
344 }
345 }
346
347 if let Ok(ty) = ty {
348 let ty_name = format!("{}{}", memory::TYPE_PREFIX, import_item.identifier());
349 exec_state.mut_stack().add(
350 ty_name.clone(),
351 ty,
352 SourceRange::from(&import_item.name),
353 )?;
354
355 if let ItemVisibility::Export = import_stmt.visibility {
356 exec_state.mod_local.module_exports.push(ty_name);
357 }
358 }
359
360 if let Ok(mod_value) = mod_value {
361 let mod_name = format!("{}{}", memory::MODULE_PREFIX, import_item.identifier());
362 exec_state.mut_stack().add(
363 mod_name.clone(),
364 mod_value,
365 SourceRange::from(&import_item.name),
366 )?;
367
368 if let ItemVisibility::Export = import_stmt.visibility {
369 exec_state.mod_local.module_exports.push(mod_name);
370 }
371 }
372 }
373 }
374 ImportSelector::Glob(_) => {
375 let (env_ref, module_exports) =
376 self.exec_module_for_items(module_id, exec_state, source_range).await?;
377 for name in module_exports.iter() {
378 let item = exec_state
379 .stack()
380 .memory
381 .get_from(name, env_ref, source_range, 0)
382 .map_err(|_err| {
383 internal_err(
384 format!("{name} is not defined in module (but was exported?)"),
385 source_range,
386 )
387 })?
388 .clone();
389 exec_state.mut_stack().add(name.to_owned(), item, source_range)?;
390
391 if let ItemVisibility::Export = import_stmt.visibility {
392 exec_state.mod_local.module_exports.push(name.clone());
393 }
394 }
395 }
396 ImportSelector::None { .. } => {
397 let name = import_stmt.module_name().unwrap();
398 let item = KclValue::Module {
399 value: module_id,
400 meta: vec![source_range.into()],
401 };
402 exec_state.mut_stack().add(
403 format!("{}{}", memory::MODULE_PREFIX, name),
404 item,
405 source_range,
406 )?;
407 }
408 }
409 last_expr = None;
410 }
411 BodyItem::ExpressionStatement(expression_statement) => {
412 if exec_state.sketch_mode() && sketch_mode_should_skip(&expression_statement.expression) {
413 continue;
414 }
415
416 let metadata = Metadata::from(expression_statement);
417 let value = self
418 .execute_expr(
419 &expression_statement.expression,
420 exec_state,
421 &metadata,
422 &[],
423 StatementKind::Expression,
424 )
425 .await?;
426
427 let is_return = value.is_some_return();
428 last_expr = Some(value);
429
430 if is_return {
431 break;
432 }
433 }
434 BodyItem::VariableDeclaration(variable_declaration) => {
435 if exec_state.sketch_mode() && sketch_mode_should_skip(&variable_declaration.declaration.init) {
436 continue;
437 }
438
439 let var_name = variable_declaration.declaration.id.name.to_string();
440 let source_range = SourceRange::from(&variable_declaration.declaration.init);
441 let metadata = Metadata { source_range };
442
443 let annotations = &variable_declaration.outer_attrs;
444
445 let lhs = variable_declaration.inner.name().to_owned();
448 let prev_being_declared = exec_state.mod_local.being_declared.take();
449 exec_state.mod_local.being_declared = Some(lhs);
450 let rhs_result = self
451 .execute_expr(
452 &variable_declaration.declaration.init,
453 exec_state,
454 &metadata,
455 annotations,
456 StatementKind::Declaration { name: &var_name },
457 )
458 .await;
459 exec_state.mod_local.being_declared = prev_being_declared;
461 let rhs = rhs_result?;
462
463 if rhs.is_some_return() {
464 last_expr = Some(rhs);
465 break;
466 }
467 let mut rhs = rhs.into_value();
468
469 if let KclValue::Segment { value } = &mut rhs
473 && let SegmentRepr::Unsolved { segment } = &mut value.repr
474 {
475 segment.tag = Some(TagIdentifier {
476 value: variable_declaration.declaration.id.name.clone(),
477 info: Default::default(),
478 meta: vec![SourceRange::from(&variable_declaration.declaration.id).into()],
479 });
480 }
481 let rhs = rhs; let should_bind_name =
484 if let Some(fn_name) = variable_declaration.declaration.init.fn_declaring_name() {
485 var_name != fn_name
489 } else {
490 true
493 };
494 if should_bind_name {
495 exec_state
496 .mut_stack()
497 .add(var_name.clone(), rhs.clone(), source_range)?;
498 }
499
500 if let Some(sketch_block_state) = exec_state.mod_local.sketch_block.as_mut()
501 && let KclValue::Segment { value } = &rhs
502 {
503 let segment_object_id = match &value.repr {
506 SegmentRepr::Unsolved { segment } => segment.object_id,
507 SegmentRepr::Solved { segment } => segment.object_id,
508 };
509 sketch_block_state
510 .segment_tags
511 .entry(segment_object_id)
512 .or_insert_with(|| {
513 let id_node = &variable_declaration.declaration.id;
514 Node::new(
515 TagDeclarator {
516 name: id_node.name.clone(),
517 digest: None,
518 },
519 id_node.start,
520 id_node.end,
521 id_node.module_id,
522 )
523 });
524 }
525
526 let should_show_in_feature_tree =
530 !exec_state.mod_local.inside_stdlib && rhs.show_variable_in_feature_tree();
531 if should_show_in_feature_tree {
532 exec_state.push_op(Operation::VariableDeclaration {
533 name: var_name.clone(),
534 value: OpKclValue::from(&rhs),
535 visibility: variable_declaration.visibility,
536 node_path: NodePath::placeholder(),
537 source_range,
538 });
539 }
540
541 if let ItemVisibility::Export = variable_declaration.visibility {
543 if matches!(body_type, BodyType::Root) {
544 exec_state.mod_local.module_exports.push(var_name);
545 } else {
546 exec_state.err(CompilationError::err(
547 variable_declaration.as_source_range(),
548 "Exports are only supported at the top-level of a file. Remove `export` or move it to the top-level.",
549 ));
550 }
551 }
552 last_expr = matches!(body_type, BodyType::Root).then_some(rhs.continue_());
554 }
555 BodyItem::TypeDeclaration(ty) => {
556 if exec_state.sketch_mode() {
557 continue;
558 }
559
560 let metadata = Metadata::from(&**ty);
561 let attrs = annotations::get_fn_attrs(&ty.outer_attrs, metadata.source_range)?.unwrap_or_default();
562 match attrs.impl_ {
563 annotations::Impl::Rust
564 | annotations::Impl::RustConstrainable
565 | annotations::Impl::RustConstraint => {
566 let std_path = match &exec_state.mod_local.path {
567 ModulePath::Std { value } => value,
568 ModulePath::Local { .. } | ModulePath::Main => {
569 return Err(KclError::new_semantic(KclErrorDetails::new(
570 "User-defined types are not yet supported.".to_owned(),
571 vec![metadata.source_range],
572 )));
573 }
574 };
575 let (t, props) = crate::std::std_ty(std_path, &ty.name.name);
576 let value = KclValue::Type {
577 value: TypeDef::RustRepr(t, props),
578 meta: vec![metadata],
579 experimental: attrs.experimental,
580 };
581 let name_in_mem = format!("{}{}", memory::TYPE_PREFIX, ty.name.name);
582 exec_state
583 .mut_stack()
584 .add(name_in_mem.clone(), value, metadata.source_range)
585 .map_err(|_| {
586 KclError::new_semantic(KclErrorDetails::new(
587 format!("Redefinition of type {}.", ty.name.name),
588 vec![metadata.source_range],
589 ))
590 })?;
591
592 if let ItemVisibility::Export = ty.visibility {
593 exec_state.mod_local.module_exports.push(name_in_mem);
594 }
595 }
596 annotations::Impl::Primitive => {}
598 annotations::Impl::Kcl | annotations::Impl::KclConstrainable => match &ty.alias {
599 Some(alias) => {
600 let value = KclValue::Type {
601 value: TypeDef::Alias(
602 RuntimeType::from_parsed(
603 alias.inner.clone(),
604 exec_state,
605 metadata.source_range,
606 attrs.impl_ == annotations::Impl::KclConstrainable,
607 false,
608 )
609 .map_err(|e| KclError::new_semantic(e.into()))?,
610 ),
611 meta: vec![metadata],
612 experimental: attrs.experimental,
613 };
614 let name_in_mem = format!("{}{}", memory::TYPE_PREFIX, ty.name.name);
615 exec_state
616 .mut_stack()
617 .add(name_in_mem.clone(), value, metadata.source_range)
618 .map_err(|_| {
619 KclError::new_semantic(KclErrorDetails::new(
620 format!("Redefinition of type {}.", ty.name.name),
621 vec![metadata.source_range],
622 ))
623 })?;
624
625 if let ItemVisibility::Export = ty.visibility {
626 exec_state.mod_local.module_exports.push(name_in_mem);
627 }
628 }
629 None => {
630 return Err(KclError::new_semantic(KclErrorDetails::new(
631 "User-defined types are not yet supported.".to_owned(),
632 vec![metadata.source_range],
633 )));
634 }
635 },
636 }
637
638 last_expr = None;
639 }
640 BodyItem::ReturnStatement(return_statement) => {
641 if exec_state.sketch_mode() && sketch_mode_should_skip(&return_statement.argument) {
642 continue;
643 }
644
645 let metadata = Metadata::from(return_statement);
646
647 if matches!(body_type, BodyType::Root) {
648 return Err(KclError::new_semantic(KclErrorDetails::new(
649 "Cannot return from outside a function.".to_owned(),
650 vec![metadata.source_range],
651 )));
652 }
653
654 let value_cf = self
655 .execute_expr(
656 &return_statement.argument,
657 exec_state,
658 &metadata,
659 &[],
660 StatementKind::Expression,
661 )
662 .await?;
663 if value_cf.is_some_return() {
664 last_expr = Some(value_cf);
665 break;
666 }
667 let value = value_cf.into_value();
668 exec_state
669 .mut_stack()
670 .add(memory::RETURN_NAME.to_owned(), value, metadata.source_range)
671 .map_err(|_| {
672 KclError::new_semantic(KclErrorDetails::new(
673 "Multiple returns from a single function.".to_owned(),
674 vec![metadata.source_range],
675 ))
676 })?;
677 last_expr = None;
678 }
679 }
680 }
681
682 if matches!(body_type, BodyType::Root) {
683 exec_state
685 .flush_batch(
686 ModelingCmdMeta::new(exec_state, self, block.to_source_range()),
687 true,
690 )
691 .await?;
692 }
693
694 Ok(last_expr)
695 }
696
697 pub async fn open_module(
698 &self,
699 path: &ImportPath,
700 attrs: &[Node<Annotation>],
701 resolved_path: &ModulePath,
702 exec_state: &mut ExecState,
703 source_range: SourceRange,
704 ) -> Result<ModuleId, KclError> {
705 match path {
706 ImportPath::Kcl { .. } => {
707 exec_state.global.mod_loader.cycle_check(resolved_path, source_range)?;
708
709 if let Some(id) = exec_state.id_for_module(resolved_path) {
710 return Ok(id);
711 }
712
713 let id = exec_state.next_module_id();
714 exec_state.add_path_to_source_id(resolved_path.clone(), id);
716 let source = resolved_path.source(&self.fs, source_range).await?;
717 exec_state.add_id_to_source(id, source.clone());
718 let parsed = crate::parsing::parse_str(&source.source, id).parse_errs_as_err()?;
720 exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Kcl(parsed, None));
721
722 Ok(id)
723 }
724 ImportPath::Foreign { .. } => {
725 if let Some(id) = exec_state.id_for_module(resolved_path) {
726 return Ok(id);
727 }
728
729 let id = exec_state.next_module_id();
730 let path = resolved_path.expect_path();
731 exec_state.add_path_to_source_id(resolved_path.clone(), id);
733 let format = super::import::format_from_annotations(attrs, path, source_range)?;
734 let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?;
735 exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Foreign(geom, None));
736 Ok(id)
737 }
738 ImportPath::Std { .. } => {
739 if let Some(id) = exec_state.id_for_module(resolved_path) {
740 return Ok(id);
741 }
742
743 let id = exec_state.next_module_id();
744 exec_state.add_path_to_source_id(resolved_path.clone(), id);
746 let source = resolved_path.source(&self.fs, source_range).await?;
747 exec_state.add_id_to_source(id, source.clone());
748 let parsed = crate::parsing::parse_str(&source.source, id)
749 .parse_errs_as_err()
750 .unwrap();
751 exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Kcl(parsed, None));
752 Ok(id)
753 }
754 }
755 }
756
757 pub(super) async fn exec_module_for_items(
758 &self,
759 module_id: ModuleId,
760 exec_state: &mut ExecState,
761 source_range: SourceRange,
762 ) -> Result<(EnvironmentRef, Vec<String>), KclError> {
763 let path = exec_state.global.module_infos[&module_id].path.clone();
764 let mut repr = exec_state.global.module_infos[&module_id].take_repr();
765 let result = match &mut repr {
768 ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
769 ModuleRepr::Kcl(_, Some(outcome)) => Ok((outcome.environment, outcome.exports.clone())),
770 ModuleRepr::Kcl(program, cache) => self
771 .exec_module_from_ast(program, module_id, &path, exec_state, source_range, PreserveMem::Normal)
772 .await
773 .map(|outcome| {
774 *cache = Some(outcome.clone());
775 (outcome.environment, outcome.exports)
776 }),
777 ModuleRepr::Foreign(geom, _) => Err(KclError::new_semantic(KclErrorDetails::new(
778 "Cannot import items from foreign modules".to_owned(),
779 vec![geom.source_range],
780 ))),
781 ModuleRepr::Dummy => unreachable!("Looking up {}, but it is still being interpreted", path),
782 };
783
784 exec_state.global.module_infos[&module_id].restore_repr(repr);
785 result
786 }
787
788 async fn exec_module_for_result(
789 &self,
790 module_id: ModuleId,
791 exec_state: &mut ExecState,
792 source_range: SourceRange,
793 ) -> Result<Option<KclValue>, KclError> {
794 let path = exec_state.global.module_infos[&module_id].path.clone();
795 let mut repr = exec_state.global.module_infos[&module_id].take_repr();
796 let result = match &mut repr {
799 ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
800 ModuleRepr::Kcl(_, Some(outcome)) => Ok(outcome.last_expr.clone()),
801 ModuleRepr::Kcl(program, cached_items) => {
802 let result = self
803 .exec_module_from_ast(program, module_id, &path, exec_state, source_range, PreserveMem::Normal)
804 .await;
805 match result {
806 Ok(outcome) => {
807 let value = outcome.last_expr.clone();
808 *cached_items = Some(outcome);
809 Ok(value)
810 }
811 Err(e) => Err(e),
812 }
813 }
814 ModuleRepr::Foreign(_, Some((imported, _))) => Ok(imported.clone()),
815 ModuleRepr::Foreign(geom, cached) => {
816 let result = super::import::send_to_engine(geom.clone(), exec_state, self)
817 .await
818 .map(|geom| Some(KclValue::ImportedGeometry(geom)));
819
820 match result {
821 Ok(val) => {
822 *cached = Some((val.clone(), exec_state.mod_local.artifacts.clone()));
823 Ok(val)
824 }
825 Err(e) => Err(e),
826 }
827 }
828 ModuleRepr::Dummy => unreachable!(),
829 };
830
831 exec_state.global.module_infos[&module_id].restore_repr(repr);
832
833 result
834 }
835
836 pub async fn exec_module_from_ast(
837 &self,
838 program: &Node<Program>,
839 module_id: ModuleId,
840 path: &ModulePath,
841 exec_state: &mut ExecState,
842 source_range: SourceRange,
843 preserve_mem: PreserveMem,
844 ) -> Result<ModuleExecutionOutcome, KclError> {
845 exec_state.global.mod_loader.enter_module(path);
846 let result = self
847 .exec_module_body(program, exec_state, preserve_mem, module_id, path)
848 .await;
849 exec_state.global.mod_loader.leave_module(path, source_range)?;
850
851 result.map_err(|(err, _, _)| {
854 if let KclError::ImportCycle { .. } = err {
855 err.override_source_ranges(vec![source_range])
857 } else {
858 KclError::new_semantic(KclErrorDetails::new(
860 format!(
861 "Error loading imported file ({path}). Open it to view more details.\n {}",
862 err.message()
863 ),
864 vec![source_range],
865 ))
866 }
867 })
868 }
869
870 #[async_recursion]
871 pub(crate) async fn execute_expr<'a: 'async_recursion>(
872 &self,
873 init: &Expr,
874 exec_state: &mut ExecState,
875 metadata: &Metadata,
876 annotations: &[Node<Annotation>],
877 statement_kind: StatementKind<'a>,
878 ) -> Result<KclValueControlFlow, KclError> {
879 let item = match init {
880 Expr::None(none) => KclValue::from(none).continue_(),
881 Expr::Literal(literal) => KclValue::from_literal((**literal).clone(), exec_state).continue_(),
882 Expr::TagDeclarator(tag) => tag.execute(exec_state).await?.continue_(),
883 Expr::Name(name) => {
884 let being_declared = exec_state.mod_local.being_declared.clone();
885 let value = name
886 .get_result(exec_state, self)
887 .await
888 .map_err(|e| var_in_own_ref_err(e, &being_declared))?
889 .clone();
890 if let KclValue::Module { value: module_id, meta } = value {
891 self.exec_module_for_result(
892 module_id,
893 exec_state,
894 metadata.source_range
895 ).await?.map(|v| v.continue_())
896 .unwrap_or_else(|| {
897 exec_state.warn(CompilationError::err(
898 metadata.source_range,
899 "Imported module has no return value. The last statement of the module must be an expression, usually the Solid.",
900 ),
901 annotations::WARN_MOD_RETURN_VALUE);
902
903 let mut new_meta = vec![metadata.to_owned()];
904 new_meta.extend(meta);
905 KclValue::KclNone {
906 value: Default::default(),
907 meta: new_meta,
908 }.continue_()
909 })
910 } else {
911 value.continue_()
912 }
913 }
914 Expr::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, self).await?,
915 Expr::FunctionExpression(function_expression) => {
916 let attrs = annotations::get_fn_attrs(annotations, metadata.source_range)?;
917 let experimental = attrs.map(|a| a.experimental).unwrap_or_default();
918 let is_std = matches!(&exec_state.mod_local.path, ModulePath::Std { .. });
919
920 let include_in_feature_tree = attrs.unwrap_or_default().include_in_feature_tree;
922 let (mut closure, placeholder_env_ref) = if let Some(attrs) = attrs
923 && (attrs.impl_ == annotations::Impl::Rust
924 || attrs.impl_ == annotations::Impl::RustConstrainable
925 || attrs.impl_ == annotations::Impl::RustConstraint)
926 {
927 if let ModulePath::Std { value: std_path } = &exec_state.mod_local.path {
928 let (func, props) = crate::std::std_fn(std_path, statement_kind.expect_name());
929 (
930 KclValue::Function {
931 value: Box::new(FunctionSource::rust(func, function_expression.clone(), props, attrs)),
932 meta: vec![metadata.to_owned()],
933 },
934 None,
935 )
936 } else {
937 return Err(KclError::new_semantic(KclErrorDetails::new(
938 "Rust implementation of functions is restricted to the standard library".to_owned(),
939 vec![metadata.source_range],
940 )));
941 }
942 } else {
943 let (env_ref, placeholder_env_ref) = if function_expression.name.is_some() {
947 let dummy = EnvironmentRef::dummy();
950 (dummy, Some(dummy))
951 } else {
952 (exec_state.mut_stack().snapshot(), None)
953 };
954 (
955 KclValue::Function {
956 value: Box::new(FunctionSource::kcl(
957 function_expression.clone(),
958 env_ref,
959 KclFunctionSourceParams {
960 is_std,
961 experimental,
962 include_in_feature_tree,
963 },
964 )),
965 meta: vec![metadata.to_owned()],
966 },
967 placeholder_env_ref,
968 )
969 };
970
971 if let Some(fn_name) = &function_expression.name {
974 if let Some(placeholder_env_ref) = placeholder_env_ref {
978 closure = exec_state.mut_stack().add_recursive_closure(
979 fn_name.name.to_owned(),
980 closure,
981 placeholder_env_ref,
982 metadata.source_range,
983 )?;
984 } else {
985 exec_state
987 .mut_stack()
988 .add(fn_name.name.clone(), closure.clone(), metadata.source_range)?;
989 }
990 }
991
992 closure.continue_()
993 }
994 Expr::CallExpressionKw(call_expression) => call_expression.execute(exec_state, self).await?,
995 Expr::PipeExpression(pipe_expression) => pipe_expression.get_result(exec_state, self).await?,
996 Expr::PipeSubstitution(pipe_substitution) => match statement_kind {
997 StatementKind::Declaration { name } => {
998 let message = format!(
999 "you cannot declare variable {name} as %, because % can only be used in function calls"
1000 );
1001
1002 return Err(KclError::new_semantic(KclErrorDetails::new(
1003 message,
1004 vec![pipe_substitution.into()],
1005 )));
1006 }
1007 StatementKind::Expression => match exec_state.mod_local.pipe_value.clone() {
1008 Some(x) => x.continue_(),
1009 None => {
1010 return Err(KclError::new_semantic(KclErrorDetails::new(
1011 "cannot use % outside a pipe expression".to_owned(),
1012 vec![pipe_substitution.into()],
1013 )));
1014 }
1015 },
1016 },
1017 Expr::ArrayExpression(array_expression) => array_expression.execute(exec_state, self).await?,
1018 Expr::ArrayRangeExpression(range_expression) => range_expression.execute(exec_state, self).await?,
1019 Expr::ObjectExpression(object_expression) => object_expression.execute(exec_state, self).await?,
1020 Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state, self).await?,
1021 Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
1022 Expr::IfExpression(expr) => expr.get_result(exec_state, self).await?,
1023 Expr::LabelledExpression(expr) => {
1024 let value_cf = self
1025 .execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
1026 .await?;
1027 let value = control_continue!(value_cf);
1028 exec_state
1029 .mut_stack()
1030 .add(expr.label.name.clone(), value.clone(), init.into())?;
1031 value.continue_()
1033 }
1034 Expr::AscribedExpression(expr) => expr.get_result(exec_state, self).await?,
1035 Expr::SketchBlock(expr) => expr.get_result(exec_state, self).await?,
1036 Expr::SketchVar(expr) => expr.get_result(exec_state, self).await?.continue_(),
1037 };
1038 Ok(item)
1039 }
1040}
1041
1042fn sketch_mode_should_skip(expr: &Expr) -> bool {
1045 match expr {
1046 Expr::SketchBlock(sketch_block) => !sketch_block.is_being_edited,
1047 _ => true,
1048 }
1049}
1050
1051fn var_in_own_ref_err(e: KclError, being_declared: &Option<String>) -> KclError {
1054 let KclError::UndefinedValue { name, mut details } = e else {
1055 return e;
1056 };
1057 if let (Some(name0), Some(name1)) = (&being_declared, &name)
1061 && name0 == name1
1062 {
1063 details.message = format!(
1064 "You can't use `{name0}` because you're currently trying to define it. Use a different variable here instead."
1065 );
1066 }
1067 KclError::UndefinedValue { details, name }
1068}
1069
1070impl Node<AscribedExpression> {
1071 #[async_recursion]
1072 pub(super) async fn get_result(
1073 &self,
1074 exec_state: &mut ExecState,
1075 ctx: &ExecutorContext,
1076 ) -> Result<KclValueControlFlow, KclError> {
1077 let metadata = Metadata {
1078 source_range: SourceRange::from(self),
1079 };
1080 let result = ctx
1081 .execute_expr(&self.expr, exec_state, &metadata, &[], StatementKind::Expression)
1082 .await?;
1083 let result = control_continue!(result);
1084 apply_ascription(&result, &self.ty, exec_state, self.into()).map(KclValue::continue_)
1085 }
1086}
1087
1088impl Node<SketchBlock> {
1089 pub(super) async fn get_result(
1090 &self,
1091 exec_state: &mut ExecState,
1092 ctx: &ExecutorContext,
1093 ) -> Result<KclValueControlFlow, KclError> {
1094 if exec_state.mod_local.sketch_block.is_some() {
1095 return Err(KclError::new_semantic(KclErrorDetails::new(
1097 "Cannot execute a sketch block from within another sketch block".to_owned(),
1098 vec![SourceRange::from(self)],
1099 )));
1100 }
1101
1102 let range = SourceRange::from(self);
1103
1104 let mut labeled = IndexMap::new();
1106 for labeled_arg in &self.arguments {
1107 let source_range = SourceRange::from(labeled_arg.arg.clone());
1108 let metadata = Metadata { source_range };
1109 let value_cf = ctx
1110 .execute_expr(&labeled_arg.arg, exec_state, &metadata, &[], StatementKind::Expression)
1111 .await?;
1112 let value = control_continue!(value_cf);
1113 let arg = Arg::new(value, source_range);
1114 match &labeled_arg.label {
1115 Some(label) => {
1116 labeled.insert(label.name.clone(), arg);
1117 }
1118 None => {
1119 let name = labeled_arg.arg.ident_name();
1120 if let Some(name) = name {
1121 labeled.insert(name.to_owned(), arg);
1122 } else {
1123 return Err(KclError::new_semantic(KclErrorDetails::new(
1124 "Arguments to sketch blocks must be either labeled or simple identifiers".to_owned(),
1125 vec![SourceRange::from(&labeled_arg.arg)],
1126 )));
1127 }
1128 }
1129 }
1130 }
1131 let mut args = Args::new_no_args(range, ctx.clone(), Some("sketch block".to_owned()));
1132 args.labeled = labeled;
1133
1134 let arg_on: SketchOrSurface = args.get_kw_arg("on", &RuntimeType::sketch_or_surface(), exec_state)?;
1139 let mut sketch_surface = arg_on.into_sketch_surface();
1140 if exec_state.sketch_mode() {
1143 if sketch_surface.object_id().is_none() {
1144 #[cfg(not(feature = "artifact-graph"))]
1145 {
1146 sketch_surface.set_object_id(exec_state.next_object_id());
1149 }
1150 #[cfg(feature = "artifact-graph")]
1151 {
1152 let Some(last_object) = exec_state.mod_local.artifacts.scene_objects.last() else {
1155 return Err(internal_err(
1156 "In sketch mode, the `on` plane argument must refer to an existing plane object.",
1157 range,
1158 ));
1159 };
1160 sketch_surface.set_object_id(last_object.id);
1161 }
1162 }
1163 } else {
1164 match &mut sketch_surface {
1165 SketchSurface::Plane(plane) => {
1166 ensure_sketch_plane_in_engine(plane, exec_state, &args).await?;
1168 }
1169 SketchSurface::Face(_) => {
1170 }
1172 }
1173 }
1174 let on_object_id = if let Some(object_id) = sketch_surface.object_id() {
1175 object_id
1176 } else {
1177 let message = "The `on` argument should have an object after ensure_sketch_plane_in_engine".to_owned();
1178 debug_assert!(false, "{message}");
1179 return Err(internal_err(message, range));
1180 };
1181 let arg_on_expr_name = self
1182 .arguments
1183 .iter()
1184 .find_map(|labeled_arg| {
1185 if let Some(label) = &labeled_arg.label
1186 && label.name == "on"
1187 {
1188 if let Some(name) = labeled_arg.arg.ident_name() {
1190 Some(Ok(name))
1191 } else {
1192 let message = "A sketch block's `on` parameter must be a variable or identifier, not an arbitrary expression. The parser should have enforced this."
1193 .to_owned();
1194 debug_assert!(false, "{message}");
1195 Some(Err(internal_err(message, &labeled_arg.arg)))
1196 }
1197 } else {
1198 None
1199 }
1200 })
1201 .unwrap_or_else(|| {
1202 Err(KclError::new_invalid_expression(KclErrorDetails::new(
1203 "sketch block requires an `on` parameter".to_owned(),
1204 vec![SourceRange::from(self)],
1205 )))
1206 })?
1207 .to_owned();
1209 #[cfg(not(feature = "artifact-graph"))]
1210 {
1211 let _ = on_object_id;
1212 drop(arg_on_expr_name);
1213 }
1214 #[cfg(feature = "artifact-graph")]
1215 let sketch_id = {
1216 use crate::execution::{Artifact, ArtifactId, CodeRef, SketchBlock};
1217
1218 let on_object = exec_state.mod_local.artifacts.scene_object_by_id(on_object_id);
1219
1220 let plane_artifact_id = on_object.map(|object| object.artifact_id);
1222
1223 let sketch_id = exec_state.next_object_id();
1224
1225 let artifact_id = ArtifactId::from(exec_state.next_uuid());
1226 let sketch_scene_object = Object {
1227 id: sketch_id,
1228 kind: ObjectKind::Sketch(crate::frontend::sketch::Sketch {
1229 args: crate::front::SketchCtor { on: arg_on_expr_name },
1230 plane: on_object_id,
1231 segments: Default::default(),
1232 constraints: Default::default(),
1233 }),
1234 label: Default::default(),
1235 comments: Default::default(),
1236 artifact_id,
1237 source: range.into(),
1238 };
1239 exec_state.add_scene_object(sketch_scene_object, range);
1240
1241 exec_state.add_artifact(Artifact::SketchBlock(SketchBlock {
1243 id: artifact_id,
1244 plane_id: plane_artifact_id,
1245 code_ref: CodeRef::placeholder(range),
1246 sketch_id,
1247 }));
1248
1249 sketch_id
1250 };
1251
1252 let (return_result, variables, sketch_block_state) = {
1253 self.prep_mem(exec_state.mut_stack().snapshot(), exec_state);
1255
1256 #[cfg(feature = "artifact-graph")]
1258 let initial_sketch_block_state = {
1259 SketchBlockState {
1260 sketch_id: Some(sketch_id),
1261 ..Default::default()
1262 }
1263 };
1264 #[cfg(not(feature = "artifact-graph"))]
1265 let initial_sketch_block_state = SketchBlockState::default();
1266
1267 let original_value = exec_state.mod_local.sketch_block.replace(initial_sketch_block_state);
1268
1269 let original_sketch_mode = std::mem::replace(&mut exec_state.mod_local.sketch_mode, false);
1272
1273 let result = ctx.exec_block(&self.body, exec_state, BodyType::Block).await;
1274
1275 exec_state.mod_local.sketch_mode = original_sketch_mode;
1276
1277 let sketch_block_state = std::mem::replace(&mut exec_state.mod_local.sketch_block, original_value);
1278
1279 let block_variables = exec_state
1280 .stack()
1281 .find_all_in_current_env()
1282 .map(|(name, value)| (name.clone(), value.clone()))
1283 .collect::<IndexMap<_, _>>();
1284
1285 exec_state.mut_stack().pop_env();
1286
1287 (result, block_variables, sketch_block_state)
1288 };
1289
1290 return_result?;
1292 let Some(sketch_block_state) = sketch_block_state else {
1293 debug_assert!(false, "Sketch block state should still be set to Some from just above");
1294 return Err(internal_err(
1295 "Sketch block state should still be set to Some from just above",
1296 self,
1297 ));
1298 };
1299 #[cfg(feature = "artifact-graph")]
1300 let mut sketch_block_state = sketch_block_state;
1301
1302 let constraints = sketch_block_state
1304 .solver_constraints
1305 .iter()
1306 .cloned()
1307 .map(kcl_ezpz::ConstraintRequest::highest_priority)
1308 .chain(
1309 sketch_block_state
1311 .solver_optional_constraints
1312 .iter()
1313 .cloned()
1314 .map(|c| kcl_ezpz::ConstraintRequest::new(c, 1)),
1315 )
1316 .collect::<Vec<_>>();
1317 let initial_guesses = sketch_block_state
1318 .sketch_vars
1319 .iter()
1320 .map(|v| {
1321 let Some(sketch_var) = v.as_sketch_var() else {
1322 return Err(internal_err("Expected sketch variable", self));
1323 };
1324 let constraint_id = sketch_var.id.to_constraint_id(range)?;
1325 let number_value = KclValue::Number {
1327 value: sketch_var.initial_value,
1328 ty: sketch_var.ty,
1329 meta: sketch_var.meta.clone(),
1330 };
1331 let initial_guess_value =
1332 normalize_to_solver_unit(&number_value, v.into(), exec_state, "sketch variable initial value")?;
1333 let initial_guess = if let Some(n) = initial_guess_value.as_ty_f64() {
1334 n.n
1335 } else {
1336 let message = format!(
1337 "Expected number after coercion, but found {}",
1338 initial_guess_value.human_friendly_type()
1339 );
1340 debug_assert!(false, "{}", &message);
1341 return Err(internal_err(message, self));
1342 };
1343 Ok((constraint_id, initial_guess))
1344 })
1345 .collect::<Result<Vec<_>, KclError>>()?;
1346 let config = kcl_ezpz::Config::default().with_max_iterations(50);
1348 let solve_result = if exec_state.mod_local.freedom_analysis {
1349 kcl_ezpz::solve_analysis(&constraints, initial_guesses.clone(), config).map(|outcome| {
1350 let freedom_analysis = FreedomAnalysis::from_ezpz_analysis(outcome.analysis, constraints.len());
1351 (outcome.outcome, Some(freedom_analysis))
1352 })
1353 } else {
1354 kcl_ezpz::solve(&constraints, initial_guesses.clone(), config).map(|outcome| (outcome, None))
1355 };
1356 let num_required_constraints = sketch_block_state.solver_constraints.len();
1358 let all_constraints: Vec<kcl_ezpz::Constraint> = sketch_block_state
1359 .solver_constraints
1360 .iter()
1361 .cloned()
1362 .chain(sketch_block_state.solver_optional_constraints.iter().cloned())
1363 .collect();
1364
1365 let (solve_outcome, solve_analysis) = match solve_result {
1366 Ok((solved, freedom)) => {
1367 let outcome = Solved::from_ezpz_outcome(solved, &all_constraints, num_required_constraints);
1368 (outcome, freedom)
1369 }
1370 Err(failure) => {
1371 match &failure.error {
1372 NonLinearSystemError::FaerMatrix { .. }
1373 | NonLinearSystemError::Faer { .. }
1374 | NonLinearSystemError::FaerSolve { .. }
1375 | NonLinearSystemError::FaerSvd(..)
1376 | NonLinearSystemError::DidNotConverge => {
1377 exec_state.warn(
1380 CompilationError::err(range, "Constraint solver failed to find a solution".to_owned()),
1381 annotations::WARN_SOLVER,
1382 );
1383 let final_values = initial_guesses.iter().map(|(_, v)| *v).collect::<Vec<_>>();
1384 (
1385 Solved {
1386 final_values,
1387 iterations: Default::default(),
1388 warnings: failure.warnings,
1389 priority_solved: Default::default(),
1390 variables_in_conflicts: Default::default(),
1391 },
1392 None,
1393 )
1394 }
1395 NonLinearSystemError::EmptySystemNotAllowed
1396 | NonLinearSystemError::WrongNumberGuesses { .. }
1397 | NonLinearSystemError::MissingGuess { .. }
1398 | NonLinearSystemError::NotFound(..) => {
1399 #[cfg(target_arch = "wasm32")]
1402 web_sys::console::error_1(
1403 &format!("Internal error from constraint solver: {}", &failure.error).into(),
1404 );
1405 return Err(internal_err(
1406 format!("Internal error from constraint solver: {}", &failure.error),
1407 self,
1408 ));
1409 }
1410 _ => {
1411 return Err(internal_err(
1413 format!("Error from constraint solver: {}", &failure.error),
1414 self,
1415 ));
1416 }
1417 }
1418 }
1419 };
1420 #[cfg(not(feature = "artifact-graph"))]
1421 let _ = solve_analysis;
1422 for warning in &solve_outcome.warnings {
1424 let message = if let Some(index) = warning.about_constraint.as_ref() {
1425 format!("{}; constraint index {}", &warning.content, index)
1426 } else {
1427 format!("{}", &warning.content)
1428 };
1429 exec_state.warn(CompilationError::err(range, message), annotations::WARN_SOLVER);
1430 }
1431 let sketch_engine_id = exec_state.next_uuid();
1433 let solution_ty = solver_numeric_type(exec_state);
1434 let mut solved_segments = Vec::with_capacity(sketch_block_state.needed_by_engine.len());
1435 for unsolved_segment in &sketch_block_state.needed_by_engine {
1436 solved_segments.push(substitute_sketch_var_in_segment(
1437 unsolved_segment.clone(),
1438 &sketch_surface,
1439 sketch_engine_id,
1440 None,
1441 &solve_outcome,
1442 solver_numeric_type(exec_state),
1443 solve_analysis.as_ref(),
1444 )?);
1445 }
1446 #[cfg(feature = "artifact-graph")]
1447 {
1448 exec_state.mod_local.artifacts.var_solutions =
1454 sketch_block_state.var_solutions(&solve_outcome, solution_ty, SourceRange::from(self))?;
1455 }
1456
1457 let scene_objects = create_segment_scene_objects(&solved_segments, range, exec_state)?;
1459
1460 let sketch = create_segments_in_engine(
1462 &sketch_surface,
1463 sketch_engine_id,
1464 &mut solved_segments,
1465 &sketch_block_state.segment_tags,
1466 ctx,
1467 exec_state,
1468 range,
1469 )
1470 .await?;
1471
1472 let variables = substitute_sketch_vars(
1477 variables,
1478 &sketch_surface,
1479 sketch_engine_id,
1480 sketch,
1481 &solve_outcome,
1482 solution_ty,
1483 solve_analysis.as_ref(),
1484 )?;
1485
1486 #[cfg(not(feature = "artifact-graph"))]
1487 drop(scene_objects);
1488 #[cfg(feature = "artifact-graph")]
1489 {
1490 let mut segment_object_ids = Vec::with_capacity(scene_objects.len());
1491 for scene_object in scene_objects {
1492 segment_object_ids.push(scene_object.id);
1493 exec_state.set_scene_object(scene_object);
1495 }
1496 let Some(sketch_object) = exec_state.mod_local.artifacts.scene_object_by_id_mut(sketch_id) else {
1498 let message = format!("Sketch object not found after it was just created; id={:?}", sketch_id);
1499 debug_assert!(false, "{}", &message);
1500 return Err(internal_err(message, range));
1501 };
1502 let ObjectKind::Sketch(sketch) = &mut sketch_object.kind else {
1503 let message = format!(
1504 "Expected Sketch object after it was just created to be a sketch kind; id={:?}, actual={:?}",
1505 sketch_id, sketch_object
1506 );
1507 debug_assert!(
1508 false,
1509 "{}; scene_objects={:#?}",
1510 &message, &exec_state.mod_local.artifacts.scene_objects
1511 );
1512 return Err(internal_err(message, range));
1513 };
1514 sketch.segments.extend(segment_object_ids);
1515 sketch
1517 .constraints
1518 .extend(std::mem::take(&mut sketch_block_state.sketch_constraints));
1519
1520 exec_state.push_op(Operation::SketchSolve {
1522 sketch_id,
1523 node_path: NodePath::placeholder(),
1524 source_range: range,
1525 });
1526 }
1527
1528 let metadata = Metadata {
1529 source_range: SourceRange::from(self),
1530 };
1531 let return_value = KclValue::Object {
1532 value: variables,
1533 constrainable: Default::default(),
1534 meta: vec![metadata],
1535 };
1536 Ok(if self.is_being_edited {
1537 return_value.exit()
1540 } else {
1541 return_value.continue_()
1542 })
1543 }
1544}
1545
1546impl SketchBlock {
1547 fn prep_mem(&self, parent: EnvironmentRef, exec_state: &mut ExecState) {
1548 exec_state.mut_stack().push_new_env_for_call(parent);
1549 }
1550}
1551
1552impl Node<SketchVar> {
1553 pub async fn get_result(&self, exec_state: &mut ExecState, _ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1554 let Some(sketch_block_state) = &exec_state.mod_local.sketch_block else {
1555 return Err(KclError::new_semantic(KclErrorDetails::new(
1556 "Cannot use a sketch variable outside of a sketch block".to_owned(),
1557 vec![SourceRange::from(self)],
1558 )));
1559 };
1560 let id = sketch_block_state.next_sketch_var_id();
1561 let sketch_var = if let Some(initial) = &self.initial {
1562 KclValue::from_sketch_var_literal(initial, id, exec_state)
1563 } else {
1564 let metadata = Metadata {
1565 source_range: SourceRange::from(self),
1566 };
1567
1568 KclValue::SketchVar {
1569 value: Box::new(super::SketchVar {
1570 id,
1571 initial_value: 0.0,
1572 ty: NumericType::default(),
1573 meta: vec![metadata],
1574 }),
1575 }
1576 };
1577
1578 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
1579 return Err(KclError::new_semantic(KclErrorDetails::new(
1580 "Cannot use a sketch variable outside of a sketch block".to_owned(),
1581 vec![SourceRange::from(self)],
1582 )));
1583 };
1584 sketch_block_state.sketch_vars.push(sketch_var.clone());
1585
1586 Ok(sketch_var)
1587 }
1588}
1589
1590fn apply_ascription(
1591 value: &KclValue,
1592 ty: &Node<Type>,
1593 exec_state: &mut ExecState,
1594 source_range: SourceRange,
1595) -> Result<KclValue, KclError> {
1596 let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into(), false, false)
1597 .map_err(|e| KclError::new_semantic(e.into()))?;
1598
1599 if matches!(&ty, &RuntimeType::Primitive(PrimitiveType::Number(..))) {
1600 exec_state.clear_units_warnings(&source_range);
1601 }
1602
1603 value.coerce(&ty, false, exec_state).map_err(|_| {
1604 let suggestion = if ty == RuntimeType::length() {
1605 ", you might try coercing to a fully specified numeric type such as `mm`"
1606 } else if ty == RuntimeType::angle() {
1607 ", you might try coercing to a fully specified numeric type such as `deg`"
1608 } else {
1609 ""
1610 };
1611 let ty_str = if let Some(ty) = value.principal_type() {
1612 format!("(with type `{ty}`) ")
1613 } else {
1614 String::new()
1615 };
1616 KclError::new_semantic(KclErrorDetails::new(
1617 format!(
1618 "could not coerce {} {ty_str}to type `{ty}`{suggestion}",
1619 value.human_friendly_type()
1620 ),
1621 vec![source_range],
1622 ))
1623 })
1624}
1625
1626impl BinaryPart {
1627 #[async_recursion]
1628 pub(super) async fn get_result(
1629 &self,
1630 exec_state: &mut ExecState,
1631 ctx: &ExecutorContext,
1632 ) -> Result<KclValueControlFlow, KclError> {
1633 match self {
1634 BinaryPart::Literal(literal) => Ok(KclValue::from_literal((**literal).clone(), exec_state).continue_()),
1635 BinaryPart::Name(name) => name.get_result(exec_state, ctx).await.cloned().map(KclValue::continue_),
1636 BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
1637 BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await,
1638 BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
1639 BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state, ctx).await,
1640 BinaryPart::ArrayExpression(e) => e.execute(exec_state, ctx).await,
1641 BinaryPart::ArrayRangeExpression(e) => e.execute(exec_state, ctx).await,
1642 BinaryPart::ObjectExpression(e) => e.execute(exec_state, ctx).await,
1643 BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
1644 BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
1645 BinaryPart::SketchVar(e) => e.get_result(exec_state, ctx).await.map(KclValue::continue_),
1646 }
1647 }
1648}
1649
1650impl Node<Name> {
1651 pub(super) async fn get_result<'a>(
1652 &self,
1653 exec_state: &'a mut ExecState,
1654 ctx: &ExecutorContext,
1655 ) -> Result<&'a KclValue, KclError> {
1656 let being_declared = exec_state.mod_local.being_declared.clone();
1657 self.get_result_inner(exec_state, ctx)
1658 .await
1659 .map_err(|e| var_in_own_ref_err(e, &being_declared))
1660 }
1661
1662 async fn get_result_inner<'a>(
1663 &self,
1664 exec_state: &'a mut ExecState,
1665 ctx: &ExecutorContext,
1666 ) -> Result<&'a KclValue, KclError> {
1667 if self.abs_path {
1668 return Err(KclError::new_semantic(KclErrorDetails::new(
1669 "Absolute paths (names beginning with `::` are not yet supported)".to_owned(),
1670 self.as_source_ranges(),
1671 )));
1672 }
1673
1674 let mod_name = format!("{}{}", memory::MODULE_PREFIX, self.name.name);
1675
1676 if self.path.is_empty() {
1677 let item_value = exec_state.stack().get(&self.name.name, self.into());
1678 if item_value.is_ok() {
1679 return item_value;
1680 }
1681 return exec_state.stack().get(&mod_name, self.into());
1682 }
1683
1684 let mut mem_spec: Option<(EnvironmentRef, Vec<String>)> = None;
1685 for p in &self.path {
1686 let value = match mem_spec {
1687 Some((env, exports)) => {
1688 if !exports.contains(&p.name) {
1689 return Err(KclError::new_semantic(KclErrorDetails::new(
1690 format!("Item {} not found in module's exported items", p.name),
1691 p.as_source_ranges(),
1692 )));
1693 }
1694
1695 exec_state
1696 .stack()
1697 .memory
1698 .get_from(&p.name, env, p.as_source_range(), 0)?
1699 }
1700 None => exec_state
1701 .stack()
1702 .get(&format!("{}{}", memory::MODULE_PREFIX, p.name), self.into())?,
1703 };
1704
1705 let KclValue::Module { value: module_id, .. } = value else {
1706 return Err(KclError::new_semantic(KclErrorDetails::new(
1707 format!(
1708 "Identifier in path must refer to a module, found {}",
1709 value.human_friendly_type()
1710 ),
1711 p.as_source_ranges(),
1712 )));
1713 };
1714
1715 mem_spec = Some(
1716 ctx.exec_module_for_items(*module_id, exec_state, p.as_source_range())
1717 .await?,
1718 );
1719 }
1720
1721 let (env, exports) = mem_spec.unwrap();
1722
1723 let item_exported = exports.contains(&self.name.name);
1724 let item_value = exec_state
1725 .stack()
1726 .memory
1727 .get_from(&self.name.name, env, self.name.as_source_range(), 0);
1728
1729 if item_exported && item_value.is_ok() {
1731 return item_value;
1732 }
1733
1734 let mod_exported = exports.contains(&mod_name);
1735 let mod_value = exec_state
1736 .stack()
1737 .memory
1738 .get_from(&mod_name, env, self.name.as_source_range(), 0);
1739
1740 if mod_exported && mod_value.is_ok() {
1742 return mod_value;
1743 }
1744
1745 if item_value.is_err() && mod_value.is_err() {
1747 return item_value;
1748 }
1749
1750 debug_assert!((item_value.is_ok() && !item_exported) || (mod_value.is_ok() && !mod_exported));
1752 Err(KclError::new_semantic(KclErrorDetails::new(
1753 format!("Item {} not found in module's exported items", self.name.name),
1754 self.name.as_source_ranges(),
1755 )))
1756 }
1757}
1758
1759impl Node<MemberExpression> {
1760 async fn get_result(
1761 &self,
1762 exec_state: &mut ExecState,
1763 ctx: &ExecutorContext,
1764 ) -> Result<KclValueControlFlow, KclError> {
1765 let meta = Metadata {
1766 source_range: SourceRange::from(self),
1767 };
1768 let property = Property::try_from(
1771 self.computed,
1772 self.property.clone(),
1773 exec_state,
1774 self.into(),
1775 ctx,
1776 &meta,
1777 &[],
1778 StatementKind::Expression,
1779 )
1780 .await?;
1781 let object_cf = ctx
1782 .execute_expr(&self.object, exec_state, &meta, &[], StatementKind::Expression)
1783 .await?;
1784 let object = control_continue!(object_cf);
1785
1786 match (object, property, self.computed) {
1788 (KclValue::Segment { value: segment }, Property::String(property), false) => match property.as_str() {
1789 "at" => match &segment.repr {
1790 SegmentRepr::Unsolved { segment } => {
1791 match &segment.kind {
1792 UnsolvedSegmentKind::Point { position, .. } => {
1793 Ok(KclValue::HomArray {
1795 value: vec![
1796 KclValue::from_unsolved_expr(position[0].clone(), segment.meta.clone()),
1797 KclValue::from_unsolved_expr(position[1].clone(), segment.meta.clone()),
1798 ],
1799 ty: RuntimeType::any(),
1800 }
1801 .continue_())
1802 }
1803 _ => Err(KclError::new_undefined_value(
1804 KclErrorDetails::new(
1805 format!("Property '{property}' not found in segment"),
1806 vec![self.clone().into()],
1807 ),
1808 None,
1809 )),
1810 }
1811 }
1812 SegmentRepr::Solved { segment } => {
1813 match &segment.kind {
1814 SegmentKind::Point { position, .. } => {
1815 Ok(KclValue::array_from_point2d(
1817 [position[0].n, position[1].n],
1818 position[0].ty,
1819 segment.meta.clone(),
1820 )
1821 .continue_())
1822 }
1823 _ => Err(KclError::new_undefined_value(
1824 KclErrorDetails::new(
1825 format!("Property '{property}' not found in segment"),
1826 vec![self.clone().into()],
1827 ),
1828 None,
1829 )),
1830 }
1831 }
1832 },
1833 "start" => match &segment.repr {
1834 SegmentRepr::Unsolved { segment } => match &segment.kind {
1835 UnsolvedSegmentKind::Point { .. } => Err(KclError::new_undefined_value(
1836 KclErrorDetails::new(
1837 format!("Property '{property}' not found in point segment"),
1838 vec![self.clone().into()],
1839 ),
1840 None,
1841 )),
1842 UnsolvedSegmentKind::Line {
1843 start,
1844 ctor,
1845 start_object_id,
1846 ..
1847 } => Ok(KclValue::Segment {
1848 value: Box::new(AbstractSegment {
1849 repr: SegmentRepr::Unsolved {
1850 segment: Box::new(UnsolvedSegment {
1851 id: segment.id,
1852 object_id: *start_object_id,
1853 kind: UnsolvedSegmentKind::Point {
1854 position: start.clone(),
1855 ctor: Box::new(PointCtor {
1856 position: ctor.start.clone(),
1857 }),
1858 },
1859 tag: segment.tag.clone(),
1860 meta: segment.meta.clone(),
1861 }),
1862 },
1863 meta: segment.meta.clone(),
1864 }),
1865 }
1866 .continue_()),
1867 UnsolvedSegmentKind::Arc {
1868 start,
1869 ctor,
1870 start_object_id,
1871 ..
1872 } => Ok(KclValue::Segment {
1873 value: Box::new(AbstractSegment {
1874 repr: SegmentRepr::Unsolved {
1875 segment: Box::new(UnsolvedSegment {
1876 id: segment.id,
1877 object_id: *start_object_id,
1878 kind: UnsolvedSegmentKind::Point {
1879 position: start.clone(),
1880 ctor: Box::new(PointCtor {
1881 position: ctor.start.clone(),
1882 }),
1883 },
1884 tag: segment.tag.clone(),
1885 meta: segment.meta.clone(),
1886 }),
1887 },
1888 meta: segment.meta.clone(),
1889 }),
1890 }
1891 .continue_()),
1892 },
1893 SegmentRepr::Solved { segment } => match &segment.kind {
1894 SegmentKind::Point { .. } => Err(KclError::new_undefined_value(
1895 KclErrorDetails::new(
1896 format!("Property '{property}' not found in point segment"),
1897 vec![self.clone().into()],
1898 ),
1899 None,
1900 )),
1901 SegmentKind::Line {
1902 start,
1903 ctor,
1904 start_object_id,
1905 start_freedom,
1906 ..
1907 } => Ok(KclValue::Segment {
1908 value: Box::new(AbstractSegment {
1909 repr: SegmentRepr::Solved {
1910 segment: Box::new(Segment {
1911 id: segment.id,
1912 object_id: *start_object_id,
1913 kind: SegmentKind::Point {
1914 position: start.clone(),
1915 ctor: Box::new(PointCtor {
1916 position: ctor.start.clone(),
1917 }),
1918 freedom: *start_freedom,
1919 },
1920 surface: segment.surface.clone(),
1921 sketch_id: segment.sketch_id,
1922 sketch: None,
1923 tag: segment.tag.clone(),
1924 meta: segment.meta.clone(),
1925 }),
1926 },
1927 meta: segment.meta.clone(),
1928 }),
1929 }
1930 .continue_()),
1931 SegmentKind::Arc {
1932 start,
1933 ctor,
1934 start_object_id,
1935 start_freedom,
1936 ..
1937 } => Ok(KclValue::Segment {
1938 value: Box::new(AbstractSegment {
1939 repr: SegmentRepr::Solved {
1940 segment: Box::new(Segment {
1941 id: segment.id,
1942 object_id: *start_object_id,
1943 kind: SegmentKind::Point {
1944 position: start.clone(),
1945 ctor: Box::new(PointCtor {
1946 position: ctor.start.clone(),
1947 }),
1948 freedom: *start_freedom,
1949 },
1950 surface: segment.surface.clone(),
1951 sketch_id: segment.sketch_id,
1952 sketch: None,
1953 tag: segment.tag.clone(),
1954 meta: segment.meta.clone(),
1955 }),
1956 },
1957 meta: segment.meta.clone(),
1958 }),
1959 }
1960 .continue_()),
1961 },
1962 },
1963 "end" => match &segment.repr {
1964 SegmentRepr::Unsolved { segment } => match &segment.kind {
1965 UnsolvedSegmentKind::Point { .. } => Err(KclError::new_undefined_value(
1966 KclErrorDetails::new(
1967 format!("Property '{property}' not found in point segment"),
1968 vec![self.clone().into()],
1969 ),
1970 None,
1971 )),
1972 UnsolvedSegmentKind::Line {
1973 end,
1974 ctor,
1975 end_object_id,
1976 ..
1977 } => Ok(KclValue::Segment {
1978 value: Box::new(AbstractSegment {
1979 repr: SegmentRepr::Unsolved {
1980 segment: Box::new(UnsolvedSegment {
1981 id: segment.id,
1982 object_id: *end_object_id,
1983 kind: UnsolvedSegmentKind::Point {
1984 position: end.clone(),
1985 ctor: Box::new(PointCtor {
1986 position: ctor.end.clone(),
1987 }),
1988 },
1989 tag: segment.tag.clone(),
1990 meta: segment.meta.clone(),
1991 }),
1992 },
1993 meta: segment.meta.clone(),
1994 }),
1995 }
1996 .continue_()),
1997 UnsolvedSegmentKind::Arc {
1998 end,
1999 ctor,
2000 end_object_id,
2001 ..
2002 } => Ok(KclValue::Segment {
2003 value: Box::new(AbstractSegment {
2004 repr: SegmentRepr::Unsolved {
2005 segment: Box::new(UnsolvedSegment {
2006 id: segment.id,
2007 object_id: *end_object_id,
2008 kind: UnsolvedSegmentKind::Point {
2009 position: end.clone(),
2010 ctor: Box::new(PointCtor {
2011 position: ctor.end.clone(),
2012 }),
2013 },
2014 tag: segment.tag.clone(),
2015 meta: segment.meta.clone(),
2016 }),
2017 },
2018 meta: segment.meta.clone(),
2019 }),
2020 }
2021 .continue_()),
2022 },
2023 SegmentRepr::Solved { segment } => match &segment.kind {
2024 SegmentKind::Point { .. } => Err(KclError::new_undefined_value(
2025 KclErrorDetails::new(
2026 format!("Property '{property}' not found in point segment"),
2027 vec![self.clone().into()],
2028 ),
2029 None,
2030 )),
2031 SegmentKind::Line {
2032 end,
2033 ctor,
2034 end_object_id,
2035 end_freedom,
2036 ..
2037 } => Ok(KclValue::Segment {
2038 value: Box::new(AbstractSegment {
2039 repr: SegmentRepr::Solved {
2040 segment: Box::new(Segment {
2041 id: segment.id,
2042 object_id: *end_object_id,
2043 kind: SegmentKind::Point {
2044 position: end.clone(),
2045 ctor: Box::new(PointCtor {
2046 position: ctor.end.clone(),
2047 }),
2048 freedom: *end_freedom,
2049 },
2050 surface: segment.surface.clone(),
2051 sketch_id: segment.sketch_id,
2052 sketch: None,
2053 tag: segment.tag.clone(),
2054 meta: segment.meta.clone(),
2055 }),
2056 },
2057 meta: segment.meta.clone(),
2058 }),
2059 }
2060 .continue_()),
2061 SegmentKind::Arc {
2062 end,
2063 ctor,
2064 end_object_id,
2065 end_freedom,
2066 ..
2067 } => Ok(KclValue::Segment {
2068 value: Box::new(AbstractSegment {
2069 repr: SegmentRepr::Solved {
2070 segment: Box::new(Segment {
2071 id: segment.id,
2072 object_id: *end_object_id,
2073 kind: SegmentKind::Point {
2074 position: end.clone(),
2075 ctor: Box::new(PointCtor {
2076 position: ctor.end.clone(),
2077 }),
2078 freedom: *end_freedom,
2079 },
2080 surface: segment.surface.clone(),
2081 sketch_id: segment.sketch_id,
2082 sketch: None,
2083 tag: segment.tag.clone(),
2084 meta: segment.meta.clone(),
2085 }),
2086 },
2087 meta: segment.meta.clone(),
2088 }),
2089 }
2090 .continue_()),
2091 },
2092 },
2093 "center" => match &segment.repr {
2094 SegmentRepr::Unsolved { segment } => match &segment.kind {
2095 UnsolvedSegmentKind::Arc {
2096 center,
2097 ctor,
2098 center_object_id,
2099 ..
2100 } => Ok(KclValue::Segment {
2101 value: Box::new(AbstractSegment {
2102 repr: SegmentRepr::Unsolved {
2103 segment: Box::new(UnsolvedSegment {
2104 id: segment.id,
2105 object_id: *center_object_id,
2106 kind: UnsolvedSegmentKind::Point {
2107 position: center.clone(),
2108 ctor: Box::new(PointCtor {
2109 position: ctor.center.clone(),
2110 }),
2111 },
2112 tag: segment.tag.clone(),
2113 meta: segment.meta.clone(),
2114 }),
2115 },
2116 meta: segment.meta.clone(),
2117 }),
2118 }
2119 .continue_()),
2120 _ => Err(KclError::new_undefined_value(
2121 KclErrorDetails::new(
2122 format!("Property '{property}' not found in segment"),
2123 vec![self.clone().into()],
2124 ),
2125 None,
2126 )),
2127 },
2128 SegmentRepr::Solved { segment } => match &segment.kind {
2129 SegmentKind::Arc {
2130 center,
2131 ctor,
2132 center_object_id,
2133 center_freedom,
2134 ..
2135 } => Ok(KclValue::Segment {
2136 value: Box::new(AbstractSegment {
2137 repr: SegmentRepr::Solved {
2138 segment: Box::new(Segment {
2139 id: segment.id,
2140 object_id: *center_object_id,
2141 kind: SegmentKind::Point {
2142 position: center.clone(),
2143 ctor: Box::new(PointCtor {
2144 position: ctor.center.clone(),
2145 }),
2146 freedom: *center_freedom,
2147 },
2148 surface: segment.surface.clone(),
2149 sketch_id: segment.sketch_id,
2150 sketch: None,
2151 tag: segment.tag.clone(),
2152 meta: segment.meta.clone(),
2153 }),
2154 },
2155 meta: segment.meta.clone(),
2156 }),
2157 }
2158 .continue_()),
2159 _ => Err(KclError::new_undefined_value(
2160 KclErrorDetails::new(
2161 format!("Property '{property}' not found in segment"),
2162 vec![self.clone().into()],
2163 ),
2164 None,
2165 )),
2166 },
2167 },
2168 other => Err(KclError::new_undefined_value(
2169 KclErrorDetails::new(
2170 format!("Property '{other}' not found in segment"),
2171 vec![self.clone().into()],
2172 ),
2173 None,
2174 )),
2175 },
2176 (KclValue::Plane { value: plane }, Property::String(property), false) => match property.as_str() {
2177 "zAxis" => {
2178 let (p, u) = plane.info.z_axis.as_3_dims();
2179 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
2180 }
2181 "yAxis" => {
2182 let (p, u) = plane.info.y_axis.as_3_dims();
2183 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
2184 }
2185 "xAxis" => {
2186 let (p, u) = plane.info.x_axis.as_3_dims();
2187 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
2188 }
2189 "origin" => {
2190 let (p, u) = plane.info.origin.as_3_dims();
2191 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
2192 }
2193 other => Err(KclError::new_undefined_value(
2194 KclErrorDetails::new(
2195 format!("Property '{other}' not found in plane"),
2196 vec![self.clone().into()],
2197 ),
2198 None,
2199 )),
2200 },
2201 (KclValue::Object { value: map, .. }, Property::String(property), false) => {
2202 if let Some(value) = map.get(&property) {
2203 Ok(value.to_owned().continue_())
2204 } else {
2205 Err(KclError::new_undefined_value(
2206 KclErrorDetails::new(
2207 format!("Property '{property}' not found in object"),
2208 vec![self.clone().into()],
2209 ),
2210 None,
2211 ))
2212 }
2213 }
2214 (KclValue::Object { .. }, Property::String(property), true) => {
2215 Err(KclError::new_semantic(KclErrorDetails::new(
2216 format!("Cannot index object with string; use dot notation instead, e.g. `obj.{property}`"),
2217 vec![self.clone().into()],
2218 )))
2219 }
2220 (KclValue::Object { value: map, .. }, p @ Property::UInt(i), _) => {
2221 if i == 0
2222 && let Some(value) = map.get("x")
2223 {
2224 return Ok(value.to_owned().continue_());
2225 }
2226 if i == 1
2227 && let Some(value) = map.get("y")
2228 {
2229 return Ok(value.to_owned().continue_());
2230 }
2231 if i == 2
2232 && let Some(value) = map.get("z")
2233 {
2234 return Ok(value.to_owned().continue_());
2235 }
2236 let t = p.type_name();
2237 let article = article_for(t);
2238 Err(KclError::new_semantic(KclErrorDetails::new(
2239 format!("Only strings can be used as the property of an object, but you're using {article} {t}",),
2240 vec![self.clone().into()],
2241 )))
2242 }
2243 (KclValue::HomArray { value: arr, .. }, Property::UInt(index), _) => {
2244 let value_of_arr = arr.get(index);
2245 if let Some(value) = value_of_arr {
2246 Ok(value.to_owned().continue_())
2247 } else {
2248 Err(KclError::new_undefined_value(
2249 KclErrorDetails::new(
2250 format!("The array doesn't have any item at index {index}"),
2251 vec![self.clone().into()],
2252 ),
2253 None,
2254 ))
2255 }
2256 }
2257 (obj, Property::UInt(0), _) => Ok(obj.continue_()),
2260 (KclValue::HomArray { .. }, p, _) => {
2261 let t = p.type_name();
2262 let article = article_for(t);
2263 Err(KclError::new_semantic(KclErrorDetails::new(
2264 format!("Only integers >= 0 can be used as the index of an array, but you're using {article} {t}",),
2265 vec![self.clone().into()],
2266 )))
2267 }
2268 (KclValue::Solid { value }, Property::String(prop), false) if prop == "sketch" => {
2269 let Some(sketch) = value.sketch() else {
2270 return Err(KclError::new_semantic(KclErrorDetails::new(
2271 "This solid was created without a sketch, so `solid.sketch` is unavailable.".to_owned(),
2272 vec![self.clone().into()],
2273 )));
2274 };
2275 Ok(KclValue::Sketch {
2276 value: Box::new(sketch.clone()),
2277 }
2278 .continue_())
2279 }
2280 (geometry @ KclValue::Solid { .. }, Property::String(prop), false) if prop == "tags" => {
2281 Err(KclError::new_semantic(KclErrorDetails::new(
2283 format!(
2284 "Property `{prop}` not found on {}. You can get a solid's tags through its sketch, as in, `exampleSolid.sketch.tags`.",
2285 geometry.human_friendly_type()
2286 ),
2287 vec![self.clone().into()],
2288 )))
2289 }
2290 (KclValue::Sketch { value: sk }, Property::String(prop), false) if prop == "tags" => Ok(KclValue::Object {
2291 meta: vec![Metadata {
2292 source_range: SourceRange::from(self.clone()),
2293 }],
2294 value: sk
2295 .tags
2296 .iter()
2297 .map(|(k, tag)| (k.to_owned(), KclValue::TagIdentifier(Box::new(tag.to_owned()))))
2298 .collect(),
2299 constrainable: false,
2300 }
2301 .continue_()),
2302 (geometry @ (KclValue::Sketch { .. } | KclValue::Solid { .. }), Property::String(property), false) => {
2303 Err(KclError::new_semantic(KclErrorDetails::new(
2304 format!("Property `{property}` not found on {}", geometry.human_friendly_type()),
2305 vec![self.clone().into()],
2306 )))
2307 }
2308 (being_indexed, _, false) => Err(KclError::new_semantic(KclErrorDetails::new(
2309 format!(
2310 "Only objects can have members accessed with dot notation, but you're trying to access {}",
2311 being_indexed.human_friendly_type()
2312 ),
2313 vec![self.clone().into()],
2314 ))),
2315 (being_indexed, _, true) => Err(KclError::new_semantic(KclErrorDetails::new(
2316 format!(
2317 "Only arrays can be indexed, but you're trying to index {}",
2318 being_indexed.human_friendly_type()
2319 ),
2320 vec![self.clone().into()],
2321 ))),
2322 }
2323 }
2324}
2325
2326impl Node<BinaryExpression> {
2327 pub(super) async fn get_result(
2328 &self,
2329 exec_state: &mut ExecState,
2330 ctx: &ExecutorContext,
2331 ) -> Result<KclValueControlFlow, KclError> {
2332 enum State {
2333 EvaluateLeft(Node<BinaryExpression>),
2334 FromLeft {
2335 node: Node<BinaryExpression>,
2336 },
2337 EvaluateRight {
2338 node: Node<BinaryExpression>,
2339 left: KclValue,
2340 },
2341 FromRight {
2342 node: Node<BinaryExpression>,
2343 left: KclValue,
2344 },
2345 }
2346
2347 let mut stack = vec![State::EvaluateLeft(self.clone())];
2348 let mut last_result: Option<KclValue> = None;
2349
2350 while let Some(state) = stack.pop() {
2351 match state {
2352 State::EvaluateLeft(node) => {
2353 let left_part = node.left.clone();
2354 match left_part {
2355 BinaryPart::BinaryExpression(child) => {
2356 stack.push(State::FromLeft { node });
2357 stack.push(State::EvaluateLeft(*child));
2358 }
2359 part => {
2360 let left_value = part.get_result(exec_state, ctx).await?;
2361 let left_value = control_continue!(left_value);
2362 stack.push(State::EvaluateRight { node, left: left_value });
2363 }
2364 }
2365 }
2366 State::FromLeft { node } => {
2367 let Some(left_value) = last_result.take() else {
2368 return Err(Self::missing_result_error(&node));
2369 };
2370 stack.push(State::EvaluateRight { node, left: left_value });
2371 }
2372 State::EvaluateRight { node, left } => {
2373 let right_part = node.right.clone();
2374 match right_part {
2375 BinaryPart::BinaryExpression(child) => {
2376 stack.push(State::FromRight { node, left });
2377 stack.push(State::EvaluateLeft(*child));
2378 }
2379 part => {
2380 let right_value = part.get_result(exec_state, ctx).await?;
2381 let right_value = control_continue!(right_value);
2382 let result = node.apply_operator(exec_state, ctx, left, right_value).await?;
2383 last_result = Some(result);
2384 }
2385 }
2386 }
2387 State::FromRight { node, left } => {
2388 let Some(right_value) = last_result.take() else {
2389 return Err(Self::missing_result_error(&node));
2390 };
2391 let result = node.apply_operator(exec_state, ctx, left, right_value).await?;
2392 last_result = Some(result);
2393 }
2394 }
2395 }
2396
2397 last_result
2398 .map(KclValue::continue_)
2399 .ok_or_else(|| Self::missing_result_error(self))
2400 }
2401
2402 async fn apply_operator(
2403 &self,
2404 exec_state: &mut ExecState,
2405 ctx: &ExecutorContext,
2406 left_value: KclValue,
2407 right_value: KclValue,
2408 ) -> Result<KclValue, KclError> {
2409 let mut meta = left_value.metadata();
2410 meta.extend(right_value.metadata());
2411
2412 if self.operator == BinaryOperator::Add
2414 && let (KclValue::String { value: left, .. }, KclValue::String { value: right, .. }) =
2415 (&left_value, &right_value)
2416 {
2417 return Ok(KclValue::String {
2418 value: format!("{left}{right}"),
2419 meta,
2420 });
2421 }
2422
2423 if self.operator == BinaryOperator::Add || self.operator == BinaryOperator::Or {
2425 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
2426 let args = Args::new_no_args(self.into(), ctx.clone(), Some("union".to_owned()));
2427 let result = crate::std::csg::inner_union(
2428 vec![*left.clone(), *right.clone()],
2429 Default::default(),
2430 exec_state,
2431 args,
2432 )
2433 .await?;
2434 return Ok(result.into());
2435 }
2436 } else if self.operator == BinaryOperator::Sub {
2437 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
2439 let args = Args::new_no_args(self.into(), ctx.clone(), Some("subtract".to_owned()));
2440 let result = crate::std::csg::inner_subtract(
2441 vec![*left.clone()],
2442 vec![*right.clone()],
2443 Default::default(),
2444 exec_state,
2445 args,
2446 )
2447 .await?;
2448 return Ok(result.into());
2449 }
2450 } else if self.operator == BinaryOperator::And
2451 && let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value)
2452 {
2453 let args = Args::new_no_args(self.into(), ctx.clone(), Some("intersect".to_owned()));
2455 let result = crate::std::csg::inner_intersect(
2456 vec![*left.clone(), *right.clone()],
2457 Default::default(),
2458 exec_state,
2459 args,
2460 )
2461 .await?;
2462 return Ok(result.into());
2463 }
2464
2465 if self.operator == BinaryOperator::Or || self.operator == BinaryOperator::And {
2467 let KclValue::Bool { value: left_value, .. } = left_value else {
2468 return Err(KclError::new_semantic(KclErrorDetails::new(
2469 format!(
2470 "Cannot apply logical operator to non-boolean value: {}",
2471 left_value.human_friendly_type()
2472 ),
2473 vec![self.left.clone().into()],
2474 )));
2475 };
2476 let KclValue::Bool { value: right_value, .. } = right_value else {
2477 return Err(KclError::new_semantic(KclErrorDetails::new(
2478 format!(
2479 "Cannot apply logical operator to non-boolean value: {}",
2480 right_value.human_friendly_type()
2481 ),
2482 vec![self.right.clone().into()],
2483 )));
2484 };
2485 let raw_value = match self.operator {
2486 BinaryOperator::Or => left_value || right_value,
2487 BinaryOperator::And => left_value && right_value,
2488 _ => unreachable!(),
2489 };
2490 return Ok(KclValue::Bool { value: raw_value, meta });
2491 }
2492
2493 if self.operator == BinaryOperator::Eq && exec_state.mod_local.sketch_block.is_some() {
2495 match (&left_value, &right_value) {
2496 (KclValue::SketchVar { value: left_value, .. }, KclValue::SketchVar { value: right_value, .. })
2498 if left_value.id == right_value.id =>
2499 {
2500 return Ok(KclValue::Bool { value: true, meta });
2501 }
2502 (KclValue::SketchVar { .. }, KclValue::SketchVar { .. }) => {
2504 return Err(KclError::new_semantic(KclErrorDetails::new(
2507 "TODO: Different sketch variables".to_owned(),
2508 vec![self.into()],
2509 )));
2510 }
2511 (KclValue::SketchVar { value: var, .. }, input_number @ KclValue::Number { .. })
2513 | (input_number @ KclValue::Number { .. }, KclValue::SketchVar { value: var, .. }) => {
2514 let number_value = normalize_to_solver_unit(
2515 input_number,
2516 input_number.into(),
2517 exec_state,
2518 "fixed constraint value",
2519 )?;
2520 let Some(n) = number_value.as_ty_f64() else {
2521 let message = format!(
2522 "Expected number after coercion, but found {}",
2523 number_value.human_friendly_type()
2524 );
2525 debug_assert!(false, "{}", &message);
2526 return Err(internal_err(message, self));
2527 };
2528 let constraint = Constraint::Fixed(var.id.to_constraint_id(self.as_source_range())?, n.n);
2529 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2530 let message = "Being inside a sketch block should have already been checked above".to_owned();
2531 debug_assert!(false, "{}", &message);
2532 return Err(internal_err(message, self));
2533 };
2534 sketch_block_state.solver_constraints.push(constraint);
2535 return Ok(KclValue::Bool { value: true, meta });
2536 }
2537 (KclValue::SketchConstraint { value: constraint }, input_number @ KclValue::Number { .. })
2539 | (input_number @ KclValue::Number { .. }, KclValue::SketchConstraint { value: constraint }) => {
2540 let number_value = normalize_to_solver_unit(
2541 input_number,
2542 input_number.into(),
2543 exec_state,
2544 "fixed constraint value",
2545 )?;
2546 let Some(n) = number_value.as_ty_f64() else {
2547 let message = format!(
2548 "Expected number after coercion, but found {}",
2549 number_value.human_friendly_type()
2550 );
2551 debug_assert!(false, "{}", &message);
2552 return Err(internal_err(message, self));
2553 };
2554 #[cfg(feature = "artifact-graph")]
2556 let number_binary_part = if matches!(&left_value, KclValue::SketchConstraint { .. }) {
2557 &self.right
2558 } else {
2559 &self.left
2560 };
2561 #[cfg(feature = "artifact-graph")]
2562 let source = {
2563 use crate::unparser::ExprContext;
2564 let mut buf = String::new();
2565 number_binary_part.recast(&mut buf, &Default::default(), 0, ExprContext::Other);
2566 crate::frontend::sketch::ConstraintSource {
2567 expr: buf,
2568 is_literal: matches!(number_binary_part, BinaryPart::Literal(_)),
2569 }
2570 };
2571
2572 match &constraint.kind {
2573 SketchConstraintKind::Distance { points } => {
2574 let range = self.as_source_range();
2575 let p0 = &points[0];
2576 let p1 = &points[1];
2577 let solver_pt0 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2578 p0.vars.x.to_constraint_id(range)?,
2579 p0.vars.y.to_constraint_id(range)?,
2580 );
2581 let solver_pt1 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2582 p1.vars.x.to_constraint_id(range)?,
2583 p1.vars.y.to_constraint_id(range)?,
2584 );
2585 let solver_constraint = Constraint::Distance(solver_pt0, solver_pt1, n.n);
2586
2587 #[cfg(feature = "artifact-graph")]
2588 let constraint_id = exec_state.next_object_id();
2589 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2590 let message =
2591 "Being inside a sketch block should have already been checked above".to_owned();
2592 debug_assert!(false, "{}", &message);
2593 return Err(internal_err(message, self));
2594 };
2595 sketch_block_state.solver_constraints.push(solver_constraint);
2596 #[cfg(feature = "artifact-graph")]
2597 {
2598 use crate::{
2599 execution::{Artifact, CodeRef, SketchBlockConstraint, SketchBlockConstraintType},
2600 front::Distance,
2601 };
2602
2603 let Some(sketch_id) = sketch_block_state.sketch_id else {
2604 let message = "Sketch id missing for constraint artifact".to_owned();
2605 debug_assert!(false, "{}", &message);
2606 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![range])));
2607 };
2608 let sketch_constraint = crate::front::Constraint::Distance(Distance {
2609 points: vec![p0.object_id, p1.object_id],
2610 distance: n.try_into().map_err(|_| {
2611 internal_err("Failed to convert distance units numeric suffix:", range)
2612 })?,
2613 source,
2614 });
2615 sketch_block_state.sketch_constraints.push(constraint_id);
2616 let artifact_id = exec_state.next_artifact_id();
2617 exec_state.add_artifact(Artifact::SketchBlockConstraint(SketchBlockConstraint {
2618 id: artifact_id,
2619 sketch_id,
2620 constraint_id,
2621 constraint_type: SketchBlockConstraintType::from(&sketch_constraint),
2622 code_ref: CodeRef::placeholder(range),
2623 }));
2624 exec_state.add_scene_object(
2625 Object {
2626 id: constraint_id,
2627 kind: ObjectKind::Constraint {
2628 constraint: sketch_constraint,
2629 },
2630 label: Default::default(),
2631 comments: Default::default(),
2632 artifact_id,
2633 source: range.into(),
2634 },
2635 range,
2636 );
2637 }
2638 }
2639 SketchConstraintKind::Radius { points } | SketchConstraintKind::Diameter { points } => {
2640 let range = self.as_source_range();
2641 let center = &points[0];
2642 let start = &points[1];
2643 let Some(sketch_block_state) = &exec_state.mod_local.sketch_block else {
2645 return Err(internal_err(
2646 "Being inside a sketch block should have already been checked above",
2647 self,
2648 ));
2649 };
2650 let (constraint_name, is_diameter) = match &constraint.kind {
2652 SketchConstraintKind::Radius { .. } => ("radius", false),
2653 SketchConstraintKind::Diameter { .. } => ("diameter", true),
2654 _ => unreachable!(),
2655 };
2656 let arc_segment = sketch_block_state
2657 .needed_by_engine
2658 .iter()
2659 .find(|seg| {
2660 matches!(&seg.kind, UnsolvedSegmentKind::Arc {
2661 center_object_id,
2662 start_object_id,
2663 ..
2664 } if *center_object_id == center.object_id && *start_object_id == start.object_id)
2665 })
2666 .ok_or_else(|| {
2667 internal_err(
2668 format!("Could not find arc segment for {} constraint", constraint_name),
2669 range,
2670 )
2671 })?;
2672 let UnsolvedSegmentKind::Arc { end, .. } = &arc_segment.kind else {
2673 return Err(internal_err("Expected arc segment", range));
2674 };
2675 let (end_x_var, end_y_var) = match (&end[0], &end[1]) {
2677 (UnsolvedExpr::Unknown(end_x), UnsolvedExpr::Unknown(end_y)) => (*end_x, *end_y),
2678 _ => {
2679 return Err(internal_err(
2680 "Arc end point must have sketch vars in all coordinates",
2681 range,
2682 ));
2683 }
2684 };
2685 let solver_arc = kcl_ezpz::datatypes::inputs::DatumCircularArc {
2686 center: kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2687 center.vars.x.to_constraint_id(range)?,
2688 center.vars.y.to_constraint_id(range)?,
2689 ),
2690 start: kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2691 start.vars.x.to_constraint_id(range)?,
2692 start.vars.y.to_constraint_id(range)?,
2693 ),
2694 end: kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2695 end_x_var.to_constraint_id(range)?,
2696 end_y_var.to_constraint_id(range)?,
2697 ),
2698 };
2699 let radius_value = if is_diameter { n.n / 2.0 } else { n.n };
2702 let solver_constraint = Constraint::ArcRadius(solver_arc, radius_value);
2703
2704 #[cfg(feature = "artifact-graph")]
2705 let constraint_id = exec_state.next_object_id();
2706 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2707 let message =
2708 "Being inside a sketch block should have already been checked above".to_owned();
2709 debug_assert!(false, "{}", &message);
2710 return Err(internal_err(message, self));
2711 };
2712 sketch_block_state.solver_constraints.push(solver_constraint);
2713 #[cfg(feature = "artifact-graph")]
2714 {
2715 use crate::execution::{
2716 Artifact, CodeRef, SketchBlockConstraint, SketchBlockConstraintType,
2717 };
2718 let arc_object_id = sketch_block_state
2721 .needed_by_engine
2722 .iter()
2723 .find(|seg| {
2724 matches!(&seg.kind, UnsolvedSegmentKind::Arc {
2725 center_object_id,
2726 start_object_id,
2727 ..
2728 } if *center_object_id == center.object_id && *start_object_id == start.object_id)
2729 })
2730 .map(|seg| seg.object_id)
2731 .ok_or_else(|| {
2732 internal_err(
2733 format!(
2734 "Could not find arc segment object ID for {} constraint",
2735 constraint_name
2736 ),
2737 range,
2738 )
2739 })?;
2740
2741 let constraint = if is_diameter {
2742 use crate::frontend::sketch::Diameter;
2743 crate::front::Constraint::Diameter(Diameter {
2744 arc: arc_object_id,
2745 diameter: n.try_into().map_err(|_| {
2746 internal_err("Failed to convert diameter units numeric suffix:", range)
2747 })?,
2748 })
2749 } else {
2750 use crate::frontend::sketch::Radius;
2751 crate::front::Constraint::Radius(Radius {
2752 arc: arc_object_id,
2753 radius: n.try_into().map_err(|_| {
2754 internal_err("Failed to convert radius units numeric suffix:", range)
2755 })?,
2756 })
2757 };
2758 sketch_block_state.sketch_constraints.push(constraint_id);
2759 let Some(sketch_id) = sketch_block_state.sketch_id else {
2760 let message = "Sketch id missing for constraint artifact".to_owned();
2761 debug_assert!(false, "{}", &message);
2762 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![range])));
2763 };
2764 let artifact_id = exec_state.next_artifact_id();
2765 exec_state.add_artifact(Artifact::SketchBlockConstraint(SketchBlockConstraint {
2766 id: artifact_id,
2767 sketch_id,
2768 constraint_id,
2769 constraint_type: SketchBlockConstraintType::from(&constraint),
2770 code_ref: CodeRef::placeholder(range),
2771 }));
2772 exec_state.add_scene_object(
2773 Object {
2774 id: constraint_id,
2775 kind: ObjectKind::Constraint { constraint },
2776 label: Default::default(),
2777 comments: Default::default(),
2778 artifact_id,
2779 source: range.into(),
2780 },
2781 range,
2782 );
2783 }
2784 }
2785 SketchConstraintKind::HorizontalDistance { points } => {
2786 let range = self.as_source_range();
2787 let p0 = &points[0];
2788 let p1 = &points[1];
2789 let solver_pt0 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2790 p0.vars.x.to_constraint_id(range)?,
2791 p0.vars.y.to_constraint_id(range)?,
2792 );
2793 let solver_pt1 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2794 p1.vars.x.to_constraint_id(range)?,
2795 p1.vars.y.to_constraint_id(range)?,
2796 );
2797 let solver_constraint =
2801 kcl_ezpz::Constraint::HorizontalDistance(solver_pt1, solver_pt0, n.n);
2802
2803 #[cfg(feature = "artifact-graph")]
2804 let constraint_id = exec_state.next_object_id();
2805 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2806 let message =
2807 "Being inside a sketch block should have already been checked above".to_owned();
2808 debug_assert!(false, "{}", &message);
2809 return Err(internal_err(message, self));
2810 };
2811 sketch_block_state.solver_constraints.push(solver_constraint);
2812 #[cfg(feature = "artifact-graph")]
2813 {
2814 use crate::{
2815 execution::{Artifact, CodeRef, SketchBlockConstraint, SketchBlockConstraintType},
2816 front::Distance,
2817 };
2818
2819 let constraint = crate::front::Constraint::HorizontalDistance(Distance {
2820 points: vec![p0.object_id, p1.object_id],
2821 distance: n.try_into().map_err(|_| {
2822 internal_err("Failed to convert distance units numeric suffix:", range)
2823 })?,
2824 source,
2825 });
2826 sketch_block_state.sketch_constraints.push(constraint_id);
2827 let Some(sketch_id) = sketch_block_state.sketch_id else {
2828 let message = "Sketch id missing for constraint artifact".to_owned();
2829 debug_assert!(false, "{}", &message);
2830 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![range])));
2831 };
2832 let artifact_id = exec_state.next_artifact_id();
2833 exec_state.add_artifact(Artifact::SketchBlockConstraint(SketchBlockConstraint {
2834 id: artifact_id,
2835 sketch_id,
2836 constraint_id,
2837 constraint_type: SketchBlockConstraintType::from(&constraint),
2838 code_ref: CodeRef::placeholder(range),
2839 }));
2840 exec_state.add_scene_object(
2841 Object {
2842 id: constraint_id,
2843 kind: ObjectKind::Constraint { constraint },
2844 label: Default::default(),
2845 comments: Default::default(),
2846 artifact_id,
2847 source: range.into(),
2848 },
2849 range,
2850 );
2851 }
2852 }
2853 SketchConstraintKind::VerticalDistance { points } => {
2854 let range = self.as_source_range();
2855 let p0 = &points[0];
2856 let p1 = &points[1];
2857 let solver_pt0 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2858 p0.vars.x.to_constraint_id(range)?,
2859 p0.vars.y.to_constraint_id(range)?,
2860 );
2861 let solver_pt1 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2862 p1.vars.x.to_constraint_id(range)?,
2863 p1.vars.y.to_constraint_id(range)?,
2864 );
2865 let solver_constraint = kcl_ezpz::Constraint::VerticalDistance(solver_pt1, solver_pt0, n.n);
2869
2870 #[cfg(feature = "artifact-graph")]
2871 let constraint_id = exec_state.next_object_id();
2872 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2873 let message =
2874 "Being inside a sketch block should have already been checked above".to_owned();
2875 debug_assert!(false, "{}", &message);
2876 return Err(internal_err(message, self));
2877 };
2878 sketch_block_state.solver_constraints.push(solver_constraint);
2879 #[cfg(feature = "artifact-graph")]
2880 {
2881 use crate::{
2882 execution::{Artifact, CodeRef, SketchBlockConstraint, SketchBlockConstraintType},
2883 front::Distance,
2884 };
2885
2886 let constraint = crate::front::Constraint::VerticalDistance(Distance {
2887 points: vec![p0.object_id, p1.object_id],
2888 distance: n.try_into().map_err(|_| {
2889 internal_err("Failed to convert distance units numeric suffix:", range)
2890 })?,
2891 source,
2892 });
2893 sketch_block_state.sketch_constraints.push(constraint_id);
2894 let Some(sketch_id) = sketch_block_state.sketch_id else {
2895 let message = "Sketch id missing for constraint artifact".to_owned();
2896 debug_assert!(false, "{}", &message);
2897 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![range])));
2898 };
2899 let artifact_id = exec_state.next_artifact_id();
2900 exec_state.add_artifact(Artifact::SketchBlockConstraint(SketchBlockConstraint {
2901 id: artifact_id,
2902 sketch_id,
2903 constraint_id,
2904 constraint_type: SketchBlockConstraintType::from(&constraint),
2905 code_ref: CodeRef::placeholder(range),
2906 }));
2907 exec_state.add_scene_object(
2908 Object {
2909 id: constraint_id,
2910 kind: ObjectKind::Constraint { constraint },
2911 label: Default::default(),
2912 comments: Default::default(),
2913 artifact_id,
2914 source: range.into(),
2915 },
2916 range,
2917 );
2918 }
2919 }
2920 }
2921 return Ok(KclValue::Bool { value: true, meta });
2922 }
2923 _ => {
2924 return Err(KclError::new_semantic(KclErrorDetails::new(
2925 format!(
2926 "Cannot create an equivalence constraint between values of these types: {} and {}",
2927 left_value.human_friendly_type(),
2928 right_value.human_friendly_type()
2929 ),
2930 vec![self.into()],
2931 )));
2932 }
2933 }
2934 }
2935
2936 let left = number_as_f64(&left_value, self.left.clone().into())?;
2937 let right = number_as_f64(&right_value, self.right.clone().into())?;
2938
2939 let value = match self.operator {
2940 BinaryOperator::Add => {
2941 let (l, r, ty) = NumericType::combine_eq_coerce(left, right, None);
2942 self.warn_on_unknown(&ty, "Adding", exec_state);
2943 KclValue::Number { value: l + r, meta, ty }
2944 }
2945 BinaryOperator::Sub => {
2946 let (l, r, ty) = NumericType::combine_eq_coerce(left, right, None);
2947 self.warn_on_unknown(&ty, "Subtracting", exec_state);
2948 KclValue::Number { value: l - r, meta, ty }
2949 }
2950 BinaryOperator::Mul => {
2951 let (l, r, ty) = NumericType::combine_mul(left, right);
2952 self.warn_on_unknown(&ty, "Multiplying", exec_state);
2953 KclValue::Number { value: l * r, meta, ty }
2954 }
2955 BinaryOperator::Div => {
2956 let (l, r, ty) = NumericType::combine_div(left, right);
2957 self.warn_on_unknown(&ty, "Dividing", exec_state);
2958 KclValue::Number { value: l / r, meta, ty }
2959 }
2960 BinaryOperator::Mod => {
2961 let (l, r, ty) = NumericType::combine_mod(left, right);
2962 self.warn_on_unknown(&ty, "Modulo of", exec_state);
2963 KclValue::Number { value: l % r, meta, ty }
2964 }
2965 BinaryOperator::Pow => KclValue::Number {
2966 value: left.n.powf(right.n),
2967 meta,
2968 ty: exec_state.current_default_units(),
2969 },
2970 BinaryOperator::Neq => {
2971 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2972 self.warn_on_unknown(&ty, "Comparing", exec_state);
2973 KclValue::Bool { value: l != r, meta }
2974 }
2975 BinaryOperator::Gt => {
2976 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2977 self.warn_on_unknown(&ty, "Comparing", exec_state);
2978 KclValue::Bool { value: l > r, meta }
2979 }
2980 BinaryOperator::Gte => {
2981 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2982 self.warn_on_unknown(&ty, "Comparing", exec_state);
2983 KclValue::Bool { value: l >= r, meta }
2984 }
2985 BinaryOperator::Lt => {
2986 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2987 self.warn_on_unknown(&ty, "Comparing", exec_state);
2988 KclValue::Bool { value: l < r, meta }
2989 }
2990 BinaryOperator::Lte => {
2991 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2992 self.warn_on_unknown(&ty, "Comparing", exec_state);
2993 KclValue::Bool { value: l <= r, meta }
2994 }
2995 BinaryOperator::Eq => {
2996 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2997 self.warn_on_unknown(&ty, "Comparing", exec_state);
2998 KclValue::Bool { value: l == r, meta }
2999 }
3000 BinaryOperator::And | BinaryOperator::Or => unreachable!(),
3001 };
3002
3003 Ok(value)
3004 }
3005
3006 fn missing_result_error(node: &Node<BinaryExpression>) -> KclError {
3007 internal_err("missing result while evaluating binary expression", node)
3008 }
3009
3010 fn warn_on_unknown(&self, ty: &NumericType, verb: &str, exec_state: &mut ExecState) {
3011 if ty == &NumericType::Unknown {
3012 let sr = self.as_source_range();
3013 exec_state.clear_units_warnings(&sr);
3014 let mut err = CompilationError::err(
3015 sr,
3016 format!(
3017 "{verb} numbers which have unknown or incompatible units.\nYou can probably fix this error by specifying the units using type ascription, e.g., `len: number(mm)` or `(a * b): number(deg)`."
3018 ),
3019 );
3020 err.tag = crate::errors::Tag::UnknownNumericUnits;
3021 exec_state.warn(err, annotations::WARN_UNKNOWN_UNITS);
3022 }
3023 }
3024}
3025
3026impl Node<UnaryExpression> {
3027 pub(super) async fn get_result(
3028 &self,
3029 exec_state: &mut ExecState,
3030 ctx: &ExecutorContext,
3031 ) -> Result<KclValueControlFlow, KclError> {
3032 match self.operator {
3033 UnaryOperator::Not => {
3034 let value = self.argument.get_result(exec_state, ctx).await?;
3035 let value = control_continue!(value);
3036 let KclValue::Bool {
3037 value: bool_value,
3038 meta: _,
3039 } = value
3040 else {
3041 return Err(KclError::new_semantic(KclErrorDetails::new(
3042 format!(
3043 "Cannot apply unary operator ! to non-boolean value: {}",
3044 value.human_friendly_type()
3045 ),
3046 vec![self.into()],
3047 )));
3048 };
3049 let meta = vec![Metadata {
3050 source_range: self.into(),
3051 }];
3052 let negated = KclValue::Bool {
3053 value: !bool_value,
3054 meta,
3055 };
3056
3057 Ok(negated.continue_())
3058 }
3059 UnaryOperator::Neg => {
3060 let value = self.argument.get_result(exec_state, ctx).await?;
3061 let value = control_continue!(value);
3062 let err = || {
3063 KclError::new_semantic(KclErrorDetails::new(
3064 format!(
3065 "You can only negate numbers, planes, or lines, but this is a {}",
3066 value.human_friendly_type()
3067 ),
3068 vec![self.into()],
3069 ))
3070 };
3071 match &value {
3072 KclValue::Number { value, ty, .. } => {
3073 let meta = vec![Metadata {
3074 source_range: self.into(),
3075 }];
3076 Ok(KclValue::Number {
3077 value: -value,
3078 meta,
3079 ty: *ty,
3080 }
3081 .continue_())
3082 }
3083 KclValue::Plane { value } => {
3084 let mut plane = value.clone();
3085 if plane.info.x_axis.x != 0.0 {
3086 plane.info.x_axis.x *= -1.0;
3087 }
3088 if plane.info.x_axis.y != 0.0 {
3089 plane.info.x_axis.y *= -1.0;
3090 }
3091 if plane.info.x_axis.z != 0.0 {
3092 plane.info.x_axis.z *= -1.0;
3093 }
3094
3095 plane.id = exec_state.next_uuid();
3096 plane.object_id = None;
3097 Ok(KclValue::Plane { value: plane }.continue_())
3098 }
3099 KclValue::Object {
3100 value: values, meta, ..
3101 } => {
3102 let Some(direction) = values.get("direction") else {
3104 return Err(err());
3105 };
3106
3107 let direction = match direction {
3108 KclValue::Tuple { value: values, meta } => {
3109 let values = values
3110 .iter()
3111 .map(|v| match v {
3112 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
3113 value: *value * -1.0,
3114 ty: *ty,
3115 meta: meta.clone(),
3116 }),
3117 _ => Err(err()),
3118 })
3119 .collect::<Result<Vec<_>, _>>()?;
3120
3121 KclValue::Tuple {
3122 value: values,
3123 meta: meta.clone(),
3124 }
3125 }
3126 KclValue::HomArray {
3127 value: values,
3128 ty: ty @ RuntimeType::Primitive(PrimitiveType::Number(_)),
3129 } => {
3130 let values = values
3131 .iter()
3132 .map(|v| match v {
3133 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
3134 value: *value * -1.0,
3135 ty: *ty,
3136 meta: meta.clone(),
3137 }),
3138 _ => Err(err()),
3139 })
3140 .collect::<Result<Vec<_>, _>>()?;
3141
3142 KclValue::HomArray {
3143 value: values,
3144 ty: ty.clone(),
3145 }
3146 }
3147 _ => return Err(err()),
3148 };
3149
3150 let mut value = values.clone();
3151 value.insert("direction".to_owned(), direction);
3152 Ok(KclValue::Object {
3153 value,
3154 meta: meta.clone(),
3155 constrainable: false,
3156 }
3157 .continue_())
3158 }
3159 _ => Err(err()),
3160 }
3161 }
3162 UnaryOperator::Plus => {
3163 let operand = self.argument.get_result(exec_state, ctx).await?;
3164 let operand = control_continue!(operand);
3165 match operand {
3166 KclValue::Number { .. } | KclValue::Plane { .. } => Ok(operand.continue_()),
3167 _ => Err(KclError::new_semantic(KclErrorDetails::new(
3168 format!(
3169 "You can only apply unary + to numbers or planes, but this is a {}",
3170 operand.human_friendly_type()
3171 ),
3172 vec![self.into()],
3173 ))),
3174 }
3175 }
3176 }
3177 }
3178}
3179
3180pub(crate) async fn execute_pipe_body(
3181 exec_state: &mut ExecState,
3182 body: &[Expr],
3183 source_range: SourceRange,
3184 ctx: &ExecutorContext,
3185) -> Result<KclValueControlFlow, KclError> {
3186 let Some((first, body)) = body.split_first() else {
3187 return Err(KclError::new_semantic(KclErrorDetails::new(
3188 "Pipe expressions cannot be empty".to_owned(),
3189 vec![source_range],
3190 )));
3191 };
3192 let meta = Metadata {
3197 source_range: SourceRange::from(first),
3198 };
3199 let output = ctx
3200 .execute_expr(first, exec_state, &meta, &[], StatementKind::Expression)
3201 .await?;
3202 let output = control_continue!(output);
3203
3204 let previous_pipe_value = exec_state.mod_local.pipe_value.replace(output);
3208 let result = inner_execute_pipe_body(exec_state, body, ctx).await;
3210 exec_state.mod_local.pipe_value = previous_pipe_value;
3212
3213 result
3214}
3215
3216#[async_recursion]
3219async fn inner_execute_pipe_body(
3220 exec_state: &mut ExecState,
3221 body: &[Expr],
3222 ctx: &ExecutorContext,
3223) -> Result<KclValueControlFlow, KclError> {
3224 for expression in body {
3225 if let Expr::TagDeclarator(_) = expression {
3226 return Err(KclError::new_semantic(KclErrorDetails::new(
3227 format!("This cannot be in a PipeExpression: {expression:?}"),
3228 vec![expression.into()],
3229 )));
3230 }
3231 let metadata = Metadata {
3232 source_range: SourceRange::from(expression),
3233 };
3234 let output = ctx
3235 .execute_expr(expression, exec_state, &metadata, &[], StatementKind::Expression)
3236 .await?;
3237 let output = control_continue!(output);
3238 exec_state.mod_local.pipe_value = Some(output);
3239 }
3240 let final_output = exec_state.mod_local.pipe_value.take().unwrap();
3242 Ok(final_output.continue_())
3243}
3244
3245impl Node<TagDeclarator> {
3246 pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
3247 let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
3248 value: self.name.clone(),
3249 info: Vec::new(),
3250 meta: vec![Metadata {
3251 source_range: self.into(),
3252 }],
3253 }));
3254
3255 exec_state
3256 .mut_stack()
3257 .add(self.name.clone(), memory_item, self.into())?;
3258
3259 Ok(self.into())
3260 }
3261}
3262
3263impl Node<ArrayExpression> {
3264 #[async_recursion]
3265 pub(super) async fn execute(
3266 &self,
3267 exec_state: &mut ExecState,
3268 ctx: &ExecutorContext,
3269 ) -> Result<KclValueControlFlow, KclError> {
3270 let mut results = Vec::with_capacity(self.elements.len());
3271
3272 for element in &self.elements {
3273 let metadata = Metadata::from(element);
3274 let value = ctx
3277 .execute_expr(element, exec_state, &metadata, &[], StatementKind::Expression)
3278 .await?;
3279 let value = control_continue!(value);
3280
3281 results.push(value);
3282 }
3283
3284 Ok(KclValue::HomArray {
3285 value: results,
3286 ty: RuntimeType::Primitive(PrimitiveType::Any),
3287 }
3288 .continue_())
3289 }
3290}
3291
3292impl Node<ArrayRangeExpression> {
3293 #[async_recursion]
3294 pub(super) async fn execute(
3295 &self,
3296 exec_state: &mut ExecState,
3297 ctx: &ExecutorContext,
3298 ) -> Result<KclValueControlFlow, KclError> {
3299 let metadata = Metadata::from(&self.start_element);
3300 let start_val = ctx
3301 .execute_expr(
3302 &self.start_element,
3303 exec_state,
3304 &metadata,
3305 &[],
3306 StatementKind::Expression,
3307 )
3308 .await?;
3309 let start_val = control_continue!(start_val);
3310 let start = start_val
3311 .as_ty_f64()
3312 .ok_or(KclError::new_semantic(KclErrorDetails::new(
3313 format!(
3314 "Expected number for range start but found {}",
3315 start_val.human_friendly_type()
3316 ),
3317 vec![self.into()],
3318 )))?;
3319 let metadata = Metadata::from(&self.end_element);
3320 let end_val = ctx
3321 .execute_expr(&self.end_element, exec_state, &metadata, &[], StatementKind::Expression)
3322 .await?;
3323 let end_val = control_continue!(end_val);
3324 let end = end_val.as_ty_f64().ok_or(KclError::new_semantic(KclErrorDetails::new(
3325 format!(
3326 "Expected number for range end but found {}",
3327 end_val.human_friendly_type()
3328 ),
3329 vec![self.into()],
3330 )))?;
3331
3332 let (start, end, ty) = NumericType::combine_range(start, end, exec_state, self.as_source_range())?;
3333 let Some(start) = crate::try_f64_to_i64(start) else {
3334 return Err(KclError::new_semantic(KclErrorDetails::new(
3335 format!("Range start must be an integer, but found {start}"),
3336 vec![self.into()],
3337 )));
3338 };
3339 let Some(end) = crate::try_f64_to_i64(end) else {
3340 return Err(KclError::new_semantic(KclErrorDetails::new(
3341 format!("Range end must be an integer, but found {end}"),
3342 vec![self.into()],
3343 )));
3344 };
3345
3346 if end < start {
3347 return Err(KclError::new_semantic(KclErrorDetails::new(
3348 format!("Range start is greater than range end: {start} .. {end}"),
3349 vec![self.into()],
3350 )));
3351 }
3352
3353 let range: Vec<_> = if self.end_inclusive {
3354 (start..=end).collect()
3355 } else {
3356 (start..end).collect()
3357 };
3358
3359 let meta = vec![Metadata {
3360 source_range: self.into(),
3361 }];
3362
3363 Ok(KclValue::HomArray {
3364 value: range
3365 .into_iter()
3366 .map(|num| KclValue::Number {
3367 value: num as f64,
3368 ty,
3369 meta: meta.clone(),
3370 })
3371 .collect(),
3372 ty: RuntimeType::Primitive(PrimitiveType::Number(ty)),
3373 }
3374 .continue_())
3375 }
3376}
3377
3378impl Node<ObjectExpression> {
3379 #[async_recursion]
3380 pub(super) async fn execute(
3381 &self,
3382 exec_state: &mut ExecState,
3383 ctx: &ExecutorContext,
3384 ) -> Result<KclValueControlFlow, KclError> {
3385 let mut object = HashMap::with_capacity(self.properties.len());
3386 for property in &self.properties {
3387 let metadata = Metadata::from(&property.value);
3388 let result = ctx
3389 .execute_expr(&property.value, exec_state, &metadata, &[], StatementKind::Expression)
3390 .await?;
3391 let result = control_continue!(result);
3392 object.insert(property.key.name.clone(), result);
3393 }
3394
3395 Ok(KclValue::Object {
3396 value: object,
3397 meta: vec![Metadata {
3398 source_range: self.into(),
3399 }],
3400 constrainable: false,
3401 }
3402 .continue_())
3403 }
3404}
3405
3406fn article_for<S: AsRef<str>>(s: S) -> &'static str {
3407 if s.as_ref().starts_with(['a', 'e', 'i', 'o', 'u', '[']) {
3409 "an"
3410 } else {
3411 "a"
3412 }
3413}
3414
3415fn number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<TyF64, KclError> {
3416 v.as_ty_f64().ok_or_else(|| {
3417 let actual_type = v.human_friendly_type();
3418 KclError::new_semantic(KclErrorDetails::new(
3419 format!("Expected a number, but found {actual_type}",),
3420 vec![source_range],
3421 ))
3422 })
3423}
3424
3425impl Node<IfExpression> {
3426 #[async_recursion]
3427 pub(super) async fn get_result(
3428 &self,
3429 exec_state: &mut ExecState,
3430 ctx: &ExecutorContext,
3431 ) -> Result<KclValueControlFlow, KclError> {
3432 let cond_value = ctx
3434 .execute_expr(
3435 &self.cond,
3436 exec_state,
3437 &Metadata::from(self),
3438 &[],
3439 StatementKind::Expression,
3440 )
3441 .await?;
3442 let cond_value = control_continue!(cond_value);
3443 if cond_value.get_bool()? {
3444 let block_result = ctx.exec_block(&*self.then_val, exec_state, BodyType::Block).await?;
3445 return Ok(block_result.unwrap());
3449 }
3450
3451 for else_if in &self.else_ifs {
3453 let cond_value = ctx
3454 .execute_expr(
3455 &else_if.cond,
3456 exec_state,
3457 &Metadata::from(self),
3458 &[],
3459 StatementKind::Expression,
3460 )
3461 .await?;
3462 let cond_value = control_continue!(cond_value);
3463 if cond_value.get_bool()? {
3464 let block_result = ctx.exec_block(&*else_if.then_val, exec_state, BodyType::Block).await?;
3465 return Ok(block_result.unwrap());
3469 }
3470 }
3471
3472 ctx.exec_block(&*self.final_else, exec_state, BodyType::Block)
3474 .await
3475 .map(|expr| expr.unwrap())
3476 }
3477}
3478
3479#[derive(Debug)]
3480enum Property {
3481 UInt(usize),
3482 String(String),
3483}
3484
3485impl Property {
3486 #[allow(clippy::too_many_arguments)]
3487 async fn try_from<'a>(
3488 computed: bool,
3489 value: Expr,
3490 exec_state: &mut ExecState,
3491 sr: SourceRange,
3492 ctx: &ExecutorContext,
3493 metadata: &Metadata,
3494 annotations: &[Node<Annotation>],
3495 statement_kind: StatementKind<'a>,
3496 ) -> Result<Self, KclError> {
3497 let property_sr = vec![sr];
3498 if !computed {
3499 let Expr::Name(identifier) = value else {
3500 return Err(KclError::new_semantic(KclErrorDetails::new(
3502 "Object expressions like `obj.property` must use simple identifier names, not complex expressions"
3503 .to_owned(),
3504 property_sr,
3505 )));
3506 };
3507 return Ok(Property::String(identifier.to_string()));
3508 }
3509
3510 let prop_value = ctx
3511 .execute_expr(&value, exec_state, metadata, annotations, statement_kind)
3512 .await?;
3513 let prop_value = match prop_value.control {
3514 ControlFlowKind::Continue => prop_value.into_value(),
3515 ControlFlowKind::Exit => {
3516 let message = "Early return inside array brackets is currently not supported".to_owned();
3517 debug_assert!(false, "{}", &message);
3518 return Err(internal_err(message, sr));
3519 }
3520 };
3521 match prop_value {
3522 KclValue::Number { value, ty, meta: _ } => {
3523 if !matches!(
3524 ty,
3525 NumericType::Unknown
3526 | NumericType::Default { .. }
3527 | NumericType::Known(crate::exec::UnitType::Count)
3528 ) {
3529 return Err(KclError::new_semantic(KclErrorDetails::new(
3530 format!(
3531 "{value} is not a valid index, indices must be non-dimensional numbers. If you're sure this is correct, you can add `: number(Count)` to tell KCL this number is an index"
3532 ),
3533 property_sr,
3534 )));
3535 }
3536 if let Some(x) = crate::try_f64_to_usize(value) {
3537 Ok(Property::UInt(x))
3538 } else {
3539 Err(KclError::new_semantic(KclErrorDetails::new(
3540 format!("{value} is not a valid index, indices must be whole numbers >= 0"),
3541 property_sr,
3542 )))
3543 }
3544 }
3545 _ => Err(KclError::new_semantic(KclErrorDetails::new(
3546 "Only numbers (>= 0) can be indexes".to_owned(),
3547 vec![sr],
3548 ))),
3549 }
3550 }
3551}
3552
3553impl Property {
3554 fn type_name(&self) -> &'static str {
3555 match self {
3556 Property::UInt(_) => "number",
3557 Property::String(_) => "string",
3558 }
3559 }
3560}
3561
3562impl Node<PipeExpression> {
3563 #[async_recursion]
3564 pub(super) async fn get_result(
3565 &self,
3566 exec_state: &mut ExecState,
3567 ctx: &ExecutorContext,
3568 ) -> Result<KclValueControlFlow, KclError> {
3569 execute_pipe_body(exec_state, &self.body, self.into(), ctx).await
3570 }
3571}
3572
3573#[cfg(test)]
3574mod test {
3575 use std::sync::Arc;
3576
3577 use tokio::io::AsyncWriteExt;
3578
3579 use super::*;
3580 use crate::{
3581 ExecutorSettings,
3582 errors::Severity,
3583 exec::UnitType,
3584 execution::{ContextType, parse_execute},
3585 };
3586
3587 #[tokio::test(flavor = "multi_thread")]
3588 async fn ascription() {
3589 let program = r#"
3590a = 42: number
3591b = a: number
3592p = {
3593 origin = { x = 0, y = 0, z = 0 },
3594 xAxis = { x = 1, y = 0, z = 0 },
3595 yAxis = { x = 0, y = 1, z = 0 },
3596 zAxis = { x = 0, y = 0, z = 1 }
3597}: Plane
3598arr1 = [42]: [number(cm)]
3599"#;
3600
3601 let result = parse_execute(program).await.unwrap();
3602 let mem = result.exec_state.stack();
3603 assert!(matches!(
3604 mem.memory
3605 .get_from("p", result.mem_env, SourceRange::default(), 0)
3606 .unwrap(),
3607 KclValue::Plane { .. }
3608 ));
3609 let arr1 = mem
3610 .memory
3611 .get_from("arr1", result.mem_env, SourceRange::default(), 0)
3612 .unwrap();
3613 if let KclValue::HomArray { value, ty } = arr1 {
3614 assert_eq!(value.len(), 1, "Expected Vec with specific length: found {value:?}");
3615 assert_eq!(
3616 *ty,
3617 RuntimeType::known_length(kittycad_modeling_cmds::units::UnitLength::Centimeters)
3618 );
3619 if let KclValue::Number { value, ty, .. } = &value[0] {
3621 assert_eq!(*value, 42.0);
3623 assert_eq!(
3624 *ty,
3625 NumericType::Known(UnitType::Length(kittycad_modeling_cmds::units::UnitLength::Centimeters))
3626 );
3627 } else {
3628 panic!("Expected a number; found {:?}", value[0]);
3629 }
3630 } else {
3631 panic!("Expected HomArray; found {arr1:?}");
3632 }
3633
3634 let program = r#"
3635a = 42: string
3636"#;
3637 let result = parse_execute(program).await;
3638 let err = result.unwrap_err();
3639 assert!(
3640 err.to_string()
3641 .contains("could not coerce a number (with type `number`) to type `string`"),
3642 "Expected error but found {err:?}"
3643 );
3644
3645 let program = r#"
3646a = 42: Plane
3647"#;
3648 let result = parse_execute(program).await;
3649 let err = result.unwrap_err();
3650 assert!(
3651 err.to_string()
3652 .contains("could not coerce a number (with type `number`) to type `Plane`"),
3653 "Expected error but found {err:?}"
3654 );
3655
3656 let program = r#"
3657arr = [0]: [string]
3658"#;
3659 let result = parse_execute(program).await;
3660 let err = result.unwrap_err();
3661 assert!(
3662 err.to_string().contains(
3663 "could not coerce an array of `number` with 1 value (with type `[any; 1]`) to type `[string]`"
3664 ),
3665 "Expected error but found {err:?}"
3666 );
3667
3668 let program = r#"
3669mixedArr = [0, "a"]: [number(mm)]
3670"#;
3671 let result = parse_execute(program).await;
3672 let err = result.unwrap_err();
3673 assert!(
3674 err.to_string().contains(
3675 "could not coerce an array of `number`, `string` (with type `[any; 2]`) to type `[number(mm)]`"
3676 ),
3677 "Expected error but found {err:?}"
3678 );
3679
3680 let program = r#"
3681mixedArr = [0, "a"]: [mm]
3682"#;
3683 let result = parse_execute(program).await;
3684 let err = result.unwrap_err();
3685 assert!(
3686 err.to_string().contains(
3687 "could not coerce an array of `number`, `string` (with type `[any; 2]`) to type `[number(mm)]`"
3688 ),
3689 "Expected error but found {err:?}"
3690 );
3691 }
3692
3693 #[tokio::test(flavor = "multi_thread")]
3694 async fn neg_plane() {
3695 let program = r#"
3696p = {
3697 origin = { x = 0, y = 0, z = 0 },
3698 xAxis = { x = 1, y = 0, z = 0 },
3699 yAxis = { x = 0, y = 1, z = 0 },
3700}: Plane
3701p2 = -p
3702"#;
3703
3704 let result = parse_execute(program).await.unwrap();
3705 let mem = result.exec_state.stack();
3706 match mem
3707 .memory
3708 .get_from("p2", result.mem_env, SourceRange::default(), 0)
3709 .unwrap()
3710 {
3711 KclValue::Plane { value } => {
3712 assert_eq!(value.info.x_axis.x, -1.0);
3713 assert_eq!(value.info.x_axis.y, 0.0);
3714 assert_eq!(value.info.x_axis.z, 0.0);
3715 }
3716 _ => unreachable!(),
3717 }
3718 }
3719
3720 #[tokio::test(flavor = "multi_thread")]
3721 async fn multiple_returns() {
3722 let program = r#"fn foo() {
3723 return 0
3724 return 42
3725}
3726
3727a = foo()
3728"#;
3729
3730 let result = parse_execute(program).await;
3731 assert!(result.unwrap_err().to_string().contains("return"));
3732 }
3733
3734 #[tokio::test(flavor = "multi_thread")]
3735 async fn load_all_modules() {
3736 let program_a_kcl = r#"
3738export a = 1
3739"#;
3740 let program_b_kcl = r#"
3742import a from 'a.kcl'
3743
3744export b = a + 1
3745"#;
3746 let program_c_kcl = r#"
3748import a from 'a.kcl'
3749
3750export c = a + 2
3751"#;
3752
3753 let main_kcl = r#"
3755import b from 'b.kcl'
3756import c from 'c.kcl'
3757
3758d = b + c
3759"#;
3760
3761 let main = crate::parsing::parse_str(main_kcl, ModuleId::default())
3762 .parse_errs_as_err()
3763 .unwrap();
3764
3765 let tmpdir = tempfile::TempDir::with_prefix("zma_kcl_load_all_modules").unwrap();
3766
3767 tokio::fs::File::create(tmpdir.path().join("main.kcl"))
3768 .await
3769 .unwrap()
3770 .write_all(main_kcl.as_bytes())
3771 .await
3772 .unwrap();
3773
3774 tokio::fs::File::create(tmpdir.path().join("a.kcl"))
3775 .await
3776 .unwrap()
3777 .write_all(program_a_kcl.as_bytes())
3778 .await
3779 .unwrap();
3780
3781 tokio::fs::File::create(tmpdir.path().join("b.kcl"))
3782 .await
3783 .unwrap()
3784 .write_all(program_b_kcl.as_bytes())
3785 .await
3786 .unwrap();
3787
3788 tokio::fs::File::create(tmpdir.path().join("c.kcl"))
3789 .await
3790 .unwrap()
3791 .write_all(program_c_kcl.as_bytes())
3792 .await
3793 .unwrap();
3794
3795 let exec_ctxt = ExecutorContext {
3796 engine: Arc::new(Box::new(
3797 crate::engine::conn_mock::EngineConnection::new()
3798 .map_err(|err| {
3799 internal_err(
3800 format!("Failed to create mock engine connection: {err}"),
3801 SourceRange::default(),
3802 )
3803 })
3804 .unwrap(),
3805 )),
3806 fs: Arc::new(crate::fs::FileManager::new()),
3807 settings: ExecutorSettings {
3808 project_directory: Some(crate::TypedPath(tmpdir.path().into())),
3809 ..Default::default()
3810 },
3811 context_type: ContextType::Mock,
3812 };
3813 let mut exec_state = ExecState::new(&exec_ctxt);
3814
3815 exec_ctxt
3816 .run(
3817 &crate::Program {
3818 ast: main.clone(),
3819 original_file_contents: "".to_owned(),
3820 },
3821 &mut exec_state,
3822 )
3823 .await
3824 .unwrap();
3825 }
3826
3827 #[tokio::test(flavor = "multi_thread")]
3828 async fn user_coercion() {
3829 let program = r#"fn foo(x: Axis2d) {
3830 return 0
3831}
3832
3833foo(x = { direction = [0, 0], origin = [0, 0]})
3834"#;
3835
3836 parse_execute(program).await.unwrap();
3837
3838 let program = r#"fn foo(x: Axis3d) {
3839 return 0
3840}
3841
3842foo(x = { direction = [0, 0], origin = [0, 0]})
3843"#;
3844
3845 parse_execute(program).await.unwrap_err();
3846 }
3847
3848 #[tokio::test(flavor = "multi_thread")]
3849 async fn coerce_return() {
3850 let program = r#"fn foo(): number(mm) {
3851 return 42
3852}
3853
3854a = foo()
3855"#;
3856
3857 parse_execute(program).await.unwrap();
3858
3859 let program = r#"fn foo(): mm {
3860 return 42
3861}
3862
3863a = foo()
3864"#;
3865
3866 parse_execute(program).await.unwrap();
3867
3868 let program = r#"fn foo(): number(mm) {
3869 return { bar: 42 }
3870}
3871
3872a = foo()
3873"#;
3874
3875 parse_execute(program).await.unwrap_err();
3876
3877 let program = r#"fn foo(): mm {
3878 return { bar: 42 }
3879}
3880
3881a = foo()
3882"#;
3883
3884 parse_execute(program).await.unwrap_err();
3885 }
3886
3887 #[tokio::test(flavor = "multi_thread")]
3888 async fn test_sensible_error_when_missing_equals_in_kwarg() {
3889 for (i, call) in ["f(x=1,3,0)", "f(x=1,3,z)", "f(x=1,0,z=1)", "f(x=1, 3 + 4, z)"]
3890 .into_iter()
3891 .enumerate()
3892 {
3893 let program = format!(
3894 "fn foo() {{ return 0 }}
3895z = 0
3896fn f(x, y, z) {{ return 0 }}
3897{call}"
3898 );
3899 let err = parse_execute(&program).await.unwrap_err();
3900 let msg = err.message();
3901 assert!(
3902 msg.contains("This argument needs a label, but it doesn't have one"),
3903 "failed test {i}: {msg}"
3904 );
3905 assert!(msg.contains("`y`"), "failed test {i}, missing `y`: {msg}");
3906 if i == 0 {
3907 assert!(msg.contains("`z`"), "failed test {i}, missing `z`: {msg}");
3908 }
3909 }
3910 }
3911
3912 #[tokio::test(flavor = "multi_thread")]
3913 async fn default_param_for_unlabeled() {
3914 let ast = r#"fn myExtrude(@sk, length) {
3917 return extrude(sk, length)
3918}
3919sketch001 = startSketchOn(XY)
3920 |> circle(center = [0, 0], radius = 93.75)
3921 |> myExtrude(length = 40)
3922"#;
3923
3924 parse_execute(ast).await.unwrap();
3925 }
3926
3927 #[tokio::test(flavor = "multi_thread")]
3928 async fn dont_use_unlabelled_as_input() {
3929 let ast = r#"length = 10
3931startSketchOn(XY)
3932 |> circle(center = [0, 0], radius = 93.75)
3933 |> extrude(length)
3934"#;
3935
3936 parse_execute(ast).await.unwrap();
3937 }
3938
3939 #[tokio::test(flavor = "multi_thread")]
3940 async fn ascription_in_binop() {
3941 let ast = r#"foo = tan(0): number(rad) - 4deg"#;
3942 parse_execute(ast).await.unwrap();
3943
3944 let ast = r#"foo = tan(0): rad - 4deg"#;
3945 parse_execute(ast).await.unwrap();
3946 }
3947
3948 #[tokio::test(flavor = "multi_thread")]
3949 async fn neg_sqrt() {
3950 let ast = r#"bad = sqrt(-2)"#;
3951
3952 let e = parse_execute(ast).await.unwrap_err();
3953 assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
3955 }
3956
3957 #[tokio::test(flavor = "multi_thread")]
3958 async fn non_array_fns() {
3959 let ast = r#"push(1, item = 2)
3960pop(1)
3961map(1, f = fn(@x) { return x + 1 })
3962reduce(1, f = fn(@x, accum) { return accum + x}, initial = 0)"#;
3963
3964 parse_execute(ast).await.unwrap();
3965 }
3966
3967 #[tokio::test(flavor = "multi_thread")]
3968 async fn non_array_indexing() {
3969 let good = r#"a = 42
3970good = a[0]
3971"#;
3972 let result = parse_execute(good).await.unwrap();
3973 let mem = result.exec_state.stack();
3974 let num = mem
3975 .memory
3976 .get_from("good", result.mem_env, SourceRange::default(), 0)
3977 .unwrap()
3978 .as_ty_f64()
3979 .unwrap();
3980 assert_eq!(num.n, 42.0);
3981
3982 let bad = r#"a = 42
3983bad = a[1]
3984"#;
3985
3986 parse_execute(bad).await.unwrap_err();
3987 }
3988
3989 #[tokio::test(flavor = "multi_thread")]
3990 async fn coerce_unknown_to_length() {
3991 let ast = r#"x = 2mm * 2mm
3992y = x: number(Length)"#;
3993 let e = parse_execute(ast).await.unwrap_err();
3994 assert!(
3995 e.message().contains("could not coerce"),
3996 "Error message: '{}'",
3997 e.message()
3998 );
3999
4000 let ast = r#"x = 2mm
4001y = x: number(Length)"#;
4002 let result = parse_execute(ast).await.unwrap();
4003 let mem = result.exec_state.stack();
4004 let num = mem
4005 .memory
4006 .get_from("y", result.mem_env, SourceRange::default(), 0)
4007 .unwrap()
4008 .as_ty_f64()
4009 .unwrap();
4010 assert_eq!(num.n, 2.0);
4011 assert_eq!(num.ty, NumericType::mm());
4012 }
4013
4014 #[tokio::test(flavor = "multi_thread")]
4015 async fn one_warning_unknown() {
4016 let ast = r#"
4017// Should warn once
4018a = PI * 2
4019// Should warn once
4020b = (PI * 2) / 3
4021// Should not warn
4022c = ((PI * 2) / 3): number(deg)
4023"#;
4024
4025 let result = parse_execute(ast).await.unwrap();
4026 assert_eq!(result.exec_state.errors().len(), 2);
4027 }
4028
4029 #[tokio::test(flavor = "multi_thread")]
4030 async fn non_count_indexing() {
4031 let ast = r#"x = [0, 0]
4032y = x[1mm]
4033"#;
4034 parse_execute(ast).await.unwrap_err();
4035
4036 let ast = r#"x = [0, 0]
4037y = 1deg
4038z = x[y]
4039"#;
4040 parse_execute(ast).await.unwrap_err();
4041
4042 let ast = r#"x = [0, 0]
4043y = x[0mm + 1]
4044"#;
4045 parse_execute(ast).await.unwrap_err();
4046 }
4047
4048 #[tokio::test(flavor = "multi_thread")]
4049 async fn getting_property_of_plane() {
4050 let ast = std::fs::read_to_string("tests/inputs/planestuff.kcl").unwrap();
4051 parse_execute(&ast).await.unwrap();
4052 }
4053
4054 #[cfg(feature = "artifact-graph")]
4055 #[tokio::test(flavor = "multi_thread")]
4056 async fn no_artifacts_from_within_hole_call() {
4057 let ast = std::fs::read_to_string("tests/inputs/sample_hole.kcl").unwrap();
4062 let out = parse_execute(&ast).await.unwrap();
4063
4064 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
4066
4067 let expected = 5;
4071 assert_eq!(
4072 actual_operations.len(),
4073 expected,
4074 "expected {expected} operations, received {}:\n{actual_operations:#?}",
4075 actual_operations.len(),
4076 );
4077 }
4078
4079 #[cfg(feature = "artifact-graph")]
4080 #[tokio::test(flavor = "multi_thread")]
4081 async fn feature_tree_annotation_on_user_defined_kcl() {
4082 let ast = std::fs::read_to_string("tests/inputs/feature_tree_annotation_on_user_defined_kcl.kcl").unwrap();
4085 let out = parse_execute(&ast).await.unwrap();
4086
4087 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
4089
4090 let expected = 0;
4091 assert_eq!(
4092 actual_operations.len(),
4093 expected,
4094 "expected {expected} operations, received {}:\n{actual_operations:#?}",
4095 actual_operations.len(),
4096 );
4097 }
4098
4099 #[cfg(feature = "artifact-graph")]
4100 #[tokio::test(flavor = "multi_thread")]
4101 async fn no_feature_tree_annotation_on_user_defined_kcl() {
4102 let ast = std::fs::read_to_string("tests/inputs/no_feature_tree_annotation_on_user_defined_kcl.kcl").unwrap();
4105 let out = parse_execute(&ast).await.unwrap();
4106
4107 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
4109
4110 let expected = 2;
4111 assert_eq!(
4112 actual_operations.len(),
4113 expected,
4114 "expected {expected} operations, received {}:\n{actual_operations:#?}",
4115 actual_operations.len(),
4116 );
4117 assert!(matches!(actual_operations[0], Operation::GroupBegin { .. }));
4118 assert!(matches!(actual_operations[1], Operation::GroupEnd));
4119 }
4120
4121 #[tokio::test(flavor = "multi_thread")]
4122 async fn custom_warning() {
4123 let warn = r#"
4124a = PI * 2
4125"#;
4126 let result = parse_execute(warn).await.unwrap();
4127 assert_eq!(result.exec_state.errors().len(), 1);
4128 assert_eq!(result.exec_state.errors()[0].severity, Severity::Warning);
4129
4130 let allow = r#"
4131@warnings(allow = unknownUnits)
4132a = PI * 2
4133"#;
4134 let result = parse_execute(allow).await.unwrap();
4135 assert_eq!(result.exec_state.errors().len(), 0);
4136
4137 let deny = r#"
4138@warnings(deny = [unknownUnits])
4139a = PI * 2
4140"#;
4141 let result = parse_execute(deny).await.unwrap();
4142 assert_eq!(result.exec_state.errors().len(), 1);
4143 assert_eq!(result.exec_state.errors()[0].severity, Severity::Error);
4144 }
4145
4146 #[tokio::test(flavor = "multi_thread")]
4147 async fn cannot_solid_extrude_an_open_profile() {
4148 let code = std::fs::read_to_string("tests/inputs/cannot_solid_extrude_an_open_profile.kcl").unwrap();
4151 let program = crate::Program::parse_no_errs(&code).expect("should parse");
4152 let exec_ctxt = ExecutorContext::new_mock(None).await;
4153 let mut exec_state = ExecState::new(&exec_ctxt);
4154
4155 let err = exec_ctxt.run(&program, &mut exec_state).await.unwrap_err().error;
4156 assert!(matches!(err, KclError::Semantic { .. }));
4157 exec_ctxt.close().await;
4158 }
4159}