1use std::collections::HashMap;
2
3use async_recursion::async_recursion;
4use indexmap::IndexMap;
5use kcl_ezpz::Constraint;
6use kittycad_modeling_cmds::units::UnitLength;
7
8use crate::{
9 CompilationError, NodePath, SourceRange,
10 errors::{KclError, KclErrorDetails},
11 exec::UnitType,
12 execution::{
13 BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, ModelingCmdMeta, ModuleArtifactState,
14 Operation, PlaneType, StatementKind, TagIdentifier, annotations,
15 cad_op::OpKclValue,
16 fn_call::Args,
17 kcl_value::{FunctionSource, KclFunctionSourceParams, TypeDef},
18 memory,
19 state::{ModuleState, SketchBlockState},
20 types::{NumericType, PrimitiveType, RuntimeType},
21 },
22 fmt,
23 modules::{ModuleId, ModulePath, ModuleRepr},
24 parsing::ast::types::{
25 Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
26 BinaryPart, BodyItem, CodeBlock, Expr, IfExpression, ImportPath, ImportSelector, ItemVisibility,
27 MemberExpression, Name, Node, ObjectExpression, PipeExpression, Program, SketchBlock, SketchVar, TagDeclarator,
28 Type, UnaryExpression, UnaryOperator,
29 },
30 std::args::TyF64,
31};
32
33impl<'a> StatementKind<'a> {
34 fn expect_name(&self) -> &'a str {
35 match self {
36 StatementKind::Declaration { name } => name,
37 StatementKind::Expression => unreachable!(),
38 }
39 }
40}
41
42impl ExecutorContext {
43 async fn handle_annotations(
45 &self,
46 annotations: impl Iterator<Item = &Node<Annotation>>,
47 body_type: BodyType,
48 exec_state: &mut ExecState,
49 ) -> Result<bool, KclError> {
50 let mut no_prelude = false;
51 for annotation in annotations {
52 if annotation.name() == Some(annotations::SETTINGS) {
53 if matches!(body_type, BodyType::Root) {
54 let (updated_len, updated_angle) =
55 exec_state.mod_local.settings.update_from_annotation(annotation)?;
56 if updated_len {
57 exec_state.mod_local.explicit_length_units = true;
58 }
59 if updated_angle {
60 exec_state.warn(
61 CompilationError::err(
62 annotation.as_source_range(),
63 "Prefer to use explicit units for angles",
64 ),
65 annotations::WARN_ANGLE_UNITS,
66 );
67 }
68 } else {
69 exec_state.err(CompilationError::err(
70 annotation.as_source_range(),
71 "Settings can only be modified at the top level scope of a file",
72 ));
73 }
74 } else if annotation.name() == Some(annotations::NO_PRELUDE) {
75 if matches!(body_type, BodyType::Root) {
76 no_prelude = true;
77 } else {
78 exec_state.err(CompilationError::err(
79 annotation.as_source_range(),
80 "The standard library can only be skipped at the top level scope of a file",
81 ));
82 }
83 } else if annotation.name() == Some(annotations::WARNINGS) {
84 if matches!(body_type, BodyType::Root) {
86 let props = annotations::expect_properties(annotations::WARNINGS, annotation)?;
87 for p in props {
88 match &*p.inner.key.name {
89 annotations::WARN_ALLOW => {
90 let allowed = annotations::many_of(
91 &p.inner.value,
92 &annotations::WARN_VALUES,
93 annotation.as_source_range(),
94 )?;
95 exec_state.mod_local.allowed_warnings = allowed;
96 }
97 annotations::WARN_DENY => {
98 let denied = annotations::many_of(
99 &p.inner.value,
100 &annotations::WARN_VALUES,
101 annotation.as_source_range(),
102 )?;
103 exec_state.mod_local.denied_warnings = denied;
104 }
105 name => {
106 return Err(KclError::new_semantic(KclErrorDetails::new(
107 format!(
108 "Unexpected warnings key: `{name}`; expected one of `{}`, `{}`",
109 annotations::WARN_ALLOW,
110 annotations::WARN_DENY,
111 ),
112 vec![annotation.as_source_range()],
113 )));
114 }
115 }
116 }
117 } else {
118 exec_state.err(CompilationError::err(
119 annotation.as_source_range(),
120 "Warnings can only be customized at the top level scope of a file",
121 ));
122 }
123 } else {
124 exec_state.warn(
125 CompilationError::err(annotation.as_source_range(), "Unknown annotation"),
126 annotations::WARN_UNKNOWN_ATTR,
127 );
128 }
129 }
130 Ok(no_prelude)
131 }
132
133 pub(super) async fn exec_module_body(
134 &self,
135 program: &Node<Program>,
136 exec_state: &mut ExecState,
137 preserve_mem: bool,
138 module_id: ModuleId,
139 path: &ModulePath,
140 ) -> Result<
141 (Option<KclValue>, EnvironmentRef, Vec<String>, ModuleArtifactState),
142 (KclError, Option<EnvironmentRef>, Option<ModuleArtifactState>),
143 > {
144 crate::log::log(format!("enter module {path} {}", exec_state.stack()));
145
146 let mut local_state = ModuleState::new(path.clone(), exec_state.stack().memory.clone(), Some(module_id));
147 if !preserve_mem {
148 std::mem::swap(&mut exec_state.mod_local, &mut local_state);
149 }
150
151 let no_prelude = self
152 .handle_annotations(program.inner_attrs.iter(), crate::execution::BodyType::Root, exec_state)
153 .await
154 .map_err(|err| (err, None, None))?;
155
156 if !preserve_mem {
157 exec_state.mut_stack().push_new_root_env(!no_prelude);
158 }
159
160 let result = self
161 .exec_block(program, exec_state, crate::execution::BodyType::Root)
162 .await;
163
164 let env_ref = if preserve_mem {
165 exec_state.mut_stack().pop_and_preserve_env()
166 } else {
167 exec_state.mut_stack().pop_env()
168 };
169 let module_artifacts = if !preserve_mem {
170 std::mem::swap(&mut exec_state.mod_local, &mut local_state);
171 local_state.artifacts
172 } else {
173 std::mem::take(&mut exec_state.mod_local.artifacts)
174 };
175
176 crate::log::log(format!("leave {path}"));
177
178 result
179 .map_err(|err| (err, Some(env_ref), Some(module_artifacts.clone())))
180 .map(|result| (result, env_ref, local_state.module_exports, module_artifacts))
181 }
182
183 #[async_recursion]
185 pub(super) async fn exec_block<'a, B>(
186 &'a self,
187 block: &'a B,
188 exec_state: &mut ExecState,
189 body_type: BodyType,
190 ) -> Result<Option<KclValue>, KclError>
191 where
192 B: CodeBlock + Sync,
193 {
194 let mut last_expr = None;
195 for statement in block.body() {
197 match statement {
198 BodyItem::ImportStatement(import_stmt) => {
199 if !matches!(body_type, BodyType::Root) {
200 return Err(KclError::new_semantic(KclErrorDetails::new(
201 "Imports are only supported at the top-level of a file.".to_owned(),
202 vec![import_stmt.into()],
203 )));
204 }
205
206 let source_range = SourceRange::from(import_stmt);
207 let attrs = &import_stmt.outer_attrs;
208 let module_path = ModulePath::from_import_path(
209 &import_stmt.path,
210 &self.settings.project_directory,
211 &exec_state.mod_local.path,
212 )?;
213 let module_id = self
214 .open_module(&import_stmt.path, attrs, &module_path, exec_state, source_range)
215 .await?;
216
217 match &import_stmt.selector {
218 ImportSelector::List { items } => {
219 let (env_ref, module_exports) =
220 self.exec_module_for_items(module_id, exec_state, source_range).await?;
221 for import_item in items {
222 let mem = &exec_state.stack().memory;
224 let mut value = mem
225 .get_from(&import_item.name.name, env_ref, import_item.into(), 0)
226 .cloned();
227 let ty_name = format!("{}{}", memory::TYPE_PREFIX, import_item.name.name);
228 let mut ty = mem.get_from(&ty_name, env_ref, import_item.into(), 0).cloned();
229 let mod_name = format!("{}{}", memory::MODULE_PREFIX, import_item.name.name);
230 let mut mod_value = mem.get_from(&mod_name, env_ref, import_item.into(), 0).cloned();
231
232 if value.is_err() && ty.is_err() && mod_value.is_err() {
233 return Err(KclError::new_undefined_value(
234 KclErrorDetails::new(
235 format!("{} is not defined in module", import_item.name.name),
236 vec![SourceRange::from(&import_item.name)],
237 ),
238 None,
239 ));
240 }
241
242 if value.is_ok() && !module_exports.contains(&import_item.name.name) {
244 value = Err(KclError::new_semantic(KclErrorDetails::new(
245 format!(
246 "Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
247 import_item.name.name
248 ),
249 vec![SourceRange::from(&import_item.name)],
250 )));
251 }
252
253 if ty.is_ok() && !module_exports.contains(&ty_name) {
254 ty = Err(KclError::new_semantic(KclErrorDetails::new(
255 format!(
256 "Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
257 import_item.name.name
258 ),
259 vec![SourceRange::from(&import_item.name)],
260 )));
261 }
262
263 if mod_value.is_ok() && !module_exports.contains(&mod_name) {
264 mod_value = Err(KclError::new_semantic(KclErrorDetails::new(
265 format!(
266 "Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
267 import_item.name.name
268 ),
269 vec![SourceRange::from(&import_item.name)],
270 )));
271 }
272
273 if value.is_err() && ty.is_err() && mod_value.is_err() {
274 return value.map(Option::Some);
275 }
276
277 if let Ok(value) = value {
279 exec_state.mut_stack().add(
280 import_item.identifier().to_owned(),
281 value,
282 SourceRange::from(&import_item.name),
283 )?;
284
285 if let ItemVisibility::Export = import_stmt.visibility {
286 exec_state
287 .mod_local
288 .module_exports
289 .push(import_item.identifier().to_owned());
290 }
291 }
292
293 if let Ok(ty) = ty {
294 let ty_name = format!("{}{}", memory::TYPE_PREFIX, import_item.identifier());
295 exec_state.mut_stack().add(
296 ty_name.clone(),
297 ty,
298 SourceRange::from(&import_item.name),
299 )?;
300
301 if let ItemVisibility::Export = import_stmt.visibility {
302 exec_state.mod_local.module_exports.push(ty_name);
303 }
304 }
305
306 if let Ok(mod_value) = mod_value {
307 let mod_name = format!("{}{}", memory::MODULE_PREFIX, import_item.identifier());
308 exec_state.mut_stack().add(
309 mod_name.clone(),
310 mod_value,
311 SourceRange::from(&import_item.name),
312 )?;
313
314 if let ItemVisibility::Export = import_stmt.visibility {
315 exec_state.mod_local.module_exports.push(mod_name);
316 }
317 }
318 }
319 }
320 ImportSelector::Glob(_) => {
321 let (env_ref, module_exports) =
322 self.exec_module_for_items(module_id, exec_state, source_range).await?;
323 for name in module_exports.iter() {
324 let item = exec_state
325 .stack()
326 .memory
327 .get_from(name, env_ref, source_range, 0)
328 .map_err(|_err| {
329 KclError::new_internal(KclErrorDetails::new(
330 format!("{name} is not defined in module (but was exported?)"),
331 vec![source_range],
332 ))
333 })?
334 .clone();
335 exec_state.mut_stack().add(name.to_owned(), item, source_range)?;
336
337 if let ItemVisibility::Export = import_stmt.visibility {
338 exec_state.mod_local.module_exports.push(name.clone());
339 }
340 }
341 }
342 ImportSelector::None { .. } => {
343 let name = import_stmt.module_name().unwrap();
344 let item = KclValue::Module {
345 value: module_id,
346 meta: vec![source_range.into()],
347 };
348 exec_state.mut_stack().add(
349 format!("{}{}", memory::MODULE_PREFIX, name),
350 item,
351 source_range,
352 )?;
353 }
354 }
355 last_expr = None;
356 }
357 BodyItem::ExpressionStatement(expression_statement) => {
358 let metadata = Metadata::from(expression_statement);
359 last_expr = Some(
360 self.execute_expr(
361 &expression_statement.expression,
362 exec_state,
363 &metadata,
364 &[],
365 StatementKind::Expression,
366 )
367 .await?,
368 );
369 }
370 BodyItem::VariableDeclaration(variable_declaration) => {
371 let var_name = variable_declaration.declaration.id.name.to_string();
372 let source_range = SourceRange::from(&variable_declaration.declaration.init);
373 let metadata = Metadata { source_range };
374
375 let annotations = &variable_declaration.outer_attrs;
376
377 let lhs = variable_declaration.inner.name().to_owned();
380 let prev_being_declared = exec_state.mod_local.being_declared.take();
381 exec_state.mod_local.being_declared = Some(lhs);
382 let rhs_result = self
383 .execute_expr(
384 &variable_declaration.declaration.init,
385 exec_state,
386 &metadata,
387 annotations,
388 StatementKind::Declaration { name: &var_name },
389 )
390 .await;
391 exec_state.mod_local.being_declared = prev_being_declared;
393 let rhs = rhs_result?;
394
395 exec_state
396 .mut_stack()
397 .add(var_name.clone(), rhs.clone(), source_range)?;
398
399 let should_show_in_feature_tree =
403 !exec_state.mod_local.inside_stdlib && rhs.show_variable_in_feature_tree();
404 if should_show_in_feature_tree {
405 exec_state.push_op(Operation::VariableDeclaration {
406 name: var_name.clone(),
407 value: OpKclValue::from(&rhs),
408 visibility: variable_declaration.visibility,
409 node_path: NodePath::placeholder(),
410 source_range,
411 });
412 }
413
414 if let ItemVisibility::Export = variable_declaration.visibility {
416 if matches!(body_type, BodyType::Root) {
417 exec_state.mod_local.module_exports.push(var_name);
418 } else {
419 exec_state.err(CompilationError::err(
420 variable_declaration.as_source_range(),
421 "Exports are only supported at the top-level of a file. Remove `export` or move it to the top-level.",
422 ));
423 }
424 }
425 last_expr = matches!(body_type, BodyType::Root).then_some(rhs);
427 }
428 BodyItem::TypeDeclaration(ty) => {
429 let metadata = Metadata::from(&**ty);
430 let attrs = annotations::get_fn_attrs(&ty.outer_attrs, metadata.source_range)?.unwrap_or_default();
431 match attrs.impl_ {
432 annotations::Impl::Rust | annotations::Impl::RustConstraint => {
433 let std_path = match &exec_state.mod_local.path {
434 ModulePath::Std { value } => value,
435 ModulePath::Local { .. } | ModulePath::Main => {
436 return Err(KclError::new_semantic(KclErrorDetails::new(
437 "User-defined types are not yet supported.".to_owned(),
438 vec![metadata.source_range],
439 )));
440 }
441 };
442 let (t, props) = crate::std::std_ty(std_path, &ty.name.name);
443 let value = KclValue::Type {
444 value: TypeDef::RustRepr(t, props),
445 meta: vec![metadata],
446 experimental: attrs.experimental,
447 };
448 let name_in_mem = format!("{}{}", memory::TYPE_PREFIX, ty.name.name);
449 exec_state
450 .mut_stack()
451 .add(name_in_mem.clone(), value, metadata.source_range)
452 .map_err(|_| {
453 KclError::new_semantic(KclErrorDetails::new(
454 format!("Redefinition of type {}.", ty.name.name),
455 vec![metadata.source_range],
456 ))
457 })?;
458
459 if let ItemVisibility::Export = ty.visibility {
460 exec_state.mod_local.module_exports.push(name_in_mem);
461 }
462 }
463 annotations::Impl::Primitive => {}
465 annotations::Impl::Kcl | annotations::Impl::KclConstrainable => match &ty.alias {
466 Some(alias) => {
467 let value = KclValue::Type {
468 value: TypeDef::Alias(
469 RuntimeType::from_parsed(
470 alias.inner.clone(),
471 exec_state,
472 metadata.source_range,
473 attrs.impl_ == annotations::Impl::KclConstrainable,
474 )
475 .map_err(|e| KclError::new_semantic(e.into()))?,
476 ),
477 meta: vec![metadata],
478 experimental: attrs.experimental,
479 };
480 let name_in_mem = format!("{}{}", memory::TYPE_PREFIX, ty.name.name);
481 exec_state
482 .mut_stack()
483 .add(name_in_mem.clone(), value, metadata.source_range)
484 .map_err(|_| {
485 KclError::new_semantic(KclErrorDetails::new(
486 format!("Redefinition of type {}.", ty.name.name),
487 vec![metadata.source_range],
488 ))
489 })?;
490
491 if let ItemVisibility::Export = ty.visibility {
492 exec_state.mod_local.module_exports.push(name_in_mem);
493 }
494 }
495 None => {
496 return Err(KclError::new_semantic(KclErrorDetails::new(
497 "User-defined types are not yet supported.".to_owned(),
498 vec![metadata.source_range],
499 )));
500 }
501 },
502 }
503
504 last_expr = None;
505 }
506 BodyItem::ReturnStatement(return_statement) => {
507 let metadata = Metadata::from(return_statement);
508
509 if matches!(body_type, BodyType::Root) {
510 return Err(KclError::new_semantic(KclErrorDetails::new(
511 "Cannot return from outside a function.".to_owned(),
512 vec![metadata.source_range],
513 )));
514 }
515
516 let value = self
517 .execute_expr(
518 &return_statement.argument,
519 exec_state,
520 &metadata,
521 &[],
522 StatementKind::Expression,
523 )
524 .await?;
525 exec_state
526 .mut_stack()
527 .add(memory::RETURN_NAME.to_owned(), value, metadata.source_range)
528 .map_err(|_| {
529 KclError::new_semantic(KclErrorDetails::new(
530 "Multiple returns from a single function.".to_owned(),
531 vec![metadata.source_range],
532 ))
533 })?;
534 last_expr = None;
535 }
536 }
537 }
538
539 if matches!(body_type, BodyType::Root) {
540 exec_state
542 .flush_batch(
543 ModelingCmdMeta::new(self, block.to_source_range()),
544 true,
547 )
548 .await?;
549 }
550
551 Ok(last_expr)
552 }
553
554 pub async fn open_module(
555 &self,
556 path: &ImportPath,
557 attrs: &[Node<Annotation>],
558 resolved_path: &ModulePath,
559 exec_state: &mut ExecState,
560 source_range: SourceRange,
561 ) -> Result<ModuleId, KclError> {
562 match path {
563 ImportPath::Kcl { .. } => {
564 exec_state.global.mod_loader.cycle_check(resolved_path, source_range)?;
565
566 if let Some(id) = exec_state.id_for_module(resolved_path) {
567 return Ok(id);
568 }
569
570 let id = exec_state.next_module_id();
571 exec_state.add_path_to_source_id(resolved_path.clone(), id);
573 let source = resolved_path.source(&self.fs, source_range).await?;
574 exec_state.add_id_to_source(id, source.clone());
575 let parsed = crate::parsing::parse_str(&source.source, id).parse_errs_as_err()?;
577 exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Kcl(parsed, None));
578
579 Ok(id)
580 }
581 ImportPath::Foreign { .. } => {
582 if let Some(id) = exec_state.id_for_module(resolved_path) {
583 return Ok(id);
584 }
585
586 let id = exec_state.next_module_id();
587 let path = resolved_path.expect_path();
588 exec_state.add_path_to_source_id(resolved_path.clone(), id);
590 let format = super::import::format_from_annotations(attrs, path, source_range)?;
591 let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?;
592 exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Foreign(geom, None));
593 Ok(id)
594 }
595 ImportPath::Std { .. } => {
596 if let Some(id) = exec_state.id_for_module(resolved_path) {
597 return Ok(id);
598 }
599
600 let id = exec_state.next_module_id();
601 exec_state.add_path_to_source_id(resolved_path.clone(), id);
603 let source = resolved_path.source(&self.fs, source_range).await?;
604 exec_state.add_id_to_source(id, source.clone());
605 let parsed = crate::parsing::parse_str(&source.source, id)
606 .parse_errs_as_err()
607 .unwrap();
608 exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Kcl(parsed, None));
609 Ok(id)
610 }
611 }
612 }
613
614 pub(super) async fn exec_module_for_items(
615 &self,
616 module_id: ModuleId,
617 exec_state: &mut ExecState,
618 source_range: SourceRange,
619 ) -> Result<(EnvironmentRef, Vec<String>), KclError> {
620 let path = exec_state.global.module_infos[&module_id].path.clone();
621 let mut repr = exec_state.global.module_infos[&module_id].take_repr();
622 let result = match &mut repr {
625 ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
626 ModuleRepr::Kcl(_, Some((_, env_ref, items, _))) => Ok((*env_ref, items.clone())),
627 ModuleRepr::Kcl(program, cache) => self
628 .exec_module_from_ast(program, module_id, &path, exec_state, source_range, false)
629 .await
630 .map(|(val, er, items, module_artifacts)| {
631 *cache = Some((val, er, items.clone(), module_artifacts.clone()));
632 (er, items)
633 }),
634 ModuleRepr::Foreign(geom, _) => Err(KclError::new_semantic(KclErrorDetails::new(
635 "Cannot import items from foreign modules".to_owned(),
636 vec![geom.source_range],
637 ))),
638 ModuleRepr::Dummy => unreachable!("Looking up {}, but it is still being interpreted", path),
639 };
640
641 exec_state.global.module_infos[&module_id].restore_repr(repr);
642 result
643 }
644
645 async fn exec_module_for_result(
646 &self,
647 module_id: ModuleId,
648 exec_state: &mut ExecState,
649 source_range: SourceRange,
650 ) -> Result<Option<KclValue>, 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((val, _, _, _))) => Ok(val.clone()),
658 ModuleRepr::Kcl(program, cached_items) => {
659 let result = self
660 .exec_module_from_ast(program, module_id, &path, exec_state, source_range, false)
661 .await;
662 match result {
663 Ok((val, env, items, module_artifacts)) => {
664 *cached_items = Some((val.clone(), env, items, module_artifacts));
665 Ok(val)
666 }
667 Err(e) => Err(e),
668 }
669 }
670 ModuleRepr::Foreign(_, Some((imported, _))) => Ok(imported.clone()),
671 ModuleRepr::Foreign(geom, cached) => {
672 let result = super::import::send_to_engine(geom.clone(), exec_state, self)
673 .await
674 .map(|geom| Some(KclValue::ImportedGeometry(geom)));
675
676 match result {
677 Ok(val) => {
678 *cached = Some((val.clone(), exec_state.mod_local.artifacts.clone()));
679 Ok(val)
680 }
681 Err(e) => Err(e),
682 }
683 }
684 ModuleRepr::Dummy => unreachable!(),
685 };
686
687 exec_state.global.module_infos[&module_id].restore_repr(repr);
688
689 result
690 }
691
692 pub async fn exec_module_from_ast(
693 &self,
694 program: &Node<Program>,
695 module_id: ModuleId,
696 path: &ModulePath,
697 exec_state: &mut ExecState,
698 source_range: SourceRange,
699 preserve_mem: bool,
700 ) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>, ModuleArtifactState), KclError> {
701 exec_state.global.mod_loader.enter_module(path);
702 let result = self
703 .exec_module_body(program, exec_state, preserve_mem, module_id, path)
704 .await;
705 exec_state.global.mod_loader.leave_module(path);
706
707 result.map_err(|(err, _, _)| {
710 if let KclError::ImportCycle { .. } = err {
711 err.override_source_ranges(vec![source_range])
713 } else {
714 KclError::new_semantic(KclErrorDetails::new(
716 format!(
717 "Error loading imported file ({path}). Open it to view more details.\n {}",
718 err.message()
719 ),
720 vec![source_range],
721 ))
722 }
723 })
724 }
725
726 #[async_recursion]
727 pub(crate) async fn execute_expr<'a: 'async_recursion>(
728 &self,
729 init: &Expr,
730 exec_state: &mut ExecState,
731 metadata: &Metadata,
732 annotations: &[Node<Annotation>],
733 statement_kind: StatementKind<'a>,
734 ) -> Result<KclValue, KclError> {
735 let item = match init {
736 Expr::None(none) => KclValue::from(none),
737 Expr::Literal(literal) => KclValue::from_literal((**literal).clone(), exec_state),
738 Expr::TagDeclarator(tag) => tag.execute(exec_state).await?,
739 Expr::Name(name) => {
740 let being_declared = exec_state.mod_local.being_declared.clone();
741 let value = name
742 .get_result(exec_state, self)
743 .await
744 .map_err(|e| var_in_own_ref_err(e, &being_declared))?
745 .clone();
746 if let KclValue::Module { value: module_id, meta } = value {
747 self.exec_module_for_result(
748 module_id,
749 exec_state,
750 metadata.source_range
751 ).await?
752 .unwrap_or_else(|| {
753 exec_state.warn(CompilationError::err(
754 metadata.source_range,
755 "Imported module has no return value. The last statement of the module must be an expression, usually the Solid.",
756 ),
757 annotations::WARN_MOD_RETURN_VALUE);
758
759 let mut new_meta = vec![metadata.to_owned()];
760 new_meta.extend(meta);
761 KclValue::KclNone {
762 value: Default::default(),
763 meta: new_meta,
764 }
765 })
766 } else {
767 value
768 }
769 }
770 Expr::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, self).await?,
771 Expr::FunctionExpression(function_expression) => {
772 let attrs = annotations::get_fn_attrs(annotations, metadata.source_range)?;
773 let experimental = attrs.map(|a| a.experimental).unwrap_or_default();
774 let is_std = matches!(&exec_state.mod_local.path, ModulePath::Std { .. });
775
776 let include_in_feature_tree = attrs.unwrap_or_default().include_in_feature_tree;
778 if let Some(attrs) = attrs
779 && (attrs.impl_ == annotations::Impl::Rust || attrs.impl_ == annotations::Impl::RustConstraint)
780 {
781 if let ModulePath::Std { value: std_path } = &exec_state.mod_local.path {
782 let (func, props) = crate::std::std_fn(std_path, statement_kind.expect_name());
783 KclValue::Function {
784 value: Box::new(FunctionSource::rust(func, function_expression.clone(), props, attrs)),
785 meta: vec![metadata.to_owned()],
786 }
787 } else {
788 return Err(KclError::new_semantic(KclErrorDetails::new(
789 "Rust implementation of functions is restricted to the standard library".to_owned(),
790 vec![metadata.source_range],
791 )));
792 }
793 } else {
794 KclValue::Function {
798 value: Box::new(FunctionSource::kcl(
799 function_expression.clone(),
800 exec_state.mut_stack().snapshot(),
801 KclFunctionSourceParams {
802 is_std,
803 experimental,
804 include_in_feature_tree,
805 },
806 )),
807 meta: vec![metadata.to_owned()],
808 }
809 }
810 }
811 Expr::CallExpressionKw(call_expression) => call_expression.execute(exec_state, self).await?,
812 Expr::PipeExpression(pipe_expression) => pipe_expression.get_result(exec_state, self).await?,
813 Expr::PipeSubstitution(pipe_substitution) => match statement_kind {
814 StatementKind::Declaration { name } => {
815 let message = format!(
816 "you cannot declare variable {name} as %, because % can only be used in function calls"
817 );
818
819 return Err(KclError::new_semantic(KclErrorDetails::new(
820 message,
821 vec![pipe_substitution.into()],
822 )));
823 }
824 StatementKind::Expression => match exec_state.mod_local.pipe_value.clone() {
825 Some(x) => x,
826 None => {
827 return Err(KclError::new_semantic(KclErrorDetails::new(
828 "cannot use % outside a pipe expression".to_owned(),
829 vec![pipe_substitution.into()],
830 )));
831 }
832 },
833 },
834 Expr::ArrayExpression(array_expression) => array_expression.execute(exec_state, self).await?,
835 Expr::ArrayRangeExpression(range_expression) => range_expression.execute(exec_state, self).await?,
836 Expr::ObjectExpression(object_expression) => object_expression.execute(exec_state, self).await?,
837 Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state, self).await?,
838 Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
839 Expr::IfExpression(expr) => expr.get_result(exec_state, self).await?,
840 Expr::LabelledExpression(expr) => {
841 let result = self
842 .execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
843 .await?;
844 exec_state
845 .mut_stack()
846 .add(expr.label.name.clone(), result.clone(), init.into())?;
847 result
849 }
850 Expr::AscribedExpression(expr) => expr.get_result(exec_state, self).await?,
851 Expr::SketchBlock(expr) => expr.get_result(exec_state, self).await?,
852 Expr::SketchVar(expr) => expr.get_result(exec_state, self).await?,
853 };
854 Ok(item)
855 }
856}
857
858fn var_in_own_ref_err(e: KclError, being_declared: &Option<String>) -> KclError {
861 let KclError::UndefinedValue { name, mut details } = e else {
862 return e;
863 };
864 if let (Some(name0), Some(name1)) = (&being_declared, &name)
868 && name0 == name1
869 {
870 details.message = format!(
871 "You can't use `{name0}` because you're currently trying to define it. Use a different variable here instead."
872 );
873 }
874 KclError::UndefinedValue { details, name }
875}
876
877impl Node<AscribedExpression> {
878 #[async_recursion]
879 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
880 let metadata = Metadata {
881 source_range: SourceRange::from(self),
882 };
883 let result = ctx
884 .execute_expr(&self.expr, exec_state, &metadata, &[], StatementKind::Expression)
885 .await?;
886 apply_ascription(&result, &self.ty, exec_state, self.into())
887 }
888}
889
890impl Node<SketchBlock> {
891 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
892 if exec_state.mod_local.sketch_block.is_some() {
893 return Err(KclError::new_semantic(KclErrorDetails::new(
895 "Cannot execute a sketch block from within another sketch block".to_owned(),
896 vec![SourceRange::from(self)],
897 )));
898 }
899
900 let (return_result, variables, sketch_block_state) = {
901 self.prep_mem(exec_state.mut_stack().snapshot(), exec_state);
903
904 let original_value = exec_state.mod_local.sketch_block.replace(SketchBlockState::default());
906
907 let result = ctx.exec_block(&self.body, exec_state, BodyType::Block).await;
908
909 let sketch_block_state = std::mem::replace(&mut exec_state.mod_local.sketch_block, original_value);
910
911 let block_variables = exec_state
912 .stack()
913 .find_all_in_current_env()
914 .map(|(name, value)| (name.clone(), value.clone()))
915 .collect::<IndexMap<_, _>>();
916
917 exec_state.mut_stack().pop_env();
918
919 (result, block_variables, sketch_block_state)
920 };
921
922 return_result?;
924 let Some(sketch_block_state) = sketch_block_state else {
925 debug_assert!(false, "Sketch block state should still be set to Some from just above");
926 return Err(KclError::new_internal(KclErrorDetails::new(
927 "Sketch block state should still be set to Some from just above".to_owned(),
928 vec![SourceRange::from(self)],
929 )));
930 };
931
932 let range = SourceRange::from(self);
934 let constraints = &sketch_block_state.constraints;
935 let initial_guesses = sketch_block_state
936 .sketch_vars
937 .iter()
938 .map(|v| {
939 let Some(sketch_var) = v.as_sketch_var() else {
940 return Err(KclError::new_internal(KclErrorDetails::new(
941 "Expected sketch variable".to_owned(),
942 vec![SourceRange::from(self)],
943 )));
944 };
945 let constraint_id = sketch_var.id.to_constraint_id(range)?;
946 let number_value = KclValue::Number {
948 value: sketch_var.initial_value,
949 ty: sketch_var.ty,
950 meta: sketch_var.meta.clone(),
951 };
952 let initial_guess_value =
953 normalize_to_solver_unit(&number_value, v.into(), exec_state, "sketch variable initial value")?;
954 let initial_guess = if let Some(n) = initial_guess_value.as_ty_f64() {
955 n.n
956 } else {
957 let message = format!(
958 "Expected number after coercion, but found {}",
959 initial_guess_value.human_friendly_type()
960 );
961 debug_assert!(false, "{}", &message);
962 return Err(KclError::new_internal(KclErrorDetails::new(
963 message,
964 vec![SourceRange::from(self)],
965 )));
966 };
967 Ok((constraint_id, initial_guess))
968 })
969 .collect::<Result<Vec<_>, KclError>>()?;
970 let config = kcl_ezpz::Config::default();
972 let solve_outcome = kcl_ezpz::solve(constraints, initial_guesses, config).map_err(|e| {
973 KclError::new_internal(KclErrorDetails::new(
974 format!("Error from constraint solver: {}", e.error),
975 vec![SourceRange::from(self)],
976 ))
977 })?;
978 for warning in &solve_outcome.warnings {
980 let message = if let Some(index) = warning.about_constraint.as_ref() {
981 format!("{}; constraint index {}", &warning.content, index)
982 } else {
983 format!("{}", &warning.content)
984 };
985 exec_state.warn(CompilationError::err(range, message), annotations::WARN_SOLVER);
986 }
987 let variables =
989 substitute_sketch_vars(variables, &solve_outcome.final_values, solver_numeric_type(exec_state))?;
990
991 let metadata = Metadata {
992 source_range: SourceRange::from(self),
993 };
994 Ok(KclValue::Object {
995 value: variables,
996 constrainable: Default::default(),
997 meta: vec![metadata],
998 })
999 }
1000}
1001
1002fn solver_unit(exec_state: &ExecState) -> UnitLength {
1003 exec_state.length_unit()
1004}
1005
1006fn solver_numeric_type(exec_state: &ExecState) -> NumericType {
1007 NumericType::Known(UnitType::Length(solver_unit(exec_state)))
1008}
1009
1010fn normalize_to_solver_unit(
1013 value: &KclValue,
1014 source_range: SourceRange,
1015 exec_state: &mut ExecState,
1016 description: &str,
1017) -> Result<KclValue, KclError> {
1018 let length_ty = RuntimeType::Primitive(PrimitiveType::Number(solver_numeric_type(exec_state)));
1019 value.coerce(&length_ty, true, exec_state).map_err(|_| {
1020 KclError::new_semantic(KclErrorDetails::new(
1021 format!(
1022 "{} must be a length coercible to the module length unit {}, but found {}",
1023 description,
1024 length_ty.human_friendly_type(),
1025 value.human_friendly_type(),
1026 ),
1027 vec![source_range],
1028 ))
1029 })
1030}
1031
1032fn substitute_sketch_vars(
1033 variables: IndexMap<String, KclValue>,
1034 solutions: &[f64],
1035 solution_ty: NumericType,
1036) -> Result<HashMap<String, KclValue>, KclError> {
1037 let mut subbed = HashMap::with_capacity(variables.len());
1038 for (name, value) in variables {
1039 let subbed_value = substitute_sketch_var(value, solutions, solution_ty)?;
1040 subbed.insert(name, subbed_value);
1041 }
1042 Ok(subbed)
1043}
1044
1045fn substitute_sketch_var(value: KclValue, solutions: &[f64], solution_ty: NumericType) -> Result<KclValue, KclError> {
1046 match value {
1047 KclValue::Uuid { .. } => Ok(value),
1048 KclValue::Bool { .. } => Ok(value),
1049 KclValue::Number { .. } => Ok(value),
1050 KclValue::String { .. } => Ok(value),
1051 KclValue::SketchVar { value: var } => {
1052 let Some(solution) = solutions.get(var.id.0) else {
1053 let message = format!("No solution for sketch variable with id {}", var.id.0);
1054 debug_assert!(false, "{}", &message);
1055 return Err(KclError::new_internal(KclErrorDetails::new(
1056 message,
1057 var.meta.into_iter().map(|m| m.source_range).collect(),
1058 )));
1059 };
1060 Ok(KclValue::Number {
1061 value: *solution,
1062 ty: solution_ty,
1063 meta: var.meta.clone(),
1064 })
1065 }
1066 KclValue::Tuple { value, meta } => {
1067 let subbed = value
1068 .into_iter()
1069 .map(|v| substitute_sketch_var(v, solutions, solution_ty))
1070 .collect::<Result<Vec<_>, KclError>>()?;
1071 Ok(KclValue::Tuple { value: subbed, meta })
1072 }
1073 KclValue::HomArray { value, ty } => {
1074 let subbed = value
1075 .into_iter()
1076 .map(|v| substitute_sketch_var(v, solutions, solution_ty))
1077 .collect::<Result<Vec<_>, KclError>>()?;
1078 Ok(KclValue::HomArray { value: subbed, ty })
1079 }
1080 KclValue::Object {
1081 value,
1082 constrainable,
1083 meta,
1084 } => {
1085 let subbed = value
1086 .into_iter()
1087 .map(|(k, v)| substitute_sketch_var(v, solutions, solution_ty).map(|v| (k, v)))
1088 .collect::<Result<HashMap<_, _>, KclError>>()?;
1089 Ok(KclValue::Object {
1090 value: subbed,
1091 constrainable,
1092 meta,
1093 })
1094 }
1095 KclValue::TagIdentifier(_) => Ok(value),
1096 KclValue::TagDeclarator(_) => Ok(value),
1097 KclValue::GdtAnnotation { .. } => Ok(value),
1098 KclValue::Plane { .. } => Ok(value),
1099 KclValue::Face { .. } => Ok(value),
1100 KclValue::Sketch { .. } => Ok(value),
1101 KclValue::Solid { .. } => Ok(value),
1102 KclValue::Helix { .. } => Ok(value),
1103 KclValue::ImportedGeometry(_) => Ok(value),
1104 KclValue::Function { .. } => Ok(value),
1105 KclValue::Module { .. } => Ok(value),
1106 KclValue::Type { .. } => Ok(value),
1107 KclValue::KclNone { .. } => Ok(value),
1108 }
1109}
1110
1111impl SketchBlock {
1112 fn prep_mem(&self, parent: EnvironmentRef, exec_state: &mut ExecState) {
1113 exec_state.mut_stack().push_new_env_for_call(parent);
1114 }
1115}
1116
1117impl Node<SketchVar> {
1118 pub async fn get_result(&self, exec_state: &mut ExecState, _ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1119 let Some(sketch_block_state) = &exec_state.mod_local.sketch_block else {
1120 return Err(KclError::new_semantic(KclErrorDetails::new(
1121 "Cannot use a sketch variable outside of a sketch block".to_owned(),
1122 vec![SourceRange::from(self)],
1123 )));
1124 };
1125 let id = sketch_block_state.next_sketch_var_id();
1126 let sketch_var = if let Some(initial) = &self.initial {
1127 KclValue::from_sketch_var_literal(initial, id, exec_state)
1128 } else {
1129 let metadata = Metadata {
1130 source_range: SourceRange::from(self),
1131 };
1132
1133 KclValue::SketchVar {
1134 value: Box::new(super::SketchVar {
1135 id,
1136 initial_value: 0.0,
1137 ty: NumericType::default(),
1138 meta: vec![metadata],
1139 }),
1140 }
1141 };
1142
1143 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
1144 return Err(KclError::new_semantic(KclErrorDetails::new(
1145 "Cannot use a sketch variable outside of a sketch block".to_owned(),
1146 vec![SourceRange::from(self)],
1147 )));
1148 };
1149 sketch_block_state.sketch_vars.push(sketch_var.clone());
1150
1151 Ok(sketch_var)
1152 }
1153}
1154
1155fn apply_ascription(
1156 value: &KclValue,
1157 ty: &Node<Type>,
1158 exec_state: &mut ExecState,
1159 source_range: SourceRange,
1160) -> Result<KclValue, KclError> {
1161 let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into(), false)
1162 .map_err(|e| KclError::new_semantic(e.into()))?;
1163
1164 if matches!(&ty, &RuntimeType::Primitive(PrimitiveType::Number(..))) {
1165 exec_state.clear_units_warnings(&source_range);
1166 }
1167
1168 value.coerce(&ty, false, exec_state).map_err(|_| {
1169 let suggestion = if ty == RuntimeType::length() {
1170 ", you might try coercing to a fully specified numeric type such as `mm`"
1171 } else if ty == RuntimeType::angle() {
1172 ", you might try coercing to a fully specified numeric type such as `deg`"
1173 } else {
1174 ""
1175 };
1176 let ty_str = if let Some(ty) = value.principal_type() {
1177 format!("(with type `{ty}`) ")
1178 } else {
1179 String::new()
1180 };
1181 KclError::new_semantic(KclErrorDetails::new(
1182 format!(
1183 "could not coerce {} {ty_str}to type `{ty}`{suggestion}",
1184 value.human_friendly_type()
1185 ),
1186 vec![source_range],
1187 ))
1188 })
1189}
1190
1191impl BinaryPart {
1192 #[async_recursion]
1193 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1194 match self {
1195 BinaryPart::Literal(literal) => Ok(KclValue::from_literal((**literal).clone(), exec_state)),
1196 BinaryPart::Name(name) => name.get_result(exec_state, ctx).await.cloned(),
1197 BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
1198 BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await,
1199 BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
1200 BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state, ctx).await,
1201 BinaryPart::ArrayExpression(e) => e.execute(exec_state, ctx).await,
1202 BinaryPart::ArrayRangeExpression(e) => e.execute(exec_state, ctx).await,
1203 BinaryPart::ObjectExpression(e) => e.execute(exec_state, ctx).await,
1204 BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
1205 BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
1206 BinaryPart::SketchVar(e) => e.get_result(exec_state, ctx).await,
1207 }
1208 }
1209}
1210
1211impl Node<Name> {
1212 pub(super) async fn get_result<'a>(
1213 &self,
1214 exec_state: &'a mut ExecState,
1215 ctx: &ExecutorContext,
1216 ) -> Result<&'a KclValue, KclError> {
1217 let being_declared = exec_state.mod_local.being_declared.clone();
1218 self.get_result_inner(exec_state, ctx)
1219 .await
1220 .map_err(|e| var_in_own_ref_err(e, &being_declared))
1221 }
1222
1223 async fn get_result_inner<'a>(
1224 &self,
1225 exec_state: &'a mut ExecState,
1226 ctx: &ExecutorContext,
1227 ) -> Result<&'a KclValue, KclError> {
1228 if self.abs_path {
1229 return Err(KclError::new_semantic(KclErrorDetails::new(
1230 "Absolute paths (names beginning with `::` are not yet supported)".to_owned(),
1231 self.as_source_ranges(),
1232 )));
1233 }
1234
1235 let mod_name = format!("{}{}", memory::MODULE_PREFIX, self.name.name);
1236
1237 if self.path.is_empty() {
1238 let item_value = exec_state.stack().get(&self.name.name, self.into());
1239 if item_value.is_ok() {
1240 return item_value;
1241 }
1242 return exec_state.stack().get(&mod_name, self.into());
1243 }
1244
1245 let mut mem_spec: Option<(EnvironmentRef, Vec<String>)> = None;
1246 for p in &self.path {
1247 let value = match mem_spec {
1248 Some((env, exports)) => {
1249 if !exports.contains(&p.name) {
1250 return Err(KclError::new_semantic(KclErrorDetails::new(
1251 format!("Item {} not found in module's exported items", p.name),
1252 p.as_source_ranges(),
1253 )));
1254 }
1255
1256 exec_state
1257 .stack()
1258 .memory
1259 .get_from(&p.name, env, p.as_source_range(), 0)?
1260 }
1261 None => exec_state
1262 .stack()
1263 .get(&format!("{}{}", memory::MODULE_PREFIX, p.name), self.into())?,
1264 };
1265
1266 let KclValue::Module { value: module_id, .. } = value else {
1267 return Err(KclError::new_semantic(KclErrorDetails::new(
1268 format!(
1269 "Identifier in path must refer to a module, found {}",
1270 value.human_friendly_type()
1271 ),
1272 p.as_source_ranges(),
1273 )));
1274 };
1275
1276 mem_spec = Some(
1277 ctx.exec_module_for_items(*module_id, exec_state, p.as_source_range())
1278 .await?,
1279 );
1280 }
1281
1282 let (env, exports) = mem_spec.unwrap();
1283
1284 let item_exported = exports.contains(&self.name.name);
1285 let item_value = exec_state
1286 .stack()
1287 .memory
1288 .get_from(&self.name.name, env, self.name.as_source_range(), 0);
1289
1290 if item_exported && item_value.is_ok() {
1292 return item_value;
1293 }
1294
1295 let mod_exported = exports.contains(&mod_name);
1296 let mod_value = exec_state
1297 .stack()
1298 .memory
1299 .get_from(&mod_name, env, self.name.as_source_range(), 0);
1300
1301 if mod_exported && mod_value.is_ok() {
1303 return mod_value;
1304 }
1305
1306 if item_value.is_err() && mod_value.is_err() {
1308 return item_value;
1309 }
1310
1311 debug_assert!((item_value.is_ok() && !item_exported) || (mod_value.is_ok() && !mod_exported));
1313 Err(KclError::new_semantic(KclErrorDetails::new(
1314 format!("Item {} not found in module's exported items", self.name.name),
1315 self.name.as_source_ranges(),
1316 )))
1317 }
1318}
1319
1320impl Node<MemberExpression> {
1321 async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1322 let meta = Metadata {
1323 source_range: SourceRange::from(self),
1324 };
1325 let property = Property::try_from(
1326 self.computed,
1327 self.property.clone(),
1328 exec_state,
1329 self.into(),
1330 ctx,
1331 &meta,
1332 &[],
1333 StatementKind::Expression,
1334 )
1335 .await?;
1336 let object = ctx
1337 .execute_expr(&self.object, exec_state, &meta, &[], StatementKind::Expression)
1338 .await?;
1339
1340 match (object, property, self.computed) {
1342 (KclValue::Plane { value: plane }, Property::String(property), false) => match property.as_str() {
1343 "zAxis" => {
1344 let (p, u) = plane.info.z_axis.as_3_dims();
1345 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]))
1346 }
1347 "yAxis" => {
1348 let (p, u) = plane.info.y_axis.as_3_dims();
1349 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]))
1350 }
1351 "xAxis" => {
1352 let (p, u) = plane.info.x_axis.as_3_dims();
1353 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]))
1354 }
1355 "origin" => {
1356 let (p, u) = plane.info.origin.as_3_dims();
1357 Ok(KclValue::array_from_point3d(p, u.into(), vec![meta]))
1358 }
1359 other => Err(KclError::new_undefined_value(
1360 KclErrorDetails::new(
1361 format!("Property '{other}' not found in plane"),
1362 vec![self.clone().into()],
1363 ),
1364 None,
1365 )),
1366 },
1367 (KclValue::Object { value: map, .. }, Property::String(property), false) => {
1368 if let Some(value) = map.get(&property) {
1369 Ok(value.to_owned())
1370 } else {
1371 Err(KclError::new_undefined_value(
1372 KclErrorDetails::new(
1373 format!("Property '{property}' not found in object"),
1374 vec![self.clone().into()],
1375 ),
1376 None,
1377 ))
1378 }
1379 }
1380 (KclValue::Object { .. }, Property::String(property), true) => {
1381 Err(KclError::new_semantic(KclErrorDetails::new(
1382 format!("Cannot index object with string; use dot notation instead, e.g. `obj.{property}`"),
1383 vec![self.clone().into()],
1384 )))
1385 }
1386 (KclValue::Object { value: map, .. }, p @ Property::UInt(i), _) => {
1387 if i == 0
1388 && let Some(value) = map.get("x")
1389 {
1390 return Ok(value.to_owned());
1391 }
1392 if i == 1
1393 && let Some(value) = map.get("y")
1394 {
1395 return Ok(value.to_owned());
1396 }
1397 if i == 2
1398 && let Some(value) = map.get("z")
1399 {
1400 return Ok(value.to_owned());
1401 }
1402 let t = p.type_name();
1403 let article = article_for(t);
1404 Err(KclError::new_semantic(KclErrorDetails::new(
1405 format!("Only strings can be used as the property of an object, but you're using {article} {t}",),
1406 vec![self.clone().into()],
1407 )))
1408 }
1409 (KclValue::HomArray { value: arr, .. }, Property::UInt(index), _) => {
1410 let value_of_arr = arr.get(index);
1411 if let Some(value) = value_of_arr {
1412 Ok(value.to_owned())
1413 } else {
1414 Err(KclError::new_undefined_value(
1415 KclErrorDetails::new(
1416 format!("The array doesn't have any item at index {index}"),
1417 vec![self.clone().into()],
1418 ),
1419 None,
1420 ))
1421 }
1422 }
1423 (obj, Property::UInt(0), _) => Ok(obj),
1426 (KclValue::HomArray { .. }, p, _) => {
1427 let t = p.type_name();
1428 let article = article_for(t);
1429 Err(KclError::new_semantic(KclErrorDetails::new(
1430 format!("Only integers >= 0 can be used as the index of an array, but you're using {article} {t}",),
1431 vec![self.clone().into()],
1432 )))
1433 }
1434 (KclValue::Solid { value }, Property::String(prop), false) if prop == "sketch" => Ok(KclValue::Sketch {
1435 value: Box::new(value.sketch),
1436 }),
1437 (geometry @ KclValue::Solid { .. }, Property::String(prop), false) if prop == "tags" => {
1438 Err(KclError::new_semantic(KclErrorDetails::new(
1440 format!(
1441 "Property `{prop}` not found on {}. You can get a solid's tags through its sketch, as in, `exampleSolid.sketch.tags`.",
1442 geometry.human_friendly_type()
1443 ),
1444 vec![self.clone().into()],
1445 )))
1446 }
1447 (KclValue::Sketch { value: sk }, Property::String(prop), false) if prop == "tags" => Ok(KclValue::Object {
1448 meta: vec![Metadata {
1449 source_range: SourceRange::from(self.clone()),
1450 }],
1451 value: sk
1452 .tags
1453 .iter()
1454 .map(|(k, tag)| (k.to_owned(), KclValue::TagIdentifier(Box::new(tag.to_owned()))))
1455 .collect(),
1456 constrainable: false,
1457 }),
1458 (geometry @ (KclValue::Sketch { .. } | KclValue::Solid { .. }), Property::String(property), false) => {
1459 Err(KclError::new_semantic(KclErrorDetails::new(
1460 format!("Property `{property}` not found on {}", geometry.human_friendly_type()),
1461 vec![self.clone().into()],
1462 )))
1463 }
1464 (being_indexed, _, _) => Err(KclError::new_semantic(KclErrorDetails::new(
1465 format!(
1466 "Only arrays can be indexed, but you're trying to index {}",
1467 being_indexed.human_friendly_type()
1468 ),
1469 vec![self.clone().into()],
1470 ))),
1471 }
1472 }
1473}
1474
1475impl Node<BinaryExpression> {
1476 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1477 enum State {
1478 EvaluateLeft(Node<BinaryExpression>),
1479 FromLeft {
1480 node: Node<BinaryExpression>,
1481 },
1482 EvaluateRight {
1483 node: Node<BinaryExpression>,
1484 left: KclValue,
1485 },
1486 FromRight {
1487 node: Node<BinaryExpression>,
1488 left: KclValue,
1489 },
1490 }
1491
1492 let mut stack = vec![State::EvaluateLeft(self.clone())];
1493 let mut last_result: Option<KclValue> = None;
1494
1495 while let Some(state) = stack.pop() {
1496 match state {
1497 State::EvaluateLeft(node) => {
1498 let left_part = node.left.clone();
1499 match left_part {
1500 BinaryPart::BinaryExpression(child) => {
1501 stack.push(State::FromLeft { node });
1502 stack.push(State::EvaluateLeft(*child));
1503 }
1504 part => {
1505 let left_value = part.get_result(exec_state, ctx).await?;
1506 stack.push(State::EvaluateRight { node, left: left_value });
1507 }
1508 }
1509 }
1510 State::FromLeft { node } => {
1511 let Some(left_value) = last_result.take() else {
1512 return Err(Self::missing_result_error(&node));
1513 };
1514 stack.push(State::EvaluateRight { node, left: left_value });
1515 }
1516 State::EvaluateRight { node, left } => {
1517 let right_part = node.right.clone();
1518 match right_part {
1519 BinaryPart::BinaryExpression(child) => {
1520 stack.push(State::FromRight { node, left });
1521 stack.push(State::EvaluateLeft(*child));
1522 }
1523 part => {
1524 let right_value = part.get_result(exec_state, ctx).await?;
1525 let result = node.apply_operator(exec_state, ctx, left, right_value).await?;
1526 last_result = Some(result);
1527 }
1528 }
1529 }
1530 State::FromRight { node, left } => {
1531 let Some(right_value) = last_result.take() else {
1532 return Err(Self::missing_result_error(&node));
1533 };
1534 let result = node.apply_operator(exec_state, ctx, left, right_value).await?;
1535 last_result = Some(result);
1536 }
1537 }
1538 }
1539
1540 last_result.ok_or_else(|| Self::missing_result_error(self))
1541 }
1542
1543 async fn apply_operator(
1544 &self,
1545 exec_state: &mut ExecState,
1546 ctx: &ExecutorContext,
1547 left_value: KclValue,
1548 right_value: KclValue,
1549 ) -> Result<KclValue, KclError> {
1550 let mut meta = left_value.metadata();
1551 meta.extend(right_value.metadata());
1552
1553 if self.operator == BinaryOperator::Add
1555 && let (KclValue::String { value: left, .. }, KclValue::String { value: right, .. }) =
1556 (&left_value, &right_value)
1557 {
1558 return Ok(KclValue::String {
1559 value: format!("{left}{right}"),
1560 meta,
1561 });
1562 }
1563
1564 if self.operator == BinaryOperator::Add || self.operator == BinaryOperator::Or {
1566 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
1567 let args = Args::new_no_args(self.into(), ctx.clone());
1568 let result = crate::std::csg::inner_union(
1569 vec![*left.clone(), *right.clone()],
1570 Default::default(),
1571 exec_state,
1572 args,
1573 )
1574 .await?;
1575 return Ok(result.into());
1576 }
1577 } else if self.operator == BinaryOperator::Sub {
1578 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
1580 let args = Args::new_no_args(self.into(), ctx.clone());
1581 let result = crate::std::csg::inner_subtract(
1582 vec![*left.clone()],
1583 vec![*right.clone()],
1584 Default::default(),
1585 exec_state,
1586 args,
1587 )
1588 .await?;
1589 return Ok(result.into());
1590 }
1591 } else if self.operator == BinaryOperator::And
1592 && let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value)
1593 {
1594 let args = Args::new_no_args(self.into(), ctx.clone());
1596 let result = crate::std::csg::inner_intersect(
1597 vec![*left.clone(), *right.clone()],
1598 Default::default(),
1599 exec_state,
1600 args,
1601 )
1602 .await?;
1603 return Ok(result.into());
1604 }
1605
1606 if self.operator == BinaryOperator::Or || self.operator == BinaryOperator::And {
1608 let KclValue::Bool { value: left_value, .. } = left_value else {
1609 return Err(KclError::new_semantic(KclErrorDetails::new(
1610 format!(
1611 "Cannot apply logical operator to non-boolean value: {}",
1612 left_value.human_friendly_type()
1613 ),
1614 vec![self.left.clone().into()],
1615 )));
1616 };
1617 let KclValue::Bool { value: right_value, .. } = right_value else {
1618 return Err(KclError::new_semantic(KclErrorDetails::new(
1619 format!(
1620 "Cannot apply logical operator to non-boolean value: {}",
1621 right_value.human_friendly_type()
1622 ),
1623 vec![self.right.clone().into()],
1624 )));
1625 };
1626 let raw_value = match self.operator {
1627 BinaryOperator::Or => left_value || right_value,
1628 BinaryOperator::And => left_value && right_value,
1629 _ => unreachable!(),
1630 };
1631 return Ok(KclValue::Bool { value: raw_value, meta });
1632 }
1633
1634 if self.operator == BinaryOperator::Eq && exec_state.mod_local.sketch_block.is_some() {
1636 match (&left_value, &right_value) {
1637 (KclValue::SketchVar { value: left_value, .. }, KclValue::SketchVar { value: right_value, .. })
1639 if left_value.id == right_value.id =>
1640 {
1641 return Ok(KclValue::Bool { value: true, meta });
1642 }
1643 (KclValue::SketchVar { .. }, KclValue::SketchVar { .. }) => {
1645 return Err(KclError::new_semantic(KclErrorDetails::new(
1648 "TODO: Different sketch variables".to_owned(),
1649 vec![self.into()],
1650 )));
1651 }
1652 (KclValue::SketchVar { value: var, .. }, input_number @ KclValue::Number { .. })
1654 | (input_number @ KclValue::Number { .. }, KclValue::SketchVar { value: var, .. }) => {
1655 let number_value = normalize_to_solver_unit(
1656 input_number,
1657 input_number.into(),
1658 exec_state,
1659 "fixed constraint value",
1660 )?;
1661 let Some(n) = number_value.as_ty_f64() else {
1662 let message = format!(
1663 "Expected number after coercion, but found {}",
1664 number_value.human_friendly_type()
1665 );
1666 debug_assert!(false, "{}", &message);
1667 return Err(KclError::new_internal(KclErrorDetails::new(message, vec![self.into()])));
1668 };
1669 let constraint = Constraint::Fixed(var.id.to_constraint_id(self.as_source_range())?, n.n);
1670 let Some(sketch_block_state) = &mut exec_state.mod_local.sketch_block else {
1671 let message = "Being inside a sketch block should have already been checked above".to_owned();
1672 debug_assert!(false, "{}", &message);
1673 return Err(KclError::new_internal(KclErrorDetails::new(
1674 message,
1675 vec![SourceRange::from(self)],
1676 )));
1677 };
1678 sketch_block_state.constraints.push(constraint);
1679 return Ok(KclValue::Bool { value: true, meta });
1680 }
1681 _ => {
1682 return Err(KclError::new_semantic(KclErrorDetails::new(
1683 format!(
1684 "Cannot create an equivalence constraint between values of these types: {} and {}",
1685 left_value.human_friendly_type(),
1686 right_value.human_friendly_type()
1687 ),
1688 vec![self.into()],
1689 )));
1690 }
1691 }
1692 }
1693
1694 let left = number_as_f64(&left_value, self.left.clone().into())?;
1695 let right = number_as_f64(&right_value, self.right.clone().into())?;
1696
1697 let value = match self.operator {
1698 BinaryOperator::Add => {
1699 let (l, r, ty) = NumericType::combine_eq_coerce(left, right, None);
1700 self.warn_on_unknown(&ty, "Adding", exec_state);
1701 KclValue::Number { value: l + r, meta, ty }
1702 }
1703 BinaryOperator::Sub => {
1704 let (l, r, ty) = NumericType::combine_eq_coerce(left, right, None);
1705 self.warn_on_unknown(&ty, "Subtracting", exec_state);
1706 KclValue::Number { value: l - r, meta, ty }
1707 }
1708 BinaryOperator::Mul => {
1709 let (l, r, ty) = NumericType::combine_mul(left, right);
1710 self.warn_on_unknown(&ty, "Multiplying", exec_state);
1711 KclValue::Number { value: l * r, meta, ty }
1712 }
1713 BinaryOperator::Div => {
1714 let (l, r, ty) = NumericType::combine_div(left, right);
1715 self.warn_on_unknown(&ty, "Dividing", exec_state);
1716 KclValue::Number { value: l / r, meta, ty }
1717 }
1718 BinaryOperator::Mod => {
1719 let (l, r, ty) = NumericType::combine_mod(left, right);
1720 self.warn_on_unknown(&ty, "Modulo of", exec_state);
1721 KclValue::Number { value: l % r, meta, ty }
1722 }
1723 BinaryOperator::Pow => KclValue::Number {
1724 value: left.n.powf(right.n),
1725 meta,
1726 ty: exec_state.current_default_units(),
1727 },
1728 BinaryOperator::Neq => {
1729 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
1730 self.warn_on_unknown(&ty, "Comparing", exec_state);
1731 KclValue::Bool { value: l != r, meta }
1732 }
1733 BinaryOperator::Gt => {
1734 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
1735 self.warn_on_unknown(&ty, "Comparing", exec_state);
1736 KclValue::Bool { value: l > r, meta }
1737 }
1738 BinaryOperator::Gte => {
1739 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
1740 self.warn_on_unknown(&ty, "Comparing", exec_state);
1741 KclValue::Bool { value: l >= r, meta }
1742 }
1743 BinaryOperator::Lt => {
1744 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
1745 self.warn_on_unknown(&ty, "Comparing", exec_state);
1746 KclValue::Bool { value: l < r, meta }
1747 }
1748 BinaryOperator::Lte => {
1749 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
1750 self.warn_on_unknown(&ty, "Comparing", exec_state);
1751 KclValue::Bool { value: l <= r, meta }
1752 }
1753 BinaryOperator::Eq => {
1754 let (l, r, ty) = NumericType::combine_eq(left, right, exec_state, self.as_source_range());
1755 self.warn_on_unknown(&ty, "Comparing", exec_state);
1756 KclValue::Bool { value: l == r, meta }
1757 }
1758 BinaryOperator::And | BinaryOperator::Or => unreachable!(),
1759 };
1760
1761 Ok(value)
1762 }
1763
1764 fn missing_result_error(node: &Node<BinaryExpression>) -> KclError {
1765 KclError::new_internal(KclErrorDetails::new(
1766 "missing result while evaluating binary expression".to_owned(),
1767 vec![SourceRange::from(node)],
1768 ))
1769 }
1770
1771 fn warn_on_unknown(&self, ty: &NumericType, verb: &str, exec_state: &mut ExecState) {
1772 if ty == &NumericType::Unknown {
1773 let sr = self.as_source_range();
1774 exec_state.clear_units_warnings(&sr);
1775 let mut err = CompilationError::err(
1776 sr,
1777 format!(
1778 "{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)`."
1779 ),
1780 );
1781 err.tag = crate::errors::Tag::UnknownNumericUnits;
1782 exec_state.warn(err, annotations::WARN_UNKNOWN_UNITS);
1783 }
1784 }
1785}
1786
1787impl Node<UnaryExpression> {
1788 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1789 if self.operator == UnaryOperator::Not {
1790 let value = self.argument.get_result(exec_state, ctx).await?;
1791 let KclValue::Bool {
1792 value: bool_value,
1793 meta: _,
1794 } = value
1795 else {
1796 return Err(KclError::new_semantic(KclErrorDetails::new(
1797 format!(
1798 "Cannot apply unary operator ! to non-boolean value: {}",
1799 value.human_friendly_type()
1800 ),
1801 vec![self.into()],
1802 )));
1803 };
1804 let meta = vec![Metadata {
1805 source_range: self.into(),
1806 }];
1807 let negated = KclValue::Bool {
1808 value: !bool_value,
1809 meta,
1810 };
1811
1812 return Ok(negated);
1813 }
1814
1815 let value = &self.argument.get_result(exec_state, ctx).await?;
1816 let err = || {
1817 KclError::new_semantic(KclErrorDetails::new(
1818 format!(
1819 "You can only negate numbers, planes, or lines, but this is a {}",
1820 value.human_friendly_type()
1821 ),
1822 vec![self.into()],
1823 ))
1824 };
1825 match value {
1826 KclValue::Number { value, ty, .. } => {
1827 let meta = vec![Metadata {
1828 source_range: self.into(),
1829 }];
1830 Ok(KclValue::Number {
1831 value: -value,
1832 meta,
1833 ty: *ty,
1834 })
1835 }
1836 KclValue::Plane { value } => {
1837 let mut plane = value.clone();
1838 if plane.info.x_axis.x != 0.0 {
1839 plane.info.x_axis.x *= -1.0;
1840 }
1841 if plane.info.x_axis.y != 0.0 {
1842 plane.info.x_axis.y *= -1.0;
1843 }
1844 if plane.info.x_axis.z != 0.0 {
1845 plane.info.x_axis.z *= -1.0;
1846 }
1847
1848 plane.value = PlaneType::Uninit;
1849 plane.id = exec_state.next_uuid();
1850 Ok(KclValue::Plane { value: plane })
1851 }
1852 KclValue::Object {
1853 value: values, meta, ..
1854 } => {
1855 let Some(direction) = values.get("direction") else {
1857 return Err(err());
1858 };
1859
1860 let direction = match direction {
1861 KclValue::Tuple { value: values, meta } => {
1862 let values = values
1863 .iter()
1864 .map(|v| match v {
1865 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
1866 value: *value * -1.0,
1867 ty: *ty,
1868 meta: meta.clone(),
1869 }),
1870 _ => Err(err()),
1871 })
1872 .collect::<Result<Vec<_>, _>>()?;
1873
1874 KclValue::Tuple {
1875 value: values,
1876 meta: meta.clone(),
1877 }
1878 }
1879 KclValue::HomArray {
1880 value: values,
1881 ty: ty @ RuntimeType::Primitive(PrimitiveType::Number(_)),
1882 } => {
1883 let values = values
1884 .iter()
1885 .map(|v| match v {
1886 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
1887 value: *value * -1.0,
1888 ty: *ty,
1889 meta: meta.clone(),
1890 }),
1891 _ => Err(err()),
1892 })
1893 .collect::<Result<Vec<_>, _>>()?;
1894
1895 KclValue::HomArray {
1896 value: values,
1897 ty: ty.clone(),
1898 }
1899 }
1900 _ => return Err(err()),
1901 };
1902
1903 let mut value = values.clone();
1904 value.insert("direction".to_owned(), direction);
1905 Ok(KclValue::Object {
1906 value,
1907 meta: meta.clone(),
1908 constrainable: false,
1909 })
1910 }
1911 _ => Err(err()),
1912 }
1913 }
1914}
1915
1916pub(crate) async fn execute_pipe_body(
1917 exec_state: &mut ExecState,
1918 body: &[Expr],
1919 source_range: SourceRange,
1920 ctx: &ExecutorContext,
1921) -> Result<KclValue, KclError> {
1922 let Some((first, body)) = body.split_first() else {
1923 return Err(KclError::new_semantic(KclErrorDetails::new(
1924 "Pipe expressions cannot be empty".to_owned(),
1925 vec![source_range],
1926 )));
1927 };
1928 let meta = Metadata {
1933 source_range: SourceRange::from(first),
1934 };
1935 let output = ctx
1936 .execute_expr(first, exec_state, &meta, &[], StatementKind::Expression)
1937 .await?;
1938
1939 let previous_pipe_value = exec_state.mod_local.pipe_value.replace(output);
1943 let result = inner_execute_pipe_body(exec_state, body, ctx).await;
1945 exec_state.mod_local.pipe_value = previous_pipe_value;
1947
1948 result
1949}
1950
1951#[async_recursion]
1954async fn inner_execute_pipe_body(
1955 exec_state: &mut ExecState,
1956 body: &[Expr],
1957 ctx: &ExecutorContext,
1958) -> Result<KclValue, KclError> {
1959 for expression in body {
1960 if let Expr::TagDeclarator(_) = expression {
1961 return Err(KclError::new_semantic(KclErrorDetails::new(
1962 format!("This cannot be in a PipeExpression: {expression:?}"),
1963 vec![expression.into()],
1964 )));
1965 }
1966 let metadata = Metadata {
1967 source_range: SourceRange::from(expression),
1968 };
1969 let output = ctx
1970 .execute_expr(expression, exec_state, &metadata, &[], StatementKind::Expression)
1971 .await?;
1972 exec_state.mod_local.pipe_value = Some(output);
1973 }
1974 let final_output = exec_state.mod_local.pipe_value.take().unwrap();
1976 Ok(final_output)
1977}
1978
1979impl Node<TagDeclarator> {
1980 pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
1981 let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
1982 value: self.name.clone(),
1983 info: Vec::new(),
1984 meta: vec![Metadata {
1985 source_range: self.into(),
1986 }],
1987 }));
1988
1989 exec_state
1990 .mut_stack()
1991 .add(self.name.clone(), memory_item.clone(), self.into())?;
1992
1993 Ok(self.into())
1994 }
1995}
1996
1997impl Node<ArrayExpression> {
1998 #[async_recursion]
1999 pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
2000 let mut results = Vec::with_capacity(self.elements.len());
2001
2002 for element in &self.elements {
2003 let metadata = Metadata::from(element);
2004 let value = ctx
2007 .execute_expr(element, exec_state, &metadata, &[], StatementKind::Expression)
2008 .await?;
2009
2010 results.push(value);
2011 }
2012
2013 Ok(KclValue::HomArray {
2014 value: results,
2015 ty: RuntimeType::Primitive(PrimitiveType::Any),
2016 })
2017 }
2018}
2019
2020impl Node<ArrayRangeExpression> {
2021 #[async_recursion]
2022 pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
2023 let metadata = Metadata::from(&self.start_element);
2024 let start_val = ctx
2025 .execute_expr(
2026 &self.start_element,
2027 exec_state,
2028 &metadata,
2029 &[],
2030 StatementKind::Expression,
2031 )
2032 .await?;
2033 let (start, start_ty) = start_val
2034 .as_int_with_ty()
2035 .ok_or(KclError::new_semantic(KclErrorDetails::new(
2036 format!("Expected int but found {}", start_val.human_friendly_type()),
2037 vec![self.into()],
2038 )))?;
2039 let metadata = Metadata::from(&self.end_element);
2040 let end_val = ctx
2041 .execute_expr(&self.end_element, exec_state, &metadata, &[], StatementKind::Expression)
2042 .await?;
2043 let (end, end_ty) = end_val
2044 .as_int_with_ty()
2045 .ok_or(KclError::new_semantic(KclErrorDetails::new(
2046 format!("Expected int but found {}", end_val.human_friendly_type()),
2047 vec![self.into()],
2048 )))?;
2049
2050 if start_ty != end_ty {
2051 let start = start_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: start_ty });
2052 let start = fmt::human_display_number(start.n, start.ty);
2053 let end = end_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: end_ty });
2054 let end = fmt::human_display_number(end.n, end.ty);
2055 return Err(KclError::new_semantic(KclErrorDetails::new(
2056 format!("Range start and end must be of the same type, but found {start} and {end}"),
2057 vec![self.into()],
2058 )));
2059 }
2060
2061 if end < start {
2062 return Err(KclError::new_semantic(KclErrorDetails::new(
2063 format!("Range start is greater than range end: {start} .. {end}"),
2064 vec![self.into()],
2065 )));
2066 }
2067
2068 let range: Vec<_> = if self.end_inclusive {
2069 (start..=end).collect()
2070 } else {
2071 (start..end).collect()
2072 };
2073
2074 let meta = vec![Metadata {
2075 source_range: self.into(),
2076 }];
2077
2078 Ok(KclValue::HomArray {
2079 value: range
2080 .into_iter()
2081 .map(|num| KclValue::Number {
2082 value: num as f64,
2083 ty: start_ty,
2084 meta: meta.clone(),
2085 })
2086 .collect(),
2087 ty: RuntimeType::Primitive(PrimitiveType::Number(start_ty)),
2088 })
2089 }
2090}
2091
2092impl Node<ObjectExpression> {
2093 #[async_recursion]
2094 pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
2095 let mut object = HashMap::with_capacity(self.properties.len());
2096 for property in &self.properties {
2097 let metadata = Metadata::from(&property.value);
2098 let result = ctx
2099 .execute_expr(&property.value, exec_state, &metadata, &[], StatementKind::Expression)
2100 .await?;
2101
2102 object.insert(property.key.name.clone(), result);
2103 }
2104
2105 Ok(KclValue::Object {
2106 value: object,
2107 meta: vec![Metadata {
2108 source_range: self.into(),
2109 }],
2110 constrainable: false,
2111 })
2112 }
2113}
2114
2115fn article_for<S: AsRef<str>>(s: S) -> &'static str {
2116 if s.as_ref().starts_with(['a', 'e', 'i', 'o', 'u', '[']) {
2118 "an"
2119 } else {
2120 "a"
2121 }
2122}
2123
2124fn number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<TyF64, KclError> {
2125 v.as_ty_f64().ok_or_else(|| {
2126 let actual_type = v.human_friendly_type();
2127 KclError::new_semantic(KclErrorDetails::new(
2128 format!("Expected a number, but found {actual_type}",),
2129 vec![source_range],
2130 ))
2131 })
2132}
2133
2134impl Node<IfExpression> {
2135 #[async_recursion]
2136 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
2137 let cond = ctx
2139 .execute_expr(
2140 &self.cond,
2141 exec_state,
2142 &Metadata::from(self),
2143 &[],
2144 StatementKind::Expression,
2145 )
2146 .await?
2147 .get_bool()?;
2148 if cond {
2149 let block_result = ctx.exec_block(&*self.then_val, exec_state, BodyType::Block).await?;
2150 return Ok(block_result.unwrap());
2154 }
2155
2156 for else_if in &self.else_ifs {
2158 let cond = ctx
2159 .execute_expr(
2160 &else_if.cond,
2161 exec_state,
2162 &Metadata::from(self),
2163 &[],
2164 StatementKind::Expression,
2165 )
2166 .await?
2167 .get_bool()?;
2168 if cond {
2169 let block_result = ctx.exec_block(&*else_if.then_val, exec_state, BodyType::Block).await?;
2170 return Ok(block_result.unwrap());
2174 }
2175 }
2176
2177 ctx.exec_block(&*self.final_else, exec_state, BodyType::Block)
2179 .await
2180 .map(|expr| expr.unwrap())
2181 }
2182}
2183
2184#[derive(Debug)]
2185enum Property {
2186 UInt(usize),
2187 String(String),
2188}
2189
2190impl Property {
2191 #[allow(clippy::too_many_arguments)]
2192 async fn try_from<'a>(
2193 computed: bool,
2194 value: Expr,
2195 exec_state: &mut ExecState,
2196 sr: SourceRange,
2197 ctx: &ExecutorContext,
2198 metadata: &Metadata,
2199 annotations: &[Node<Annotation>],
2200 statement_kind: StatementKind<'a>,
2201 ) -> Result<Self, KclError> {
2202 let property_sr = vec![sr];
2203 if !computed {
2204 let Expr::Name(identifier) = value else {
2205 return Err(KclError::new_semantic(KclErrorDetails::new(
2207 "Object expressions like `obj.property` must use simple identifier names, not complex expressions"
2208 .to_owned(),
2209 property_sr,
2210 )));
2211 };
2212 return Ok(Property::String(identifier.to_string()));
2213 }
2214
2215 let prop_value = ctx
2216 .execute_expr(&value, exec_state, metadata, annotations, statement_kind)
2217 .await?;
2218 match prop_value {
2219 KclValue::Number { value, ty, meta: _ } => {
2220 if !matches!(
2221 ty,
2222 NumericType::Unknown
2223 | NumericType::Default { .. }
2224 | NumericType::Known(crate::exec::UnitType::Count)
2225 ) {
2226 return Err(KclError::new_semantic(KclErrorDetails::new(
2227 format!(
2228 "{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"
2229 ),
2230 property_sr,
2231 )));
2232 }
2233 if let Some(x) = crate::try_f64_to_usize(value) {
2234 Ok(Property::UInt(x))
2235 } else {
2236 Err(KclError::new_semantic(KclErrorDetails::new(
2237 format!("{value} is not a valid index, indices must be whole numbers >= 0"),
2238 property_sr,
2239 )))
2240 }
2241 }
2242 _ => Err(KclError::new_semantic(KclErrorDetails::new(
2243 "Only numbers (>= 0) can be indexes".to_owned(),
2244 vec![sr],
2245 ))),
2246 }
2247 }
2248}
2249
2250impl Property {
2251 fn type_name(&self) -> &'static str {
2252 match self {
2253 Property::UInt(_) => "number",
2254 Property::String(_) => "string",
2255 }
2256 }
2257}
2258
2259impl Node<PipeExpression> {
2260 #[async_recursion]
2261 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
2262 execute_pipe_body(exec_state, &self.body, self.into(), ctx).await
2263 }
2264}
2265
2266#[cfg(test)]
2267mod test {
2268 use std::sync::Arc;
2269
2270 use tokio::io::AsyncWriteExt;
2271
2272 use super::*;
2273 use crate::{
2274 ExecutorSettings,
2275 errors::Severity,
2276 exec::UnitType,
2277 execution::{ContextType, parse_execute},
2278 };
2279
2280 #[tokio::test(flavor = "multi_thread")]
2281 async fn ascription() {
2282 let program = r#"
2283a = 42: number
2284b = a: number
2285p = {
2286 origin = { x = 0, y = 0, z = 0 },
2287 xAxis = { x = 1, y = 0, z = 0 },
2288 yAxis = { x = 0, y = 1, z = 0 },
2289 zAxis = { x = 0, y = 0, z = 1 }
2290}: Plane
2291arr1 = [42]: [number(cm)]
2292"#;
2293
2294 let result = parse_execute(program).await.unwrap();
2295 let mem = result.exec_state.stack();
2296 assert!(matches!(
2297 mem.memory
2298 .get_from("p", result.mem_env, SourceRange::default(), 0)
2299 .unwrap(),
2300 KclValue::Plane { .. }
2301 ));
2302 let arr1 = mem
2303 .memory
2304 .get_from("arr1", result.mem_env, SourceRange::default(), 0)
2305 .unwrap();
2306 if let KclValue::HomArray { value, ty } = arr1 {
2307 assert_eq!(value.len(), 1, "Expected Vec with specific length: found {value:?}");
2308 assert_eq!(
2309 *ty,
2310 RuntimeType::known_length(kittycad_modeling_cmds::units::UnitLength::Centimeters)
2311 );
2312 if let KclValue::Number { value, ty, .. } = &value[0] {
2314 assert_eq!(*value, 42.0);
2316 assert_eq!(
2317 *ty,
2318 NumericType::Known(UnitType::Length(kittycad_modeling_cmds::units::UnitLength::Centimeters))
2319 );
2320 } else {
2321 panic!("Expected a number; found {:?}", value[0]);
2322 }
2323 } else {
2324 panic!("Expected HomArray; found {arr1:?}");
2325 }
2326
2327 let program = r#"
2328a = 42: string
2329"#;
2330 let result = parse_execute(program).await;
2331 let err = result.unwrap_err();
2332 assert!(
2333 err.to_string()
2334 .contains("could not coerce a number (with type `number`) to type `string`"),
2335 "Expected error but found {err:?}"
2336 );
2337
2338 let program = r#"
2339a = 42: Plane
2340"#;
2341 let result = parse_execute(program).await;
2342 let err = result.unwrap_err();
2343 assert!(
2344 err.to_string()
2345 .contains("could not coerce a number (with type `number`) to type `Plane`"),
2346 "Expected error but found {err:?}"
2347 );
2348
2349 let program = r#"
2350arr = [0]: [string]
2351"#;
2352 let result = parse_execute(program).await;
2353 let err = result.unwrap_err();
2354 assert!(
2355 err.to_string().contains(
2356 "could not coerce an array of `number` with 1 value (with type `[any; 1]`) to type `[string]`"
2357 ),
2358 "Expected error but found {err:?}"
2359 );
2360
2361 let program = r#"
2362mixedArr = [0, "a"]: [number(mm)]
2363"#;
2364 let result = parse_execute(program).await;
2365 let err = result.unwrap_err();
2366 assert!(
2367 err.to_string().contains(
2368 "could not coerce an array of `number`, `string` (with type `[any; 2]`) to type `[number(mm)]`"
2369 ),
2370 "Expected error but found {err:?}"
2371 );
2372
2373 let program = r#"
2374mixedArr = [0, "a"]: [mm]
2375"#;
2376 let result = parse_execute(program).await;
2377 let err = result.unwrap_err();
2378 assert!(
2379 err.to_string().contains(
2380 "could not coerce an array of `number`, `string` (with type `[any; 2]`) to type `[number(mm)]`"
2381 ),
2382 "Expected error but found {err:?}"
2383 );
2384 }
2385
2386 #[tokio::test(flavor = "multi_thread")]
2387 async fn neg_plane() {
2388 let program = r#"
2389p = {
2390 origin = { x = 0, y = 0, z = 0 },
2391 xAxis = { x = 1, y = 0, z = 0 },
2392 yAxis = { x = 0, y = 1, z = 0 },
2393}: Plane
2394p2 = -p
2395"#;
2396
2397 let result = parse_execute(program).await.unwrap();
2398 let mem = result.exec_state.stack();
2399 match mem
2400 .memory
2401 .get_from("p2", result.mem_env, SourceRange::default(), 0)
2402 .unwrap()
2403 {
2404 KclValue::Plane { value } => {
2405 assert_eq!(value.info.x_axis.x, -1.0);
2406 assert_eq!(value.info.x_axis.y, 0.0);
2407 assert_eq!(value.info.x_axis.z, 0.0);
2408 }
2409 _ => unreachable!(),
2410 }
2411 }
2412
2413 #[tokio::test(flavor = "multi_thread")]
2414 async fn multiple_returns() {
2415 let program = r#"fn foo() {
2416 return 0
2417 return 42
2418}
2419
2420a = foo()
2421"#;
2422
2423 let result = parse_execute(program).await;
2424 assert!(result.unwrap_err().to_string().contains("return"));
2425 }
2426
2427 #[tokio::test(flavor = "multi_thread")]
2428 async fn load_all_modules() {
2429 let program_a_kcl = r#"
2431export a = 1
2432"#;
2433 let program_b_kcl = r#"
2435import a from 'a.kcl'
2436
2437export b = a + 1
2438"#;
2439 let program_c_kcl = r#"
2441import a from 'a.kcl'
2442
2443export c = a + 2
2444"#;
2445
2446 let main_kcl = r#"
2448import b from 'b.kcl'
2449import c from 'c.kcl'
2450
2451d = b + c
2452"#;
2453
2454 let main = crate::parsing::parse_str(main_kcl, ModuleId::default())
2455 .parse_errs_as_err()
2456 .unwrap();
2457
2458 let tmpdir = tempfile::TempDir::with_prefix("zma_kcl_load_all_modules").unwrap();
2459
2460 tokio::fs::File::create(tmpdir.path().join("main.kcl"))
2461 .await
2462 .unwrap()
2463 .write_all(main_kcl.as_bytes())
2464 .await
2465 .unwrap();
2466
2467 tokio::fs::File::create(tmpdir.path().join("a.kcl"))
2468 .await
2469 .unwrap()
2470 .write_all(program_a_kcl.as_bytes())
2471 .await
2472 .unwrap();
2473
2474 tokio::fs::File::create(tmpdir.path().join("b.kcl"))
2475 .await
2476 .unwrap()
2477 .write_all(program_b_kcl.as_bytes())
2478 .await
2479 .unwrap();
2480
2481 tokio::fs::File::create(tmpdir.path().join("c.kcl"))
2482 .await
2483 .unwrap()
2484 .write_all(program_c_kcl.as_bytes())
2485 .await
2486 .unwrap();
2487
2488 let exec_ctxt = ExecutorContext {
2489 engine: Arc::new(Box::new(
2490 crate::engine::conn_mock::EngineConnection::new()
2491 .map_err(|err| {
2492 KclError::new_internal(KclErrorDetails::new(
2493 format!("Failed to create mock engine connection: {err}"),
2494 vec![SourceRange::default()],
2495 ))
2496 })
2497 .unwrap(),
2498 )),
2499 fs: Arc::new(crate::fs::FileManager::new()),
2500 settings: ExecutorSettings {
2501 project_directory: Some(crate::TypedPath(tmpdir.path().into())),
2502 ..Default::default()
2503 },
2504 context_type: ContextType::Mock,
2505 };
2506 let mut exec_state = ExecState::new(&exec_ctxt);
2507
2508 exec_ctxt
2509 .run(
2510 &crate::Program {
2511 ast: main.clone(),
2512 original_file_contents: "".to_owned(),
2513 },
2514 &mut exec_state,
2515 )
2516 .await
2517 .unwrap();
2518 }
2519
2520 #[tokio::test(flavor = "multi_thread")]
2521 async fn user_coercion() {
2522 let program = r#"fn foo(x: Axis2d) {
2523 return 0
2524}
2525
2526foo(x = { direction = [0, 0], origin = [0, 0]})
2527"#;
2528
2529 parse_execute(program).await.unwrap();
2530
2531 let program = r#"fn foo(x: Axis3d) {
2532 return 0
2533}
2534
2535foo(x = { direction = [0, 0], origin = [0, 0]})
2536"#;
2537
2538 parse_execute(program).await.unwrap_err();
2539 }
2540
2541 #[tokio::test(flavor = "multi_thread")]
2542 async fn coerce_return() {
2543 let program = r#"fn foo(): number(mm) {
2544 return 42
2545}
2546
2547a = foo()
2548"#;
2549
2550 parse_execute(program).await.unwrap();
2551
2552 let program = r#"fn foo(): mm {
2553 return 42
2554}
2555
2556a = foo()
2557"#;
2558
2559 parse_execute(program).await.unwrap();
2560
2561 let program = r#"fn foo(): number(mm) {
2562 return { bar: 42 }
2563}
2564
2565a = foo()
2566"#;
2567
2568 parse_execute(program).await.unwrap_err();
2569
2570 let program = r#"fn foo(): mm {
2571 return { bar: 42 }
2572}
2573
2574a = foo()
2575"#;
2576
2577 parse_execute(program).await.unwrap_err();
2578 }
2579
2580 #[tokio::test(flavor = "multi_thread")]
2581 async fn test_sensible_error_when_missing_equals_in_kwarg() {
2582 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)"]
2583 .into_iter()
2584 .enumerate()
2585 {
2586 let program = format!(
2587 "fn foo() {{ return 0 }}
2588z = 0
2589fn f(x, y, z) {{ return 0 }}
2590{call}"
2591 );
2592 let err = parse_execute(&program).await.unwrap_err();
2593 let msg = err.message();
2594 assert!(
2595 msg.contains("This argument needs a label, but it doesn't have one"),
2596 "failed test {i}: {msg}"
2597 );
2598 assert!(msg.contains("`y`"), "failed test {i}, missing `y`: {msg}");
2599 if i == 0 {
2600 assert!(msg.contains("`z`"), "failed test {i}, missing `z`: {msg}");
2601 }
2602 }
2603 }
2604
2605 #[tokio::test(flavor = "multi_thread")]
2606 async fn default_param_for_unlabeled() {
2607 let ast = r#"fn myExtrude(@sk, length) {
2610 return extrude(sk, length)
2611}
2612sketch001 = startSketchOn(XY)
2613 |> circle(center = [0, 0], radius = 93.75)
2614 |> myExtrude(length = 40)
2615"#;
2616
2617 parse_execute(ast).await.unwrap();
2618 }
2619
2620 #[tokio::test(flavor = "multi_thread")]
2621 async fn dont_use_unlabelled_as_input() {
2622 let ast = r#"length = 10
2624startSketchOn(XY)
2625 |> circle(center = [0, 0], radius = 93.75)
2626 |> extrude(length)
2627"#;
2628
2629 parse_execute(ast).await.unwrap();
2630 }
2631
2632 #[tokio::test(flavor = "multi_thread")]
2633 async fn ascription_in_binop() {
2634 let ast = r#"foo = tan(0): number(rad) - 4deg"#;
2635 parse_execute(ast).await.unwrap();
2636
2637 let ast = r#"foo = tan(0): rad - 4deg"#;
2638 parse_execute(ast).await.unwrap();
2639 }
2640
2641 #[tokio::test(flavor = "multi_thread")]
2642 async fn neg_sqrt() {
2643 let ast = r#"bad = sqrt(-2)"#;
2644
2645 let e = parse_execute(ast).await.unwrap_err();
2646 assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
2648 }
2649
2650 #[tokio::test(flavor = "multi_thread")]
2651 async fn non_array_fns() {
2652 let ast = r#"push(1, item = 2)
2653pop(1)
2654map(1, f = fn(@x) { return x + 1 })
2655reduce(1, f = fn(@x, accum) { return accum + x}, initial = 0)"#;
2656
2657 parse_execute(ast).await.unwrap();
2658 }
2659
2660 #[tokio::test(flavor = "multi_thread")]
2661 async fn non_array_indexing() {
2662 let good = r#"a = 42
2663good = a[0]
2664"#;
2665 let result = parse_execute(good).await.unwrap();
2666 let mem = result.exec_state.stack();
2667 let num = mem
2668 .memory
2669 .get_from("good", result.mem_env, SourceRange::default(), 0)
2670 .unwrap()
2671 .as_ty_f64()
2672 .unwrap();
2673 assert_eq!(num.n, 42.0);
2674
2675 let bad = r#"a = 42
2676bad = a[1]
2677"#;
2678
2679 parse_execute(bad).await.unwrap_err();
2680 }
2681
2682 #[tokio::test(flavor = "multi_thread")]
2683 async fn coerce_unknown_to_length() {
2684 let ast = r#"x = 2mm * 2mm
2685y = x: number(Length)"#;
2686 let e = parse_execute(ast).await.unwrap_err();
2687 assert!(
2688 e.message().contains("could not coerce"),
2689 "Error message: '{}'",
2690 e.message()
2691 );
2692
2693 let ast = r#"x = 2mm
2694y = x: number(Length)"#;
2695 let result = parse_execute(ast).await.unwrap();
2696 let mem = result.exec_state.stack();
2697 let num = mem
2698 .memory
2699 .get_from("y", result.mem_env, SourceRange::default(), 0)
2700 .unwrap()
2701 .as_ty_f64()
2702 .unwrap();
2703 assert_eq!(num.n, 2.0);
2704 assert_eq!(num.ty, NumericType::mm());
2705 }
2706
2707 #[tokio::test(flavor = "multi_thread")]
2708 async fn one_warning_unknown() {
2709 let ast = r#"
2710// Should warn once
2711a = PI * 2
2712// Should warn once
2713b = (PI * 2) / 3
2714// Should not warn
2715c = ((PI * 2) / 3): number(deg)
2716"#;
2717
2718 let result = parse_execute(ast).await.unwrap();
2719 assert_eq!(result.exec_state.errors().len(), 2);
2720 }
2721
2722 #[tokio::test(flavor = "multi_thread")]
2723 async fn non_count_indexing() {
2724 let ast = r#"x = [0, 0]
2725y = x[1mm]
2726"#;
2727 parse_execute(ast).await.unwrap_err();
2728
2729 let ast = r#"x = [0, 0]
2730y = 1deg
2731z = x[y]
2732"#;
2733 parse_execute(ast).await.unwrap_err();
2734
2735 let ast = r#"x = [0, 0]
2736y = x[0mm + 1]
2737"#;
2738 parse_execute(ast).await.unwrap_err();
2739 }
2740
2741 #[tokio::test(flavor = "multi_thread")]
2742 async fn getting_property_of_plane() {
2743 let ast = std::fs::read_to_string("tests/inputs/planestuff.kcl").unwrap();
2744 parse_execute(&ast).await.unwrap();
2745 }
2746
2747 #[cfg(feature = "artifact-graph")]
2748 #[tokio::test(flavor = "multi_thread")]
2749 async fn no_artifacts_from_within_hole_call() {
2750 let ast = std::fs::read_to_string("tests/inputs/sample_hole.kcl").unwrap();
2755 let out = parse_execute(&ast).await.unwrap();
2756
2757 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
2759
2760 let expected = 5;
2764 assert_eq!(
2765 actual_operations.len(),
2766 expected,
2767 "expected {expected} operations, received {}:\n{actual_operations:#?}",
2768 actual_operations.len(),
2769 );
2770 }
2771
2772 #[cfg(feature = "artifact-graph")]
2773 #[tokio::test(flavor = "multi_thread")]
2774 async fn feature_tree_annotation_on_user_defined_kcl() {
2775 let ast = std::fs::read_to_string("tests/inputs/feature_tree_annotation_on_user_defined_kcl.kcl").unwrap();
2778 let out = parse_execute(&ast).await.unwrap();
2779
2780 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
2782
2783 let expected = 0;
2784 assert_eq!(
2785 actual_operations.len(),
2786 expected,
2787 "expected {expected} operations, received {}:\n{actual_operations:#?}",
2788 actual_operations.len(),
2789 );
2790 }
2791
2792 #[cfg(feature = "artifact-graph")]
2793 #[tokio::test(flavor = "multi_thread")]
2794 async fn no_feature_tree_annotation_on_user_defined_kcl() {
2795 let ast = std::fs::read_to_string("tests/inputs/no_feature_tree_annotation_on_user_defined_kcl.kcl").unwrap();
2798 let out = parse_execute(&ast).await.unwrap();
2799
2800 let actual_operations = out.exec_state.global.root_module_artifacts.operations;
2802
2803 let expected = 2;
2804 assert_eq!(
2805 actual_operations.len(),
2806 expected,
2807 "expected {expected} operations, received {}:\n{actual_operations:#?}",
2808 actual_operations.len(),
2809 );
2810 assert!(matches!(actual_operations[0], Operation::GroupBegin { .. }));
2811 assert!(matches!(actual_operations[1], Operation::GroupEnd));
2812 }
2813
2814 #[tokio::test(flavor = "multi_thread")]
2815 async fn custom_warning() {
2816 let warn = r#"
2817a = PI * 2
2818"#;
2819 let result = parse_execute(warn).await.unwrap();
2820 assert_eq!(result.exec_state.errors().len(), 1);
2821 assert_eq!(result.exec_state.errors()[0].severity, Severity::Warning);
2822
2823 let allow = r#"
2824@warnings(allow = unknownUnits)
2825a = PI * 2
2826"#;
2827 let result = parse_execute(allow).await.unwrap();
2828 assert_eq!(result.exec_state.errors().len(), 0);
2829
2830 let deny = r#"
2831@warnings(deny = [unknownUnits])
2832a = PI * 2
2833"#;
2834 let result = parse_execute(deny).await.unwrap();
2835 assert_eq!(result.exec_state.errors().len(), 1);
2836 assert_eq!(result.exec_state.errors()[0].severity, Severity::Error);
2837 }
2838}