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