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, PreserveMem,
15 Segment, 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: PreserveMem,
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.normal() {
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.normal() {
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 = match preserve_mem {
178 PreserveMem::Always => exec_state.mut_stack().pop_and_preserve_env(),
179 PreserveMem::Normal => exec_state.mut_stack().pop_env(),
180 };
181 let module_artifacts = match preserve_mem {
182 PreserveMem::Always => std::mem::take(&mut exec_state.mod_local.artifacts),
183 PreserveMem::Normal => {
184 std::mem::swap(&mut exec_state.mod_local, &mut local_state);
185 local_state.artifacts
186 }
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, PreserveMem::Normal)
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, PreserveMem::Normal)
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: PreserveMem,
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 (mut closure, placeholder_env_ref) = 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 (
837 KclValue::Function {
838 value: Box::new(FunctionSource::rust(func, function_expression.clone(), props, attrs)),
839 meta: vec![metadata.to_owned()],
840 },
841 None,
842 )
843 } else {
844 return Err(KclError::new_semantic(KclErrorDetails::new(
845 "Rust implementation of functions is restricted to the standard library".to_owned(),
846 vec![metadata.source_range],
847 )));
848 }
849 } else {
850 let (env_ref, placeholder_env_ref) = if function_expression.name.is_some() {
854 let dummy = EnvironmentRef::dummy();
857 (dummy, Some(dummy))
858 } else {
859 (exec_state.mut_stack().snapshot(), None)
860 };
861 (
862 KclValue::Function {
863 value: Box::new(FunctionSource::kcl(
864 function_expression.clone(),
865 env_ref,
866 KclFunctionSourceParams {
867 is_std,
868 experimental,
869 include_in_feature_tree,
870 },
871 )),
872 meta: vec![metadata.to_owned()],
873 },
874 placeholder_env_ref,
875 )
876 };
877
878 if let Some(fn_name) = &function_expression.name {
881 if let Some(placeholder_env_ref) = placeholder_env_ref {
885 closure = exec_state.mut_stack().add_recursive_closure(
886 fn_name.name.to_owned(),
887 closure,
888 placeholder_env_ref,
889 metadata.source_range,
890 )?;
891 } else {
892 exec_state
894 .mut_stack()
895 .add(fn_name.name.clone(), closure.clone(), metadata.source_range)?;
896 }
897 }
898
899 closure.continue_()
900 }
901 Expr::CallExpressionKw(call_expression) => call_expression.execute(exec_state, self).await?,
902 Expr::PipeExpression(pipe_expression) => pipe_expression.get_result(exec_state, self).await?,
903 Expr::PipeSubstitution(pipe_substitution) => match statement_kind {
904 StatementKind::Declaration { name } => {
905 let message = format!(
906 "you cannot declare variable {name} as %, because % can only be used in function calls"
907 );
908
909 return Err(KclError::new_semantic(KclErrorDetails::new(
910 message,
911 vec![pipe_substitution.into()],
912 )));
913 }
914 StatementKind::Expression => match exec_state.mod_local.pipe_value.clone() {
915 Some(x) => x.continue_(),
916 None => {
917 return Err(KclError::new_semantic(KclErrorDetails::new(
918 "cannot use % outside a pipe expression".to_owned(),
919 vec![pipe_substitution.into()],
920 )));
921 }
922 },
923 },
924 Expr::ArrayExpression(array_expression) => array_expression.execute(exec_state, self).await?,
925 Expr::ArrayRangeExpression(range_expression) => range_expression.execute(exec_state, self).await?,
926 Expr::ObjectExpression(object_expression) => object_expression.execute(exec_state, self).await?,
927 Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state, self).await?,
928 Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
929 Expr::IfExpression(expr) => expr.get_result(exec_state, self).await?,
930 Expr::LabelledExpression(expr) => {
931 let value_cf = self
932 .execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
933 .await?;
934 let value = control_continue!(value_cf);
935 exec_state
936 .mut_stack()
937 .add(expr.label.name.clone(), value.clone(), init.into())?;
938 value.continue_()
940 }
941 Expr::AscribedExpression(expr) => expr.get_result(exec_state, self).await?,
942 Expr::SketchBlock(expr) => expr.get_result(exec_state, self).await?,
943 Expr::SketchVar(expr) => expr.get_result(exec_state, self).await?.continue_(),
944 };
945 Ok(item)
946 }
947}
948
949fn var_in_own_ref_err(e: KclError, being_declared: &Option<String>) -> KclError {
952 let KclError::UndefinedValue { name, mut details } = e else {
953 return e;
954 };
955 if let (Some(name0), Some(name1)) = (&being_declared, &name)
959 && name0 == name1
960 {
961 details.message = format!(
962 "You can't use `{name0}` because you're currently trying to define it. Use a different variable here instead."
963 );
964 }
965 KclError::UndefinedValue { details, name }
966}
967
968impl Node<AscribedExpression> {
969 #[async_recursion]
970 pub(super) async fn get_result(
971 &self,
972 exec_state: &mut ExecState,
973 ctx: &ExecutorContext,
974 ) -> Result<KclValueControlFlow, KclError> {
975 let metadata = Metadata {
976 source_range: SourceRange::from(self),
977 };
978 let result = ctx
979 .execute_expr(&self.expr, exec_state, &metadata, &[], StatementKind::Expression)
980 .await?;
981 let result = control_continue!(result);
982 apply_ascription(&result, &self.ty, exec_state, self.into()).map(KclValue::continue_)
983 }
984}
985
986impl Node<SketchBlock> {
987 pub(super) async fn get_result(
988 &self,
989 exec_state: &mut ExecState,
990 ctx: &ExecutorContext,
991 ) -> Result<KclValueControlFlow, KclError> {
992 if exec_state.mod_local.sketch_block.is_some() {
993 return Err(KclError::new_semantic(KclErrorDetails::new(
995 "Cannot execute a sketch block from within another sketch block".to_owned(),
996 vec![SourceRange::from(self)],
997 )));
998 }
999
1000 let range = SourceRange::from(self);
1001
1002 let mut labeled = IndexMap::new();
1004 for labeled_arg in &self.arguments {
1005 let source_range = SourceRange::from(labeled_arg.arg.clone());
1006 let metadata = Metadata { source_range };
1007 let value_cf = ctx
1008 .execute_expr(&labeled_arg.arg, exec_state, &metadata, &[], StatementKind::Expression)
1009 .await?;
1010 let value = control_continue!(value_cf);
1011 let arg = Arg::new(value, source_range);
1012 match &labeled_arg.label {
1013 Some(label) => {
1014 labeled.insert(label.name.clone(), arg);
1015 }
1016 None => {
1017 let name = labeled_arg.arg.ident_name();
1018 if let Some(name) = name {
1019 labeled.insert(name.to_owned(), arg);
1020 } else {
1021 return Err(KclError::new_semantic(KclErrorDetails::new(
1022 "Arguments to sketch blocks must be either labeled or simple identifiers".to_owned(),
1023 vec![SourceRange::from(&labeled_arg.arg)],
1024 )));
1025 }
1026 }
1027 }
1028 }
1029 let mut args = Args::new_no_args(range, ctx.clone(), Some("sketch block".to_owned()));
1030 args.labeled = labeled;
1031
1032 #[cfg(feature = "artifact-graph")]
1033 let sketch_id = {
1034 use crate::{
1039 engine::PlaneName,
1040 execution::{Artifact, ArtifactId, CodeRef, SketchBlock},
1041 };
1042 let sketch_id = exec_state.next_object_id();
1043 let arg_on: Option<crate::execution::Plane> =
1044 args.get_kw_arg_opt("on", &RuntimeType::plane(), exec_state)?;
1045 let on_object = arg_on.as_ref().and_then(|plane| plane.object_id);
1046
1047 let plane_artifact_id = arg_on.as_ref().map(|plane| plane.artifact_id);
1049
1050 let artifact_id = ArtifactId::from(exec_state.next_uuid());
1051 let sketch_scene_object = Object {
1052 id: sketch_id,
1053 kind: ObjectKind::Sketch(crate::frontend::sketch::Sketch {
1054 args: crate::front::SketchArgs {
1055 on: on_object
1056 .map(crate::front::Plane::Object)
1057 .unwrap_or(crate::front::Plane::Default(PlaneName::Xy)),
1058 },
1059 segments: Default::default(),
1060 constraints: Default::default(),
1061 }),
1062 label: Default::default(),
1063 comments: Default::default(),
1064 artifact_id,
1065 source: range.into(),
1066 };
1067 exec_state.add_scene_object(sketch_scene_object, range);
1068
1069 exec_state.add_artifact(Artifact::SketchBlock(SketchBlock {
1071 id: artifact_id,
1072 plane_id: plane_artifact_id,
1073 code_ref: CodeRef::placeholder(range),
1074 sketch_id,
1075 }));
1076
1077 sketch_id
1078 };
1079
1080 let (return_result, variables, sketch_block_state) = {
1081 self.prep_mem(exec_state.mut_stack().snapshot(), exec_state);
1083
1084 let original_value = exec_state.mod_local.sketch_block.replace(SketchBlockState::default());
1086
1087 let result = ctx.exec_block(&self.body, exec_state, BodyType::Block).await;
1088
1089 let sketch_block_state = std::mem::replace(&mut exec_state.mod_local.sketch_block, original_value);
1090
1091 let block_variables = exec_state
1092 .stack()
1093 .find_all_in_current_env()
1094 .map(|(name, value)| (name.clone(), value.clone()))
1095 .collect::<IndexMap<_, _>>();
1096
1097 exec_state.mut_stack().pop_env();
1098
1099 (result, block_variables, sketch_block_state)
1100 };
1101
1102 return_result?;
1104 let Some(sketch_block_state) = sketch_block_state else {
1105 debug_assert!(false, "Sketch block state should still be set to Some from just above");
1106 return Err(KclError::new_internal(KclErrorDetails::new(
1107 "Sketch block state should still be set to Some from just above".to_owned(),
1108 vec![SourceRange::from(self)],
1109 )));
1110 };
1111
1112 let constraints = sketch_block_state
1114 .solver_constraints
1115 .iter()
1116 .cloned()
1117 .map(kcl_ezpz::ConstraintRequest::highest_priority)
1118 .chain(
1119 sketch_block_state
1121 .solver_optional_constraints
1122 .iter()
1123 .cloned()
1124 .map(|c| kcl_ezpz::ConstraintRequest::new(c, 1)),
1125 )
1126 .collect::<Vec<_>>();
1127 let initial_guesses = sketch_block_state
1128 .sketch_vars
1129 .iter()
1130 .map(|v| {
1131 let Some(sketch_var) = v.as_sketch_var() else {
1132 return Err(KclError::new_internal(KclErrorDetails::new(
1133 "Expected sketch variable".to_owned(),
1134 vec![SourceRange::from(self)],
1135 )));
1136 };
1137 let constraint_id = sketch_var.id.to_constraint_id(range)?;
1138 let number_value = KclValue::Number {
1140 value: sketch_var.initial_value,
1141 ty: sketch_var.ty,
1142 meta: sketch_var.meta.clone(),
1143 };
1144 let initial_guess_value =
1145 normalize_to_solver_unit(&number_value, v.into(), exec_state, "sketch variable initial value")?;
1146 let initial_guess = if let Some(n) = initial_guess_value.as_ty_f64() {
1147 n.n
1148 } else {
1149 let message = format!(
1150 "Expected number after coercion, but found {}",
1151 initial_guess_value.human_friendly_type()
1152 );
1153 debug_assert!(false, "{}", &message);
1154 return Err(KclError::new_internal(KclErrorDetails::new(
1155 message,
1156 vec![SourceRange::from(self)],
1157 )));
1158 };
1159 Ok((constraint_id, initial_guess))
1160 })
1161 .collect::<Result<Vec<_>, KclError>>()?;
1162 let config = kcl_ezpz::Config::default().with_max_iterations(50);
1164 let solve_result = if exec_state.mod_local.freedom_analysis {
1165 kcl_ezpz::solve_analysis(&constraints, initial_guesses.clone(), config)
1166 .map(|outcome| (outcome.outcome, Some(FreedomAnalysis::from(outcome.analysis))))
1167 } else {
1168 kcl_ezpz::solve(&constraints, initial_guesses.clone(), config).map(|outcome| (outcome, None))
1169 };
1170 let (solve_outcome, solve_analysis) = match solve_result {
1171 Ok((solved, freedom)) => (Solved::from(solved), freedom),
1172 Err(failure) => {
1173 match &failure.error {
1174 NonLinearSystemError::FaerMatrix { .. }
1175 | NonLinearSystemError::Faer { .. }
1176 | NonLinearSystemError::FaerSolve { .. }
1177 | NonLinearSystemError::FaerSvd(..)
1178 | NonLinearSystemError::DidNotConverge => {
1179 exec_state.warn(
1182 CompilationError::err(range, "Constraint solver failed to find a solution".to_owned()),
1183 annotations::WARN_SOLVER,
1184 );
1185 let final_values = initial_guesses.iter().map(|(_, v)| *v).collect::<Vec<_>>();
1186 (
1187 Solved {
1188 final_values,
1189 iterations: Default::default(),
1190 warnings: failure.warnings,
1191 unsatisfied: Default::default(),
1192 priority_solved: Default::default(),
1193 },
1194 None,
1195 )
1196 }
1197 NonLinearSystemError::EmptySystemNotAllowed
1198 | NonLinearSystemError::WrongNumberGuesses { .. }
1199 | NonLinearSystemError::MissingGuess { .. }
1200 | NonLinearSystemError::NotFound(..) => {
1201 #[cfg(target_arch = "wasm32")]
1204 web_sys::console::error_1(
1205 &format!("Internal error from constraint solver: {}", &failure.error).into(),
1206 );
1207 return Err(KclError::new_internal(KclErrorDetails::new(
1208 format!("Internal error from constraint solver: {}", &failure.error),
1209 vec![SourceRange::from(self)],
1210 )));
1211 }
1212 _ => {
1213 return Err(KclError::new_internal(KclErrorDetails::new(
1215 format!("Error from constraint solver: {}", &failure.error),
1216 vec![SourceRange::from(self)],
1217 )));
1218 }
1219 }
1220 }
1221 };
1222 #[cfg(not(feature = "artifact-graph"))]
1223 let _ = solve_analysis;
1224 for warning in &solve_outcome.warnings {
1226 let message = if let Some(index) = warning.about_constraint.as_ref() {
1227 format!("{}; constraint index {}", &warning.content, index)
1228 } else {
1229 format!("{}", &warning.content)
1230 };
1231 exec_state.warn(CompilationError::err(range, message), annotations::WARN_SOLVER);
1232 }
1233 let solution_ty = solver_numeric_type(exec_state);
1235 let variables = substitute_sketch_vars(variables, &solve_outcome, solution_ty, solve_analysis.as_ref())?;
1236 let mut solved_segments = Vec::with_capacity(sketch_block_state.needed_by_engine.len());
1237 for unsolved_segment in &sketch_block_state.needed_by_engine {
1238 solved_segments.push(substitute_sketch_var_in_segment(
1239 unsolved_segment.clone(),
1240 &solve_outcome,
1241 solver_numeric_type(exec_state),
1242 solve_analysis.as_ref(),
1243 )?);
1244 }
1245 #[cfg(feature = "artifact-graph")]
1246 {
1247 exec_state.mod_local.artifacts.var_solutions =
1253 sketch_block_state.var_solutions(solve_outcome, solution_ty, SourceRange::from(self))?;
1254 }
1255
1256 let scene_objects = create_segment_scene_objects(&solved_segments, range, exec_state)?;
1258
1259 #[cfg(not(feature = "artifact-graph"))]
1260 drop(scene_objects);
1261 #[cfg(feature = "artifact-graph")]
1262 {
1263 let mut segment_object_ids = Vec::with_capacity(scene_objects.len());
1264 for scene_object in scene_objects {
1265 segment_object_ids.push(scene_object.id);
1266 exec_state.set_scene_object(scene_object);
1268 }
1269 let Some(sketch_object) = exec_state.mod_local.artifacts.scene_objects.get_mut(sketch_id.0) else {
1271 let message = format!("Sketch object not found after it was just created; id={:?}", sketch_id);
1272 debug_assert!(false, "{}", &message);
1273 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![range])));
1274 };
1275 let ObjectKind::Sketch(sketch) = &mut sketch_object.kind else {
1276 let message = format!(
1277 "Expected Sketch object after it was just created to be a sketch kind; id={:?}, actual={:?}",
1278 sketch_id, sketch_object
1279 );
1280 debug_assert!(false, "{}", &message);
1281 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![range])));
1282 };
1283 sketch.segments.extend(segment_object_ids);
1284 let mut sketch_block_state = sketch_block_state;
1286 sketch
1287 .constraints
1288 .extend(std::mem::take(&mut sketch_block_state.sketch_constraints));
1289
1290 exec_state.push_op(Operation::SketchSolve {
1292 sketch_id,
1293 node_path: NodePath::placeholder(),
1294 source_range: range,
1295 });
1296 }
1297
1298 let metadata = Metadata {
1301 source_range: SourceRange::from(self),
1302 };
1303 let return_value = KclValue::Object {
1304 value: variables,
1305 constrainable: Default::default(),
1306 meta: vec![metadata],
1307 };
1308 Ok(if self.is_being_edited {
1309 return_value.exit()
1312 } else {
1313 return_value.continue_()
1314 })
1315 }
1316}
1317
1318impl SketchBlock {
1319 fn prep_mem(&self, parent: EnvironmentRef, exec_state: &mut ExecState) {
1320 exec_state.mut_stack().push_new_env_for_call(parent);
1321 }
1322}
1323
1324impl Node<SketchVar> {
1325 pub async fn get_result(&self, exec_state: &mut ExecState, _ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1326 let Some(sketch_block_state) = &exec_state.mod_local.sketch_block else {
1327 return Err(KclError::new_semantic(KclErrorDetails::new(
1328 "Cannot use a sketch variable outside of a sketch block".to_owned(),
1329 vec![SourceRange::from(self)],
1330 )));
1331 };
1332 let id = sketch_block_state.next_sketch_var_id();
1333 let sketch_var = if let Some(initial) = &self.initial {
1334 KclValue::from_sketch_var_literal(initial, id, exec_state)
1335 } else {
1336 let metadata = Metadata {
1337 source_range: SourceRange::from(self),
1338 };
1339
1340 KclValue::SketchVar {
1341 value: Box::new(super::SketchVar {
1342 id,
1343 initial_value: 0.0,
1344 ty: NumericType::default(),
1345 meta: vec![metadata],
1346 }),
1347 }
1348 };
1349
1350 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
1351 return Err(KclError::new_semantic(KclErrorDetails::new(
1352 "Cannot use a sketch variable outside of a sketch block".to_owned(),
1353 vec![SourceRange::from(self)],
1354 )));
1355 };
1356 sketch_block_state.sketch_vars.push(sketch_var.clone());
1357
1358 Ok(sketch_var)
1359 }
1360}
1361
1362fn apply_ascription(
1363 value: &KclValue,
1364 ty: &Node<Type>,
1365 exec_state: &mut ExecState,
1366 source_range: SourceRange,
1367) -> Result<KclValue, KclError> {
1368 let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into(), false)
1369 .map_err(|e| KclError::new_semantic(e.into()))?;
1370
1371 if matches!(&ty, &RuntimeType::Primitive(PrimitiveType::Number(..))) {
1372 exec_state.clear_units_warnings(&source_range);
1373 }
1374
1375 value.coerce(&ty, false, exec_state).map_err(|_| {
1376 let suggestion = if ty == RuntimeType::length() {
1377 ", you might try coercing to a fully specified numeric type such as `mm`"
1378 } else if ty == RuntimeType::angle() {
1379 ", you might try coercing to a fully specified numeric type such as `deg`"
1380 } else {
1381 ""
1382 };
1383 let ty_str = if let Some(ty) = value.principal_type() {
1384 format!("(with type `{ty}`) ")
1385 } else {
1386 String::new()
1387 };
1388 KclError::new_semantic(KclErrorDetails::new(
1389 format!(
1390 "could not coerce {} {ty_str}to type `{ty}`{suggestion}",
1391 value.human_friendly_type()
1392 ),
1393 vec![source_range],
1394 ))
1395 })
1396}
1397
1398impl BinaryPart {
1399 #[async_recursion]
1400 pub(super) async fn get_result(
1401 &self,
1402 exec_state: &mut ExecState,
1403 ctx: &ExecutorContext,
1404 ) -> Result<KclValueControlFlow, KclError> {
1405 match self {
1406 BinaryPart::Literal(literal) => Ok(KclValue::from_literal((**literal).clone(), exec_state).continue_()),
1407 BinaryPart::Name(name) => name.get_result(exec_state, ctx).await.cloned().map(KclValue::continue_),
1408 BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
1409 BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await,
1410 BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
1411 BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state, ctx).await,
1412 BinaryPart::ArrayExpression(e) => e.execute(exec_state, ctx).await,
1413 BinaryPart::ArrayRangeExpression(e) => e.execute(exec_state, ctx).await,
1414 BinaryPart::ObjectExpression(e) => e.execute(exec_state, ctx).await,
1415 BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
1416 BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
1417 BinaryPart::SketchVar(e) => e.get_result(exec_state, ctx).await.map(KclValue::continue_),
1418 }
1419 }
1420}
1421
1422impl Node<Name> {
1423 pub(super) async fn get_result<'a>(
1424 &self,
1425 exec_state: &'a mut ExecState,
1426 ctx: &ExecutorContext,
1427 ) -> Result<&'a KclValue, KclError> {
1428 let being_declared = exec_state.mod_local.being_declared.clone();
1429 self.get_result_inner(exec_state, ctx)
1430 .await
1431 .map_err(|e| var_in_own_ref_err(e, &being_declared))
1432 }
1433
1434 async fn get_result_inner<'a>(
1435 &self,
1436 exec_state: &'a mut ExecState,
1437 ctx: &ExecutorContext,
1438 ) -> Result<&'a KclValue, KclError> {
1439 if self.abs_path {
1440 return Err(KclError::new_semantic(KclErrorDetails::new(
1441 "Absolute paths (names beginning with `::` are not yet supported)".to_owned(),
1442 self.as_source_ranges(),
1443 )));
1444 }
1445
1446 let mod_name = format!("{}{}", memory::MODULE_PREFIX, self.name.name);
1447
1448 if self.path.is_empty() {
1449 let item_value = exec_state.stack().get(&self.name.name, self.into());
1450 if item_value.is_ok() {
1451 return item_value;
1452 }
1453 return exec_state.stack().get(&mod_name, self.into());
1454 }
1455
1456 let mut mem_spec: Option<(EnvironmentRef, Vec<String>)> = None;
1457 for p in &self.path {
1458 let value = match mem_spec {
1459 Some((env, exports)) => {
1460 if !exports.contains(&p.name) {
1461 return Err(KclError::new_semantic(KclErrorDetails::new(
1462 format!("Item {} not found in module's exported items", p.name),
1463 p.as_source_ranges(),
1464 )));
1465 }
1466
1467 exec_state
1468 .stack()
1469 .memory
1470 .get_from(&p.name, env, p.as_source_range(), 0)?
1471 }
1472 None => exec_state
1473 .stack()
1474 .get(&format!("{}{}", memory::MODULE_PREFIX, p.name), self.into())?,
1475 };
1476
1477 let KclValue::Module { value: module_id, .. } = value else {
1478 return Err(KclError::new_semantic(KclErrorDetails::new(
1479 format!(
1480 "Identifier in path must refer to a module, found {}",
1481 value.human_friendly_type()
1482 ),
1483 p.as_source_ranges(),
1484 )));
1485 };
1486
1487 mem_spec = Some(
1488 ctx.exec_module_for_items(*module_id, exec_state, p.as_source_range())
1489 .await?,
1490 );
1491 }
1492
1493 let (env, exports) = mem_spec.unwrap();
1494
1495 let item_exported = exports.contains(&self.name.name);
1496 let item_value = exec_state
1497 .stack()
1498 .memory
1499 .get_from(&self.name.name, env, self.name.as_source_range(), 0);
1500
1501 if item_exported && item_value.is_ok() {
1503 return item_value;
1504 }
1505
1506 let mod_exported = exports.contains(&mod_name);
1507 let mod_value = exec_state
1508 .stack()
1509 .memory
1510 .get_from(&mod_name, env, self.name.as_source_range(), 0);
1511
1512 if mod_exported && mod_value.is_ok() {
1514 return mod_value;
1515 }
1516
1517 if item_value.is_err() && mod_value.is_err() {
1519 return item_value;
1520 }
1521
1522 debug_assert!((item_value.is_ok() && !item_exported) || (mod_value.is_ok() && !mod_exported));
1524 Err(KclError::new_semantic(KclErrorDetails::new(
1525 format!("Item {} not found in module's exported items", self.name.name),
1526 self.name.as_source_ranges(),
1527 )))
1528 }
1529}
1530
1531impl Node<MemberExpression> {
1532 async fn get_result(
1533 &self,
1534 exec_state: &mut ExecState,
1535 ctx: &ExecutorContext,
1536 ) -> Result<KclValueControlFlow, KclError> {
1537 let meta = Metadata {
1538 source_range: SourceRange::from(self),
1539 };
1540 let property = Property::try_from(
1543 self.computed,
1544 self.property.clone(),
1545 exec_state,
1546 self.into(),
1547 ctx,
1548 &meta,
1549 &[],
1550 StatementKind::Expression,
1551 )
1552 .await?;
1553 let object_cf = ctx
1554 .execute_expr(&self.object, exec_state, &meta, &[], StatementKind::Expression)
1555 .await?;
1556 let object = control_continue!(object_cf);
1557
1558 match (object, property, self.computed) {
1560 (KclValue::Segment { value: segment }, Property::String(property), false) => match property.as_str() {
1561 "at" => match &segment.repr {
1562 SegmentRepr::Unsolved { segment } => {
1563 match &segment.kind {
1564 UnsolvedSegmentKind::Point { position, .. } => {
1565 Ok(KclValue::HomArray {
1567 value: vec![
1568 KclValue::from_unsolved_expr(position[0].clone(), segment.meta.clone()),
1569 KclValue::from_unsolved_expr(position[1].clone(), segment.meta.clone()),
1570 ],
1571 ty: RuntimeType::any(),
1572 }
1573 .continue_())
1574 }
1575 _ => Err(KclError::new_undefined_value(
1576 KclErrorDetails::new(
1577 format!("Property '{property}' not found in segment"),
1578 vec![self.clone().into()],
1579 ),
1580 None,
1581 )),
1582 }
1583 }
1584 SegmentRepr::Solved { segment } => {
1585 match &segment.kind {
1586 SegmentKind::Point { position, .. } => {
1587 Ok(KclValue::array_from_point2d(
1589 [position[0].n, position[1].n],
1590 position[0].ty,
1591 segment.meta.clone(),
1592 )
1593 .continue_())
1594 }
1595 _ => Err(KclError::new_undefined_value(
1596 KclErrorDetails::new(
1597 format!("Property '{property}' not found in segment"),
1598 vec![self.clone().into()],
1599 ),
1600 None,
1601 )),
1602 }
1603 }
1604 },
1605 "start" => match &segment.repr {
1606 SegmentRepr::Unsolved { segment } => match &segment.kind {
1607 UnsolvedSegmentKind::Point { .. } => Err(KclError::new_undefined_value(
1608 KclErrorDetails::new(
1609 format!("Property '{property}' not found in point segment"),
1610 vec![self.clone().into()],
1611 ),
1612 None,
1613 )),
1614 UnsolvedSegmentKind::Line {
1615 start,
1616 ctor,
1617 start_object_id,
1618 ..
1619 } => Ok(KclValue::Segment {
1620 value: Box::new(AbstractSegment {
1621 repr: SegmentRepr::Unsolved {
1622 segment: UnsolvedSegment {
1623 object_id: *start_object_id,
1624 kind: UnsolvedSegmentKind::Point {
1625 position: start.clone(),
1626 ctor: Box::new(PointCtor {
1627 position: ctor.start.clone(),
1628 }),
1629 },
1630 meta: segment.meta.clone(),
1631 },
1632 },
1633 meta: segment.meta.clone(),
1634 }),
1635 }
1636 .continue_()),
1637 UnsolvedSegmentKind::Arc {
1638 start,
1639 ctor,
1640 start_object_id,
1641 ..
1642 } => Ok(KclValue::Segment {
1643 value: Box::new(AbstractSegment {
1644 repr: SegmentRepr::Unsolved {
1645 segment: UnsolvedSegment {
1646 object_id: *start_object_id,
1647 kind: UnsolvedSegmentKind::Point {
1648 position: start.clone(),
1649 ctor: Box::new(PointCtor {
1650 position: ctor.start.clone(),
1651 }),
1652 },
1653 meta: segment.meta.clone(),
1654 },
1655 },
1656 meta: segment.meta.clone(),
1657 }),
1658 }
1659 .continue_()),
1660 },
1661 SegmentRepr::Solved { segment } => match &segment.kind {
1662 SegmentKind::Point { .. } => Err(KclError::new_undefined_value(
1663 KclErrorDetails::new(
1664 format!("Property '{property}' not found in point segment"),
1665 vec![self.clone().into()],
1666 ),
1667 None,
1668 )),
1669 SegmentKind::Line {
1670 start,
1671 ctor,
1672 start_object_id,
1673 start_freedom,
1674 ..
1675 } => Ok(KclValue::Segment {
1676 value: Box::new(AbstractSegment {
1677 repr: SegmentRepr::Solved {
1678 segment: Segment {
1679 object_id: *start_object_id,
1680 kind: SegmentKind::Point {
1681 position: start.clone(),
1682 ctor: Box::new(PointCtor {
1683 position: ctor.start.clone(),
1684 }),
1685 freedom: *start_freedom,
1686 },
1687 meta: segment.meta.clone(),
1688 },
1689 },
1690 meta: segment.meta.clone(),
1691 }),
1692 }
1693 .continue_()),
1694 SegmentKind::Arc {
1695 start,
1696 ctor,
1697 start_object_id,
1698 start_freedom,
1699 ..
1700 } => Ok(KclValue::Segment {
1701 value: Box::new(AbstractSegment {
1702 repr: SegmentRepr::Solved {
1703 segment: Segment {
1704 object_id: *start_object_id,
1705 kind: SegmentKind::Point {
1706 position: start.clone(),
1707 ctor: Box::new(PointCtor {
1708 position: ctor.start.clone(),
1709 }),
1710 freedom: *start_freedom,
1711 },
1712 meta: segment.meta.clone(),
1713 },
1714 },
1715 meta: segment.meta.clone(),
1716 }),
1717 }
1718 .continue_()),
1719 },
1720 },
1721 "end" => match &segment.repr {
1722 SegmentRepr::Unsolved { segment } => match &segment.kind {
1723 UnsolvedSegmentKind::Point { .. } => Err(KclError::new_undefined_value(
1724 KclErrorDetails::new(
1725 format!("Property '{property}' not found in point segment"),
1726 vec![self.clone().into()],
1727 ),
1728 None,
1729 )),
1730 UnsolvedSegmentKind::Line {
1731 end,
1732 ctor,
1733 end_object_id,
1734 ..
1735 } => Ok(KclValue::Segment {
1736 value: Box::new(AbstractSegment {
1737 repr: SegmentRepr::Unsolved {
1738 segment: UnsolvedSegment {
1739 object_id: *end_object_id,
1740 kind: UnsolvedSegmentKind::Point {
1741 position: end.clone(),
1742 ctor: Box::new(PointCtor {
1743 position: ctor.end.clone(),
1744 }),
1745 },
1746 meta: segment.meta.clone(),
1747 },
1748 },
1749 meta: segment.meta.clone(),
1750 }),
1751 }
1752 .continue_()),
1753 UnsolvedSegmentKind::Arc {
1754 end,
1755 ctor,
1756 end_object_id,
1757 ..
1758 } => Ok(KclValue::Segment {
1759 value: Box::new(AbstractSegment {
1760 repr: SegmentRepr::Unsolved {
1761 segment: UnsolvedSegment {
1762 object_id: *end_object_id,
1763 kind: UnsolvedSegmentKind::Point {
1764 position: end.clone(),
1765 ctor: Box::new(PointCtor {
1766 position: ctor.end.clone(),
1767 }),
1768 },
1769 meta: segment.meta.clone(),
1770 },
1771 },
1772 meta: segment.meta.clone(),
1773 }),
1774 }
1775 .continue_()),
1776 },
1777 SegmentRepr::Solved { segment } => match &segment.kind {
1778 SegmentKind::Point { .. } => Err(KclError::new_undefined_value(
1779 KclErrorDetails::new(
1780 format!("Property '{property}' not found in point segment"),
1781 vec![self.clone().into()],
1782 ),
1783 None,
1784 )),
1785 SegmentKind::Line {
1786 end,
1787 ctor,
1788 end_object_id,
1789 end_freedom,
1790 ..
1791 } => Ok(KclValue::Segment {
1792 value: Box::new(AbstractSegment {
1793 repr: SegmentRepr::Solved {
1794 segment: Segment {
1795 object_id: *end_object_id,
1796 kind: SegmentKind::Point {
1797 position: end.clone(),
1798 ctor: Box::new(PointCtor {
1799 position: ctor.end.clone(),
1800 }),
1801 freedom: *end_freedom,
1802 },
1803 meta: segment.meta.clone(),
1804 },
1805 },
1806 meta: segment.meta.clone(),
1807 }),
1808 }
1809 .continue_()),
1810 SegmentKind::Arc {
1811 end,
1812 ctor,
1813 end_object_id,
1814 end_freedom,
1815 ..
1816 } => Ok(KclValue::Segment {
1817 value: Box::new(AbstractSegment {
1818 repr: SegmentRepr::Solved {
1819 segment: Segment {
1820 object_id: *end_object_id,
1821 kind: SegmentKind::Point {
1822 position: end.clone(),
1823 ctor: Box::new(PointCtor {
1824 position: ctor.end.clone(),
1825 }),
1826 freedom: *end_freedom,
1827 },
1828 meta: segment.meta.clone(),
1829 },
1830 },
1831 meta: segment.meta.clone(),
1832 }),
1833 }
1834 .continue_()),
1835 },
1836 },
1837 "center" => match &segment.repr {
1838 SegmentRepr::Unsolved { segment } => match &segment.kind {
1839 UnsolvedSegmentKind::Arc {
1840 center,
1841 ctor,
1842 center_object_id,
1843 ..
1844 } => Ok(KclValue::Segment {
1845 value: Box::new(AbstractSegment {
1846 repr: SegmentRepr::Unsolved {
1847 segment: UnsolvedSegment {
1848 object_id: *center_object_id,
1849 kind: UnsolvedSegmentKind::Point {
1850 position: center.clone(),
1851 ctor: Box::new(PointCtor {
1852 position: ctor.center.clone(),
1853 }),
1854 },
1855 meta: segment.meta.clone(),
1856 },
1857 },
1858 meta: segment.meta.clone(),
1859 }),
1860 }
1861 .continue_()),
1862 _ => Err(KclError::new_undefined_value(
1863 KclErrorDetails::new(
1864 format!("Property '{property}' not found in segment"),
1865 vec![self.clone().into()],
1866 ),
1867 None,
1868 )),
1869 },
1870 SegmentRepr::Solved { segment } => match &segment.kind {
1871 SegmentKind::Arc {
1872 center,
1873 ctor,
1874 center_object_id,
1875 center_freedom,
1876 ..
1877 } => Ok(KclValue::Segment {
1878 value: Box::new(AbstractSegment {
1879 repr: SegmentRepr::Solved {
1880 segment: Segment {
1881 object_id: *center_object_id,
1882 kind: SegmentKind::Point {
1883 position: center.clone(),
1884 ctor: Box::new(PointCtor {
1885 position: ctor.center.clone(),
1886 }),
1887 freedom: *center_freedom,
1888 },
1889 meta: segment.meta.clone(),
1890 },
1891 },
1892 meta: segment.meta.clone(),
1893 }),
1894 }
1895 .continue_()),
1896 _ => Err(KclError::new_undefined_value(
1897 KclErrorDetails::new(
1898 format!("Property '{property}' not found in segment"),
1899 vec![self.clone().into()],
1900 ),
1901 None,
1902 )),
1903 },
1904 },
1905 other => Err(KclError::new_undefined_value(
1906 KclErrorDetails::new(
1907 format!("Property '{other}' not found in segment"),
1908 vec![self.clone().into()],
1909 ),
1910 None,
1911 )),
1912 },
1913 (KclValue::Plane { value: plane }, Property::String(property), false) => match property.as_str() {
1914 "zAxis" => {
1915 let (p, u) = plane.info.z_axis.as_3_dims();
1916 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
1917 }
1918 "yAxis" => {
1919 let (p, u) = plane.info.y_axis.as_3_dims();
1920 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
1921 }
1922 "xAxis" => {
1923 let (p, u) = plane.info.x_axis.as_3_dims();
1924 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
1925 }
1926 "origin" => {
1927 let (p, u) = plane.info.origin.as_3_dims();
1928 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]).continue_())
1929 }
1930 other => Err(KclError::new_undefined_value(
1931 KclErrorDetails::new(
1932 format!("Property '{other}' not found in plane"),
1933 vec![self.clone().into()],
1934 ),
1935 None,
1936 )),
1937 },
1938 (KclValue::Object { value: map, .. }, Property::String(property), false) => {
1939 if let Some(value) = map.get(&property) {
1940 Ok(value.to_owned().continue_())
1941 } else {
1942 Err(KclError::new_undefined_value(
1943 KclErrorDetails::new(
1944 format!("Property '{property}' not found in object"),
1945 vec![self.clone().into()],
1946 ),
1947 None,
1948 ))
1949 }
1950 }
1951 (KclValue::Object { .. }, Property::String(property), true) => {
1952 Err(KclError::new_semantic(KclErrorDetails::new(
1953 format!("Cannot index object with string; use dot notation instead, e.g. `obj.{property}`"),
1954 vec![self.clone().into()],
1955 )))
1956 }
1957 (KclValue::Object { value: map, .. }, p @ Property::UInt(i), _) => {
1958 if i == 0
1959 && let Some(value) = map.get("x")
1960 {
1961 return Ok(value.to_owned().continue_());
1962 }
1963 if i == 1
1964 && let Some(value) = map.get("y")
1965 {
1966 return Ok(value.to_owned().continue_());
1967 }
1968 if i == 2
1969 && let Some(value) = map.get("z")
1970 {
1971 return Ok(value.to_owned().continue_());
1972 }
1973 let t = p.type_name();
1974 let article = article_for(t);
1975 Err(KclError::new_semantic(KclErrorDetails::new(
1976 format!("Only strings can be used as the property of an object, but you're using {article} {t}",),
1977 vec![self.clone().into()],
1978 )))
1979 }
1980 (KclValue::HomArray { value: arr, .. }, Property::UInt(index), _) => {
1981 let value_of_arr = arr.get(index);
1982 if let Some(value) = value_of_arr {
1983 Ok(value.to_owned().continue_())
1984 } else {
1985 Err(KclError::new_undefined_value(
1986 KclErrorDetails::new(
1987 format!("The array doesn't have any item at index {index}"),
1988 vec![self.clone().into()],
1989 ),
1990 None,
1991 ))
1992 }
1993 }
1994 (obj, Property::UInt(0), _) => Ok(obj.continue_()),
1997 (KclValue::HomArray { .. }, p, _) => {
1998 let t = p.type_name();
1999 let article = article_for(t);
2000 Err(KclError::new_semantic(KclErrorDetails::new(
2001 format!("Only integers >= 0 can be used as the index of an array, but you're using {article} {t}",),
2002 vec![self.clone().into()],
2003 )))
2004 }
2005 (KclValue::Solid { value }, Property::String(prop), false) if prop == "sketch" => Ok(KclValue::Sketch {
2006 value: Box::new(value.sketch),
2007 }
2008 .continue_()),
2009 (geometry @ KclValue::Solid { .. }, Property::String(prop), false) if prop == "tags" => {
2010 Err(KclError::new_semantic(KclErrorDetails::new(
2012 format!(
2013 "Property `{prop}` not found on {}. You can get a solid's tags through its sketch, as in, `exampleSolid.sketch.tags`.",
2014 geometry.human_friendly_type()
2015 ),
2016 vec![self.clone().into()],
2017 )))
2018 }
2019 (KclValue::Sketch { value: sk }, Property::String(prop), false) if prop == "tags" => Ok(KclValue::Object {
2020 meta: vec![Metadata {
2021 source_range: SourceRange::from(self.clone()),
2022 }],
2023 value: sk
2024 .tags
2025 .iter()
2026 .map(|(k, tag)| (k.to_owned(), KclValue::TagIdentifier(Box::new(tag.to_owned()))))
2027 .collect(),
2028 constrainable: false,
2029 }
2030 .continue_()),
2031 (geometry @ (KclValue::Sketch { .. } | KclValue::Solid { .. }), Property::String(property), false) => {
2032 Err(KclError::new_semantic(KclErrorDetails::new(
2033 format!("Property `{property}` not found on {}", geometry.human_friendly_type()),
2034 vec![self.clone().into()],
2035 )))
2036 }
2037 (being_indexed, _, false) => Err(KclError::new_semantic(KclErrorDetails::new(
2038 format!(
2039 "Only objects can have members accessed with dot notation, but you're trying to access {}",
2040 being_indexed.human_friendly_type()
2041 ),
2042 vec![self.clone().into()],
2043 ))),
2044 (being_indexed, _, true) => Err(KclError::new_semantic(KclErrorDetails::new(
2045 format!(
2046 "Only arrays can be indexed, but you're trying to index {}",
2047 being_indexed.human_friendly_type()
2048 ),
2049 vec![self.clone().into()],
2050 ))),
2051 }
2052 }
2053}
2054
2055impl Node<BinaryExpression> {
2056 pub(super) async fn get_result(
2057 &self,
2058 exec_state: &mut ExecState,
2059 ctx: &ExecutorContext,
2060 ) -> Result<KclValueControlFlow, KclError> {
2061 enum State {
2062 EvaluateLeft(Node<BinaryExpression>),
2063 FromLeft {
2064 node: Node<BinaryExpression>,
2065 },
2066 EvaluateRight {
2067 node: Node<BinaryExpression>,
2068 left: KclValue,
2069 },
2070 FromRight {
2071 node: Node<BinaryExpression>,
2072 left: KclValue,
2073 },
2074 }
2075
2076 let mut stack = vec![State::EvaluateLeft(self.clone())];
2077 let mut last_result: Option<KclValue> = None;
2078
2079 while let Some(state) = stack.pop() {
2080 match state {
2081 State::EvaluateLeft(node) => {
2082 let left_part = node.left.clone();
2083 match left_part {
2084 BinaryPart::BinaryExpression(child) => {
2085 stack.push(State::FromLeft { node });
2086 stack.push(State::EvaluateLeft(*child));
2087 }
2088 part => {
2089 let left_value = part.get_result(exec_state, ctx).await?;
2090 let left_value = control_continue!(left_value);
2091 stack.push(State::EvaluateRight { node, left: left_value });
2092 }
2093 }
2094 }
2095 State::FromLeft { node } => {
2096 let Some(left_value) = last_result.take() else {
2097 return Err(Self::missing_result_error(&node));
2098 };
2099 stack.push(State::EvaluateRight { node, left: left_value });
2100 }
2101 State::EvaluateRight { node, left } => {
2102 let right_part = node.right.clone();
2103 match right_part {
2104 BinaryPart::BinaryExpression(child) => {
2105 stack.push(State::FromRight { node, left });
2106 stack.push(State::EvaluateLeft(*child));
2107 }
2108 part => {
2109 let right_value = part.get_result(exec_state, ctx).await?;
2110 let right_value = control_continue!(right_value);
2111 let result = node.apply_operator(exec_state, ctx, left, right_value).await?;
2112 last_result = Some(result);
2113 }
2114 }
2115 }
2116 State::FromRight { node, left } => {
2117 let Some(right_value) = last_result.take() else {
2118 return Err(Self::missing_result_error(&node));
2119 };
2120 let result = node.apply_operator(exec_state, ctx, left, right_value).await?;
2121 last_result = Some(result);
2122 }
2123 }
2124 }
2125
2126 last_result
2127 .map(KclValue::continue_)
2128 .ok_or_else(|| Self::missing_result_error(self))
2129 }
2130
2131 async fn apply_operator(
2132 &self,
2133 exec_state: &mut ExecState,
2134 ctx: &ExecutorContext,
2135 left_value: KclValue,
2136 right_value: KclValue,
2137 ) -> Result<KclValue, KclError> {
2138 let mut meta = left_value.metadata();
2139 meta.extend(right_value.metadata());
2140
2141 if self.operator == BinaryOperator::Add
2143 && let (KclValue::String { value: left, .. }, KclValue::String { value: right, .. }) =
2144 (&left_value, &right_value)
2145 {
2146 return Ok(KclValue::String {
2147 value: format!("{left}{right}"),
2148 meta,
2149 });
2150 }
2151
2152 if self.operator == BinaryOperator::Add || self.operator == BinaryOperator::Or {
2154 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
2155 let args = Args::new_no_args(self.into(), ctx.clone(), Some("union".to_owned()));
2156 let result = crate::std::csg::inner_union(
2157 vec![*left.clone(), *right.clone()],
2158 Default::default(),
2159 exec_state,
2160 args,
2161 )
2162 .await?;
2163 return Ok(result.into());
2164 }
2165 } else if self.operator == BinaryOperator::Sub {
2166 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
2168 let args = Args::new_no_args(self.into(), ctx.clone(), Some("subtract".to_owned()));
2169 let result = crate::std::csg::inner_subtract(
2170 vec![*left.clone()],
2171 vec![*right.clone()],
2172 Default::default(),
2173 exec_state,
2174 args,
2175 )
2176 .await?;
2177 return Ok(result.into());
2178 }
2179 } else if self.operator == BinaryOperator::And
2180 && let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value)
2181 {
2182 let args = Args::new_no_args(self.into(), ctx.clone(), Some("intersect".to_owned()));
2184 let result = crate::std::csg::inner_intersect(
2185 vec![*left.clone(), *right.clone()],
2186 Default::default(),
2187 exec_state,
2188 args,
2189 )
2190 .await?;
2191 return Ok(result.into());
2192 }
2193
2194 if self.operator == BinaryOperator::Or || self.operator == BinaryOperator::And {
2196 let KclValue::Bool { value: left_value, .. } = left_value else {
2197 return Err(KclError::new_semantic(KclErrorDetails::new(
2198 format!(
2199 "Cannot apply logical operator to non-boolean value: {}",
2200 left_value.human_friendly_type()
2201 ),
2202 vec![self.left.clone().into()],
2203 )));
2204 };
2205 let KclValue::Bool { value: right_value, .. } = right_value else {
2206 return Err(KclError::new_semantic(KclErrorDetails::new(
2207 format!(
2208 "Cannot apply logical operator to non-boolean value: {}",
2209 right_value.human_friendly_type()
2210 ),
2211 vec![self.right.clone().into()],
2212 )));
2213 };
2214 let raw_value = match self.operator {
2215 BinaryOperator::Or => left_value || right_value,
2216 BinaryOperator::And => left_value && right_value,
2217 _ => unreachable!(),
2218 };
2219 return Ok(KclValue::Bool { value: raw_value, meta });
2220 }
2221
2222 if self.operator == BinaryOperator::Eq && exec_state.mod_local.sketch_block.is_some() {
2224 match (&left_value, &right_value) {
2225 (KclValue::SketchVar { value: left_value, .. }, KclValue::SketchVar { value: right_value, .. })
2227 if left_value.id == right_value.id =>
2228 {
2229 return Ok(KclValue::Bool { value: true, meta });
2230 }
2231 (KclValue::SketchVar { .. }, KclValue::SketchVar { .. }) => {
2233 return Err(KclError::new_semantic(KclErrorDetails::new(
2236 "TODO: Different sketch variables".to_owned(),
2237 vec![self.into()],
2238 )));
2239 }
2240 (KclValue::SketchVar { value: var, .. }, input_number @ KclValue::Number { .. })
2242 | (input_number @ KclValue::Number { .. }, KclValue::SketchVar { value: var, .. }) => {
2243 let number_value = normalize_to_solver_unit(
2244 input_number,
2245 input_number.into(),
2246 exec_state,
2247 "fixed constraint value",
2248 )?;
2249 let Some(n) = number_value.as_ty_f64() else {
2250 let message = format!(
2251 "Expected number after coercion, but found {}",
2252 number_value.human_friendly_type()
2253 );
2254 debug_assert!(false, "{}", &message);
2255 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![self.into()])));
2256 };
2257 let constraint = Constraint::Fixed(var.id.to_constraint_id(self.as_source_range())?, n.n);
2258 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2259 let message = "Being inside a sketch block should have already been checked above".to_owned();
2260 debug_assert!(false, "{}", &message);
2261 return Err(KclError::new_internal(KclErrorDetails::new(
2262 message,
2263 vec![SourceRange::from(self)],
2264 )));
2265 };
2266 sketch_block_state.solver_constraints.push(constraint);
2267 return Ok(KclValue::Bool { value: true, meta });
2268 }
2269 (KclValue::SketchConstraint { value: constraint }, input_number @ KclValue::Number { .. })
2271 | (input_number @ KclValue::Number { .. }, KclValue::SketchConstraint { value: constraint }) => {
2272 let number_value = normalize_to_solver_unit(
2273 input_number,
2274 input_number.into(),
2275 exec_state,
2276 "fixed constraint value",
2277 )?;
2278 let Some(n) = number_value.as_ty_f64() else {
2279 let message = format!(
2280 "Expected number after coercion, but found {}",
2281 number_value.human_friendly_type()
2282 );
2283 debug_assert!(false, "{}", &message);
2284 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![self.into()])));
2285 };
2286 match &constraint.kind {
2287 SketchConstraintKind::Distance { points } => {
2288 let range = self.as_source_range();
2289 let p0 = &points[0];
2290 let p1 = &points[1];
2291 let solver_pt0 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2292 p0.vars.x.to_constraint_id(range)?,
2293 p0.vars.y.to_constraint_id(range)?,
2294 );
2295 let solver_pt1 = kcl_ezpz::datatypes::inputs::DatumPoint::new_xy(
2296 p1.vars.x.to_constraint_id(range)?,
2297 p1.vars.y.to_constraint_id(range)?,
2298 );
2299 let solver_constraint = Constraint::Distance(solver_pt0, solver_pt1, n.n);
2300
2301 #[cfg(feature = "artifact-graph")]
2302 let constraint_id = exec_state.next_object_id();
2303 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
2304 let message =
2305 "Being inside a sketch block should have already been checked above".to_owned();
2306 debug_assert!(false, "{}", &message);
2307 return Err(KclError::new_internal(KclErrorDetails::new(
2308 message,
2309 vec![SourceRange::from(self)],
2310 )));
2311 };
2312 sketch_block_state.solver_constraints.push(solver_constraint);
2313 #[cfg(feature = "artifact-graph")]
2314 {
2315 use crate::{execution::ArtifactId, front::Distance};
2316
2317 let constraint = crate::front::Constraint::Distance(Distance {
2318 points: vec![p0.object_id, p1.object_id],
2319 distance: n.try_into().map_err(|_| {
2320 KclError::new_internal(KclErrorDetails::new(
2321 "Failed to convert distance units numeric suffix:".to_owned(),
2322 vec![range],
2323 ))
2324 })?,
2325 });
2326 sketch_block_state.sketch_constraints.push(constraint_id);
2327 exec_state.add_scene_object(
2328 Object {
2329 id: constraint_id,
2330 kind: ObjectKind::Constraint { constraint },
2331 label: Default::default(),
2332 comments: Default::default(),
2333 artifact_id: ArtifactId::constraint(),
2334 source: range.into(),
2335 },
2336 range,
2337 );
2338 }
2339 }
2340 }
2341 return Ok(KclValue::Bool { value: true, meta });
2342 }
2343 _ => {
2344 return Err(KclError::new_semantic(KclErrorDetails::new(
2345 format!(
2346 "Cannot create an equivalence constraint between values of these types: {} and {}",
2347 left_value.human_friendly_type(),
2348 right_value.human_friendly_type()
2349 ),
2350 vec![self.into()],
2351 )));
2352 }
2353 }
2354 }
2355
2356 let left = number_as_f64(&left_value, self.left.clone().into())?;
2357 let right = number_as_f64(&right_value, self.right.clone().into())?;
2358
2359 let value = match self.operator {
2360 BinaryOperator::Add => {
2361 let (l, r, ty) = NumericType::combine_eq_coerce(left, right, None);
2362 self.warn_on_unknown(&ty, "Adding", exec_state);
2363 KclValue::Number { value: l + r, meta, ty }
2364 }
2365 BinaryOperator::Sub => {
2366 let (l, r, ty) = NumericType::combine_eq_coerce(left, right, None);
2367 self.warn_on_unknown(&ty, "Subtracting", exec_state);
2368 KclValue::Number { value: l - r, meta, ty }
2369 }
2370 BinaryOperator::Mul => {
2371 let (l, r, ty) = NumericType::combine_mul(left, right);
2372 self.warn_on_unknown(&ty, "Multiplying", exec_state);
2373 KclValue::Number { value: l * r, meta, ty }
2374 }
2375 BinaryOperator::Div => {
2376 let (l, r, ty) = NumericType::combine_div(left, right);
2377 self.warn_on_unknown(&ty, "Dividing", exec_state);
2378 KclValue::Number { value: l / r, meta, ty }
2379 }
2380 BinaryOperator::Mod => {
2381 let (l, r, ty) = NumericType::combine_mod(left, right);
2382 self.warn_on_unknown(&ty, "Modulo of", exec_state);
2383 KclValue::Number { value: l % r, meta, ty }
2384 }
2385 BinaryOperator::Pow => KclValue::Number {
2386 value: left.n.powf(right.n),
2387 meta,
2388 ty: exec_state.current_default_units(),
2389 },
2390 BinaryOperator::Neq => {
2391 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2392 self.warn_on_unknown(&ty, "Comparing", exec_state);
2393 KclValue::Bool { value: l != r, meta }
2394 }
2395 BinaryOperator::Gt => {
2396 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2397 self.warn_on_unknown(&ty, "Comparing", exec_state);
2398 KclValue::Bool { value: l > r, meta }
2399 }
2400 BinaryOperator::Gte => {
2401 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2402 self.warn_on_unknown(&ty, "Comparing", exec_state);
2403 KclValue::Bool { value: l >= r, meta }
2404 }
2405 BinaryOperator::Lt => {
2406 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2407 self.warn_on_unknown(&ty, "Comparing", exec_state);
2408 KclValue::Bool { value: l < r, meta }
2409 }
2410 BinaryOperator::Lte => {
2411 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2412 self.warn_on_unknown(&ty, "Comparing", exec_state);
2413 KclValue::Bool { value: l <= r, meta }
2414 }
2415 BinaryOperator::Eq => {
2416 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
2417 self.warn_on_unknown(&ty, "Comparing", exec_state);
2418 KclValue::Bool { value: l == r, meta }
2419 }
2420 BinaryOperator::And | BinaryOperator::Or => unreachable!(),
2421 };
2422
2423 Ok(value)
2424 }
2425
2426 fn missing_result_error(node: &Node<BinaryExpression>) -> KclError {
2427 KclError::new_internal(KclErrorDetails::new(
2428 "missing result while evaluating binary expression".to_owned(),
2429 vec![SourceRange::from(node)],
2430 ))
2431 }
2432
2433 fn warn_on_unknown(&self, ty: &NumericType, verb: &str, exec_state: &mut ExecState) {
2434 if ty == &NumericType::Unknown {
2435 let sr = self.as_source_range();
2436 exec_state.clear_units_warnings(&sr);
2437 let mut err = CompilationError::err(
2438 sr,
2439 format!(
2440 "{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)`."
2441 ),
2442 );
2443 err.tag = crate::errors::Tag::UnknownNumericUnits;
2444 exec_state.warn(err, annotations::WARN_UNKNOWN_UNITS);
2445 }
2446 }
2447}
2448
2449impl Node<UnaryExpression> {
2450 pub(super) async fn get_result(
2451 &self,
2452 exec_state: &mut ExecState,
2453 ctx: &ExecutorContext,
2454 ) -> Result<KclValueControlFlow, KclError> {
2455 match self.operator {
2456 UnaryOperator::Not => {
2457 let value = self.argument.get_result(exec_state, ctx).await?;
2458 let value = control_continue!(value);
2459 let KclValue::Bool {
2460 value: bool_value,
2461 meta: _,
2462 } = value
2463 else {
2464 return Err(KclError::new_semantic(KclErrorDetails::new(
2465 format!(
2466 "Cannot apply unary operator ! to non-boolean value: {}",
2467 value.human_friendly_type()
2468 ),
2469 vec![self.into()],
2470 )));
2471 };
2472 let meta = vec![Metadata {
2473 source_range: self.into(),
2474 }];
2475 let negated = KclValue::Bool {
2476 value: !bool_value,
2477 meta,
2478 };
2479
2480 Ok(negated.continue_())
2481 }
2482 UnaryOperator::Neg => {
2483 let value = self.argument.get_result(exec_state, ctx).await?;
2484 let value = control_continue!(value);
2485 let err = || {
2486 KclError::new_semantic(KclErrorDetails::new(
2487 format!(
2488 "You can only negate numbers, planes, or lines, but this is a {}",
2489 value.human_friendly_type()
2490 ),
2491 vec![self.into()],
2492 ))
2493 };
2494 match &value {
2495 KclValue::Number { value, ty, .. } => {
2496 let meta = vec![Metadata {
2497 source_range: self.into(),
2498 }];
2499 Ok(KclValue::Number {
2500 value: -value,
2501 meta,
2502 ty: *ty,
2503 }
2504 .continue_())
2505 }
2506 KclValue::Plane { value } => {
2507 let mut plane = value.clone();
2508 if plane.info.x_axis.x != 0.0 {
2509 plane.info.x_axis.x *= -1.0;
2510 }
2511 if plane.info.x_axis.y != 0.0 {
2512 plane.info.x_axis.y *= -1.0;
2513 }
2514 if plane.info.x_axis.z != 0.0 {
2515 plane.info.x_axis.z *= -1.0;
2516 }
2517
2518 plane.value = PlaneType::Uninit;
2519 plane.id = exec_state.next_uuid();
2520 Ok(KclValue::Plane { value: plane }.continue_())
2521 }
2522 KclValue::Object {
2523 value: values, meta, ..
2524 } => {
2525 let Some(direction) = values.get("direction") else {
2527 return Err(err());
2528 };
2529
2530 let direction = match direction {
2531 KclValue::Tuple { value: values, meta } => {
2532 let values = values
2533 .iter()
2534 .map(|v| match v {
2535 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
2536 value: *value * -1.0,
2537 ty: *ty,
2538 meta: meta.clone(),
2539 }),
2540 _ => Err(err()),
2541 })
2542 .collect::<Result<Vec<_>, _>>()?;
2543
2544 KclValue::Tuple {
2545 value: values,
2546 meta: meta.clone(),
2547 }
2548 }
2549 KclValue::HomArray {
2550 value: values,
2551 ty: ty @ RuntimeType::Primitive(PrimitiveType::Number(_)),
2552 } => {
2553 let values = values
2554 .iter()
2555 .map(|v| match v {
2556 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
2557 value: *value * -1.0,
2558 ty: *ty,
2559 meta: meta.clone(),
2560 }),
2561 _ => Err(err()),
2562 })
2563 .collect::<Result<Vec<_>, _>>()?;
2564
2565 KclValue::HomArray {
2566 value: values,
2567 ty: ty.clone(),
2568 }
2569 }
2570 _ => return Err(err()),
2571 };
2572
2573 let mut value = values.clone();
2574 value.insert("direction".to_owned(), direction);
2575 Ok(KclValue::Object {
2576 value,
2577 meta: meta.clone(),
2578 constrainable: false,
2579 }
2580 .continue_())
2581 }
2582 _ => Err(err()),
2583 }
2584 }
2585 UnaryOperator::Plus => {
2586 let operand = self.argument.get_result(exec_state, ctx).await?;
2587 let operand = control_continue!(operand);
2588 match operand {
2589 KclValue::Number { .. } | KclValue::Plane { .. } => Ok(operand.continue_()),
2590 _ => Err(KclError::new_semantic(KclErrorDetails::new(
2591 format!(
2592 "You can only apply unary + to numbers or planes, but this is a {}",
2593 operand.human_friendly_type()
2594 ),
2595 vec![self.into()],
2596 ))),
2597 }
2598 }
2599 }
2600 }
2601}
2602
2603pub(crate) async fn execute_pipe_body(
2604 exec_state: &mut ExecState,
2605 body: &[Expr],
2606 source_range: SourceRange,
2607 ctx: &ExecutorContext,
2608) -> Result<KclValueControlFlow, KclError> {
2609 let Some((first, body)) = body.split_first() else {
2610 return Err(KclError::new_semantic(KclErrorDetails::new(
2611 "Pipe expressions cannot be empty".to_owned(),
2612 vec![source_range],
2613 )));
2614 };
2615 let meta = Metadata {
2620 source_range: SourceRange::from(first),
2621 };
2622 let output = ctx
2623 .execute_expr(first, exec_state, &meta, &[], StatementKind::Expression)
2624 .await?;
2625 let output = control_continue!(output);
2626
2627 let previous_pipe_value = exec_state.mod_local.pipe_value.replace(output);
2631 let result = inner_execute_pipe_body(exec_state, body, ctx).await;
2633 exec_state.mod_local.pipe_value = previous_pipe_value;
2635
2636 result
2637}
2638
2639#[async_recursion]
2642async fn inner_execute_pipe_body(
2643 exec_state: &mut ExecState,
2644 body: &[Expr],
2645 ctx: &ExecutorContext,
2646) -> Result<KclValueControlFlow, KclError> {
2647 for expression in body {
2648 if let Expr::TagDeclarator(_) = expression {
2649 return Err(KclError::new_semantic(KclErrorDetails::new(
2650 format!("This cannot be in a PipeExpression: {expression:?}"),
2651 vec![expression.into()],
2652 )));
2653 }
2654 let metadata = Metadata {
2655 source_range: SourceRange::from(expression),
2656 };
2657 let output = ctx
2658 .execute_expr(expression, exec_state, &metadata, &[], StatementKind::Expression)
2659 .await?;
2660 let output = control_continue!(output);
2661 exec_state.mod_local.pipe_value = Some(output);
2662 }
2663 let final_output = exec_state.mod_local.pipe_value.take().unwrap();
2665 Ok(final_output.continue_())
2666}
2667
2668impl Node<TagDeclarator> {
2669 pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
2670 let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
2671 value: self.name.clone(),
2672 info: Vec::new(),
2673 meta: vec![Metadata {
2674 source_range: self.into(),
2675 }],
2676 }));
2677
2678 exec_state
2679 .mut_stack()
2680 .add(self.name.clone(), memory_item, self.into())?;
2681
2682 Ok(self.into())
2683 }
2684}
2685
2686impl Node<ArrayExpression> {
2687 #[async_recursion]
2688 pub(super) async fn execute(
2689 &self,
2690 exec_state: &mut ExecState,
2691 ctx: &ExecutorContext,
2692 ) -> Result<KclValueControlFlow, KclError> {
2693 let mut results = Vec::with_capacity(self.elements.len());
2694
2695 for element in &self.elements {
2696 let metadata = Metadata::from(element);
2697 let value = ctx
2700 .execute_expr(element, exec_state, &metadata, &[], StatementKind::Expression)
2701 .await?;
2702 let value = control_continue!(value);
2703
2704 results.push(value);
2705 }
2706
2707 Ok(KclValue::HomArray {
2708 value: results,
2709 ty: RuntimeType::Primitive(PrimitiveType::Any),
2710 }
2711 .continue_())
2712 }
2713}
2714
2715impl Node<ArrayRangeExpression> {
2716 #[async_recursion]
2717 pub(super) async fn execute(
2718 &self,
2719 exec_state: &mut ExecState,
2720 ctx: &ExecutorContext,
2721 ) -> Result<KclValueControlFlow, KclError> {
2722 let metadata = Metadata::from(&self.start_element);
2723 let start_val = ctx
2724 .execute_expr(
2725 &self.start_element,
2726 exec_state,
2727 &metadata,
2728 &[],
2729 StatementKind::Expression,
2730 )
2731 .await?;
2732 let start_val = control_continue!(start_val);
2733 let start = start_val
2734 .as_ty_f64()
2735 .ok_or(KclError::new_semantic(KclErrorDetails::new(
2736 format!(
2737 "Expected number for range start but found {}",
2738 start_val.human_friendly_type()
2739 ),
2740 vec![self.into()],
2741 )))?;
2742 let metadata = Metadata::from(&self.end_element);
2743 let end_val = ctx
2744 .execute_expr(&self.end_element, exec_state, &metadata, &[], StatementKind::Expression)
2745 .await?;
2746 let end_val = control_continue!(end_val);
2747 let end = end_val.as_ty_f64().ok_or(KclError::new_semantic(KclErrorDetails::new(
2748 format!(
2749 "Expected number for range end but found {}",
2750 end_val.human_friendly_type()
2751 ),
2752 vec![self.into()],
2753 )))?;
2754
2755 let (start, end, ty) = NumericType::combine_range(start, end, exec_state, self.as_source_range())?;
2756 let Some(start) = crate::try_f64_to_i64(start) else {
2757 return Err(KclError::new_semantic(KclErrorDetails::new(
2758 format!("Range start must be an integer, but found {start}"),
2759 vec![self.into()],
2760 )));
2761 };
2762 let Some(end) = crate::try_f64_to_i64(end) else {
2763 return Err(KclError::new_semantic(KclErrorDetails::new(
2764 format!("Range end must be an integer, but found {end}"),
2765 vec![self.into()],
2766 )));
2767 };
2768
2769 if end < start {
2770 return Err(KclError::new_semantic(KclErrorDetails::new(
2771 format!("Range start is greater than range end: {start} .. {end}"),
2772 vec![self.into()],
2773 )));
2774 }
2775
2776 let range: Vec<_> = if self.end_inclusive {
2777 (start..=end).collect()
2778 } else {
2779 (start..end).collect()
2780 };
2781
2782 let meta = vec![Metadata {
2783 source_range: self.into(),
2784 }];
2785
2786 Ok(KclValue::HomArray {
2787 value: range
2788 .into_iter()
2789 .map(|num| KclValue::Number {
2790 value: num as f64,
2791 ty,
2792 meta: meta.clone(),
2793 })
2794 .collect(),
2795 ty: RuntimeType::Primitive(PrimitiveType::Number(ty)),
2796 }
2797 .continue_())
2798 }
2799}
2800
2801impl Node<ObjectExpression> {
2802 #[async_recursion]
2803 pub(super) async fn execute(
2804 &self,
2805 exec_state: &mut ExecState,
2806 ctx: &ExecutorContext,
2807 ) -> Result<KclValueControlFlow, KclError> {
2808 let mut object = HashMap::with_capacity(self.properties.len());
2809 for property in &self.properties {
2810 let metadata = Metadata::from(&property.value);
2811 let result = ctx
2812 .execute_expr(&property.value, exec_state, &metadata, &[], StatementKind::Expression)
2813 .await?;
2814 let result = control_continue!(result);
2815 object.insert(property.key.name.clone(), result);
2816 }
2817
2818 Ok(KclValue::Object {
2819 value: object,
2820 meta: vec![Metadata {
2821 source_range: self.into(),
2822 }],
2823 constrainable: false,
2824 }
2825 .continue_())
2826 }
2827}
2828
2829fn article_for<S: AsRef<str>>(s: S) -> &'static str {
2830 if s.as_ref().starts_with(['a', 'e', 'i', 'o', 'u', '[']) {
2832 "an"
2833 } else {
2834 "a"
2835 }
2836}
2837
2838fn number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<TyF64, KclError> {
2839 v.as_ty_f64().ok_or_else(|| {
2840 let actual_type = v.human_friendly_type();
2841 KclError::new_semantic(KclErrorDetails::new(
2842 format!("Expected a number, but found {actual_type}",),
2843 vec![source_range],
2844 ))
2845 })
2846}
2847
2848impl Node<IfExpression> {
2849 #[async_recursion]
2850 pub(super) async fn get_result(
2851 &self,
2852 exec_state: &mut ExecState,
2853 ctx: &ExecutorContext,
2854 ) -> Result<KclValueControlFlow, KclError> {
2855 let cond_value = ctx
2857 .execute_expr(
2858 &self.cond,
2859 exec_state,
2860 &Metadata::from(self),
2861 &[],
2862 StatementKind::Expression,
2863 )
2864 .await?;
2865 let cond_value = control_continue!(cond_value);
2866 if cond_value.get_bool()? {
2867 let block_result = ctx.exec_block(&*self.then_val, exec_state, BodyType::Block).await?;
2868 return Ok(block_result.unwrap());
2872 }
2873
2874 for else_if in &self.else_ifs {
2876 let cond_value = ctx
2877 .execute_expr(
2878 &else_if.cond,
2879 exec_state,
2880 &Metadata::from(self),
2881 &[],
2882 StatementKind::Expression,
2883 )
2884 .await?;
2885 let cond_value = control_continue!(cond_value);
2886 if cond_value.get_bool()? {
2887 let block_result = ctx.exec_block(&*else_if.then_val, exec_state, BodyType::Block).await?;
2888 return Ok(block_result.unwrap());
2892 }
2893 }
2894
2895 ctx.exec_block(&*self.final_else, exec_state, BodyType::Block)
2897 .await
2898 .map(|expr| expr.unwrap())
2899 }
2900}
2901
2902#[derive(Debug)]
2903enum Property {
2904 UInt(usize),
2905 String(String),
2906}
2907
2908impl Property {
2909 #[allow(clippy::too_many_arguments)]
2910 async fn try_from<'a>(
2911 computed: bool,
2912 value: Expr,
2913 exec_state: &mut ExecState,
2914 sr: SourceRange,
2915 ctx: &ExecutorContext,
2916 metadata: &Metadata,
2917 annotations: &[Node<Annotation>],
2918 statement_kind: StatementKind<'a>,
2919 ) -> Result<Self, KclError> {
2920 let property_sr = vec![sr];
2921 if !computed {
2922 let Expr::Name(identifier) = value else {
2923 return Err(KclError::new_semantic(KclErrorDetails::new(
2925 "Object expressions like `obj.property` must use simple identifier names, not complex expressions"
2926 .to_owned(),
2927 property_sr,
2928 )));
2929 };
2930 return Ok(Property::String(identifier.to_string()));
2931 }
2932
2933 let prop_value = ctx
2934 .execute_expr(&value, exec_state, metadata, annotations, statement_kind)
2935 .await?;
2936 let prop_value = match prop_value.control {
2937 ControlFlowKind::Continue => prop_value.into_value(),
2938 ControlFlowKind::Exit => {
2939 let message = "Early return inside array brackets is currently not supported".to_owned();
2940 debug_assert!(false, "{}", &message);
2941 return Err(KclError::new_internal(KclErrorDetails::new(message, property_sr)));
2942 }
2943 };
2944 match prop_value {
2945 KclValue::Number { value, ty, meta: _ } => {
2946 if !matches!(
2947 ty,
2948 NumericType::Unknown
2949 | NumericType::Default { .. }
2950 | NumericType::Known(crate::exec::UnitType::Count)
2951 ) {
2952 return Err(KclError::new_semantic(KclErrorDetails::new(
2953 format!(
2954 "{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"
2955 ),
2956 property_sr,
2957 )));
2958 }
2959 if let Some(x) = crate::try_f64_to_usize(value) {
2960 Ok(Property::UInt(x))
2961 } else {
2962 Err(KclError::new_semantic(KclErrorDetails::new(
2963 format!("{value} is not a valid index, indices must be whole numbers >= 0"),
2964 property_sr,
2965 )))
2966 }
2967 }
2968 _ => Err(KclError::new_semantic(KclErrorDetails::new(
2969 "Only numbers (>= 0) can be indexes".to_owned(),
2970 vec![sr],
2971 ))),
2972 }
2973 }
2974}
2975
2976impl Property {
2977 fn type_name(&self) -> &'static str {
2978 match self {
2979 Property::UInt(_) => "number",
2980 Property::String(_) => "string",
2981 }
2982 }
2983}
2984
2985impl Node<PipeExpression> {
2986 #[async_recursion]
2987 pub(super) async fn get_result(
2988 &self,
2989 exec_state: &mut ExecState,
2990 ctx: &ExecutorContext,
2991 ) -> Result<KclValueControlFlow, KclError> {
2992 execute_pipe_body(exec_state, &self.body, self.into(), ctx).await
2993 }
2994}
2995
2996#[cfg(test)]
2997mod test {
2998 use std::sync::Arc;
2999
3000 use tokio::io::AsyncWriteExt;
3001
3002 use super::*;
3003 use crate::{
3004 ExecutorSettings,
3005 errors::Severity,
3006 exec::UnitType,
3007 execution::{ContextType, parse_execute},
3008 };
3009
3010 #[tokio::test(flavor = "multi_thread")]
3011 async fn ascription() {
3012 let program = r#"
3013a = 42: number
3014b = a: number
3015p = {
3016 origin = { x = 0, y = 0, z = 0 },
3017 xAxis = { x = 1, y = 0, z = 0 },
3018 yAxis = { x = 0, y = 1, z = 0 },
3019 zAxis = { x = 0, y = 0, z = 1 }
3020}: Plane
3021arr1 = [42]: [number(cm)]
3022"#;
3023
3024 let result = parse_execute(program).await.unwrap();
3025 let mem = result.exec_state.stack();
3026 assert!(matches!(
3027 mem.memory
3028 .get_from("p", result.mem_env, SourceRange::default(), 0)
3029 .unwrap(),
3030 KclValue::Plane { .. }
3031 ));
3032 let arr1 = mem
3033 .memory
3034 .get_from("arr1", result.mem_env, SourceRange::default(), 0)
3035 .unwrap();
3036 if let KclValue::HomArray { value, ty } = arr1 {
3037 assert_eq!(value.len(), 1, "Expected Vec with specific length: found {value:?}");
3038 assert_eq!(
3039 *ty,
3040 RuntimeType::known_length(kittycad_modeling_cmds::units::UnitLength::Centimeters)
3041 );
3042 if let KclValue::Number { value, ty, .. } = &value[0] {
3044 assert_eq!(*value, 42.0);
3046 assert_eq!(
3047 *ty,
3048 NumericType::Known(UnitType::Length(kittycad_modeling_cmds::units::UnitLength::Centimeters))
3049 );
3050 } else {
3051 panic!("Expected a number; found {:?}", value[0]);
3052 }
3053 } else {
3054 panic!("Expected HomArray; found {arr1:?}");
3055 }
3056
3057 let program = r#"
3058a = 42: string
3059"#;
3060 let result = parse_execute(program).await;
3061 let err = result.unwrap_err();
3062 assert!(
3063 err.to_string()
3064 .contains("could not coerce a number (with type `number`) to type `string`"),
3065 "Expected error but found {err:?}"
3066 );
3067
3068 let program = r#"
3069a = 42: Plane
3070"#;
3071 let result = parse_execute(program).await;
3072 let err = result.unwrap_err();
3073 assert!(
3074 err.to_string()
3075 .contains("could not coerce a number (with type `number`) to type `Plane`"),
3076 "Expected error but found {err:?}"
3077 );
3078
3079 let program = r#"
3080arr = [0]: [string]
3081"#;
3082 let result = parse_execute(program).await;
3083 let err = result.unwrap_err();
3084 assert!(
3085 err.to_string().contains(
3086 "could not coerce an array of `number` with 1 value (with type `[any; 1]`) to type `[string]`"
3087 ),
3088 "Expected error but found {err:?}"
3089 );
3090
3091 let program = r#"
3092mixedArr = [0, "a"]: [number(mm)]
3093"#;
3094 let result = parse_execute(program).await;
3095 let err = result.unwrap_err();
3096 assert!(
3097 err.to_string().contains(
3098 "could not coerce an array of `number`, `string` (with type `[any; 2]`) to type `[number(mm)]`"
3099 ),
3100 "Expected error but found {err:?}"
3101 );
3102
3103 let program = r#"
3104mixedArr = [0, "a"]: [mm]
3105"#;
3106 let result = parse_execute(program).await;
3107 let err = result.unwrap_err();
3108 assert!(
3109 err.to_string().contains(
3110 "could not coerce an array of `number`, `string` (with type `[any; 2]`) to type `[number(mm)]`"
3111 ),
3112 "Expected error but found {err:?}"
3113 );
3114 }
3115
3116 #[tokio::test(flavor = "multi_thread")]
3117 async fn neg_plane() {
3118 let program = r#"
3119p = {
3120 origin = { x = 0, y = 0, z = 0 },
3121 xAxis = { x = 1, y = 0, z = 0 },
3122 yAxis = { x = 0, y = 1, z = 0 },
3123}: Plane
3124p2 = -p
3125"#;
3126
3127 let result = parse_execute(program).await.unwrap();
3128 let mem = result.exec_state.stack();
3129 match mem
3130 .memory
3131 .get_from("p2", result.mem_env, SourceRange::default(), 0)
3132 .unwrap()
3133 {
3134 KclValue::Plane { value } => {
3135 assert_eq!(value.info.x_axis.x, -1.0);
3136 assert_eq!(value.info.x_axis.y, 0.0);
3137 assert_eq!(value.info.x_axis.z, 0.0);
3138 }
3139 _ => unreachable!(),
3140 }
3141 }
3142
3143 #[tokio::test(flavor = "multi_thread")]
3144 async fn multiple_returns() {
3145 let program = r#"fn foo() {
3146 return 0
3147 return 42
3148}
3149
3150a = foo()
3151"#;
3152
3153 let result = parse_execute(program).await;
3154 assert!(result.unwrap_err().to_string().contains("return"));
3155 }
3156
3157 #[tokio::test(flavor = "multi_thread")]
3158 async fn load_all_modules() {
3159 let program_a_kcl = r#"
3161export a = 1
3162"#;
3163 let program_b_kcl = r#"
3165import a from 'a.kcl'
3166
3167export b = a + 1
3168"#;
3169 let program_c_kcl = r#"
3171import a from 'a.kcl'
3172
3173export c = a + 2
3174"#;
3175
3176 let main_kcl = r#"
3178import b from 'b.kcl'
3179import c from 'c.kcl'
3180
3181d = b + c
3182"#;
3183
3184 let main = crate::parsing::parse_str(main_kcl, ModuleId::default())
3185 .parse_errs_as_err()
3186 .unwrap();
3187
3188 let tmpdir = tempfile::TempDir::with_prefix("zma_kcl_load_all_modules").unwrap();
3189
3190 tokio::fs::File::create(tmpdir.path().join("main.kcl"))
3191 .await
3192 .unwrap()
3193 .write_all(main_kcl.as_bytes())
3194 .await
3195 .unwrap();
3196
3197 tokio::fs::File::create(tmpdir.path().join("a.kcl"))
3198 .await
3199 .unwrap()
3200 .write_all(program_a_kcl.as_bytes())
3201 .await
3202 .unwrap();
3203
3204 tokio::fs::File::create(tmpdir.path().join("b.kcl"))
3205 .await
3206 .unwrap()
3207 .write_all(program_b_kcl.as_bytes())
3208 .await
3209 .unwrap();
3210
3211 tokio::fs::File::create(tmpdir.path().join("c.kcl"))
3212 .await
3213 .unwrap()
3214 .write_all(program_c_kcl.as_bytes())
3215 .await
3216 .unwrap();
3217
3218 let exec_ctxt = ExecutorContext {
3219 engine: Arc::new(Box::new(
3220 crate::engine::conn_mock::EngineConnection::new()
3221 .map_err(|err| {
3222 KclError::new_internal(KclErrorDetails::new(
3223 format!("Failed to create mock engine connection: {err}"),
3224 vec![SourceRange::default()],
3225 ))
3226 })
3227 .unwrap(),
3228 )),
3229 fs: Arc::new(crate::fs::FileManager::new()),
3230 settings: ExecutorSettings {
3231 project_directory: Some(crate::TypedPath(tmpdir.path().into())),
3232 ..Default::default()
3233 },
3234 context_type: ContextType::Mock,
3235 };
3236 let mut exec_state = ExecState::new(&exec_ctxt);
3237
3238 exec_ctxt
3239 .run(
3240 &crate::Program {
3241 ast: main.clone(),
3242 original_file_contents: "".to_owned(),
3243 },
3244 &mut exec_state,
3245 )
3246 .await
3247 .unwrap();
3248 }
3249
3250 #[tokio::test(flavor = "multi_thread")]
3251 async fn user_coercion() {
3252 let program = r#"fn foo(x: Axis2d) {
3253 return 0
3254}
3255
3256foo(x = { direction = [0, 0], origin = [0, 0]})
3257"#;
3258
3259 parse_execute(program).await.unwrap();
3260
3261 let program = r#"fn foo(x: Axis3d) {
3262 return 0
3263}
3264
3265foo(x = { direction = [0, 0], origin = [0, 0]})
3266"#;
3267
3268 parse_execute(program).await.unwrap_err();
3269 }
3270
3271 #[tokio::test(flavor = "multi_thread")]
3272 async fn coerce_return() {
3273 let program = r#"fn foo(): number(mm) {
3274 return 42
3275}
3276
3277a = foo()
3278"#;
3279
3280 parse_execute(program).await.unwrap();
3281
3282 let program = r#"fn foo(): mm {
3283 return 42
3284}
3285
3286a = foo()
3287"#;
3288
3289 parse_execute(program).await.unwrap();
3290
3291 let program = r#"fn foo(): number(mm) {
3292 return { bar: 42 }
3293}
3294
3295a = foo()
3296"#;
3297
3298 parse_execute(program).await.unwrap_err();
3299
3300 let program = r#"fn foo(): mm {
3301 return { bar: 42 }
3302}
3303
3304a = foo()
3305"#;
3306
3307 parse_execute(program).await.unwrap_err();
3308 }
3309
3310 #[tokio::test(flavor = "multi_thread")]
3311 async fn test_sensible_error_when_missing_equals_in_kwarg() {
3312 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)"]
3313 .into_iter()
3314 .enumerate()
3315 {
3316 let program = format!(
3317 "fn foo() {{ return 0 }}
3318z = 0
3319fn f(x, y, z) {{ return 0 }}
3320{call}"
3321 );
3322 let err = parse_execute(&program).await.unwrap_err();
3323 let msg = err.message();
3324 assert!(
3325 msg.contains("This argument needs a label, but it doesn't have one"),
3326 "failed test {i}: {msg}"
3327 );
3328 assert!(msg.contains("`y`"), "failed test {i}, missing `y`: {msg}");
3329 if i == 0 {
3330 assert!(msg.contains("`z`"), "failed test {i}, missing `z`: {msg}");
3331 }
3332 }
3333 }
3334
3335 #[tokio::test(flavor = "multi_thread")]
3336 async fn default_param_for_unlabeled() {
3337 let ast = r#"fn myExtrude(@sk, length) {
3340 return extrude(sk, length)
3341}
3342sketch001 = startSketchOn(XY)
3343 |> circle(center = [0, 0], radius = 93.75)
3344 |> myExtrude(length = 40)
3345"#;
3346
3347 parse_execute(ast).await.unwrap();
3348 }
3349
3350 #[tokio::test(flavor = "multi_thread")]
3351 async fn dont_use_unlabelled_as_input() {
3352 let ast = r#"length = 10
3354startSketchOn(XY)
3355 |> circle(center = [0, 0], radius = 93.75)
3356 |> extrude(length)
3357"#;
3358
3359 parse_execute(ast).await.unwrap();
3360 }
3361
3362 #[tokio::test(flavor = "multi_thread")]
3363 async fn ascription_in_binop() {
3364 let ast = r#"foo = tan(0): number(rad) - 4deg"#;
3365 parse_execute(ast).await.unwrap();
3366
3367 let ast = r#"foo = tan(0): rad - 4deg"#;
3368 parse_execute(ast).await.unwrap();
3369 }
3370
3371 #[tokio::test(flavor = "multi_thread")]
3372 async fn neg_sqrt() {
3373 let ast = r#"bad = sqrt(-2)"#;
3374
3375 let e = parse_execute(ast).await.unwrap_err();
3376 assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
3378 }
3379
3380 #[tokio::test(flavor = "multi_thread")]
3381 async fn non_array_fns() {
3382 let ast = r#"push(1, item = 2)
3383pop(1)
3384map(1, f = fn(@x) { return x + 1 })
3385reduce(1, f = fn(@x, accum) { return accum + x}, initial = 0)"#;
3386
3387 parse_execute(ast).await.unwrap();
3388 }
3389
3390 #[tokio::test(flavor = "multi_thread")]
3391 async fn non_array_indexing() {
3392 let good = r#"a = 42
3393good = a[0]
3394"#;
3395 let result = parse_execute(good).await.unwrap();
3396 let mem = result.exec_state.stack();
3397 let num = mem
3398 .memory
3399 .get_from("good", result.mem_env, SourceRange::default(), 0)
3400 .unwrap()
3401 .as_ty_f64()
3402 .unwrap();
3403 assert_eq!(num.n, 42.0);
3404
3405 let bad = r#"a = 42
3406bad = a[1]
3407"#;
3408
3409 parse_execute(bad).await.unwrap_err();
3410 }
3411
3412 #[tokio::test(flavor = "multi_thread")]
3413 async fn coerce_unknown_to_length() {
3414 let ast = r#"x = 2mm * 2mm
3415y = x: number(Length)"#;
3416 let e = parse_execute(ast).await.unwrap_err();
3417 assert!(
3418 e.message().contains("could not coerce"),
3419 "Error message: '{}'",
3420 e.message()
3421 );
3422
3423 let ast = r#"x = 2mm
3424y = x: number(Length)"#;
3425 let result = parse_execute(ast).await.unwrap();
3426 let mem = result.exec_state.stack();
3427 let num = mem
3428 .memory
3429 .get_from("y", result.mem_env, SourceRange::default(), 0)
3430 .unwrap()
3431 .as_ty_f64()
3432 .unwrap();
3433 assert_eq!(num.n, 2.0);
3434 assert_eq!(num.ty, NumericType::mm());
3435 }
3436
3437 #[tokio::test(flavor = "multi_thread")]
3438 async fn one_warning_unknown() {
3439 let ast = r#"
3440// Should warn once
3441a = PI * 2
3442// Should warn once
3443b = (PI * 2) / 3
3444// Should not warn
3445c = ((PI * 2) / 3): number(deg)
3446"#;
3447
3448 let result = parse_execute(ast).await.unwrap();
3449 assert_eq!(result.exec_state.errors().len(), 2);
3450 }
3451
3452 #[tokio::test(flavor = "multi_thread")]
3453 async fn non_count_indexing() {
3454 let ast = r#"x = [0, 0]
3455y = x[1mm]
3456"#;
3457 parse_execute(ast).await.unwrap_err();
3458
3459 let ast = r#"x = [0, 0]
3460y = 1deg
3461z = x[y]
3462"#;
3463 parse_execute(ast).await.unwrap_err();
3464
3465 let ast = r#"x = [0, 0]
3466y = x[0mm + 1]
3467"#;
3468 parse_execute(ast).await.unwrap_err();
3469 }
3470
3471 #[tokio::test(flavor = "multi_thread")]
3472 async fn getting_property_of_plane() {
3473 let ast = std::fs::read_to_string("tests/inputs/planestuff.kcl").unwrap();
3474 parse_execute(&ast).await.unwrap();
3475 }
3476
3477 #[cfg(feature = "artifact-graph")]
3478 #[tokio::test(flavor = "multi_thread")]
3479 async fn no_artifacts_from_within_hole_call() {
3480 let ast = std::fs::read_to_string("tests/inputs/sample_hole.kcl").unwrap();
3485 let out = parse_execute(&ast).await.unwrap();
3486
3487 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
3489
3490 let expected = 5;
3494 assert_eq!(
3495 actual_operations.len(),
3496 expected,
3497 "expected {expected} operations, received {}:\n{actual_operations:#?}",
3498 actual_operations.len(),
3499 );
3500 }
3501
3502 #[cfg(feature = "artifact-graph")]
3503 #[tokio::test(flavor = "multi_thread")]
3504 async fn feature_tree_annotation_on_user_defined_kcl() {
3505 let ast = std::fs::read_to_string("tests/inputs/feature_tree_annotation_on_user_defined_kcl.kcl").unwrap();
3508 let out = parse_execute(&ast).await.unwrap();
3509
3510 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
3512
3513 let expected = 0;
3514 assert_eq!(
3515 actual_operations.len(),
3516 expected,
3517 "expected {expected} operations, received {}:\n{actual_operations:#?}",
3518 actual_operations.len(),
3519 );
3520 }
3521
3522 #[cfg(feature = "artifact-graph")]
3523 #[tokio::test(flavor = "multi_thread")]
3524 async fn no_feature_tree_annotation_on_user_defined_kcl() {
3525 let ast = std::fs::read_to_string("tests/inputs/no_feature_tree_annotation_on_user_defined_kcl.kcl").unwrap();
3528 let out = parse_execute(&ast).await.unwrap();
3529
3530 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
3532
3533 let expected = 2;
3534 assert_eq!(
3535 actual_operations.len(),
3536 expected,
3537 "expected {expected} operations, received {}:\n{actual_operations:#?}",
3538 actual_operations.len(),
3539 );
3540 assert!(matches!(actual_operations[0], Operation::GroupBegin { .. }));
3541 assert!(matches!(actual_operations[1], Operation::GroupEnd));
3542 }
3543
3544 #[tokio::test(flavor = "multi_thread")]
3545 async fn custom_warning() {
3546 let warn = r#"
3547a = PI * 2
3548"#;
3549 let result = parse_execute(warn).await.unwrap();
3550 assert_eq!(result.exec_state.errors().len(), 1);
3551 assert_eq!(result.exec_state.errors()[0].severity, Severity::Warning);
3552
3553 let allow = r#"
3554@warnings(allow = unknownUnits)
3555a = PI * 2
3556"#;
3557 let result = parse_execute(allow).await.unwrap();
3558 assert_eq!(result.exec_state.errors().len(), 0);
3559
3560 let deny = r#"
3561@warnings(deny = [unknownUnits])
3562a = PI * 2
3563"#;
3564 let result = parse_execute(deny).await.unwrap();
3565 assert_eq!(result.exec_state.errors().len(), 1);
3566 assert_eq!(result.exec_state.errors()[0].severity, Severity::Error);
3567 }
3568
3569 #[tokio::test(flavor = "multi_thread")]
3570 async fn cannot_solid_extrude_an_open_profile() {
3571 let code = std::fs::read_to_string("tests/inputs/cannot_solid_extrude_an_open_profile.kcl").unwrap();
3574 let program = crate::Program::parse_no_errs(&code).expect("should parse");
3575 let exec_ctxt = ExecutorContext::new_mock(None).await;
3576 let mut exec_state = ExecState::new(&exec_ctxt);
3577
3578 let err = exec_ctxt.run(&program, &mut exec_state).await.unwrap_err().error;
3579 assert!(matches!(err, KclError::Semantic { .. }));
3580 exec_ctxt.close().await;
3581 }
3582}