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