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