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, block_variables) = match self.load_sketch2_into_current_scope(exec_state, ctx, range).await {
1278 Ok(()) => {
1279 let parent = exec_state.mut_stack().snapshot();
1280 exec_state.mut_stack().push_new_env_for_call(parent);
1281 let result = ctx.exec_block(&self.body, exec_state, BodyType::Block).await;
1282 let block_variables = exec_state
1283 .stack()
1284 .find_all_in_current_env()
1285 .map(|(name, value)| (name.clone(), value.clone()))
1286 .collect::<IndexMap<_, _>>();
1287 exec_state.mut_stack().pop_env();
1288 (result, block_variables)
1289 }
1290 Err(err) => (Err(err), IndexMap::new()),
1291 };
1292
1293 exec_state.mod_local.sketch_mode = original_sketch_mode;
1294
1295 let sketch_block_state = std::mem::replace(&mut exec_state.mod_local.sketch_block, original_value);
1296
1297 exec_state.mut_stack().pop_env();
1299
1300 (result, block_variables, sketch_block_state)
1301 };
1302
1303 return_result?;
1305 let Some(sketch_block_state) = sketch_block_state else {
1306 debug_assert!(false, "Sketch block state should still be set to Some from just above");
1307 return Err(internal_err(
1308 "Sketch block state should still be set to Some from just above",
1309 self,
1310 ));
1311 };
1312 #[cfg(feature = "artifact-graph")]
1313 let mut sketch_block_state = sketch_block_state;
1314
1315 let constraints = sketch_block_state
1317 .solver_constraints
1318 .iter()
1319 .cloned()
1320 .map(kcl_ezpz::ConstraintRequest::highest_priority)
1321 .chain(
1322 sketch_block_state
1324 .solver_optional_constraints
1325 .iter()
1326 .cloned()
1327 .map(|c| kcl_ezpz::ConstraintRequest::new(c, 1)),
1328 )
1329 .collect::<Vec<_>>();
1330 let initial_guesses = sketch_block_state
1331 .sketch_vars
1332 .iter()
1333 .map(|v| {
1334 let Some(sketch_var) = v.as_sketch_var() else {
1335 return Err(internal_err("Expected sketch variable", self));
1336 };
1337 let constraint_id = sketch_var.id.to_constraint_id(range)?;
1338 let number_value = KclValue::Number {
1340 value: sketch_var.initial_value,
1341 ty: sketch_var.ty,
1342 meta: sketch_var.meta.clone(),
1343 };
1344 let initial_guess_value =
1345 normalize_to_solver_unit(&number_value, v.into(), exec_state, "sketch variable initial value")?;
1346 let initial_guess = if let Some(n) = initial_guess_value.as_ty_f64() {
1347 n.n
1348 } else {
1349 let message = format!(
1350 "Expected number after coercion, but found {}",
1351 initial_guess_value.human_friendly_type()
1352 );
1353 debug_assert!(false, "{}", &message);
1354 return Err(internal_err(message, self));
1355 };
1356 Ok((constraint_id, initial_guess))
1357 })
1358 .collect::<Result<Vec<_>, KclError>>()?;
1359 let config = kcl_ezpz::Config::default().with_max_iterations(50);
1361 let solve_result = if exec_state.mod_local.freedom_analysis {
1362 kcl_ezpz::solve_analysis(&constraints, initial_guesses.clone(), config).map(|outcome| {
1363 let freedom_analysis = FreedomAnalysis::from_ezpz_analysis(outcome.analysis, constraints.len());
1364 (outcome.outcome, Some(freedom_analysis))
1365 })
1366 } else {
1367 kcl_ezpz::solve(&constraints, initial_guesses.clone(), config).map(|outcome| (outcome, None))
1368 };
1369 let num_required_constraints = sketch_block_state.solver_constraints.len();
1371 let all_constraints: Vec<kcl_ezpz::Constraint> = sketch_block_state
1372 .solver_constraints
1373 .iter()
1374 .cloned()
1375 .chain(sketch_block_state.solver_optional_constraints.iter().cloned())
1376 .collect();
1377
1378 let (solve_outcome, solve_analysis) = match solve_result {
1379 Ok((solved, freedom)) => {
1380 let outcome = Solved::from_ezpz_outcome(solved, &all_constraints, num_required_constraints);
1381 (outcome, freedom)
1382 }
1383 Err(failure) => {
1384 match &failure.error {
1385 NonLinearSystemError::FaerMatrix { .. }
1386 | NonLinearSystemError::Faer { .. }
1387 | NonLinearSystemError::FaerSolve { .. }
1388 | NonLinearSystemError::FaerSvd(..)
1389 | NonLinearSystemError::DidNotConverge => {
1390 exec_state.warn(
1393 CompilationError::err(range, "Constraint solver failed to find a solution".to_owned()),
1394 annotations::WARN_SOLVER,
1395 );
1396 let final_values = initial_guesses.iter().map(|(_, v)| *v).collect::<Vec<_>>();
1397 (
1398 Solved {
1399 final_values,
1400 iterations: Default::default(),
1401 warnings: failure.warnings,
1402 priority_solved: Default::default(),
1403 variables_in_conflicts: Default::default(),
1404 },
1405 None,
1406 )
1407 }
1408 NonLinearSystemError::EmptySystemNotAllowed
1409 | NonLinearSystemError::WrongNumberGuesses { .. }
1410 | NonLinearSystemError::MissingGuess { .. }
1411 | NonLinearSystemError::NotFound(..) => {
1412 #[cfg(target_arch = "wasm32")]
1415 web_sys::console::error_1(
1416 &format!("Internal error from constraint solver: {}", &failure.error).into(),
1417 );
1418 return Err(internal_err(
1419 format!("Internal error from constraint solver: {}", &failure.error),
1420 self,
1421 ));
1422 }
1423 _ => {
1424 return Err(internal_err(
1426 format!("Error from constraint solver: {}", &failure.error),
1427 self,
1428 ));
1429 }
1430 }
1431 }
1432 };
1433 #[cfg(not(feature = "artifact-graph"))]
1434 let _ = solve_analysis;
1435 for warning in &solve_outcome.warnings {
1437 let message = if let Some(index) = warning.about_constraint.as_ref() {
1438 format!("{}; constraint index {}", &warning.content, index)
1439 } else {
1440 format!("{}", &warning.content)
1441 };
1442 exec_state.warn(CompilationError::err(range, message), annotations::WARN_SOLVER);
1443 }
1444 let sketch_engine_id = exec_state.next_uuid();
1446 let solution_ty = solver_numeric_type(exec_state);
1447 let mut solved_segments = Vec::with_capacity(sketch_block_state.needed_by_engine.len());
1448 for unsolved_segment in &sketch_block_state.needed_by_engine {
1449 solved_segments.push(substitute_sketch_var_in_segment(
1450 unsolved_segment.clone(),
1451 &sketch_surface,
1452 sketch_engine_id,
1453 None,
1454 &solve_outcome,
1455 solver_numeric_type(exec_state),
1456 solve_analysis.as_ref(),
1457 )?);
1458 }
1459 #[cfg(feature = "artifact-graph")]
1460 {
1461 exec_state.mod_local.artifacts.var_solutions =
1467 sketch_block_state.var_solutions(&solve_outcome, solution_ty, SourceRange::from(self))?;
1468 }
1469
1470 let scene_objects = create_segment_scene_objects(&solved_segments, range, exec_state)?;
1472
1473 let sketch = create_segments_in_engine(
1475 &sketch_surface,
1476 sketch_engine_id,
1477 &mut solved_segments,
1478 &sketch_block_state.segment_tags,
1479 ctx,
1480 exec_state,
1481 range,
1482 )
1483 .await?;
1484
1485 let variables = substitute_sketch_vars(
1490 variables,
1491 &sketch_surface,
1492 sketch_engine_id,
1493 sketch,
1494 &solve_outcome,
1495 solution_ty,
1496 solve_analysis.as_ref(),
1497 )?;
1498
1499 #[cfg(not(feature = "artifact-graph"))]
1500 drop(scene_objects);
1501 #[cfg(feature = "artifact-graph")]
1502 {
1503 let mut segment_object_ids = Vec::with_capacity(scene_objects.len());
1504 for scene_object in scene_objects {
1505 segment_object_ids.push(scene_object.id);
1506 exec_state.set_scene_object(scene_object);
1508 }
1509 let Some(sketch_object) = exec_state.mod_local.artifacts.scene_object_by_id_mut(sketch_id) else {
1511 let message = format!("Sketch object not found after it was just created; id={:?}", sketch_id);
1512 debug_assert!(false, "{}", &message);
1513 return Err(internal_err(message, range));
1514 };
1515 let ObjectKind::Sketch(sketch) = &mut sketch_object.kind else {
1516 let message = format!(
1517 "Expected Sketch object after it was just created to be a sketch kind; id={:?}, actual={:?}",
1518 sketch_id, sketch_object
1519 );
1520 debug_assert!(
1521 false,
1522 "{}; scene_objects={:#?}",
1523 &message, &exec_state.mod_local.artifacts.scene_objects
1524 );
1525 return Err(internal_err(message, range));
1526 };
1527 sketch.segments.extend(segment_object_ids);
1528 sketch
1530 .constraints
1531 .extend(std::mem::take(&mut sketch_block_state.sketch_constraints));
1532
1533 exec_state.push_op(Operation::SketchSolve {
1535 sketch_id,
1536 node_path: NodePath::placeholder(),
1537 source_range: range,
1538 });
1539 }
1540
1541 let metadata = Metadata {
1542 source_range: SourceRange::from(self),
1543 };
1544 let return_value = KclValue::Object {
1545 value: variables,
1546 constrainable: Default::default(),
1547 meta: vec![metadata],
1548 };
1549 Ok(if self.is_being_edited {
1550 return_value.exit()
1553 } else {
1554 return_value.continue_()
1555 })
1556 }
1557
1558 async fn load_sketch2_into_current_scope(
1559 &self,
1560 exec_state: &mut ExecState,
1561 ctx: &ExecutorContext,
1562 source_range: SourceRange,
1563 ) -> Result<(), KclError> {
1564 let path = vec!["std".to_owned(), "sketch2".to_owned()];
1565 let resolved_path = ModulePath::from_std_import_path(&path)?;
1566 let module_id = ctx
1567 .open_module(&ImportPath::Std { path }, &[], &resolved_path, exec_state, source_range)
1568 .await?;
1569 let (env_ref, exports) = ctx.exec_module_for_items(module_id, exec_state, source_range).await?;
1570
1571 for name in exports {
1572 let value = exec_state
1573 .stack()
1574 .memory
1575 .get_from(&name, env_ref, source_range, 0)?
1576 .clone();
1577 exec_state.mut_stack().add(name, value, source_range)?;
1578 }
1579 Ok(())
1580 }
1581}
1582
1583impl SketchBlock {
1584 fn prep_mem(&self, parent: EnvironmentRef, exec_state: &mut ExecState) {
1585 exec_state.mut_stack().push_new_env_for_call(parent);
1586 }
1587}
1588
1589impl Node<SketchVar> {
1590 pub async fn get_result(&self, exec_state: &mut ExecState, _ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1591 let Some(sketch_block_state) = &exec_state.mod_local.sketch_block else {
1592 return Err(KclError::new_semantic(KclErrorDetails::new(
1593 "Cannot use a sketch variable outside of a sketch block".to_owned(),
1594 vec![SourceRange::from(self)],
1595 )));
1596 };
1597 let id = sketch_block_state.next_sketch_var_id();
1598 let sketch_var = if let Some(initial) = &self.initial {
1599 KclValue::from_sketch_var_literal(initial, id, exec_state)
1600 } else {
1601 let metadata = Metadata {
1602 source_range: SourceRange::from(self),
1603 };
1604
1605 KclValue::SketchVar {
1606 value: Box::new(super::SketchVar {
1607 id,
1608 initial_value: 0.0,
1609 ty: NumericType::default(),
1610 meta: vec![metadata],
1611 }),
1612 }
1613 };
1614
1615 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
1616 return Err(KclError::new_semantic(KclErrorDetails::new(
1617 "Cannot use a sketch variable outside of a sketch block".to_owned(),
1618 vec![SourceRange::from(self)],
1619 )));
1620 };
1621 sketch_block_state.sketch_vars.push(sketch_var.clone());
1622
1623 Ok(sketch_var)
1624 }
1625}
1626
1627fn apply_ascription(
1628 value: &KclValue,
1629 ty: &Node<Type>,
1630 exec_state: &mut ExecState,
1631 source_range: SourceRange,
1632) -> Result<KclValue, KclError> {
1633 let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into(), false, false)
1634 .map_err(|e| KclError::new_semantic(e.into()))?;
1635
1636 if matches!(&ty, &RuntimeType::Primitive(PrimitiveType::Number(..))) {
1637 exec_state.clear_units_warnings(&source_range);
1638 }
1639
1640 value.coerce(&ty, false, exec_state).map_err(|_| {
1641 let suggestion = if ty == RuntimeType::length() {
1642 ", you might try coercing to a fully specified numeric type such as `mm`"
1643 } else if ty == RuntimeType::angle() {
1644 ", you might try coercing to a fully specified numeric type such as `deg`"
1645 } else {
1646 ""
1647 };
1648 let ty_str = if let Some(ty) = value.principal_type() {
1649 format!("(with type `{ty}`) ")
1650 } else {
1651 String::new()
1652 };
1653 KclError::new_semantic(KclErrorDetails::new(
1654 format!(
1655 "could not coerce {} {ty_str}to type `{ty}`{suggestion}",
1656 value.human_friendly_type()
1657 ),
1658 vec![source_range],
1659 ))
1660 })
1661}
1662
1663impl BinaryPart {
1664 #[async_recursion]
1665 pub(super) async fn get_result(
1666 &self,
1667 exec_state: &mut ExecState,
1668 ctx: &ExecutorContext,
1669 ) -> Result<KclValueControlFlow, KclError> {
1670 match self {
1671 BinaryPart::Literal(literal) => Ok(KclValue::from_literal((**literal).clone(), exec_state).continue_()),
1672 BinaryPart::Name(name) => name.get_result(exec_state, ctx).await.cloned().map(KclValue::continue_),
1673 BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
1674 BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await,
1675 BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
1676 BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state, ctx).await,
1677 BinaryPart::ArrayExpression(e) => e.execute(exec_state, ctx).await,
1678 BinaryPart::ArrayRangeExpression(e) => e.execute(exec_state, ctx).await,
1679 BinaryPart::ObjectExpression(e) => e.execute(exec_state, ctx).await,
1680 BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
1681 BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
1682 BinaryPart::SketchVar(e) => e.get_result(exec_state, ctx).await.map(KclValue::continue_),
1683 }
1684 }
1685}
1686
1687impl Node<Name> {
1688 pub(super) async fn get_result<'a>(
1689 &self,
1690 exec_state: &'a mut ExecState,
1691 ctx: &ExecutorContext,
1692 ) -> Result<&'a KclValue, KclError> {
1693 let being_declared = exec_state.mod_local.being_declared.clone();
1694 self.get_result_inner(exec_state, ctx)
1695 .await
1696 .map_err(|e| var_in_own_ref_err(e, &being_declared))
1697 }
1698
1699 async fn get_result_inner<'a>(
1700 &self,
1701 exec_state: &'a mut ExecState,
1702 ctx: &ExecutorContext,
1703 ) -> Result<&'a KclValue, KclError> {
1704 if self.abs_path {
1705 return Err(KclError::new_semantic(KclErrorDetails::new(
1706 "Absolute paths (names beginning with `::` are not yet supported)".to_owned(),
1707 self.as_source_ranges(),
1708 )));
1709 }
1710
1711 let mod_name = format!("{}{}", memory::MODULE_PREFIX, self.name.name);
1712
1713 if self.path.is_empty() {
1714 let item_value = exec_state.stack().get(&self.name.name, self.into());
1715 if item_value.is_ok() {
1716 return item_value;
1717 }
1718 return exec_state.stack().get(&mod_name, self.into());
1719 }
1720
1721 let mut mem_spec: Option<(EnvironmentRef, Vec<String>)> = None;
1722 for p in &self.path {
1723 let value = match mem_spec {
1724 Some((env, exports)) => {
1725 if !exports.contains(&p.name) {
1726 return Err(KclError::new_semantic(KclErrorDetails::new(
1727 format!("Item {} not found in module's exported items", p.name),
1728 p.as_source_ranges(),
1729 )));
1730 }
1731
1732 exec_state
1733 .stack()
1734 .memory
1735 .get_from(&p.name, env, p.as_source_range(), 0)?
1736 }
1737 None => exec_state
1738 .stack()
1739 .get(&format!("{}{}", memory::MODULE_PREFIX, p.name), self.into())?,
1740 };
1741
1742 let KclValue::Module { value: module_id, .. } = value else {
1743 return Err(KclError::new_semantic(KclErrorDetails::new(
1744 format!(
1745 "Identifier in path must refer to a module, found {}",
1746 value.human_friendly_type()
1747 ),
1748 p.as_source_ranges(),
1749 )));
1750 };
1751
1752 mem_spec = Some(
1753 ctx.exec_module_for_items(*module_id, exec_state, p.as_source_range())
1754 .await?,
1755 );
1756 }
1757
1758 let (env, exports) = mem_spec.unwrap();
1759
1760 let item_exported = exports.contains(&self.name.name);
1761 let item_value = exec_state
1762 .stack()
1763 .memory
1764 .get_from(&self.name.name, env, self.name.as_source_range(), 0);
1765
1766 if item_exported && item_value.is_ok() {
1768 return item_value;
1769 }
1770
1771 let mod_exported = exports.contains(&mod_name);
1772 let mod_value = exec_state
1773 .stack()
1774 .memory
1775 .get_from(&mod_name, env, self.name.as_source_range(), 0);
1776
1777 if mod_exported && mod_value.is_ok() {
1779 return mod_value;
1780 }
1781
1782 if item_value.is_err() && mod_value.is_err() {
1784 return item_value;
1785 }
1786
1787 debug_assert!((item_value.is_ok() && !item_exported) || (mod_value.is_ok() && !mod_exported));
1789 Err(KclError::new_semantic(KclErrorDetails::new(
1790 format!("Item {} not found in module's exported items", self.name.name),
1791 self.name.as_source_ranges(),
1792 )))
1793 }
1794}
1795
1796impl Node<MemberExpression> {
1797 async fn get_result(
1798 &self,
1799 exec_state: &mut ExecState,
1800 ctx: &ExecutorContext,
1801 ) -> Result<KclValueControlFlow, KclError> {
1802 let meta = Metadata {
1803 source_range: SourceRange::from(self),
1804 };
1805 let property = Property::try_from(
1808 self.computed,
1809 self.property.clone(),
1810 exec_state,
1811 self.into(),
1812 ctx,
1813 &meta,
1814 &[],
1815 StatementKind::Expression,
1816 )
1817 .await?;
1818 let object_cf = ctx
1819 .execute_expr(&self.object, exec_state, &meta, &[], StatementKind::Expression)
1820 .await?;
1821 let object = control_continue!(object_cf);
1822
1823 match (object, property, self.computed) {
1825 (KclValue::Segment { value: segment }, Property::String(property), false) => match property.as_str() {
1826 "at" => match &segment.repr {
1827 SegmentRepr::Unsolved { segment } => {
1828 match &segment.kind {
1829 UnsolvedSegmentKind::Point { position, .. } => {
1830 Ok(KclValue::HomArray {
1832 value: vec![
1833 KclValue::from_unsolved_expr(position[0].clone(), segment.meta.clone()),
1834 KclValue::from_unsolved_expr(position[1].clone(), segment.meta.clone()),
1835 ],
1836 ty: RuntimeType::any(),
1837 }
1838 .continue_())
1839 }
1840 _ => Err(KclError::new_undefined_value(
1841 KclErrorDetails::new(
1842 format!("Property '{property}' not found in segment"),
1843 vec![self.clone().into()],
1844 ),
1845 None,
1846 )),
1847 }
1848 }
1849 SegmentRepr::Solved { segment } => {
1850 match &segment.kind {
1851 SegmentKind::Point { position, .. } => {
1852 Ok(KclValue::array_from_point2d(
1854 [position[0].n, position[1].n],
1855 position[0].ty,
1856 segment.meta.clone(),
1857 )
1858 .continue_())
1859 }
1860 _ => Err(KclError::new_undefined_value(
1861 KclErrorDetails::new(
1862 format!("Property '{property}' not found in segment"),
1863 vec![self.clone().into()],
1864 ),
1865 None,
1866 )),
1867 }
1868 }
1869 },
1870 "start" => match &segment.repr {
1871 SegmentRepr::Unsolved { segment } => match &segment.kind {
1872 UnsolvedSegmentKind::Point { .. } => Err(KclError::new_undefined_value(
1873 KclErrorDetails::new(
1874 format!("Property '{property}' not found in point segment"),
1875 vec![self.clone().into()],
1876 ),
1877 None,
1878 )),
1879 UnsolvedSegmentKind::Line {
1880 start,
1881 ctor,
1882 start_object_id,
1883 ..
1884 } => Ok(KclValue::Segment {
1885 value: Box::new(AbstractSegment {
1886 repr: SegmentRepr::Unsolved {
1887 segment: Box::new(UnsolvedSegment {
1888 id: segment.id,
1889 object_id: *start_object_id,
1890 kind: UnsolvedSegmentKind::Point {
1891 position: start.clone(),
1892 ctor: Box::new(PointCtor {
1893 position: ctor.start.clone(),
1894 }),
1895 },
1896 tag: segment.tag.clone(),
1897 meta: segment.meta.clone(),
1898 }),
1899 },
1900 meta: segment.meta.clone(),
1901 }),
1902 }
1903 .continue_()),
1904 UnsolvedSegmentKind::Arc {
1905 start,
1906 ctor,
1907 start_object_id,
1908 ..
1909 } => Ok(KclValue::Segment {
1910 value: Box::new(AbstractSegment {
1911 repr: SegmentRepr::Unsolved {
1912 segment: Box::new(UnsolvedSegment {
1913 id: segment.id,
1914 object_id: *start_object_id,
1915 kind: UnsolvedSegmentKind::Point {
1916 position: start.clone(),
1917 ctor: Box::new(PointCtor {
1918 position: ctor.start.clone(),
1919 }),
1920 },
1921 tag: segment.tag.clone(),
1922 meta: segment.meta.clone(),
1923 }),
1924 },
1925 meta: segment.meta.clone(),
1926 }),
1927 }
1928 .continue_()),
1929 },
1930 SegmentRepr::Solved { segment } => match &segment.kind {
1931 SegmentKind::Point { .. } => Err(KclError::new_undefined_value(
1932 KclErrorDetails::new(
1933 format!("Property '{property}' not found in point segment"),
1934 vec![self.clone().into()],
1935 ),
1936 None,
1937 )),
1938 SegmentKind::Line {
1939 start,
1940 ctor,
1941 start_object_id,
1942 start_freedom,
1943 ..
1944 } => Ok(KclValue::Segment {
1945 value: Box::new(AbstractSegment {
1946 repr: SegmentRepr::Solved {
1947 segment: Box::new(Segment {
1948 id: segment.id,
1949 object_id: *start_object_id,
1950 kind: SegmentKind::Point {
1951 position: start.clone(),
1952 ctor: Box::new(PointCtor {
1953 position: ctor.start.clone(),
1954 }),
1955 freedom: *start_freedom,
1956 },
1957 surface: segment.surface.clone(),
1958 sketch_id: segment.sketch_id,
1959 sketch: None,
1960 tag: segment.tag.clone(),
1961 meta: segment.meta.clone(),
1962 }),
1963 },
1964 meta: segment.meta.clone(),
1965 }),
1966 }
1967 .continue_()),
1968 SegmentKind::Arc {
1969 start,
1970 ctor,
1971 start_object_id,
1972 start_freedom,
1973 ..
1974 } => Ok(KclValue::Segment {
1975 value: Box::new(AbstractSegment {
1976 repr: SegmentRepr::Solved {
1977 segment: Box::new(Segment {
1978 id: segment.id,
1979 object_id: *start_object_id,
1980 kind: SegmentKind::Point {
1981 position: start.clone(),
1982 ctor: Box::new(PointCtor {
1983 position: ctor.start.clone(),
1984 }),
1985 freedom: *start_freedom,
1986 },
1987 surface: segment.surface.clone(),
1988 sketch_id: segment.sketch_id,
1989 sketch: None,
1990 tag: segment.tag.clone(),
1991 meta: segment.meta.clone(),
1992 }),
1993 },
1994 meta: segment.meta.clone(),
1995 }),
1996 }
1997 .continue_()),
1998 },
1999 },
2000 "end" => match &segment.repr {
2001 SegmentRepr::Unsolved { segment } => match &segment.kind {
2002 UnsolvedSegmentKind::Point { .. } => Err(KclError::new_undefined_value(
2003 KclErrorDetails::new(
2004 format!("Property '{property}' not found in point segment"),
2005 vec![self.clone().into()],
2006 ),
2007 None,
2008 )),
2009 UnsolvedSegmentKind::Line {
2010 end,
2011 ctor,
2012 end_object_id,
2013 ..
2014 } => Ok(KclValue::Segment {
2015 value: Box::new(AbstractSegment {
2016 repr: SegmentRepr::Unsolved {
2017 segment: Box::new(UnsolvedSegment {
2018 id: segment.id,
2019 object_id: *end_object_id,
2020 kind: UnsolvedSegmentKind::Point {
2021 position: end.clone(),
2022 ctor: Box::new(PointCtor {
2023 position: ctor.end.clone(),
2024 }),
2025 },
2026 tag: segment.tag.clone(),
2027 meta: segment.meta.clone(),
2028 }),
2029 },
2030 meta: segment.meta.clone(),
2031 }),
2032 }
2033 .continue_()),
2034 UnsolvedSegmentKind::Arc {
2035 end,
2036 ctor,
2037 end_object_id,
2038 ..
2039 } => Ok(KclValue::Segment {
2040 value: Box::new(AbstractSegment {
2041 repr: SegmentRepr::Unsolved {
2042 segment: Box::new(UnsolvedSegment {
2043 id: segment.id,
2044 object_id: *end_object_id,
2045 kind: UnsolvedSegmentKind::Point {
2046 position: end.clone(),
2047 ctor: Box::new(PointCtor {
2048 position: ctor.end.clone(),
2049 }),
2050 },
2051 tag: segment.tag.clone(),
2052 meta: segment.meta.clone(),
2053 }),
2054 },
2055 meta: segment.meta.clone(),
2056 }),
2057 }
2058 .continue_()),
2059 },
2060 SegmentRepr::Solved { segment } => match &segment.kind {
2061 SegmentKind::Point { .. } => Err(KclError::new_undefined_value(
2062 KclErrorDetails::new(
2063 format!("Property '{property}' not found in point segment"),
2064 vec![self.clone().into()],
2065 ),
2066 None,
2067 )),
2068 SegmentKind::Line {
2069 end,
2070 ctor,
2071 end_object_id,
2072 end_freedom,
2073 ..
2074 } => Ok(KclValue::Segment {
2075 value: Box::new(AbstractSegment {
2076 repr: SegmentRepr::Solved {
2077 segment: Box::new(Segment {
2078 id: segment.id,
2079 object_id: *end_object_id,
2080 kind: SegmentKind::Point {
2081 position: end.clone(),
2082 ctor: Box::new(PointCtor {
2083 position: ctor.end.clone(),
2084 }),
2085 freedom: *end_freedom,
2086 },
2087 surface: segment.surface.clone(),
2088 sketch_id: segment.sketch_id,
2089 sketch: None,
2090 tag: segment.tag.clone(),
2091 meta: segment.meta.clone(),
2092 }),
2093 },
2094 meta: segment.meta.clone(),
2095 }),
2096 }
2097 .continue_()),
2098 SegmentKind::Arc {
2099 end,
2100 ctor,
2101 end_object_id,
2102 end_freedom,
2103 ..
2104 } => Ok(KclValue::Segment {
2105 value: Box::new(AbstractSegment {
2106 repr: SegmentRepr::Solved {
2107 segment: Box::new(Segment {
2108 id: segment.id,
2109 object_id: *end_object_id,
2110 kind: SegmentKind::Point {
2111 position: end.clone(),
2112 ctor: Box::new(PointCtor {
2113 position: ctor.end.clone(),
2114 }),
2115 freedom: *end_freedom,
2116 },
2117 surface: segment.surface.clone(),
2118 sketch_id: segment.sketch_id,
2119 sketch: None,
2120 tag: segment.tag.clone(),
2121 meta: segment.meta.clone(),
2122 }),
2123 },
2124 meta: segment.meta.clone(),
2125 }),
2126 }
2127 .continue_()),
2128 },
2129 },
2130 "center" => match &segment.repr {
2131 SegmentRepr::Unsolved { segment } => match &segment.kind {
2132 UnsolvedSegmentKind::Arc {
2133 center,
2134 ctor,
2135 center_object_id,
2136 ..
2137 } => Ok(KclValue::Segment {
2138 value: Box::new(AbstractSegment {
2139 repr: SegmentRepr::Unsolved {
2140 segment: Box::new(UnsolvedSegment {
2141 id: segment.id,
2142 object_id: *center_object_id,
2143 kind: UnsolvedSegmentKind::Point {
2144 position: center.clone(),
2145 ctor: Box::new(PointCtor {
2146 position: ctor.center.clone(),
2147 }),
2148 },
2149 tag: segment.tag.clone(),
2150 meta: segment.meta.clone(),
2151 }),
2152 },
2153 meta: segment.meta.clone(),
2154 }),
2155 }
2156 .continue_()),
2157 _ => Err(KclError::new_undefined_value(
2158 KclErrorDetails::new(
2159 format!("Property '{property}' not found in segment"),
2160 vec![self.clone().into()],
2161 ),
2162 None,
2163 )),
2164 },
2165 SegmentRepr::Solved { segment } => match &segment.kind {
2166 SegmentKind::Arc {
2167 center,
2168 ctor,
2169 center_object_id,
2170 center_freedom,
2171 ..
2172 } => Ok(KclValue::Segment {
2173 value: Box::new(AbstractSegment {
2174 repr: SegmentRepr::Solved {
2175 segment: Box::new(Segment {
2176 id: segment.id,
2177 object_id: *center_object_id,
2178 kind: SegmentKind::Point {
2179 position: center.clone(),
2180 ctor: Box::new(PointCtor {
2181 position: ctor.center.clone(),
2182 }),
2183 freedom: *center_freedom,
2184 },
2185 surface: segment.surface.clone(),
2186 sketch_id: segment.sketch_id,
2187 sketch: None,
2188 tag: segment.tag.clone(),
2189 meta: segment.meta.clone(),
2190 }),
2191 },
2192 meta: segment.meta.clone(),
2193 }),
2194 }
2195 .continue_()),
2196 _ => Err(KclError::new_undefined_value(
2197 KclErrorDetails::new(
2198 format!("Property '{property}' not found in segment"),
2199 vec![self.clone().into()],
2200 ),
2201 None,
2202 )),
2203 },
2204 },
2205 other => Err(KclError::new_undefined_value(
2206 KclErrorDetails::new(
2207 format!("Property '{other}' not found in segment"),
2208 vec![self.clone().into()],
2209 ),
2210 None,
2211 )),
2212 },
2213 (KclValue::Plane { value: plane }, Property::String(property), false) => match property.as_str() {
2214 "zAxis" => {
2215 let (p, u) = plane.info.z_axis.as_3_dims();
2216 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
2217 }
2218 "yAxis" => {
2219 let (p, u) = plane.info.y_axis.as_3_dims();
2220 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
2221 }
2222 "xAxis" => {
2223 let (p, u) = plane.info.x_axis.as_3_dims();
2224 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
2225 }
2226 "origin" => {
2227 let (p, u) = plane.info.origin.as_3_dims();
2228 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
2229 }
2230 other => Err(KclError::new_undefined_value(
2231 KclErrorDetails::new(
2232 format!("Property '{other}' not found in plane"),
2233 vec![self.clone().into()],
2234 ),
2235 None,
2236 )),
2237 },
2238 (KclValue::Object { value: map, .. }, Property::String(property), false) => {
2239 if let Some(value) = map.get(&property) {
2240 Ok(value.to_owned().continue_())
2241 } else {
2242 Err(KclError::new_undefined_value(
2243 KclErrorDetails::new(
2244 format!("Property '{property}' not found in object"),
2245 vec![self.clone().into()],
2246 ),
2247 None,
2248 ))
2249 }
2250 }
2251 (KclValue::Object { .. }, Property::String(property), true) => {
2252 Err(KclError::new_semantic(KclErrorDetails::new(
2253 format!("Cannot index object with string; use dot notation instead, e.g. `obj.{property}`"),
2254 vec![self.clone().into()],
2255 )))
2256 }
2257 (KclValue::Object { value: map, .. }, p @ Property::UInt(i), _) => {
2258 if i == 0
2259 && let Some(value) = map.get("x")
2260 {
2261 return Ok(value.to_owned().continue_());
2262 }
2263 if i == 1
2264 && let Some(value) = map.get("y")
2265 {
2266 return Ok(value.to_owned().continue_());
2267 }
2268 if i == 2
2269 && let Some(value) = map.get("z")
2270 {
2271 return Ok(value.to_owned().continue_());
2272 }
2273 let t = p.type_name();
2274 let article = article_for(t);
2275 Err(KclError::new_semantic(KclErrorDetails::new(
2276 format!("Only strings can be used as the property of an object, but you're using {article} {t}",),
2277 vec![self.clone().into()],
2278 )))
2279 }
2280 (KclValue::HomArray { value: arr, .. }, Property::UInt(index), _) => {
2281 let value_of_arr = arr.get(index);
2282 if let Some(value) = value_of_arr {
2283 Ok(value.to_owned().continue_())
2284 } else {
2285 Err(KclError::new_undefined_value(
2286 KclErrorDetails::new(
2287 format!("The array doesn't have any item at index {index}"),
2288 vec![self.clone().into()],
2289 ),
2290 None,
2291 ))
2292 }
2293 }
2294 (obj, Property::UInt(0), _) => Ok(obj.continue_()),
2297 (KclValue::HomArray { .. }, p, _) => {
2298 let t = p.type_name();
2299 let article = article_for(t);
2300 Err(KclError::new_semantic(KclErrorDetails::new(
2301 format!("Only integers >= 0 can be used as the index of an array, but you're using {article} {t}",),
2302 vec![self.clone().into()],
2303 )))
2304 }
2305 (KclValue::Solid { value }, Property::String(prop), false) if prop == "sketch" => {
2306 let Some(sketch) = value.sketch() else {
2307 return Err(KclError::new_semantic(KclErrorDetails::new(
2308 "This solid was created without a sketch, so `solid.sketch` is unavailable.".to_owned(),
2309 vec![self.clone().into()],
2310 )));
2311 };
2312 Ok(KclValue::Sketch {
2313 value: Box::new(sketch.clone()),
2314 }
2315 .continue_())
2316 }
2317 (geometry @ KclValue::Solid { .. }, Property::String(prop), false) if prop == "tags" => {
2318 Err(KclError::new_semantic(KclErrorDetails::new(
2320 format!(
2321 "Property `{prop}` not found on {}. You can get a solid's tags through its sketch, as in, `exampleSolid.sketch.tags`.",
2322 geometry.human_friendly_type()
2323 ),
2324 vec![self.clone().into()],
2325 )))
2326 }
2327 (KclValue::Sketch { value: sk }, Property::String(prop), false) if prop == "tags" => Ok(KclValue::Object {
2328 meta: vec![Metadata {
2329 source_range: SourceRange::from(self.clone()),
2330 }],
2331 value: sk
2332 .tags
2333 .iter()
2334 .map(|(k, tag)| (k.to_owned(), KclValue::TagIdentifier(Box::new(tag.to_owned()))))
2335 .collect(),
2336 constrainable: false,
2337 }
2338 .continue_()),
2339 (geometry @ (KclValue::Sketch { .. } | KclValue::Solid { .. }), Property::String(property), false) => {
2340 Err(KclError::new_semantic(KclErrorDetails::new(
2341 format!("Property `{property}` not found on {}", geometry.human_friendly_type()),
2342 vec![self.clone().into()],
2343 )))
2344 }
2345 (being_indexed, _, false) => Err(KclError::new_semantic(KclErrorDetails::new(
2346 format!(
2347 "Only objects can have members accessed with dot notation, but you're trying to access {}",
2348 being_indexed.human_friendly_type()
2349 ),
2350 vec![self.clone().into()],
2351 ))),
2352 (being_indexed, _, true) => Err(KclError::new_semantic(KclErrorDetails::new(
2353 format!(
2354 "Only arrays can be indexed, but you're trying to index {}",
2355 being_indexed.human_friendly_type()
2356 ),
2357 vec![self.clone().into()],
2358 ))),
2359 }
2360 }
2361}
2362
2363impl Node<BinaryExpression> {
2364 pub(super) async fn get_result(
2365 &self,
2366 exec_state: &mut ExecState,
2367 ctx: &ExecutorContext,
2368 ) -> Result<KclValueControlFlow, KclError> {
2369 enum State {
2370 EvaluateLeft(Node<BinaryExpression>),
2371 FromLeft {
2372 node: Node<BinaryExpression>,
2373 },
2374 EvaluateRight {
2375 node: Node<BinaryExpression>,
2376 left: KclValue,
2377 },
2378 FromRight {
2379 node: Node<BinaryExpression>,
2380 left: KclValue,
2381 },
2382 }
2383
2384 let mut stack = vec![State::EvaluateLeft(self.clone())];
2385 let mut last_result: Option<KclValue> = None;
2386
2387 while let Some(state) = stack.pop() {
2388 match state {
2389 State::EvaluateLeft(node) => {
2390 let left_part = node.left.clone();
2391 match left_part {
2392 BinaryPart::BinaryExpression(child) => {
2393 stack.push(State::FromLeft { node });
2394 stack.push(State::EvaluateLeft(*child));
2395 }
2396 part => {
2397 let left_value = part.get_result(exec_state, ctx).await?;
2398 let left_value = control_continue!(left_value);
2399 stack.push(State::EvaluateRight { node, left: left_value });
2400 }
2401 }
2402 }
2403 State::FromLeft { node } => {
2404 let Some(left_value) = last_result.take() else {
2405 return Err(Self::missing_result_error(&node));
2406 };
2407 stack.push(State::EvaluateRight { node, left: left_value });
2408 }
2409 State::EvaluateRight { node, left } => {
2410 let right_part = node.right.clone();
2411 match right_part {
2412 BinaryPart::BinaryExpression(child) => {
2413 stack.push(State::FromRight { node, left });
2414 stack.push(State::EvaluateLeft(*child));
2415 }
2416 part => {
2417 let right_value = part.get_result(exec_state, ctx).await?;
2418 let right_value = control_continue!(right_value);
2419 let result = node.apply_operator(exec_state, ctx, left, right_value).await?;
2420 last_result = Some(result);
2421 }
2422 }
2423 }
2424 State::FromRight { node, left } => {
2425 let Some(right_value) = last_result.take() else {
2426 return Err(Self::missing_result_error(&node));
2427 };
2428 let result = node.apply_operator(exec_state, ctx, left, right_value).await?;
2429 last_result = Some(result);
2430 }
2431 }
2432 }
2433
2434 last_result
2435 .map(KclValue::continue_)
2436 .ok_or_else(|| Self::missing_result_error(self))
2437 }
2438
2439 async fn apply_operator(
2440 &self,
2441 exec_state: &mut ExecState,
2442 ctx: &ExecutorContext,
2443 left_value: KclValue,
2444 right_value: KclValue,
2445 ) -> Result<KclValue, KclError> {
2446 let mut meta = left_value.metadata();
2447 meta.extend(right_value.metadata());
2448
2449 if self.operator == BinaryOperator::Add
2451 && let (KclValue::String { value: left, .. }, KclValue::String { value: right, .. }) =
2452 (&left_value, &right_value)
2453 {
2454 return Ok(KclValue::String {
2455 value: format!("{left}{right}"),
2456 meta,
2457 });
2458 }
2459
2460 if self.operator == BinaryOperator::Add || self.operator == BinaryOperator::Or {
2462 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
2463 let args = Args::new_no_args(self.into(), ctx.clone(), Some("union".to_owned()));
2464 let result = crate::std::csg::inner_union(
2465 vec![*left.clone(), *right.clone()],
2466 Default::default(),
2467 exec_state,
2468 args,
2469 )
2470 .await?;
2471 return Ok(result.into());
2472 }
2473 } else if self.operator == BinaryOperator::Sub {
2474 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
2476 let args = Args::new_no_args(self.into(), ctx.clone(), Some("subtract".to_owned()));
2477 let result = crate::std::csg::inner_subtract(
2478 vec![*left.clone()],
2479 vec![*right.clone()],
2480 Default::default(),
2481 exec_state,
2482 args,
2483 )
2484 .await?;
2485 return Ok(result.into());
2486 }
2487 } else if self.operator == BinaryOperator::And
2488 && let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value)
2489 {
2490 let args = Args::new_no_args(self.into(), ctx.clone(), Some("intersect".to_owned()));
2492 let result = crate::std::csg::inner_intersect(
2493 vec![*left.clone(), *right.clone()],
2494 Default::default(),
2495 exec_state,
2496 args,
2497 )
2498 .await?;
2499 return Ok(result.into());
2500 }
2501
2502 if self.operator == BinaryOperator::Or || self.operator == BinaryOperator::And {
2504 let KclValue::Bool { value: left_value, .. } = left_value else {
2505 return Err(KclError::new_semantic(KclErrorDetails::new(
2506 format!(
2507 "Cannot apply logical operator to non-boolean value: {}",
2508 left_value.human_friendly_type()
2509 ),
2510 vec![self.left.clone().into()],
2511 )));
2512 };
2513 let KclValue::Bool { value: right_value, .. } = right_value else {
2514 return Err(KclError::new_semantic(KclErrorDetails::new(
2515 format!(
2516 "Cannot apply logical operator to non-boolean value: {}",
2517 right_value.human_friendly_type()
2518 ),
2519 vec![self.right.clone().into()],
2520 )));
2521 };
2522 let raw_value = match self.operator {
2523 BinaryOperator::Or => left_value || right_value,
2524 BinaryOperator::And => left_value && right_value,
2525 _ => unreachable!(),
2526 };
2527 return Ok(KclValue::Bool { value: raw_value, meta });
2528 }
2529
2530 if self.operator == BinaryOperator::Eq && exec_state.mod_local.sketch_block.is_some() {
2532 match (&left_value, &right_value) {
2533 (KclValue::SketchVar { value: left_value, .. }, KclValue::SketchVar { value: right_value, .. })
2535 if left_value.id == right_value.id =>
2536 {
2537 return Ok(KclValue::Bool { value: true, meta });
2538 }
2539 (KclValue::SketchVar { .. }, KclValue::SketchVar { .. }) => {
2541 return Err(KclError::new_semantic(KclErrorDetails::new(
2544 "TODO: Different sketch variables".to_owned(),
2545 vec![self.into()],
2546 )));
2547 }
2548 (KclValue::SketchVar { value: var, .. }, input_number @ KclValue::Number { .. })
2550 | (input_number @ KclValue::Number { .. }, KclValue::SketchVar { value: var, .. }) => {
2551 let number_value = normalize_to_solver_unit(
2552 input_number,
2553 input_number.into(),
2554 exec_state,
2555 "fixed constraint value",
2556 )?;
2557 let Some(n) = number_value.as_ty_f64() else {
2558 let message = format!(
2559 "Expected number after coercion, but found {}",
2560 number_value.human_friendly_type()
2561 );
2562 debug_assert!(false, "{}", &message);
2563 return Err(internal_err(message, self));
2564 };
2565 let constraint = Constraint::Fixed(var.id.to_constraint_id(self.as_source_range())?, n.n);
2566 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2567 let message = "Being inside a sketch block should have already been checked above".to_owned();
2568 debug_assert!(false, "{}", &message);
2569 return Err(internal_err(message, self));
2570 };
2571 sketch_block_state.solver_constraints.push(constraint);
2572 return Ok(KclValue::Bool { value: true, meta });
2573 }
2574 (KclValue::SketchConstraint { value: constraint }, input_number @ KclValue::Number { .. })
2576 | (input_number @ KclValue::Number { .. }, KclValue::SketchConstraint { value: constraint }) => {
2577 let number_value = normalize_to_solver_unit(
2578 input_number,
2579 input_number.into(),
2580 exec_state,
2581 "fixed constraint value",
2582 )?;
2583 let Some(n) = number_value.as_ty_f64() else {
2584 let message = format!(
2585 "Expected number after coercion, but found {}",
2586 number_value.human_friendly_type()
2587 );
2588 debug_assert!(false, "{}", &message);
2589 return Err(internal_err(message, self));
2590 };
2591 #[cfg(feature = "artifact-graph")]
2593 let number_binary_part = if matches!(&left_value, KclValue::SketchConstraint { .. }) {
2594 &self.right
2595 } else {
2596 &self.left
2597 };
2598 #[cfg(feature = "artifact-graph")]
2599 let source = {
2600 use crate::unparser::ExprContext;
2601 let mut buf = String::new();
2602 number_binary_part.recast(&mut buf, &Default::default(), 0, ExprContext::Other);
2603 crate::frontend::sketch::ConstraintSource {
2604 expr: buf,
2605 is_literal: matches!(number_binary_part, BinaryPart::Literal(_)),
2606 }
2607 };
2608
2609 match &constraint.kind {
2610 SketchConstraintKind::Distance { points } => {
2611 let range = self.as_source_range();
2612 let p0 = &points[0];
2613 let p1 = &points[1];
2614 let solver_pt0 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2615 p0.vars.x.to_constraint_id(range)?,
2616 p0.vars.y.to_constraint_id(range)?,
2617 );
2618 let solver_pt1 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2619 p1.vars.x.to_constraint_id(range)?,
2620 p1.vars.y.to_constraint_id(range)?,
2621 );
2622 let solver_constraint = Constraint::Distance(solver_pt0, solver_pt1, n.n);
2623
2624 #[cfg(feature = "artifact-graph")]
2625 let constraint_id = exec_state.next_object_id();
2626 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2627 let message =
2628 "Being inside a sketch block should have already been checked above".to_owned();
2629 debug_assert!(false, "{}", &message);
2630 return Err(internal_err(message, self));
2631 };
2632 sketch_block_state.solver_constraints.push(solver_constraint);
2633 #[cfg(feature = "artifact-graph")]
2634 {
2635 use crate::{
2636 execution::{Artifact, CodeRef, SketchBlockConstraint, SketchBlockConstraintType},
2637 front::Distance,
2638 };
2639
2640 let Some(sketch_id) = sketch_block_state.sketch_id else {
2641 let message = "Sketch id missing for constraint artifact".to_owned();
2642 debug_assert!(false, "{}", &message);
2643 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![range])));
2644 };
2645 let sketch_constraint = crate::front::Constraint::Distance(Distance {
2646 points: vec![p0.object_id, p1.object_id],
2647 distance: n.try_into().map_err(|_| {
2648 internal_err("Failed to convert distance units numeric suffix:", range)
2649 })?,
2650 source,
2651 });
2652 sketch_block_state.sketch_constraints.push(constraint_id);
2653 let artifact_id = exec_state.next_artifact_id();
2654 exec_state.add_artifact(Artifact::SketchBlockConstraint(SketchBlockConstraint {
2655 id: artifact_id,
2656 sketch_id,
2657 constraint_id,
2658 constraint_type: SketchBlockConstraintType::from(&sketch_constraint),
2659 code_ref: CodeRef::placeholder(range),
2660 }));
2661 exec_state.add_scene_object(
2662 Object {
2663 id: constraint_id,
2664 kind: ObjectKind::Constraint {
2665 constraint: sketch_constraint,
2666 },
2667 label: Default::default(),
2668 comments: Default::default(),
2669 artifact_id,
2670 source: range.into(),
2671 },
2672 range,
2673 );
2674 }
2675 }
2676 SketchConstraintKind::Radius { points } | SketchConstraintKind::Diameter { points } => {
2677 let range = self.as_source_range();
2678 let center = &points[0];
2679 let start = &points[1];
2680 let Some(sketch_block_state) = &exec_state.mod_local.sketch_block else {
2682 return Err(internal_err(
2683 "Being inside a sketch block should have already been checked above",
2684 self,
2685 ));
2686 };
2687 let (constraint_name, is_diameter) = match &constraint.kind {
2689 SketchConstraintKind::Radius { .. } => ("radius", false),
2690 SketchConstraintKind::Diameter { .. } => ("diameter", true),
2691 _ => unreachable!(),
2692 };
2693 let arc_segment = sketch_block_state
2694 .needed_by_engine
2695 .iter()
2696 .find(|seg| {
2697 matches!(&seg.kind, UnsolvedSegmentKind::Arc {
2698 center_object_id,
2699 start_object_id,
2700 ..
2701 } if *center_object_id == center.object_id && *start_object_id == start.object_id)
2702 })
2703 .ok_or_else(|| {
2704 internal_err(
2705 format!("Could not find arc segment for {} constraint", constraint_name),
2706 range,
2707 )
2708 })?;
2709 let UnsolvedSegmentKind::Arc { end, .. } = &arc_segment.kind else {
2710 return Err(internal_err("Expected arc segment", range));
2711 };
2712 let (end_x_var, end_y_var) = match (&end[0], &end[1]) {
2714 (UnsolvedExpr::Unknown(end_x), UnsolvedExpr::Unknown(end_y)) => (*end_x, *end_y),
2715 _ => {
2716 return Err(internal_err(
2717 "Arc end point must have sketch vars in all coordinates",
2718 range,
2719 ));
2720 }
2721 };
2722 let solver_arc = kcl_ezpz::datatypes::inputs::DatumCircularArc {
2723 center: kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2724 center.vars.x.to_constraint_id(range)?,
2725 center.vars.y.to_constraint_id(range)?,
2726 ),
2727 start: kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2728 start.vars.x.to_constraint_id(range)?,
2729 start.vars.y.to_constraint_id(range)?,
2730 ),
2731 end: kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2732 end_x_var.to_constraint_id(range)?,
2733 end_y_var.to_constraint_id(range)?,
2734 ),
2735 };
2736 let radius_value = if is_diameter { n.n / 2.0 } else { n.n };
2739 let solver_constraint = Constraint::ArcRadius(solver_arc, radius_value);
2740
2741 #[cfg(feature = "artifact-graph")]
2742 let constraint_id = exec_state.next_object_id();
2743 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2744 let message =
2745 "Being inside a sketch block should have already been checked above".to_owned();
2746 debug_assert!(false, "{}", &message);
2747 return Err(internal_err(message, self));
2748 };
2749 sketch_block_state.solver_constraints.push(solver_constraint);
2750 #[cfg(feature = "artifact-graph")]
2751 {
2752 use crate::execution::{
2753 Artifact, CodeRef, SketchBlockConstraint, SketchBlockConstraintType,
2754 };
2755 let arc_object_id = sketch_block_state
2758 .needed_by_engine
2759 .iter()
2760 .find(|seg| {
2761 matches!(&seg.kind, UnsolvedSegmentKind::Arc {
2762 center_object_id,
2763 start_object_id,
2764 ..
2765 } if *center_object_id == center.object_id && *start_object_id == start.object_id)
2766 })
2767 .map(|seg| seg.object_id)
2768 .ok_or_else(|| {
2769 internal_err(
2770 format!(
2771 "Could not find arc segment object ID for {} constraint",
2772 constraint_name
2773 ),
2774 range,
2775 )
2776 })?;
2777
2778 let constraint = if is_diameter {
2779 use crate::frontend::sketch::Diameter;
2780 crate::front::Constraint::Diameter(Diameter {
2781 arc: arc_object_id,
2782 diameter: n.try_into().map_err(|_| {
2783 internal_err("Failed to convert diameter units numeric suffix:", range)
2784 })?,
2785 })
2786 } else {
2787 use crate::frontend::sketch::Radius;
2788 crate::front::Constraint::Radius(Radius {
2789 arc: arc_object_id,
2790 radius: n.try_into().map_err(|_| {
2791 internal_err("Failed to convert radius units numeric suffix:", range)
2792 })?,
2793 })
2794 };
2795 sketch_block_state.sketch_constraints.push(constraint_id);
2796 let Some(sketch_id) = sketch_block_state.sketch_id else {
2797 let message = "Sketch id missing for constraint artifact".to_owned();
2798 debug_assert!(false, "{}", &message);
2799 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![range])));
2800 };
2801 let artifact_id = exec_state.next_artifact_id();
2802 exec_state.add_artifact(Artifact::SketchBlockConstraint(SketchBlockConstraint {
2803 id: artifact_id,
2804 sketch_id,
2805 constraint_id,
2806 constraint_type: SketchBlockConstraintType::from(&constraint),
2807 code_ref: CodeRef::placeholder(range),
2808 }));
2809 exec_state.add_scene_object(
2810 Object {
2811 id: constraint_id,
2812 kind: ObjectKind::Constraint { constraint },
2813 label: Default::default(),
2814 comments: Default::default(),
2815 artifact_id,
2816 source: range.into(),
2817 },
2818 range,
2819 );
2820 }
2821 }
2822 SketchConstraintKind::HorizontalDistance { points } => {
2823 let range = self.as_source_range();
2824 let p0 = &points[0];
2825 let p1 = &points[1];
2826 let solver_pt0 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2827 p0.vars.x.to_constraint_id(range)?,
2828 p0.vars.y.to_constraint_id(range)?,
2829 );
2830 let solver_pt1 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2831 p1.vars.x.to_constraint_id(range)?,
2832 p1.vars.y.to_constraint_id(range)?,
2833 );
2834 let solver_constraint =
2838 kcl_ezpz::Constraint::HorizontalDistance(solver_pt1, solver_pt0, n.n);
2839
2840 #[cfg(feature = "artifact-graph")]
2841 let constraint_id = exec_state.next_object_id();
2842 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2843 let message =
2844 "Being inside a sketch block should have already been checked above".to_owned();
2845 debug_assert!(false, "{}", &message);
2846 return Err(internal_err(message, self));
2847 };
2848 sketch_block_state.solver_constraints.push(solver_constraint);
2849 #[cfg(feature = "artifact-graph")]
2850 {
2851 use crate::{
2852 execution::{Artifact, CodeRef, SketchBlockConstraint, SketchBlockConstraintType},
2853 front::Distance,
2854 };
2855
2856 let constraint = crate::front::Constraint::HorizontalDistance(Distance {
2857 points: vec![p0.object_id, p1.object_id],
2858 distance: n.try_into().map_err(|_| {
2859 internal_err("Failed to convert distance units numeric suffix:", range)
2860 })?,
2861 source,
2862 });
2863 sketch_block_state.sketch_constraints.push(constraint_id);
2864 let Some(sketch_id) = sketch_block_state.sketch_id else {
2865 let message = "Sketch id missing for constraint artifact".to_owned();
2866 debug_assert!(false, "{}", &message);
2867 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![range])));
2868 };
2869 let artifact_id = exec_state.next_artifact_id();
2870 exec_state.add_artifact(Artifact::SketchBlockConstraint(SketchBlockConstraint {
2871 id: artifact_id,
2872 sketch_id,
2873 constraint_id,
2874 constraint_type: SketchBlockConstraintType::from(&constraint),
2875 code_ref: CodeRef::placeholder(range),
2876 }));
2877 exec_state.add_scene_object(
2878 Object {
2879 id: constraint_id,
2880 kind: ObjectKind::Constraint { constraint },
2881 label: Default::default(),
2882 comments: Default::default(),
2883 artifact_id,
2884 source: range.into(),
2885 },
2886 range,
2887 );
2888 }
2889 }
2890 SketchConstraintKind::VerticalDistance { points } => {
2891 let range = self.as_source_range();
2892 let p0 = &points[0];
2893 let p1 = &points[1];
2894 let solver_pt0 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2895 p0.vars.x.to_constraint_id(range)?,
2896 p0.vars.y.to_constraint_id(range)?,
2897 );
2898 let solver_pt1 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2899 p1.vars.x.to_constraint_id(range)?,
2900 p1.vars.y.to_constraint_id(range)?,
2901 );
2902 let solver_constraint = kcl_ezpz::Constraint::VerticalDistance(solver_pt1, solver_pt0, n.n);
2906
2907 #[cfg(feature = "artifact-graph")]
2908 let constraint_id = exec_state.next_object_id();
2909 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2910 let message =
2911 "Being inside a sketch block should have already been checked above".to_owned();
2912 debug_assert!(false, "{}", &message);
2913 return Err(internal_err(message, self));
2914 };
2915 sketch_block_state.solver_constraints.push(solver_constraint);
2916 #[cfg(feature = "artifact-graph")]
2917 {
2918 use crate::{
2919 execution::{Artifact, CodeRef, SketchBlockConstraint, SketchBlockConstraintType},
2920 front::Distance,
2921 };
2922
2923 let constraint = crate::front::Constraint::VerticalDistance(Distance {
2924 points: vec![p0.object_id, p1.object_id],
2925 distance: n.try_into().map_err(|_| {
2926 internal_err("Failed to convert distance units numeric suffix:", range)
2927 })?,
2928 source,
2929 });
2930 sketch_block_state.sketch_constraints.push(constraint_id);
2931 let Some(sketch_id) = sketch_block_state.sketch_id else {
2932 let message = "Sketch id missing for constraint artifact".to_owned();
2933 debug_assert!(false, "{}", &message);
2934 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![range])));
2935 };
2936 let artifact_id = exec_state.next_artifact_id();
2937 exec_state.add_artifact(Artifact::SketchBlockConstraint(SketchBlockConstraint {
2938 id: artifact_id,
2939 sketch_id,
2940 constraint_id,
2941 constraint_type: SketchBlockConstraintType::from(&constraint),
2942 code_ref: CodeRef::placeholder(range),
2943 }));
2944 exec_state.add_scene_object(
2945 Object {
2946 id: constraint_id,
2947 kind: ObjectKind::Constraint { constraint },
2948 label: Default::default(),
2949 comments: Default::default(),
2950 artifact_id,
2951 source: range.into(),
2952 },
2953 range,
2954 );
2955 }
2956 }
2957 }
2958 return Ok(KclValue::Bool { value: true, meta });
2959 }
2960 _ => {
2961 return Err(KclError::new_semantic(KclErrorDetails::new(
2962 format!(
2963 "Cannot create an equivalence constraint between values of these types: {} and {}",
2964 left_value.human_friendly_type(),
2965 right_value.human_friendly_type()
2966 ),
2967 vec![self.into()],
2968 )));
2969 }
2970 }
2971 }
2972
2973 let left = number_as_f64(&left_value, self.left.clone().into())?;
2974 let right = number_as_f64(&right_value, self.right.clone().into())?;
2975
2976 let value = match self.operator {
2977 BinaryOperator::Add => {
2978 let (l, r, ty) = NumericType::combine_eq_coerce(left, right, None);
2979 self.warn_on_unknown(&ty, "Adding", exec_state);
2980 KclValue::Number { value: l + r, meta, ty }
2981 }
2982 BinaryOperator::Sub => {
2983 let (l, r, ty) = NumericType::combine_eq_coerce(left, right, None);
2984 self.warn_on_unknown(&ty, "Subtracting", exec_state);
2985 KclValue::Number { value: l - r, meta, ty }
2986 }
2987 BinaryOperator::Mul => {
2988 let (l, r, ty) = NumericType::combine_mul(left, right);
2989 self.warn_on_unknown(&ty, "Multiplying", exec_state);
2990 KclValue::Number { value: l * r, meta, ty }
2991 }
2992 BinaryOperator::Div => {
2993 let (l, r, ty) = NumericType::combine_div(left, right);
2994 self.warn_on_unknown(&ty, "Dividing", exec_state);
2995 KclValue::Number { value: l / r, meta, ty }
2996 }
2997 BinaryOperator::Mod => {
2998 let (l, r, ty) = NumericType::combine_mod(left, right);
2999 self.warn_on_unknown(&ty, "Modulo of", exec_state);
3000 KclValue::Number { value: l % r, meta, ty }
3001 }
3002 BinaryOperator::Pow => KclValue::Number {
3003 value: left.n.powf(right.n),
3004 meta,
3005 ty: exec_state.current_default_units(),
3006 },
3007 BinaryOperator::Neq => {
3008 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
3009 self.warn_on_unknown(&ty, "Comparing", exec_state);
3010 KclValue::Bool { value: l != r, meta }
3011 }
3012 BinaryOperator::Gt => {
3013 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
3014 self.warn_on_unknown(&ty, "Comparing", exec_state);
3015 KclValue::Bool { value: l > r, meta }
3016 }
3017 BinaryOperator::Gte => {
3018 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
3019 self.warn_on_unknown(&ty, "Comparing", exec_state);
3020 KclValue::Bool { value: l >= r, meta }
3021 }
3022 BinaryOperator::Lt => {
3023 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
3024 self.warn_on_unknown(&ty, "Comparing", exec_state);
3025 KclValue::Bool { value: l < r, meta }
3026 }
3027 BinaryOperator::Lte => {
3028 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
3029 self.warn_on_unknown(&ty, "Comparing", exec_state);
3030 KclValue::Bool { value: l <= r, meta }
3031 }
3032 BinaryOperator::Eq => {
3033 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
3034 self.warn_on_unknown(&ty, "Comparing", exec_state);
3035 KclValue::Bool { value: l == r, meta }
3036 }
3037 BinaryOperator::And | BinaryOperator::Or => unreachable!(),
3038 };
3039
3040 Ok(value)
3041 }
3042
3043 fn missing_result_error(node: &Node<BinaryExpression>) -> KclError {
3044 internal_err("missing result while evaluating binary expression", node)
3045 }
3046
3047 fn warn_on_unknown(&self, ty: &NumericType, verb: &str, exec_state: &mut ExecState) {
3048 if ty == &NumericType::Unknown {
3049 let sr = self.as_source_range();
3050 exec_state.clear_units_warnings(&sr);
3051 let mut err = CompilationError::err(
3052 sr,
3053 format!(
3054 "{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)`."
3055 ),
3056 );
3057 err.tag = crate::errors::Tag::UnknownNumericUnits;
3058 exec_state.warn(err, annotations::WARN_UNKNOWN_UNITS);
3059 }
3060 }
3061}
3062
3063impl Node<UnaryExpression> {
3064 pub(super) async fn get_result(
3065 &self,
3066 exec_state: &mut ExecState,
3067 ctx: &ExecutorContext,
3068 ) -> Result<KclValueControlFlow, KclError> {
3069 match self.operator {
3070 UnaryOperator::Not => {
3071 let value = self.argument.get_result(exec_state, ctx).await?;
3072 let value = control_continue!(value);
3073 let KclValue::Bool {
3074 value: bool_value,
3075 meta: _,
3076 } = value
3077 else {
3078 return Err(KclError::new_semantic(KclErrorDetails::new(
3079 format!(
3080 "Cannot apply unary operator ! to non-boolean value: {}",
3081 value.human_friendly_type()
3082 ),
3083 vec![self.into()],
3084 )));
3085 };
3086 let meta = vec![Metadata {
3087 source_range: self.into(),
3088 }];
3089 let negated = KclValue::Bool {
3090 value: !bool_value,
3091 meta,
3092 };
3093
3094 Ok(negated.continue_())
3095 }
3096 UnaryOperator::Neg => {
3097 let value = self.argument.get_result(exec_state, ctx).await?;
3098 let value = control_continue!(value);
3099 let err = || {
3100 KclError::new_semantic(KclErrorDetails::new(
3101 format!(
3102 "You can only negate numbers, planes, or lines, but this is a {}",
3103 value.human_friendly_type()
3104 ),
3105 vec![self.into()],
3106 ))
3107 };
3108 match &value {
3109 KclValue::Number { value, ty, .. } => {
3110 let meta = vec![Metadata {
3111 source_range: self.into(),
3112 }];
3113 Ok(KclValue::Number {
3114 value: -value,
3115 meta,
3116 ty: *ty,
3117 }
3118 .continue_())
3119 }
3120 KclValue::Plane { value } => {
3121 let mut plane = value.clone();
3122 if plane.info.x_axis.x != 0.0 {
3123 plane.info.x_axis.x *= -1.0;
3124 }
3125 if plane.info.x_axis.y != 0.0 {
3126 plane.info.x_axis.y *= -1.0;
3127 }
3128 if plane.info.x_axis.z != 0.0 {
3129 plane.info.x_axis.z *= -1.0;
3130 }
3131
3132 plane.id = exec_state.next_uuid();
3133 plane.object_id = None;
3134 Ok(KclValue::Plane { value: plane }.continue_())
3135 }
3136 KclValue::Object {
3137 value: values, meta, ..
3138 } => {
3139 let Some(direction) = values.get("direction") else {
3141 return Err(err());
3142 };
3143
3144 let direction = match direction {
3145 KclValue::Tuple { value: values, meta } => {
3146 let values = values
3147 .iter()
3148 .map(|v| match v {
3149 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
3150 value: *value * -1.0,
3151 ty: *ty,
3152 meta: meta.clone(),
3153 }),
3154 _ => Err(err()),
3155 })
3156 .collect::<Result<Vec<_>, _>>()?;
3157
3158 KclValue::Tuple {
3159 value: values,
3160 meta: meta.clone(),
3161 }
3162 }
3163 KclValue::HomArray {
3164 value: values,
3165 ty: ty @ RuntimeType::Primitive(PrimitiveType::Number(_)),
3166 } => {
3167 let values = values
3168 .iter()
3169 .map(|v| match v {
3170 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
3171 value: *value * -1.0,
3172 ty: *ty,
3173 meta: meta.clone(),
3174 }),
3175 _ => Err(err()),
3176 })
3177 .collect::<Result<Vec<_>, _>>()?;
3178
3179 KclValue::HomArray {
3180 value: values,
3181 ty: ty.clone(),
3182 }
3183 }
3184 _ => return Err(err()),
3185 };
3186
3187 let mut value = values.clone();
3188 value.insert("direction".to_owned(), direction);
3189 Ok(KclValue::Object {
3190 value,
3191 meta: meta.clone(),
3192 constrainable: false,
3193 }
3194 .continue_())
3195 }
3196 _ => Err(err()),
3197 }
3198 }
3199 UnaryOperator::Plus => {
3200 let operand = self.argument.get_result(exec_state, ctx).await?;
3201 let operand = control_continue!(operand);
3202 match operand {
3203 KclValue::Number { .. } | KclValue::Plane { .. } => Ok(operand.continue_()),
3204 _ => Err(KclError::new_semantic(KclErrorDetails::new(
3205 format!(
3206 "You can only apply unary + to numbers or planes, but this is a {}",
3207 operand.human_friendly_type()
3208 ),
3209 vec![self.into()],
3210 ))),
3211 }
3212 }
3213 }
3214 }
3215}
3216
3217pub(crate) async fn execute_pipe_body(
3218 exec_state: &mut ExecState,
3219 body: &[Expr],
3220 source_range: SourceRange,
3221 ctx: &ExecutorContext,
3222) -> Result<KclValueControlFlow, KclError> {
3223 let Some((first, body)) = body.split_first() else {
3224 return Err(KclError::new_semantic(KclErrorDetails::new(
3225 "Pipe expressions cannot be empty".to_owned(),
3226 vec![source_range],
3227 )));
3228 };
3229 let meta = Metadata {
3234 source_range: SourceRange::from(first),
3235 };
3236 let output = ctx
3237 .execute_expr(first, exec_state, &meta, &[], StatementKind::Expression)
3238 .await?;
3239 let output = control_continue!(output);
3240
3241 let previous_pipe_value = exec_state.mod_local.pipe_value.replace(output);
3245 let result = inner_execute_pipe_body(exec_state, body, ctx).await;
3247 exec_state.mod_local.pipe_value = previous_pipe_value;
3249
3250 result
3251}
3252
3253#[async_recursion]
3256async fn inner_execute_pipe_body(
3257 exec_state: &mut ExecState,
3258 body: &[Expr],
3259 ctx: &ExecutorContext,
3260) -> Result<KclValueControlFlow, KclError> {
3261 for expression in body {
3262 if let Expr::TagDeclarator(_) = expression {
3263 return Err(KclError::new_semantic(KclErrorDetails::new(
3264 format!("This cannot be in a PipeExpression: {expression:?}"),
3265 vec![expression.into()],
3266 )));
3267 }
3268 let metadata = Metadata {
3269 source_range: SourceRange::from(expression),
3270 };
3271 let output = ctx
3272 .execute_expr(expression, exec_state, &metadata, &[], StatementKind::Expression)
3273 .await?;
3274 let output = control_continue!(output);
3275 exec_state.mod_local.pipe_value = Some(output);
3276 }
3277 let final_output = exec_state.mod_local.pipe_value.take().unwrap();
3279 Ok(final_output.continue_())
3280}
3281
3282impl Node<TagDeclarator> {
3283 pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
3284 let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
3285 value: self.name.clone(),
3286 info: Vec::new(),
3287 meta: vec![Metadata {
3288 source_range: self.into(),
3289 }],
3290 }));
3291
3292 exec_state
3293 .mut_stack()
3294 .add(self.name.clone(), memory_item, self.into())?;
3295
3296 Ok(self.into())
3297 }
3298}
3299
3300impl Node<ArrayExpression> {
3301 #[async_recursion]
3302 pub(super) async fn execute(
3303 &self,
3304 exec_state: &mut ExecState,
3305 ctx: &ExecutorContext,
3306 ) -> Result<KclValueControlFlow, KclError> {
3307 let mut results = Vec::with_capacity(self.elements.len());
3308
3309 for element in &self.elements {
3310 let metadata = Metadata::from(element);
3311 let value = ctx
3314 .execute_expr(element, exec_state, &metadata, &[], StatementKind::Expression)
3315 .await?;
3316 let value = control_continue!(value);
3317
3318 results.push(value);
3319 }
3320
3321 Ok(KclValue::HomArray {
3322 value: results,
3323 ty: RuntimeType::Primitive(PrimitiveType::Any),
3324 }
3325 .continue_())
3326 }
3327}
3328
3329impl Node<ArrayRangeExpression> {
3330 #[async_recursion]
3331 pub(super) async fn execute(
3332 &self,
3333 exec_state: &mut ExecState,
3334 ctx: &ExecutorContext,
3335 ) -> Result<KclValueControlFlow, KclError> {
3336 let metadata = Metadata::from(&self.start_element);
3337 let start_val = ctx
3338 .execute_expr(
3339 &self.start_element,
3340 exec_state,
3341 &metadata,
3342 &[],
3343 StatementKind::Expression,
3344 )
3345 .await?;
3346 let start_val = control_continue!(start_val);
3347 let start = start_val
3348 .as_ty_f64()
3349 .ok_or(KclError::new_semantic(KclErrorDetails::new(
3350 format!(
3351 "Expected number for range start but found {}",
3352 start_val.human_friendly_type()
3353 ),
3354 vec![self.into()],
3355 )))?;
3356 let metadata = Metadata::from(&self.end_element);
3357 let end_val = ctx
3358 .execute_expr(&self.end_element, exec_state, &metadata, &[], StatementKind::Expression)
3359 .await?;
3360 let end_val = control_continue!(end_val);
3361 let end = end_val.as_ty_f64().ok_or(KclError::new_semantic(KclErrorDetails::new(
3362 format!(
3363 "Expected number for range end but found {}",
3364 end_val.human_friendly_type()
3365 ),
3366 vec![self.into()],
3367 )))?;
3368
3369 let (start, end, ty) = NumericType::combine_range(start, end, exec_state, self.as_source_range())?;
3370 let Some(start) = crate::try_f64_to_i64(start) else {
3371 return Err(KclError::new_semantic(KclErrorDetails::new(
3372 format!("Range start must be an integer, but found {start}"),
3373 vec![self.into()],
3374 )));
3375 };
3376 let Some(end) = crate::try_f64_to_i64(end) else {
3377 return Err(KclError::new_semantic(KclErrorDetails::new(
3378 format!("Range end must be an integer, but found {end}"),
3379 vec![self.into()],
3380 )));
3381 };
3382
3383 if end < start {
3384 return Err(KclError::new_semantic(KclErrorDetails::new(
3385 format!("Range start is greater than range end: {start} .. {end}"),
3386 vec![self.into()],
3387 )));
3388 }
3389
3390 let range: Vec<_> = if self.end_inclusive {
3391 (start..=end).collect()
3392 } else {
3393 (start..end).collect()
3394 };
3395
3396 let meta = vec![Metadata {
3397 source_range: self.into(),
3398 }];
3399
3400 Ok(KclValue::HomArray {
3401 value: range
3402 .into_iter()
3403 .map(|num| KclValue::Number {
3404 value: num as f64,
3405 ty,
3406 meta: meta.clone(),
3407 })
3408 .collect(),
3409 ty: RuntimeType::Primitive(PrimitiveType::Number(ty)),
3410 }
3411 .continue_())
3412 }
3413}
3414
3415impl Node<ObjectExpression> {
3416 #[async_recursion]
3417 pub(super) async fn execute(
3418 &self,
3419 exec_state: &mut ExecState,
3420 ctx: &ExecutorContext,
3421 ) -> Result<KclValueControlFlow, KclError> {
3422 let mut object = HashMap::with_capacity(self.properties.len());
3423 for property in &self.properties {
3424 let metadata = Metadata::from(&property.value);
3425 let result = ctx
3426 .execute_expr(&property.value, exec_state, &metadata, &[], StatementKind::Expression)
3427 .await?;
3428 let result = control_continue!(result);
3429 object.insert(property.key.name.clone(), result);
3430 }
3431
3432 Ok(KclValue::Object {
3433 value: object,
3434 meta: vec![Metadata {
3435 source_range: self.into(),
3436 }],
3437 constrainable: false,
3438 }
3439 .continue_())
3440 }
3441}
3442
3443fn article_for<S: AsRef<str>>(s: S) -> &'static str {
3444 if s.as_ref().starts_with(['a', 'e', 'i', 'o', 'u', '[']) {
3446 "an"
3447 } else {
3448 "a"
3449 }
3450}
3451
3452fn number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<TyF64, KclError> {
3453 v.as_ty_f64().ok_or_else(|| {
3454 let actual_type = v.human_friendly_type();
3455 KclError::new_semantic(KclErrorDetails::new(
3456 format!("Expected a number, but found {actual_type}",),
3457 vec![source_range],
3458 ))
3459 })
3460}
3461
3462impl Node<IfExpression> {
3463 #[async_recursion]
3464 pub(super) async fn get_result(
3465 &self,
3466 exec_state: &mut ExecState,
3467 ctx: &ExecutorContext,
3468 ) -> Result<KclValueControlFlow, KclError> {
3469 let cond_value = ctx
3471 .execute_expr(
3472 &self.cond,
3473 exec_state,
3474 &Metadata::from(self),
3475 &[],
3476 StatementKind::Expression,
3477 )
3478 .await?;
3479 let cond_value = control_continue!(cond_value);
3480 if cond_value.get_bool()? {
3481 let block_result = ctx.exec_block(&*self.then_val, exec_state, BodyType::Block).await?;
3482 return Ok(block_result.unwrap());
3486 }
3487
3488 for else_if in &self.else_ifs {
3490 let cond_value = ctx
3491 .execute_expr(
3492 &else_if.cond,
3493 exec_state,
3494 &Metadata::from(self),
3495 &[],
3496 StatementKind::Expression,
3497 )
3498 .await?;
3499 let cond_value = control_continue!(cond_value);
3500 if cond_value.get_bool()? {
3501 let block_result = ctx.exec_block(&*else_if.then_val, exec_state, BodyType::Block).await?;
3502 return Ok(block_result.unwrap());
3506 }
3507 }
3508
3509 ctx.exec_block(&*self.final_else, exec_state, BodyType::Block)
3511 .await
3512 .map(|expr| expr.unwrap())
3513 }
3514}
3515
3516#[derive(Debug)]
3517enum Property {
3518 UInt(usize),
3519 String(String),
3520}
3521
3522impl Property {
3523 #[allow(clippy::too_many_arguments)]
3524 async fn try_from<'a>(
3525 computed: bool,
3526 value: Expr,
3527 exec_state: &mut ExecState,
3528 sr: SourceRange,
3529 ctx: &ExecutorContext,
3530 metadata: &Metadata,
3531 annotations: &[Node<Annotation>],
3532 statement_kind: StatementKind<'a>,
3533 ) -> Result<Self, KclError> {
3534 let property_sr = vec![sr];
3535 if !computed {
3536 let Expr::Name(identifier) = value else {
3537 return Err(KclError::new_semantic(KclErrorDetails::new(
3539 "Object expressions like `obj.property` must use simple identifier names, not complex expressions"
3540 .to_owned(),
3541 property_sr,
3542 )));
3543 };
3544 return Ok(Property::String(identifier.to_string()));
3545 }
3546
3547 let prop_value = ctx
3548 .execute_expr(&value, exec_state, metadata, annotations, statement_kind)
3549 .await?;
3550 let prop_value = match prop_value.control {
3551 ControlFlowKind::Continue => prop_value.into_value(),
3552 ControlFlowKind::Exit => {
3553 let message = "Early return inside array brackets is currently not supported".to_owned();
3554 debug_assert!(false, "{}", &message);
3555 return Err(internal_err(message, sr));
3556 }
3557 };
3558 match prop_value {
3559 KclValue::Number { value, ty, meta: _ } => {
3560 if !matches!(
3561 ty,
3562 NumericType::Unknown
3563 | NumericType::Default { .. }
3564 | NumericType::Known(crate::exec::UnitType::Count)
3565 ) {
3566 return Err(KclError::new_semantic(KclErrorDetails::new(
3567 format!(
3568 "{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"
3569 ),
3570 property_sr,
3571 )));
3572 }
3573 if let Some(x) = crate::try_f64_to_usize(value) {
3574 Ok(Property::UInt(x))
3575 } else {
3576 Err(KclError::new_semantic(KclErrorDetails::new(
3577 format!("{value} is not a valid index, indices must be whole numbers >= 0"),
3578 property_sr,
3579 )))
3580 }
3581 }
3582 _ => Err(KclError::new_semantic(KclErrorDetails::new(
3583 "Only numbers (>= 0) can be indexes".to_owned(),
3584 vec![sr],
3585 ))),
3586 }
3587 }
3588}
3589
3590impl Property {
3591 fn type_name(&self) -> &'static str {
3592 match self {
3593 Property::UInt(_) => "number",
3594 Property::String(_) => "string",
3595 }
3596 }
3597}
3598
3599impl Node<PipeExpression> {
3600 #[async_recursion]
3601 pub(super) async fn get_result(
3602 &self,
3603 exec_state: &mut ExecState,
3604 ctx: &ExecutorContext,
3605 ) -> Result<KclValueControlFlow, KclError> {
3606 execute_pipe_body(exec_state, &self.body, self.into(), ctx).await
3607 }
3608}
3609
3610#[cfg(test)]
3611mod test {
3612 use std::sync::Arc;
3613
3614 use tokio::io::AsyncWriteExt;
3615
3616 use super::*;
3617 use crate::{
3618 ExecutorSettings,
3619 errors::Severity,
3620 exec::UnitType,
3621 execution::{ContextType, parse_execute},
3622 };
3623
3624 #[tokio::test(flavor = "multi_thread")]
3625 async fn ascription() {
3626 let program = r#"
3627a = 42: number
3628b = a: number
3629p = {
3630 origin = { x = 0, y = 0, z = 0 },
3631 xAxis = { x = 1, y = 0, z = 0 },
3632 yAxis = { x = 0, y = 1, z = 0 },
3633 zAxis = { x = 0, y = 0, z = 1 }
3634}: Plane
3635arr1 = [42]: [number(cm)]
3636"#;
3637
3638 let result = parse_execute(program).await.unwrap();
3639 let mem = result.exec_state.stack();
3640 assert!(matches!(
3641 mem.memory
3642 .get_from("p", result.mem_env, SourceRange::default(), 0)
3643 .unwrap(),
3644 KclValue::Plane { .. }
3645 ));
3646 let arr1 = mem
3647 .memory
3648 .get_from("arr1", result.mem_env, SourceRange::default(), 0)
3649 .unwrap();
3650 if let KclValue::HomArray { value, ty } = arr1 {
3651 assert_eq!(value.len(), 1, "Expected Vec with specific length: found {value:?}");
3652 assert_eq!(
3653 *ty,
3654 RuntimeType::known_length(kittycad_modeling_cmds::units::UnitLength::Centimeters)
3655 );
3656 if let KclValue::Number { value, ty, .. } = &value[0] {
3658 assert_eq!(*value, 42.0);
3660 assert_eq!(
3661 *ty,
3662 NumericType::Known(UnitType::Length(kittycad_modeling_cmds::units::UnitLength::Centimeters))
3663 );
3664 } else {
3665 panic!("Expected a number; found {:?}", value[0]);
3666 }
3667 } else {
3668 panic!("Expected HomArray; found {arr1:?}");
3669 }
3670
3671 let program = r#"
3672a = 42: string
3673"#;
3674 let result = parse_execute(program).await;
3675 let err = result.unwrap_err();
3676 assert!(
3677 err.to_string()
3678 .contains("could not coerce a number (with type `number`) to type `string`"),
3679 "Expected error but found {err:?}"
3680 );
3681
3682 let program = r#"
3683a = 42: Plane
3684"#;
3685 let result = parse_execute(program).await;
3686 let err = result.unwrap_err();
3687 assert!(
3688 err.to_string()
3689 .contains("could not coerce a number (with type `number`) to type `Plane`"),
3690 "Expected error but found {err:?}"
3691 );
3692
3693 let program = r#"
3694arr = [0]: [string]
3695"#;
3696 let result = parse_execute(program).await;
3697 let err = result.unwrap_err();
3698 assert!(
3699 err.to_string().contains(
3700 "could not coerce an array of `number` with 1 value (with type `[any; 1]`) to type `[string]`"
3701 ),
3702 "Expected error but found {err:?}"
3703 );
3704
3705 let program = r#"
3706mixedArr = [0, "a"]: [number(mm)]
3707"#;
3708 let result = parse_execute(program).await;
3709 let err = result.unwrap_err();
3710 assert!(
3711 err.to_string().contains(
3712 "could not coerce an array of `number`, `string` (with type `[any; 2]`) to type `[number(mm)]`"
3713 ),
3714 "Expected error but found {err:?}"
3715 );
3716
3717 let program = r#"
3718mixedArr = [0, "a"]: [mm]
3719"#;
3720 let result = parse_execute(program).await;
3721 let err = result.unwrap_err();
3722 assert!(
3723 err.to_string().contains(
3724 "could not coerce an array of `number`, `string` (with type `[any; 2]`) to type `[number(mm)]`"
3725 ),
3726 "Expected error but found {err:?}"
3727 );
3728 }
3729
3730 #[tokio::test(flavor = "multi_thread")]
3731 async fn neg_plane() {
3732 let program = r#"
3733p = {
3734 origin = { x = 0, y = 0, z = 0 },
3735 xAxis = { x = 1, y = 0, z = 0 },
3736 yAxis = { x = 0, y = 1, z = 0 },
3737}: Plane
3738p2 = -p
3739"#;
3740
3741 let result = parse_execute(program).await.unwrap();
3742 let mem = result.exec_state.stack();
3743 match mem
3744 .memory
3745 .get_from("p2", result.mem_env, SourceRange::default(), 0)
3746 .unwrap()
3747 {
3748 KclValue::Plane { value } => {
3749 assert_eq!(value.info.x_axis.x, -1.0);
3750 assert_eq!(value.info.x_axis.y, 0.0);
3751 assert_eq!(value.info.x_axis.z, 0.0);
3752 }
3753 _ => unreachable!(),
3754 }
3755 }
3756
3757 #[tokio::test(flavor = "multi_thread")]
3758 async fn multiple_returns() {
3759 let program = r#"fn foo() {
3760 return 0
3761 return 42
3762}
3763
3764a = foo()
3765"#;
3766
3767 let result = parse_execute(program).await;
3768 assert!(result.unwrap_err().to_string().contains("return"));
3769 }
3770
3771 #[tokio::test(flavor = "multi_thread")]
3772 async fn load_all_modules() {
3773 let program_a_kcl = r#"
3775export a = 1
3776"#;
3777 let program_b_kcl = r#"
3779import a from 'a.kcl'
3780
3781export b = a + 1
3782"#;
3783 let program_c_kcl = r#"
3785import a from 'a.kcl'
3786
3787export c = a + 2
3788"#;
3789
3790 let main_kcl = r#"
3792import b from 'b.kcl'
3793import c from 'c.kcl'
3794
3795d = b + c
3796"#;
3797
3798 let main = crate::parsing::parse_str(main_kcl, ModuleId::default())
3799 .parse_errs_as_err()
3800 .unwrap();
3801
3802 let tmpdir = tempfile::TempDir::with_prefix("zma_kcl_load_all_modules").unwrap();
3803
3804 tokio::fs::File::create(tmpdir.path().join("main.kcl"))
3805 .await
3806 .unwrap()
3807 .write_all(main_kcl.as_bytes())
3808 .await
3809 .unwrap();
3810
3811 tokio::fs::File::create(tmpdir.path().join("a.kcl"))
3812 .await
3813 .unwrap()
3814 .write_all(program_a_kcl.as_bytes())
3815 .await
3816 .unwrap();
3817
3818 tokio::fs::File::create(tmpdir.path().join("b.kcl"))
3819 .await
3820 .unwrap()
3821 .write_all(program_b_kcl.as_bytes())
3822 .await
3823 .unwrap();
3824
3825 tokio::fs::File::create(tmpdir.path().join("c.kcl"))
3826 .await
3827 .unwrap()
3828 .write_all(program_c_kcl.as_bytes())
3829 .await
3830 .unwrap();
3831
3832 let exec_ctxt = ExecutorContext {
3833 engine: Arc::new(Box::new(
3834 crate::engine::conn_mock::EngineConnection::new()
3835 .map_err(|err| {
3836 internal_err(
3837 format!("Failed to create mock engine connection: {err}"),
3838 SourceRange::default(),
3839 )
3840 })
3841 .unwrap(),
3842 )),
3843 fs: Arc::new(crate::fs::FileManager::new()),
3844 settings: ExecutorSettings {
3845 project_directory: Some(crate::TypedPath(tmpdir.path().into())),
3846 ..Default::default()
3847 },
3848 context_type: ContextType::Mock,
3849 };
3850 let mut exec_state = ExecState::new(&exec_ctxt);
3851
3852 exec_ctxt
3853 .run(
3854 &crate::Program {
3855 ast: main.clone(),
3856 original_file_contents: "".to_owned(),
3857 },
3858 &mut exec_state,
3859 )
3860 .await
3861 .unwrap();
3862 }
3863
3864 #[tokio::test(flavor = "multi_thread")]
3865 async fn user_coercion() {
3866 let program = r#"fn foo(x: Axis2d) {
3867 return 0
3868}
3869
3870foo(x = { direction = [0, 0], origin = [0, 0]})
3871"#;
3872
3873 parse_execute(program).await.unwrap();
3874
3875 let program = r#"fn foo(x: Axis3d) {
3876 return 0
3877}
3878
3879foo(x = { direction = [0, 0], origin = [0, 0]})
3880"#;
3881
3882 parse_execute(program).await.unwrap_err();
3883 }
3884
3885 #[tokio::test(flavor = "multi_thread")]
3886 async fn coerce_return() {
3887 let program = r#"fn foo(): number(mm) {
3888 return 42
3889}
3890
3891a = foo()
3892"#;
3893
3894 parse_execute(program).await.unwrap();
3895
3896 let program = r#"fn foo(): mm {
3897 return 42
3898}
3899
3900a = foo()
3901"#;
3902
3903 parse_execute(program).await.unwrap();
3904
3905 let program = r#"fn foo(): number(mm) {
3906 return { bar: 42 }
3907}
3908
3909a = foo()
3910"#;
3911
3912 parse_execute(program).await.unwrap_err();
3913
3914 let program = r#"fn foo(): mm {
3915 return { bar: 42 }
3916}
3917
3918a = foo()
3919"#;
3920
3921 parse_execute(program).await.unwrap_err();
3922 }
3923
3924 #[tokio::test(flavor = "multi_thread")]
3925 async fn test_sensible_error_when_missing_equals_in_kwarg() {
3926 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)"]
3927 .into_iter()
3928 .enumerate()
3929 {
3930 let program = format!(
3931 "fn foo() {{ return 0 }}
3932z = 0
3933fn f(x, y, z) {{ return 0 }}
3934{call}"
3935 );
3936 let err = parse_execute(&program).await.unwrap_err();
3937 let msg = err.message();
3938 assert!(
3939 msg.contains("This argument needs a label, but it doesn't have one"),
3940 "failed test {i}: {msg}"
3941 );
3942 assert!(msg.contains("`y`"), "failed test {i}, missing `y`: {msg}");
3943 if i == 0 {
3944 assert!(msg.contains("`z`"), "failed test {i}, missing `z`: {msg}");
3945 }
3946 }
3947 }
3948
3949 #[tokio::test(flavor = "multi_thread")]
3950 async fn default_param_for_unlabeled() {
3951 let ast = r#"fn myExtrude(@sk, length) {
3954 return extrude(sk, length)
3955}
3956sketch001 = startSketchOn(XY)
3957 |> circle(center = [0, 0], radius = 93.75)
3958 |> myExtrude(length = 40)
3959"#;
3960
3961 parse_execute(ast).await.unwrap();
3962 }
3963
3964 #[tokio::test(flavor = "multi_thread")]
3965 async fn dont_use_unlabelled_as_input() {
3966 let ast = r#"length = 10
3968startSketchOn(XY)
3969 |> circle(center = [0, 0], radius = 93.75)
3970 |> extrude(length)
3971"#;
3972
3973 parse_execute(ast).await.unwrap();
3974 }
3975
3976 #[tokio::test(flavor = "multi_thread")]
3977 async fn ascription_in_binop() {
3978 let ast = r#"foo = tan(0): number(rad) - 4deg"#;
3979 parse_execute(ast).await.unwrap();
3980
3981 let ast = r#"foo = tan(0): rad - 4deg"#;
3982 parse_execute(ast).await.unwrap();
3983 }
3984
3985 #[tokio::test(flavor = "multi_thread")]
3986 async fn neg_sqrt() {
3987 let ast = r#"bad = sqrt(-2)"#;
3988
3989 let e = parse_execute(ast).await.unwrap_err();
3990 assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
3992 }
3993
3994 #[tokio::test(flavor = "multi_thread")]
3995 async fn non_array_fns() {
3996 let ast = r#"push(1, item = 2)
3997pop(1)
3998map(1, f = fn(@x) { return x + 1 })
3999reduce(1, f = fn(@x, accum) { return accum + x}, initial = 0)"#;
4000
4001 parse_execute(ast).await.unwrap();
4002 }
4003
4004 #[tokio::test(flavor = "multi_thread")]
4005 async fn non_array_indexing() {
4006 let good = r#"a = 42
4007good = a[0]
4008"#;
4009 let result = parse_execute(good).await.unwrap();
4010 let mem = result.exec_state.stack();
4011 let num = mem
4012 .memory
4013 .get_from("good", result.mem_env, SourceRange::default(), 0)
4014 .unwrap()
4015 .as_ty_f64()
4016 .unwrap();
4017 assert_eq!(num.n, 42.0);
4018
4019 let bad = r#"a = 42
4020bad = a[1]
4021"#;
4022
4023 parse_execute(bad).await.unwrap_err();
4024 }
4025
4026 #[tokio::test(flavor = "multi_thread")]
4027 async fn coerce_unknown_to_length() {
4028 let ast = r#"x = 2mm * 2mm
4029y = x: number(Length)"#;
4030 let e = parse_execute(ast).await.unwrap_err();
4031 assert!(
4032 e.message().contains("could not coerce"),
4033 "Error message: '{}'",
4034 e.message()
4035 );
4036
4037 let ast = r#"x = 2mm
4038y = x: number(Length)"#;
4039 let result = parse_execute(ast).await.unwrap();
4040 let mem = result.exec_state.stack();
4041 let num = mem
4042 .memory
4043 .get_from("y", result.mem_env, SourceRange::default(), 0)
4044 .unwrap()
4045 .as_ty_f64()
4046 .unwrap();
4047 assert_eq!(num.n, 2.0);
4048 assert_eq!(num.ty, NumericType::mm());
4049 }
4050
4051 #[tokio::test(flavor = "multi_thread")]
4052 async fn one_warning_unknown() {
4053 let ast = r#"
4054// Should warn once
4055a = PI * 2
4056// Should warn once
4057b = (PI * 2) / 3
4058// Should not warn
4059c = ((PI * 2) / 3): number(deg)
4060"#;
4061
4062 let result = parse_execute(ast).await.unwrap();
4063 assert_eq!(result.exec_state.errors().len(), 2);
4064 }
4065
4066 #[tokio::test(flavor = "multi_thread")]
4067 async fn non_count_indexing() {
4068 let ast = r#"x = [0, 0]
4069y = x[1mm]
4070"#;
4071 parse_execute(ast).await.unwrap_err();
4072
4073 let ast = r#"x = [0, 0]
4074y = 1deg
4075z = x[y]
4076"#;
4077 parse_execute(ast).await.unwrap_err();
4078
4079 let ast = r#"x = [0, 0]
4080y = x[0mm + 1]
4081"#;
4082 parse_execute(ast).await.unwrap_err();
4083 }
4084
4085 #[tokio::test(flavor = "multi_thread")]
4086 async fn getting_property_of_plane() {
4087 let ast = std::fs::read_to_string("tests/inputs/planestuff.kcl").unwrap();
4088 parse_execute(&ast).await.unwrap();
4089 }
4090
4091 #[cfg(feature = "artifact-graph")]
4092 #[tokio::test(flavor = "multi_thread")]
4093 async fn no_artifacts_from_within_hole_call() {
4094 let ast = std::fs::read_to_string("tests/inputs/sample_hole.kcl").unwrap();
4099 let out = parse_execute(&ast).await.unwrap();
4100
4101 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
4103
4104 let expected = 5;
4108 assert_eq!(
4109 actual_operations.len(),
4110 expected,
4111 "expected {expected} operations, received {}:\n{actual_operations:#?}",
4112 actual_operations.len(),
4113 );
4114 }
4115
4116 #[cfg(feature = "artifact-graph")]
4117 #[tokio::test(flavor = "multi_thread")]
4118 async fn feature_tree_annotation_on_user_defined_kcl() {
4119 let ast = std::fs::read_to_string("tests/inputs/feature_tree_annotation_on_user_defined_kcl.kcl").unwrap();
4122 let out = parse_execute(&ast).await.unwrap();
4123
4124 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
4126
4127 let expected = 0;
4128 assert_eq!(
4129 actual_operations.len(),
4130 expected,
4131 "expected {expected} operations, received {}:\n{actual_operations:#?}",
4132 actual_operations.len(),
4133 );
4134 }
4135
4136 #[cfg(feature = "artifact-graph")]
4137 #[tokio::test(flavor = "multi_thread")]
4138 async fn no_feature_tree_annotation_on_user_defined_kcl() {
4139 let ast = std::fs::read_to_string("tests/inputs/no_feature_tree_annotation_on_user_defined_kcl.kcl").unwrap();
4142 let out = parse_execute(&ast).await.unwrap();
4143
4144 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
4146
4147 let expected = 2;
4148 assert_eq!(
4149 actual_operations.len(),
4150 expected,
4151 "expected {expected} operations, received {}:\n{actual_operations:#?}",
4152 actual_operations.len(),
4153 );
4154 assert!(matches!(actual_operations[0], Operation::GroupBegin { .. }));
4155 assert!(matches!(actual_operations[1], Operation::GroupEnd));
4156 }
4157
4158 #[tokio::test(flavor = "multi_thread")]
4159 async fn custom_warning() {
4160 let warn = r#"
4161a = PI * 2
4162"#;
4163 let result = parse_execute(warn).await.unwrap();
4164 assert_eq!(result.exec_state.errors().len(), 1);
4165 assert_eq!(result.exec_state.errors()[0].severity, Severity::Warning);
4166
4167 let allow = r#"
4168@warnings(allow = unknownUnits)
4169a = PI * 2
4170"#;
4171 let result = parse_execute(allow).await.unwrap();
4172 assert_eq!(result.exec_state.errors().len(), 0);
4173
4174 let deny = r#"
4175@warnings(deny = [unknownUnits])
4176a = PI * 2
4177"#;
4178 let result = parse_execute(deny).await.unwrap();
4179 assert_eq!(result.exec_state.errors().len(), 1);
4180 assert_eq!(result.exec_state.errors()[0].severity, Severity::Error);
4181 }
4182
4183 #[tokio::test(flavor = "multi_thread")]
4184 async fn sketch_block_unqualified_functions_use_sketch2() {
4185 let ast = r#"
4186@settings(experimentalFeatures = allow)
4187s = sketch(on = XY) {
4188 line1 = line(start = [var 0mm, var 0mm], end = [var 1mm, var 0mm])
4189 line2 = line(start = [var 1mm, var 0mm], end = [var 1mm, var 1mm])
4190 coincident([line1.end, line2.start])
4191}
4192"#;
4193 let result = parse_execute(ast).await.unwrap();
4194 let mem = result.exec_state.stack();
4195 let sketch_value = mem
4196 .memory
4197 .get_from("s", result.mem_env, SourceRange::default(), 0)
4198 .unwrap();
4199
4200 let KclValue::Object { value, .. } = sketch_value else {
4201 panic!("Expected sketch block to return an object, got {sketch_value:?}");
4202 };
4203
4204 assert!(value.contains_key("line1"));
4205 assert!(value.contains_key("line2"));
4206 assert!(!value.contains_key("line"));
4209 assert!(!value.contains_key("coincident"));
4210 }
4211
4212 #[tokio::test(flavor = "multi_thread")]
4213 async fn cannot_solid_extrude_an_open_profile() {
4214 let code = std::fs::read_to_string("tests/inputs/cannot_solid_extrude_an_open_profile.kcl").unwrap();
4217 let program = crate::Program::parse_no_errs(&code).expect("should parse");
4218 let exec_ctxt = ExecutorContext::new_mock(None).await;
4219 let mut exec_state = ExecState::new(&exec_ctxt);
4220
4221 let err = exec_ctxt.run(&program, &mut exec_state).await.unwrap_err().error;
4222 assert!(matches!(err, KclError::Semantic { .. }));
4223 exec_ctxt.close().await;
4224 }
4225}