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