1use std::collections::HashMap;
2
3use async_recursion::async_recursion;
4
5use crate::{
6 errors::{KclError, KclErrorDetails},
7 execution::{
8 annotations,
9 fn_call::Args,
10 kcl_value::{FunctionSource, TypeDef},
11 memory,
12 state::ModuleState,
13 types::{NumericType, PrimitiveType, RuntimeType},
14 BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, PlaneType, StatementKind,
15 TagIdentifier,
16 },
17 fmt,
18 modules::{ModuleId, ModulePath, ModuleRepr},
19 parsing::ast::types::{
20 Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
21 BinaryPart, BodyItem, Expr, IfExpression, ImportPath, ImportSelector, ItemVisibility, LiteralIdentifier,
22 LiteralValue, MemberExpression, Name, Node, NodeRef, ObjectExpression, PipeExpression, Program, TagDeclarator,
23 Type, UnaryExpression, UnaryOperator,
24 },
25 source_range::SourceRange,
26 std::args::TyF64,
27 CompilationError,
28};
29
30impl<'a> StatementKind<'a> {
31 fn expect_name(&self) -> &'a str {
32 match self {
33 StatementKind::Declaration { name } => name,
34 StatementKind::Expression => unreachable!(),
35 }
36 }
37}
38
39impl ExecutorContext {
40 async fn handle_annotations(
42 &self,
43 annotations: impl Iterator<Item = &Node<Annotation>>,
44 body_type: BodyType,
45 exec_state: &mut ExecState,
46 ) -> Result<bool, KclError> {
47 let mut no_prelude = false;
48 for annotation in annotations {
49 if annotation.name() == Some(annotations::SETTINGS) {
50 if matches!(body_type, BodyType::Root) {
51 if exec_state.mod_local.settings.update_from_annotation(annotation)? {
52 exec_state.mod_local.explicit_length_units = true;
53 }
54 } else {
55 exec_state.err(CompilationError::err(
56 annotation.as_source_range(),
57 "Settings can only be modified at the top level scope of a file",
58 ));
59 }
60 } else if annotation.name() == Some(annotations::NO_PRELUDE) {
61 if matches!(body_type, BodyType::Root) {
62 no_prelude = true;
63 } else {
64 exec_state.err(CompilationError::err(
65 annotation.as_source_range(),
66 "The standard library can only be skipped at the top level scope of a file",
67 ));
68 }
69 } else {
70 exec_state.warn(CompilationError::err(
71 annotation.as_source_range(),
72 "Unknown annotation",
73 ));
74 }
75 }
76 Ok(no_prelude)
77 }
78
79 pub(super) async fn exec_module_body(
80 &self,
81 program: &Node<Program>,
82 exec_state: &mut ExecState,
83 preserve_mem: bool,
84 module_id: ModuleId,
85 path: &ModulePath,
86 ) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
87 crate::log::log(format!("enter module {path} {}", exec_state.stack()));
88
89 let mut local_state = ModuleState::new(path.clone(), exec_state.stack().memory.clone(), Some(module_id));
90 if !preserve_mem {
91 std::mem::swap(&mut exec_state.mod_local, &mut local_state);
92 }
93
94 let no_prelude = self
95 .handle_annotations(program.inner_attrs.iter(), crate::execution::BodyType::Root, exec_state)
96 .await?;
97
98 if !preserve_mem {
99 exec_state.mut_stack().push_new_root_env(!no_prelude);
100 }
101
102 let result = self
103 .exec_block(program, exec_state, crate::execution::BodyType::Root)
104 .await;
105
106 let env_ref = if preserve_mem {
107 exec_state.mut_stack().pop_and_preserve_env()
108 } else {
109 exec_state.mut_stack().pop_env()
110 };
111 if !preserve_mem {
112 std::mem::swap(&mut exec_state.mod_local, &mut local_state);
113 }
114
115 crate::log::log(format!("leave {path}"));
116
117 result.map(|result| (result, env_ref, local_state.module_exports))
118 }
119
120 #[async_recursion]
122 pub(super) async fn exec_block<'a>(
123 &'a self,
124 program: NodeRef<'a, Program>,
125 exec_state: &mut ExecState,
126 body_type: BodyType,
127 ) -> Result<Option<KclValue>, KclError> {
128 let mut last_expr = None;
129 for statement in &program.body {
131 match statement {
132 BodyItem::ImportStatement(import_stmt) => {
133 if !matches!(body_type, BodyType::Root) {
134 return Err(KclError::new_semantic(KclErrorDetails::new(
135 "Imports are only supported at the top-level of a file.".to_owned(),
136 vec![import_stmt.into()],
137 )));
138 }
139
140 let source_range = SourceRange::from(import_stmt);
141 let attrs = &import_stmt.outer_attrs;
142 let module_path = ModulePath::from_import_path(
143 &import_stmt.path,
144 &self.settings.project_directory,
145 &exec_state.mod_local.path,
146 )?;
147 let module_id = self
148 .open_module(&import_stmt.path, attrs, &module_path, exec_state, source_range)
149 .await?;
150
151 match &import_stmt.selector {
152 ImportSelector::List { items } => {
153 let (env_ref, module_exports) =
154 self.exec_module_for_items(module_id, exec_state, source_range).await?;
155 for import_item in items {
156 let mem = &exec_state.stack().memory;
158 let mut value = mem
159 .get_from(&import_item.name.name, env_ref, import_item.into(), 0)
160 .cloned();
161 let ty_name = format!("{}{}", memory::TYPE_PREFIX, import_item.name.name);
162 let mut ty = mem.get_from(&ty_name, env_ref, import_item.into(), 0).cloned();
163 let mod_name = format!("{}{}", memory::MODULE_PREFIX, import_item.name.name);
164 let mut mod_value = mem.get_from(&mod_name, env_ref, import_item.into(), 0).cloned();
165
166 if value.is_err() && ty.is_err() && mod_value.is_err() {
167 return Err(KclError::new_undefined_value(
168 KclErrorDetails::new(
169 format!("{} is not defined in module", import_item.name.name),
170 vec![SourceRange::from(&import_item.name)],
171 ),
172 None,
173 ));
174 }
175
176 if value.is_ok() && !module_exports.contains(&import_item.name.name) {
178 value = Err(KclError::new_semantic(KclErrorDetails::new(
179 format!(
180 "Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
181 import_item.name.name
182 ),
183 vec![SourceRange::from(&import_item.name)],
184 )));
185 }
186
187 if ty.is_ok() && !module_exports.contains(&ty_name) {
188 ty = Err(KclError::new_semantic(KclErrorDetails::new(format!(
189 "Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
190 import_item.name.name
191 ),
192 vec![SourceRange::from(&import_item.name)],)));
193 }
194
195 if mod_value.is_ok() && !module_exports.contains(&mod_name) {
196 mod_value = Err(KclError::new_semantic(KclErrorDetails::new(format!(
197 "Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
198 import_item.name.name
199 ),
200 vec![SourceRange::from(&import_item.name)],)));
201 }
202
203 if value.is_err() && ty.is_err() && mod_value.is_err() {
204 return value.map(Option::Some);
205 }
206
207 if let Ok(value) = value {
209 exec_state.mut_stack().add(
210 import_item.identifier().to_owned(),
211 value,
212 SourceRange::from(&import_item.name),
213 )?;
214
215 if let ItemVisibility::Export = import_stmt.visibility {
216 exec_state
217 .mod_local
218 .module_exports
219 .push(import_item.identifier().to_owned());
220 }
221 }
222
223 if let Ok(ty) = ty {
224 let ty_name = format!("{}{}", memory::TYPE_PREFIX, import_item.identifier());
225 exec_state.mut_stack().add(
226 ty_name.clone(),
227 ty,
228 SourceRange::from(&import_item.name),
229 )?;
230
231 if let ItemVisibility::Export = import_stmt.visibility {
232 exec_state.mod_local.module_exports.push(ty_name);
233 }
234 }
235
236 if let Ok(mod_value) = mod_value {
237 let mod_name = format!("{}{}", memory::MODULE_PREFIX, import_item.identifier());
238 exec_state.mut_stack().add(
239 mod_name.clone(),
240 mod_value,
241 SourceRange::from(&import_item.name),
242 )?;
243
244 if let ItemVisibility::Export = import_stmt.visibility {
245 exec_state.mod_local.module_exports.push(mod_name);
246 }
247 }
248 }
249 }
250 ImportSelector::Glob(_) => {
251 let (env_ref, module_exports) =
252 self.exec_module_for_items(module_id, exec_state, source_range).await?;
253 for name in module_exports.iter() {
254 let item = exec_state
255 .stack()
256 .memory
257 .get_from(name, env_ref, source_range, 0)
258 .map_err(|_err| {
259 KclError::new_internal(KclErrorDetails::new(
260 format!("{} is not defined in module (but was exported?)", name),
261 vec![source_range],
262 ))
263 })?
264 .clone();
265 exec_state.mut_stack().add(name.to_owned(), item, source_range)?;
266
267 if let ItemVisibility::Export = import_stmt.visibility {
268 exec_state.mod_local.module_exports.push(name.clone());
269 }
270 }
271 }
272 ImportSelector::None { .. } => {
273 let name = import_stmt.module_name().unwrap();
274 let item = KclValue::Module {
275 value: module_id,
276 meta: vec![source_range.into()],
277 };
278 exec_state.mut_stack().add(
279 format!("{}{}", memory::MODULE_PREFIX, name),
280 item,
281 source_range,
282 )?;
283 }
284 }
285 last_expr = None;
286 }
287 BodyItem::ExpressionStatement(expression_statement) => {
288 let metadata = Metadata::from(expression_statement);
289 last_expr = Some(
290 self.execute_expr(
291 &expression_statement.expression,
292 exec_state,
293 &metadata,
294 &[],
295 StatementKind::Expression,
296 )
297 .await?,
298 );
299 }
300 BodyItem::VariableDeclaration(variable_declaration) => {
301 let var_name = variable_declaration.declaration.id.name.to_string();
302 let source_range = SourceRange::from(&variable_declaration.declaration.init);
303 let metadata = Metadata { source_range };
304
305 let annotations = &variable_declaration.outer_attrs;
306
307 let lhs = variable_declaration.inner.name().to_owned();
310 let prev_being_declared = exec_state.mod_local.being_declared.take();
311 exec_state.mod_local.being_declared = Some(lhs);
312 let rhs_result = self
313 .execute_expr(
314 &variable_declaration.declaration.init,
315 exec_state,
316 &metadata,
317 annotations,
318 StatementKind::Declaration { name: &var_name },
319 )
320 .await;
321 exec_state.mod_local.being_declared = prev_being_declared;
323 let rhs = rhs_result?;
324
325 exec_state
326 .mut_stack()
327 .add(var_name.clone(), rhs.clone(), source_range)?;
328
329 if let ItemVisibility::Export = variable_declaration.visibility {
331 if matches!(body_type, BodyType::Root) {
332 exec_state.mod_local.module_exports.push(var_name);
333 } else {
334 exec_state.err(CompilationError::err(
335 variable_declaration.as_source_range(),
336 "Exports are only supported at the top-level of a file. Remove `export` or move it to the top-level.",
337 ));
338 }
339 }
340 last_expr = matches!(body_type, BodyType::Root).then_some(rhs);
342 }
343 BodyItem::TypeDeclaration(ty) => {
344 let metadata = Metadata::from(&**ty);
345 let impl_kind = annotations::get_impl(&ty.outer_attrs, metadata.source_range)?.unwrap_or_default();
346 match impl_kind {
347 annotations::Impl::Rust => {
348 let std_path = match &exec_state.mod_local.path {
349 ModulePath::Std { value } => value,
350 ModulePath::Local { .. } | ModulePath::Main => {
351 return Err(KclError::new_semantic(KclErrorDetails::new(
352 "User-defined types are not yet supported.".to_owned(),
353 vec![metadata.source_range],
354 )));
355 }
356 };
357 let (t, props) = crate::std::std_ty(std_path, &ty.name.name);
358 let value = KclValue::Type {
359 value: TypeDef::RustRepr(t, props),
360 meta: vec![metadata],
361 };
362 let name_in_mem = format!("{}{}", memory::TYPE_PREFIX, ty.name.name);
363 exec_state
364 .mut_stack()
365 .add(name_in_mem.clone(), value, metadata.source_range)
366 .map_err(|_| {
367 KclError::new_semantic(KclErrorDetails::new(
368 format!("Redefinition of type {}.", ty.name.name),
369 vec![metadata.source_range],
370 ))
371 })?;
372
373 if let ItemVisibility::Export = ty.visibility {
374 exec_state.mod_local.module_exports.push(name_in_mem);
375 }
376 }
377 annotations::Impl::Primitive => {}
379 annotations::Impl::Kcl => match &ty.alias {
380 Some(alias) => {
381 let value = KclValue::Type {
382 value: TypeDef::Alias(
383 RuntimeType::from_parsed(
384 alias.inner.clone(),
385 exec_state,
386 metadata.source_range,
387 )
388 .map_err(|e| KclError::new_semantic(e.into()))?,
389 ),
390 meta: vec![metadata],
391 };
392 let name_in_mem = format!("{}{}", memory::TYPE_PREFIX, ty.name.name);
393 exec_state
394 .mut_stack()
395 .add(name_in_mem.clone(), value, metadata.source_range)
396 .map_err(|_| {
397 KclError::new_semantic(KclErrorDetails::new(
398 format!("Redefinition of type {}.", ty.name.name),
399 vec![metadata.source_range],
400 ))
401 })?;
402
403 if let ItemVisibility::Export = ty.visibility {
404 exec_state.mod_local.module_exports.push(name_in_mem);
405 }
406 }
407 None => {
408 return Err(KclError::new_semantic(KclErrorDetails::new(
409 "User-defined types are not yet supported.".to_owned(),
410 vec![metadata.source_range],
411 )))
412 }
413 },
414 }
415
416 last_expr = None;
417 }
418 BodyItem::ReturnStatement(return_statement) => {
419 let metadata = Metadata::from(return_statement);
420
421 if matches!(body_type, BodyType::Root) {
422 return Err(KclError::new_semantic(KclErrorDetails::new(
423 "Cannot return from outside a function.".to_owned(),
424 vec![metadata.source_range],
425 )));
426 }
427
428 let value = self
429 .execute_expr(
430 &return_statement.argument,
431 exec_state,
432 &metadata,
433 &[],
434 StatementKind::Expression,
435 )
436 .await?;
437 exec_state
438 .mut_stack()
439 .add(memory::RETURN_NAME.to_owned(), value, metadata.source_range)
440 .map_err(|_| {
441 KclError::new_semantic(KclErrorDetails::new(
442 "Multiple returns from a single function.".to_owned(),
443 vec![metadata.source_range],
444 ))
445 })?;
446 last_expr = None;
447 }
448 }
449 }
450
451 if matches!(body_type, BodyType::Root) {
452 self.engine
454 .flush_batch(
455 true,
458 SourceRange::new(program.end, program.end, program.module_id),
459 )
460 .await?;
461 }
462
463 Ok(last_expr)
464 }
465
466 pub async fn open_module(
467 &self,
468 path: &ImportPath,
469 attrs: &[Node<Annotation>],
470 resolved_path: &ModulePath,
471 exec_state: &mut ExecState,
472 source_range: SourceRange,
473 ) -> Result<ModuleId, KclError> {
474 match path {
475 ImportPath::Kcl { .. } => {
476 exec_state.global.mod_loader.cycle_check(resolved_path, source_range)?;
477
478 if let Some(id) = exec_state.id_for_module(resolved_path) {
479 return Ok(id);
480 }
481
482 let id = exec_state.next_module_id();
483 exec_state.add_path_to_source_id(resolved_path.clone(), id);
485 let source = resolved_path.source(&self.fs, source_range).await?;
486 exec_state.add_id_to_source(id, source.clone());
487 let parsed = crate::parsing::parse_str(&source.source, id).parse_errs_as_err()?;
489 exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Kcl(parsed, None));
490
491 Ok(id)
492 }
493 ImportPath::Foreign { .. } => {
494 if let Some(id) = exec_state.id_for_module(resolved_path) {
495 return Ok(id);
496 }
497
498 let id = exec_state.next_module_id();
499 let path = resolved_path.expect_path();
500 exec_state.add_path_to_source_id(resolved_path.clone(), id);
502 let format = super::import::format_from_annotations(attrs, path, source_range)?;
503 let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?;
504 exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Foreign(geom, None));
505 Ok(id)
506 }
507 ImportPath::Std { .. } => {
508 if let Some(id) = exec_state.id_for_module(resolved_path) {
509 return Ok(id);
510 }
511
512 let id = exec_state.next_module_id();
513 exec_state.add_path_to_source_id(resolved_path.clone(), id);
515 let source = resolved_path.source(&self.fs, source_range).await?;
516 exec_state.add_id_to_source(id, source.clone());
517 let parsed = crate::parsing::parse_str(&source.source, id)
518 .parse_errs_as_err()
519 .unwrap();
520 exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Kcl(parsed, None));
521 Ok(id)
522 }
523 }
524 }
525
526 pub(super) async fn exec_module_for_items(
527 &self,
528 module_id: ModuleId,
529 exec_state: &mut ExecState,
530 source_range: SourceRange,
531 ) -> Result<(EnvironmentRef, Vec<String>), KclError> {
532 let path = exec_state.global.module_infos[&module_id].path.clone();
533 let mut repr = exec_state.global.module_infos[&module_id].take_repr();
534 let result = match &mut repr {
537 ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
538 ModuleRepr::Kcl(_, Some((_, env_ref, items))) => Ok((*env_ref, items.clone())),
539 ModuleRepr::Kcl(program, cache) => self
540 .exec_module_from_ast(program, module_id, &path, exec_state, source_range, false)
541 .await
542 .map(|(val, er, items)| {
543 *cache = Some((val, er, items.clone()));
544 (er, items)
545 }),
546 ModuleRepr::Foreign(geom, _) => Err(KclError::new_semantic(KclErrorDetails::new(
547 "Cannot import items from foreign modules".to_owned(),
548 vec![geom.source_range],
549 ))),
550 ModuleRepr::Dummy => unreachable!("Looking up {}, but it is still being interpreted", path),
551 };
552
553 exec_state.global.module_infos[&module_id].restore_repr(repr);
554 result
555 }
556
557 async fn exec_module_for_result(
558 &self,
559 module_id: ModuleId,
560 exec_state: &mut ExecState,
561 source_range: SourceRange,
562 ) -> Result<Option<KclValue>, KclError> {
563 let path = exec_state.global.module_infos[&module_id].path.clone();
564 let mut repr = exec_state.global.module_infos[&module_id].take_repr();
565 let result = match &mut repr {
568 ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
569 ModuleRepr::Kcl(_, Some((val, _, _))) => Ok(val.clone()),
570 ModuleRepr::Kcl(program, cached_items) => {
571 let result = self
572 .exec_module_from_ast(program, module_id, &path, exec_state, source_range, false)
573 .await;
574 match result {
575 Ok((val, env, items)) => {
576 *cached_items = Some((val.clone(), env, items));
577 Ok(val)
578 }
579 Err(e) => Err(e),
580 }
581 }
582 ModuleRepr::Foreign(_, Some(imported)) => Ok(Some(imported.clone())),
583 ModuleRepr::Foreign(geom, cached) => {
584 let result = super::import::send_to_engine(geom.clone(), self)
585 .await
586 .map(|geom| Some(KclValue::ImportedGeometry(geom)));
587
588 match result {
589 Ok(val) => {
590 *cached = val.clone();
591 Ok(val)
592 }
593 Err(e) => Err(e),
594 }
595 }
596 ModuleRepr::Dummy => unreachable!(),
597 };
598
599 exec_state.global.module_infos[&module_id].restore_repr(repr);
600
601 result
602 }
603
604 pub async fn exec_module_from_ast(
605 &self,
606 program: &Node<Program>,
607 module_id: ModuleId,
608 path: &ModulePath,
609 exec_state: &mut ExecState,
610 source_range: SourceRange,
611 preserve_mem: bool,
612 ) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
613 exec_state.global.mod_loader.enter_module(path);
614 let result = self
615 .exec_module_body(program, exec_state, preserve_mem, module_id, path)
616 .await;
617 exec_state.global.mod_loader.leave_module(path);
618
619 result.map_err(|err| {
620 if let KclError::ImportCycle { .. } = err {
621 err.override_source_ranges(vec![source_range])
623 } else {
624 KclError::new_semantic(KclErrorDetails::new(
626 format!(
627 "Error loading imported file ({path}). Open it to view more details.\n {}",
628 err.message()
629 ),
630 vec![source_range],
631 ))
632 }
633 })
634 }
635
636 #[async_recursion]
637 pub(super) async fn execute_expr<'a: 'async_recursion>(
638 &self,
639 init: &Expr,
640 exec_state: &mut ExecState,
641 metadata: &Metadata,
642 annotations: &[Node<Annotation>],
643 statement_kind: StatementKind<'a>,
644 ) -> Result<KclValue, KclError> {
645 let item = match init {
646 Expr::None(none) => KclValue::from(none),
647 Expr::Literal(literal) => KclValue::from_literal((**literal).clone(), exec_state),
648 Expr::TagDeclarator(tag) => tag.execute(exec_state).await?,
649 Expr::Name(name) => {
650 let being_declared = exec_state.mod_local.being_declared.clone();
651 let value = name
652 .get_result(exec_state, self)
653 .await
654 .map_err(|e| var_in_own_ref_err(e, &being_declared))?
655 .clone();
656 if let KclValue::Module { value: module_id, meta } = value {
657 self.exec_module_for_result(
658 module_id,
659 exec_state,
660 metadata.source_range
661 ).await?
662 .unwrap_or_else(|| {
663 exec_state.warn(CompilationError::err(
664 metadata.source_range,
665 "Imported module has no return value. The last statement of the module must be an expression, usually the Solid.",
666 ));
667
668 let mut new_meta = vec![metadata.to_owned()];
669 new_meta.extend(meta);
670 KclValue::KclNone {
671 value: Default::default(),
672 meta: new_meta,
673 }
674 })
675 } else {
676 value
677 }
678 }
679 Expr::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, self).await?,
680 Expr::FunctionExpression(function_expression) => {
681 let rust_impl = annotations::get_impl(annotations, metadata.source_range)?
682 .map(|s| s == annotations::Impl::Rust)
683 .unwrap_or(false);
684
685 if rust_impl {
686 if let ModulePath::Std { value: std_path } = &exec_state.mod_local.path {
687 let (func, props) = crate::std::std_fn(std_path, statement_kind.expect_name());
688 KclValue::Function {
689 value: FunctionSource::Std {
690 func,
691 props,
692 ast: function_expression.clone(),
693 },
694 meta: vec![metadata.to_owned()],
695 }
696 } else {
697 return Err(KclError::new_semantic(KclErrorDetails::new(
698 "Rust implementation of functions is restricted to the standard library".to_owned(),
699 vec![metadata.source_range],
700 )));
701 }
702 } else {
703 KclValue::Function {
707 value: FunctionSource::User {
708 ast: function_expression.clone(),
709 settings: exec_state.mod_local.settings.clone(),
710 memory: exec_state.mut_stack().snapshot(),
711 },
712 meta: vec![metadata.to_owned()],
713 }
714 }
715 }
716 Expr::CallExpressionKw(call_expression) => call_expression.execute(exec_state, self).await?,
717 Expr::PipeExpression(pipe_expression) => pipe_expression.get_result(exec_state, self).await?,
718 Expr::PipeSubstitution(pipe_substitution) => match statement_kind {
719 StatementKind::Declaration { name } => {
720 let message = format!(
721 "you cannot declare variable {name} as %, because % can only be used in function calls"
722 );
723
724 return Err(KclError::new_semantic(KclErrorDetails::new(
725 message,
726 vec![pipe_substitution.into()],
727 )));
728 }
729 StatementKind::Expression => match exec_state.mod_local.pipe_value.clone() {
730 Some(x) => x,
731 None => {
732 return Err(KclError::new_semantic(KclErrorDetails::new(
733 "cannot use % outside a pipe expression".to_owned(),
734 vec![pipe_substitution.into()],
735 )));
736 }
737 },
738 },
739 Expr::ArrayExpression(array_expression) => array_expression.execute(exec_state, self).await?,
740 Expr::ArrayRangeExpression(range_expression) => range_expression.execute(exec_state, self).await?,
741 Expr::ObjectExpression(object_expression) => object_expression.execute(exec_state, self).await?,
742 Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state, self).await?,
743 Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
744 Expr::IfExpression(expr) => expr.get_result(exec_state, self).await?,
745 Expr::LabelledExpression(expr) => {
746 let result = self
747 .execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
748 .await?;
749 exec_state
750 .mut_stack()
751 .add(expr.label.name.clone(), result.clone(), init.into())?;
752 result
754 }
755 Expr::AscribedExpression(expr) => expr.get_result(exec_state, self).await?,
756 };
757 Ok(item)
758 }
759}
760
761fn var_in_own_ref_err(e: KclError, being_declared: &Option<String>) -> KclError {
764 let KclError::UndefinedValue { name, mut details } = e else {
765 return e;
766 };
767 match (&being_declared, &name) {
771 (Some(name0), Some(name1)) if name0 == name1 => {
772 details.message = format!("You can't use `{name0}` because you're currently trying to define it. Use a different variable here instead.");
773 }
774 _ => {}
775 }
776 KclError::UndefinedValue { details, name }
777}
778
779impl Node<AscribedExpression> {
780 #[async_recursion]
781 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
782 let metadata = Metadata {
783 source_range: SourceRange::from(self),
784 };
785 let result = ctx
786 .execute_expr(&self.expr, exec_state, &metadata, &[], StatementKind::Expression)
787 .await?;
788 apply_ascription(&result, &self.ty, exec_state, self.into())
789 }
790}
791
792fn apply_ascription(
793 value: &KclValue,
794 ty: &Node<Type>,
795 exec_state: &mut ExecState,
796 source_range: SourceRange,
797) -> Result<KclValue, KclError> {
798 let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
799 .map_err(|e| KclError::new_semantic(e.into()))?;
800
801 value.coerce(&ty, false, exec_state).map_err(|_| {
802 let suggestion = if ty == RuntimeType::length() {
803 ", you might try coercing to a fully specified numeric type such as `number(mm)`"
804 } else if ty == RuntimeType::angle() {
805 ", you might try coercing to a fully specified numeric type such as `number(deg)`"
806 } else {
807 ""
808 };
809 KclError::new_semantic(KclErrorDetails::new(
810 format!(
811 "could not coerce value of type {} to type {ty}{suggestion}",
812 value.human_friendly_type()
813 ),
814 vec![source_range],
815 ))
816 })
817}
818
819impl BinaryPart {
820 #[async_recursion]
821 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
822 match self {
823 BinaryPart::Literal(literal) => Ok(KclValue::from_literal((**literal).clone(), exec_state)),
824 BinaryPart::Name(name) => name.get_result(exec_state, ctx).await.cloned(),
825 BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
826 BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await,
827 BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
828 BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state, ctx).await,
829 BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
830 BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
831 }
832 }
833}
834
835impl Node<Name> {
836 pub(super) async fn get_result<'a>(
837 &self,
838 exec_state: &'a mut ExecState,
839 ctx: &ExecutorContext,
840 ) -> Result<&'a KclValue, KclError> {
841 let being_declared = exec_state.mod_local.being_declared.clone();
842 self.get_result_inner(exec_state, ctx)
843 .await
844 .map_err(|e| var_in_own_ref_err(e, &being_declared))
845 }
846
847 async fn get_result_inner<'a>(
848 &self,
849 exec_state: &'a mut ExecState,
850 ctx: &ExecutorContext,
851 ) -> Result<&'a KclValue, KclError> {
852 if self.abs_path {
853 return Err(KclError::new_semantic(KclErrorDetails::new(
854 "Absolute paths (names beginning with `::` are not yet supported)".to_owned(),
855 self.as_source_ranges(),
856 )));
857 }
858
859 let mod_name = format!("{}{}", memory::MODULE_PREFIX, self.name.name);
860
861 if self.path.is_empty() {
862 let item_value = exec_state.stack().get(&self.name.name, self.into());
863 if item_value.is_ok() {
864 return item_value;
865 }
866 return exec_state.stack().get(&mod_name, self.into());
867 }
868
869 let mut mem_spec: Option<(EnvironmentRef, Vec<String>)> = None;
870 for p in &self.path {
871 let value = match mem_spec {
872 Some((env, exports)) => {
873 if !exports.contains(&p.name) {
874 return Err(KclError::new_semantic(KclErrorDetails::new(
875 format!("Item {} not found in module's exported items", p.name),
876 p.as_source_ranges(),
877 )));
878 }
879
880 exec_state
881 .stack()
882 .memory
883 .get_from(&p.name, env, p.as_source_range(), 0)?
884 }
885 None => exec_state
886 .stack()
887 .get(&format!("{}{}", memory::MODULE_PREFIX, p.name), self.into())?,
888 };
889
890 let KclValue::Module { value: module_id, .. } = value else {
891 return Err(KclError::new_semantic(KclErrorDetails::new(
892 format!(
893 "Identifier in path must refer to a module, found {}",
894 value.human_friendly_type()
895 ),
896 p.as_source_ranges(),
897 )));
898 };
899
900 mem_spec = Some(
901 ctx.exec_module_for_items(*module_id, exec_state, p.as_source_range())
902 .await?,
903 );
904 }
905
906 let (env, exports) = mem_spec.unwrap();
907
908 let item_exported = exports.contains(&self.name.name);
909 let item_value = exec_state
910 .stack()
911 .memory
912 .get_from(&self.name.name, env, self.name.as_source_range(), 0);
913
914 if item_exported && item_value.is_ok() {
916 return item_value;
917 }
918
919 let mod_exported = exports.contains(&mod_name);
920 let mod_value = exec_state
921 .stack()
922 .memory
923 .get_from(&mod_name, env, self.name.as_source_range(), 0);
924
925 if mod_exported && mod_value.is_ok() {
927 return mod_value;
928 }
929
930 if item_value.is_err() && mod_value.is_err() {
932 return item_value;
933 }
934
935 debug_assert!((item_value.is_ok() && !item_exported) || (mod_value.is_ok() && !mod_exported));
937 Err(KclError::new_semantic(KclErrorDetails::new(
938 format!("Item {} not found in module's exported items", self.name.name),
939 self.name.as_source_ranges(),
940 )))
941 }
942}
943
944impl Node<MemberExpression> {
945 async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
946 let property = Property::try_from(self.computed, self.property.clone(), exec_state, self.into())?;
947 let meta = Metadata {
948 source_range: SourceRange::from(self),
949 };
950 let object = ctx
951 .execute_expr(&self.object, exec_state, &meta, &[], StatementKind::Expression)
952 .await?;
953
954 match (object, property, self.computed) {
956 (KclValue::Object { value: map, meta: _ }, Property::String(property), false) => {
957 if let Some(value) = map.get(&property) {
958 Ok(value.to_owned())
959 } else {
960 Err(KclError::new_undefined_value(
961 KclErrorDetails::new(
962 format!("Property '{property}' not found in object"),
963 vec![self.clone().into()],
964 ),
965 None,
966 ))
967 }
968 }
969 (KclValue::Object { .. }, Property::String(property), true) => {
970 Err(KclError::new_semantic(KclErrorDetails::new(
971 format!("Cannot index object with string; use dot notation instead, e.g. `obj.{property}`"),
972 vec![self.clone().into()],
973 )))
974 }
975 (KclValue::Object { .. }, p, _) => {
976 let t = p.type_name();
977 let article = article_for(t);
978 Err(KclError::new_semantic(KclErrorDetails::new(
979 format!("Only strings can be used as the property of an object, but you're using {article} {t}",),
980 vec![self.clone().into()],
981 )))
982 }
983 (KclValue::HomArray { value: arr, .. }, Property::UInt(index), _) => {
984 let value_of_arr = arr.get(index);
985 if let Some(value) = value_of_arr {
986 Ok(value.to_owned())
987 } else {
988 Err(KclError::new_undefined_value(
989 KclErrorDetails::new(
990 format!("The array doesn't have any item at index {index}"),
991 vec![self.clone().into()],
992 ),
993 None,
994 ))
995 }
996 }
997 (obj, Property::UInt(0), _) => Ok(obj),
1000 (KclValue::HomArray { .. }, p, _) => {
1001 let t = p.type_name();
1002 let article = article_for(t);
1003 Err(KclError::new_semantic(KclErrorDetails::new(
1004 format!("Only integers >= 0 can be used as the index of an array, but you're using {article} {t}",),
1005 vec![self.clone().into()],
1006 )))
1007 }
1008 (KclValue::Solid { value }, Property::String(prop), false) if prop == "sketch" => Ok(KclValue::Sketch {
1009 value: Box::new(value.sketch),
1010 }),
1011 (KclValue::Sketch { value: sk }, Property::String(prop), false) if prop == "tags" => Ok(KclValue::Object {
1012 meta: vec![Metadata {
1013 source_range: SourceRange::from(self.clone()),
1014 }],
1015 value: sk
1016 .tags
1017 .iter()
1018 .map(|(k, tag)| (k.to_owned(), KclValue::TagIdentifier(Box::new(tag.to_owned()))))
1019 .collect(),
1020 }),
1021 (being_indexed, _, _) => {
1022 let t = being_indexed.human_friendly_type();
1023 let article = article_for(&t);
1024 Err(KclError::new_semantic(KclErrorDetails::new(
1025 format!("Only arrays can be indexed, but you're trying to index {article} {t}"),
1026 vec![self.clone().into()],
1027 )))
1028 }
1029 }
1030 }
1031}
1032
1033impl Node<BinaryExpression> {
1034 #[async_recursion]
1035 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1036 let left_value = self.left.get_result(exec_state, ctx).await?;
1037 let right_value = self.right.get_result(exec_state, ctx).await?;
1038 let mut meta = left_value.metadata();
1039 meta.extend(right_value.metadata());
1040
1041 if self.operator == BinaryOperator::Add {
1043 if let (KclValue::String { value: left, meta: _ }, KclValue::String { value: right, meta: _ }) =
1044 (&left_value, &right_value)
1045 {
1046 return Ok(KclValue::String {
1047 value: format!("{}{}", left, right),
1048 meta,
1049 });
1050 }
1051 }
1052
1053 if self.operator == BinaryOperator::Add || self.operator == BinaryOperator::Or {
1055 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
1056 let args = Args::new(Default::default(), self.into(), ctx.clone(), None);
1057 let result = crate::std::csg::inner_union(
1058 vec![*left.clone(), *right.clone()],
1059 Default::default(),
1060 exec_state,
1061 args,
1062 )
1063 .await?;
1064 return Ok(result.into());
1065 }
1066 } else if self.operator == BinaryOperator::Sub {
1067 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
1069 let args = Args::new(Default::default(), self.into(), ctx.clone(), None);
1070 let result = crate::std::csg::inner_subtract(
1071 vec![*left.clone()],
1072 vec![*right.clone()],
1073 Default::default(),
1074 exec_state,
1075 args,
1076 )
1077 .await?;
1078 return Ok(result.into());
1079 }
1080 } else if self.operator == BinaryOperator::And {
1081 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
1083 let args = Args::new(Default::default(), self.into(), ctx.clone(), None);
1084 let result = crate::std::csg::inner_intersect(
1085 vec![*left.clone(), *right.clone()],
1086 Default::default(),
1087 exec_state,
1088 args,
1089 )
1090 .await?;
1091 return Ok(result.into());
1092 }
1093 }
1094
1095 if self.operator == BinaryOperator::Or || self.operator == BinaryOperator::And {
1097 let KclValue::Bool {
1098 value: left_value,
1099 meta: _,
1100 } = left_value
1101 else {
1102 return Err(KclError::new_semantic(KclErrorDetails::new(
1103 format!(
1104 "Cannot apply logical operator to non-boolean value: {}",
1105 left_value.human_friendly_type()
1106 ),
1107 vec![self.left.clone().into()],
1108 )));
1109 };
1110 let KclValue::Bool {
1111 value: right_value,
1112 meta: _,
1113 } = right_value
1114 else {
1115 return Err(KclError::new_semantic(KclErrorDetails::new(
1116 format!(
1117 "Cannot apply logical operator to non-boolean value: {}",
1118 right_value.human_friendly_type()
1119 ),
1120 vec![self.right.clone().into()],
1121 )));
1122 };
1123 let raw_value = match self.operator {
1124 BinaryOperator::Or => left_value || right_value,
1125 BinaryOperator::And => left_value && right_value,
1126 _ => unreachable!(),
1127 };
1128 return Ok(KclValue::Bool { value: raw_value, meta });
1129 }
1130
1131 let left = number_as_f64(&left_value, self.left.clone().into())?;
1132 let right = number_as_f64(&right_value, self.right.clone().into())?;
1133
1134 let value = match self.operator {
1135 BinaryOperator::Add => {
1136 let (l, r, ty) = NumericType::combine_eq_coerce(left, right);
1137 self.warn_on_unknown(&ty, "Adding", exec_state);
1138 KclValue::Number { value: l + r, meta, ty }
1139 }
1140 BinaryOperator::Sub => {
1141 let (l, r, ty) = NumericType::combine_eq_coerce(left, right);
1142 self.warn_on_unknown(&ty, "Subtracting", exec_state);
1143 KclValue::Number { value: l - r, meta, ty }
1144 }
1145 BinaryOperator::Mul => {
1146 let (l, r, ty) = NumericType::combine_mul(left, right);
1147 self.warn_on_unknown(&ty, "Multiplying", exec_state);
1148 KclValue::Number { value: l * r, meta, ty }
1149 }
1150 BinaryOperator::Div => {
1151 let (l, r, ty) = NumericType::combine_div(left, right);
1152 self.warn_on_unknown(&ty, "Dividing", exec_state);
1153 KclValue::Number { value: l / r, meta, ty }
1154 }
1155 BinaryOperator::Mod => {
1156 let (l, r, ty) = NumericType::combine_div(left, right);
1157 self.warn_on_unknown(&ty, "Modulo of", exec_state);
1158 KclValue::Number { value: l % r, meta, ty }
1159 }
1160 BinaryOperator::Pow => KclValue::Number {
1161 value: left.n.powf(right.n),
1162 meta,
1163 ty: exec_state.current_default_units(),
1164 },
1165 BinaryOperator::Neq => {
1166 let (l, r, ty) = NumericType::combine_eq(left, right);
1167 self.warn_on_unknown(&ty, "Comparing", exec_state);
1168 KclValue::Bool { value: l != r, meta }
1169 }
1170 BinaryOperator::Gt => {
1171 let (l, r, ty) = NumericType::combine_eq(left, right);
1172 self.warn_on_unknown(&ty, "Comparing", exec_state);
1173 KclValue::Bool { value: l > r, meta }
1174 }
1175 BinaryOperator::Gte => {
1176 let (l, r, ty) = NumericType::combine_eq(left, right);
1177 self.warn_on_unknown(&ty, "Comparing", exec_state);
1178 KclValue::Bool { value: l >= r, meta }
1179 }
1180 BinaryOperator::Lt => {
1181 let (l, r, ty) = NumericType::combine_eq(left, right);
1182 self.warn_on_unknown(&ty, "Comparing", exec_state);
1183 KclValue::Bool { value: l < r, meta }
1184 }
1185 BinaryOperator::Lte => {
1186 let (l, r, ty) = NumericType::combine_eq(left, right);
1187 self.warn_on_unknown(&ty, "Comparing", exec_state);
1188 KclValue::Bool { value: l <= r, meta }
1189 }
1190 BinaryOperator::Eq => {
1191 let (l, r, ty) = NumericType::combine_eq(left, right);
1192 self.warn_on_unknown(&ty, "Comparing", exec_state);
1193 KclValue::Bool { value: l == r, meta }
1194 }
1195 BinaryOperator::And | BinaryOperator::Or => unreachable!(),
1196 };
1197
1198 Ok(value)
1199 }
1200
1201 fn warn_on_unknown(&self, ty: &NumericType, verb: &str, exec_state: &mut ExecState) {
1202 if ty == &NumericType::Unknown {
1203 exec_state.warn(CompilationError::err(
1205 self.as_source_range(),
1206 format!("{} numbers which have unknown or incompatible units.", verb),
1207 ));
1208 }
1209 }
1210}
1211
1212impl Node<UnaryExpression> {
1213 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1214 if self.operator == UnaryOperator::Not {
1215 let value = self.argument.get_result(exec_state, ctx).await?;
1216 let KclValue::Bool {
1217 value: bool_value,
1218 meta: _,
1219 } = value
1220 else {
1221 return Err(KclError::new_semantic(KclErrorDetails::new(
1222 format!(
1223 "Cannot apply unary operator ! to non-boolean value: {}",
1224 value.human_friendly_type()
1225 ),
1226 vec![self.into()],
1227 )));
1228 };
1229 let meta = vec![Metadata {
1230 source_range: self.into(),
1231 }];
1232 let negated = KclValue::Bool {
1233 value: !bool_value,
1234 meta,
1235 };
1236
1237 return Ok(negated);
1238 }
1239
1240 let value = &self.argument.get_result(exec_state, ctx).await?;
1241 let err = || {
1242 KclError::new_semantic(KclErrorDetails::new(
1243 format!(
1244 "You can only negate numbers, planes, or lines, but this is a {}",
1245 value.human_friendly_type()
1246 ),
1247 vec![self.into()],
1248 ))
1249 };
1250 match value {
1251 KclValue::Number { value, ty, .. } => {
1252 let meta = vec![Metadata {
1253 source_range: self.into(),
1254 }];
1255 Ok(KclValue::Number {
1256 value: -value,
1257 meta,
1258 ty: ty.clone(),
1259 })
1260 }
1261 KclValue::Plane { value } => {
1262 let mut plane = value.clone();
1263 if plane.info.x_axis.x != 0.0 {
1264 plane.info.x_axis.x *= -1.0;
1265 }
1266 if plane.info.x_axis.y != 0.0 {
1267 plane.info.x_axis.y *= -1.0;
1268 }
1269 if plane.info.x_axis.z != 0.0 {
1270 plane.info.x_axis.z *= -1.0;
1271 }
1272
1273 plane.value = PlaneType::Uninit;
1274 plane.id = exec_state.next_uuid();
1275 Ok(KclValue::Plane { value: plane })
1276 }
1277 KclValue::Object { value: values, meta } => {
1278 let Some(direction) = values.get("direction") else {
1280 return Err(err());
1281 };
1282
1283 let direction = match direction {
1284 KclValue::Tuple { value: values, meta } => {
1285 let values = values
1286 .iter()
1287 .map(|v| match v {
1288 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
1289 value: *value * -1.0,
1290 ty: ty.clone(),
1291 meta: meta.clone(),
1292 }),
1293 _ => Err(err()),
1294 })
1295 .collect::<Result<Vec<_>, _>>()?;
1296
1297 KclValue::Tuple {
1298 value: values,
1299 meta: meta.clone(),
1300 }
1301 }
1302 KclValue::HomArray {
1303 value: values,
1304 ty: ty @ RuntimeType::Primitive(PrimitiveType::Number(_)),
1305 } => {
1306 let values = values
1307 .iter()
1308 .map(|v| match v {
1309 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
1310 value: *value * -1.0,
1311 ty: ty.clone(),
1312 meta: meta.clone(),
1313 }),
1314 _ => Err(err()),
1315 })
1316 .collect::<Result<Vec<_>, _>>()?;
1317
1318 KclValue::HomArray {
1319 value: values,
1320 ty: ty.clone(),
1321 }
1322 }
1323 _ => return Err(err()),
1324 };
1325
1326 let mut value = values.clone();
1327 value.insert("direction".to_owned(), direction);
1328 Ok(KclValue::Object {
1329 value,
1330 meta: meta.clone(),
1331 })
1332 }
1333 _ => Err(err()),
1334 }
1335 }
1336}
1337
1338pub(crate) async fn execute_pipe_body(
1339 exec_state: &mut ExecState,
1340 body: &[Expr],
1341 source_range: SourceRange,
1342 ctx: &ExecutorContext,
1343) -> Result<KclValue, KclError> {
1344 let Some((first, body)) = body.split_first() else {
1345 return Err(KclError::new_semantic(KclErrorDetails::new(
1346 "Pipe expressions cannot be empty".to_owned(),
1347 vec![source_range],
1348 )));
1349 };
1350 let meta = Metadata {
1355 source_range: SourceRange::from(first),
1356 };
1357 let output = ctx
1358 .execute_expr(first, exec_state, &meta, &[], StatementKind::Expression)
1359 .await?;
1360
1361 let previous_pipe_value = exec_state.mod_local.pipe_value.replace(output);
1365 let result = inner_execute_pipe_body(exec_state, body, ctx).await;
1367 exec_state.mod_local.pipe_value = previous_pipe_value;
1369
1370 result
1371}
1372
1373#[async_recursion]
1376async fn inner_execute_pipe_body(
1377 exec_state: &mut ExecState,
1378 body: &[Expr],
1379 ctx: &ExecutorContext,
1380) -> Result<KclValue, KclError> {
1381 for expression in body {
1382 if let Expr::TagDeclarator(_) = expression {
1383 return Err(KclError::new_semantic(KclErrorDetails::new(
1384 format!("This cannot be in a PipeExpression: {:?}", expression),
1385 vec![expression.into()],
1386 )));
1387 }
1388 let metadata = Metadata {
1389 source_range: SourceRange::from(expression),
1390 };
1391 let output = ctx
1392 .execute_expr(expression, exec_state, &metadata, &[], StatementKind::Expression)
1393 .await?;
1394 exec_state.mod_local.pipe_value = Some(output);
1395 }
1396 let final_output = exec_state.mod_local.pipe_value.take().unwrap();
1398 Ok(final_output)
1399}
1400
1401impl Node<TagDeclarator> {
1402 pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
1403 let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
1404 value: self.name.clone(),
1405 info: Vec::new(),
1406 meta: vec![Metadata {
1407 source_range: self.into(),
1408 }],
1409 }));
1410
1411 exec_state
1412 .mut_stack()
1413 .add(self.name.clone(), memory_item.clone(), self.into())?;
1414
1415 Ok(self.into())
1416 }
1417}
1418
1419impl Node<ArrayExpression> {
1420 #[async_recursion]
1421 pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1422 let mut results = Vec::with_capacity(self.elements.len());
1423
1424 for element in &self.elements {
1425 let metadata = Metadata::from(element);
1426 let value = ctx
1429 .execute_expr(element, exec_state, &metadata, &[], StatementKind::Expression)
1430 .await?;
1431
1432 results.push(value);
1433 }
1434
1435 Ok(KclValue::HomArray {
1436 value: results,
1437 ty: RuntimeType::Primitive(PrimitiveType::Any),
1438 })
1439 }
1440}
1441
1442impl Node<ArrayRangeExpression> {
1443 #[async_recursion]
1444 pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1445 let metadata = Metadata::from(&self.start_element);
1446 let start_val = ctx
1447 .execute_expr(
1448 &self.start_element,
1449 exec_state,
1450 &metadata,
1451 &[],
1452 StatementKind::Expression,
1453 )
1454 .await?;
1455 let (start, start_ty) = start_val
1456 .as_int_with_ty()
1457 .ok_or(KclError::new_semantic(KclErrorDetails::new(
1458 format!("Expected int but found {}", start_val.human_friendly_type()),
1459 vec![self.into()],
1460 )))?;
1461 let metadata = Metadata::from(&self.end_element);
1462 let end_val = ctx
1463 .execute_expr(&self.end_element, exec_state, &metadata, &[], StatementKind::Expression)
1464 .await?;
1465 let (end, end_ty) = end_val
1466 .as_int_with_ty()
1467 .ok_or(KclError::new_semantic(KclErrorDetails::new(
1468 format!("Expected int but found {}", end_val.human_friendly_type()),
1469 vec![self.into()],
1470 )))?;
1471
1472 if start_ty != end_ty {
1473 let start = start_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: start_ty });
1474 let start = fmt::human_display_number(start.n, start.ty);
1475 let end = end_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: end_ty });
1476 let end = fmt::human_display_number(end.n, end.ty);
1477 return Err(KclError::new_semantic(KclErrorDetails::new(
1478 format!("Range start and end must be of the same type, but found {start} and {end}"),
1479 vec![self.into()],
1480 )));
1481 }
1482
1483 if end < start {
1484 return Err(KclError::new_semantic(KclErrorDetails::new(
1485 format!("Range start is greater than range end: {start} .. {end}"),
1486 vec![self.into()],
1487 )));
1488 }
1489
1490 let range: Vec<_> = if self.end_inclusive {
1491 (start..=end).collect()
1492 } else {
1493 (start..end).collect()
1494 };
1495
1496 let meta = vec![Metadata {
1497 source_range: self.into(),
1498 }];
1499
1500 Ok(KclValue::HomArray {
1501 value: range
1502 .into_iter()
1503 .map(|num| KclValue::Number {
1504 value: num as f64,
1505 ty: start_ty.clone(),
1506 meta: meta.clone(),
1507 })
1508 .collect(),
1509 ty: RuntimeType::Primitive(PrimitiveType::Number(start_ty)),
1510 })
1511 }
1512}
1513
1514impl Node<ObjectExpression> {
1515 #[async_recursion]
1516 pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1517 let mut object = HashMap::with_capacity(self.properties.len());
1518 for property in &self.properties {
1519 let metadata = Metadata::from(&property.value);
1520 let result = ctx
1521 .execute_expr(&property.value, exec_state, &metadata, &[], StatementKind::Expression)
1522 .await?;
1523
1524 object.insert(property.key.name.clone(), result);
1525 }
1526
1527 Ok(KclValue::Object {
1528 value: object,
1529 meta: vec![Metadata {
1530 source_range: self.into(),
1531 }],
1532 })
1533 }
1534}
1535
1536fn article_for<S: AsRef<str>>(s: S) -> &'static str {
1537 if s.as_ref().starts_with(['a', 'e', 'i', 'o', 'u', '[']) {
1539 "an"
1540 } else {
1541 "a"
1542 }
1543}
1544
1545fn number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<TyF64, KclError> {
1546 v.as_ty_f64().ok_or_else(|| {
1547 let actual_type = v.human_friendly_type();
1548 KclError::new_semantic(KclErrorDetails::new(
1549 format!("Expected a number, but found {actual_type}",),
1550 vec![source_range],
1551 ))
1552 })
1553}
1554
1555impl Node<IfExpression> {
1556 #[async_recursion]
1557 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1558 let cond = ctx
1560 .execute_expr(
1561 &self.cond,
1562 exec_state,
1563 &Metadata::from(self),
1564 &[],
1565 StatementKind::Expression,
1566 )
1567 .await?
1568 .get_bool()?;
1569 if cond {
1570 let block_result = ctx.exec_block(&self.then_val, exec_state, BodyType::Block).await?;
1571 return Ok(block_result.unwrap());
1575 }
1576
1577 for else_if in &self.else_ifs {
1579 let cond = ctx
1580 .execute_expr(
1581 &else_if.cond,
1582 exec_state,
1583 &Metadata::from(self),
1584 &[],
1585 StatementKind::Expression,
1586 )
1587 .await?
1588 .get_bool()?;
1589 if cond {
1590 let block_result = ctx.exec_block(&else_if.then_val, exec_state, BodyType::Block).await?;
1591 return Ok(block_result.unwrap());
1595 }
1596 }
1597
1598 ctx.exec_block(&self.final_else, exec_state, BodyType::Block)
1600 .await
1601 .map(|expr| expr.unwrap())
1602 }
1603}
1604
1605#[derive(Debug)]
1606enum Property {
1607 UInt(usize),
1608 String(String),
1609}
1610
1611impl Property {
1612 fn try_from(
1613 computed: bool,
1614 value: LiteralIdentifier,
1615 exec_state: &ExecState,
1616 sr: SourceRange,
1617 ) -> Result<Self, KclError> {
1618 let property_sr = vec![sr];
1619 let property_src: SourceRange = value.clone().into();
1620 match value {
1621 LiteralIdentifier::Identifier(identifier) => {
1622 let name = &identifier.name;
1623 if !computed {
1624 Ok(Property::String(name.to_string()))
1626 } else {
1627 let prop = exec_state.stack().get(name, property_src)?;
1630 jvalue_to_prop(prop, property_sr, name)
1631 }
1632 }
1633 LiteralIdentifier::Literal(literal) => {
1634 let value = literal.value.clone();
1635 match value {
1636 LiteralValue::Number { value, .. } => {
1637 if let Some(x) = crate::try_f64_to_usize(value) {
1638 Ok(Property::UInt(x))
1639 } else {
1640 Err(KclError::new_semantic(KclErrorDetails::new(
1641 format!("{value} is not a valid index, indices must be whole numbers >= 0"),
1642 property_sr,
1643 )))
1644 }
1645 }
1646 _ => Err(KclError::new_semantic(KclErrorDetails::new(
1647 "Only numbers (>= 0) can be indexes".to_owned(),
1648 vec![sr],
1649 ))),
1650 }
1651 }
1652 }
1653 }
1654}
1655
1656fn jvalue_to_prop(value: &KclValue, property_sr: Vec<SourceRange>, name: &str) -> Result<Property, KclError> {
1657 let make_err =
1658 |message: String| Err::<Property, _>(KclError::new_semantic(KclErrorDetails::new(message, property_sr)));
1659 match value {
1660 KclValue::Number{value: num, .. } => {
1661 let num = *num;
1662 if num < 0.0 {
1663 return make_err(format!("'{num}' is negative, so you can't index an array with it"))
1664 }
1665 let nearest_int = crate::try_f64_to_usize(num);
1666 if let Some(nearest_int) = nearest_int {
1667 Ok(Property::UInt(nearest_int))
1668 } else {
1669 make_err(format!("'{num}' is not an integer, so you can't index an array with it"))
1670 }
1671 }
1672 KclValue::String{value: x, meta:_} => Ok(Property::String(x.to_owned())),
1673 _ => {
1674 make_err(format!("{name} is not a valid property/index, you can only use a string to get the property of an object, or an int (>= 0) to get an item in an array"))
1675 }
1676 }
1677}
1678
1679impl Property {
1680 fn type_name(&self) -> &'static str {
1681 match self {
1682 Property::UInt(_) => "number",
1683 Property::String(_) => "string",
1684 }
1685 }
1686}
1687
1688impl Node<PipeExpression> {
1689 #[async_recursion]
1690 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1691 execute_pipe_body(exec_state, &self.body, self.into(), ctx).await
1692 }
1693}
1694
1695#[cfg(test)]
1696mod test {
1697 use std::sync::Arc;
1698
1699 use tokio::io::AsyncWriteExt;
1700
1701 use super::*;
1702 use crate::{
1703 exec::UnitType,
1704 execution::{parse_execute, ContextType},
1705 ExecutorSettings, UnitLen,
1706 };
1707
1708 #[tokio::test(flavor = "multi_thread")]
1709 async fn ascription() {
1710 let program = r#"
1711a = 42: number
1712b = a: number
1713p = {
1714 origin = { x = 0, y = 0, z = 0 },
1715 xAxis = { x = 1, y = 0, z = 0 },
1716 yAxis = { x = 0, y = 1, z = 0 },
1717 zAxis = { x = 0, y = 0, z = 1 }
1718}: Plane
1719arr1 = [42]: [number(cm)]
1720"#;
1721
1722 let result = parse_execute(program).await.unwrap();
1723 let mem = result.exec_state.stack();
1724 assert!(matches!(
1725 mem.memory
1726 .get_from("p", result.mem_env, SourceRange::default(), 0)
1727 .unwrap(),
1728 KclValue::Plane { .. }
1729 ));
1730 let arr1 = mem
1731 .memory
1732 .get_from("arr1", result.mem_env, SourceRange::default(), 0)
1733 .unwrap();
1734 if let KclValue::HomArray { value, ty } = arr1 {
1735 assert_eq!(value.len(), 1, "Expected Vec with specific length: found {:?}", value);
1736 assert_eq!(*ty, RuntimeType::known_length(UnitLen::Cm));
1737 if let KclValue::Number { value, ty, .. } = &value[0] {
1739 assert_eq!(*value, 42.0);
1741 assert_eq!(*ty, NumericType::Known(UnitType::Length(UnitLen::Cm)));
1742 } else {
1743 panic!("Expected a number; found {:?}", value[0]);
1744 }
1745 } else {
1746 panic!("Expected HomArray; found {arr1:?}");
1747 }
1748
1749 let program = r#"
1750a = 42: string
1751"#;
1752 let result = parse_execute(program).await;
1753 let err = result.unwrap_err();
1754 assert!(
1755 err.to_string()
1756 .contains("could not coerce value of type number(default units) to type string"),
1757 "Expected error but found {err:?}"
1758 );
1759
1760 let program = r#"
1761a = 42: Plane
1762"#;
1763 let result = parse_execute(program).await;
1764 let err = result.unwrap_err();
1765 assert!(
1766 err.to_string()
1767 .contains("could not coerce value of type number(default units) to type Plane"),
1768 "Expected error but found {err:?}"
1769 );
1770
1771 let program = r#"
1772arr = [0]: [string]
1773"#;
1774 let result = parse_execute(program).await;
1775 let err = result.unwrap_err();
1776 assert!(
1777 err.to_string().contains(
1778 "could not coerce value of type array of number(default units) with 1 value to type [string]"
1779 ),
1780 "Expected error but found {err:?}"
1781 );
1782
1783 let program = r#"
1784mixedArr = [0, "a"]: [number(mm)]
1785"#;
1786 let result = parse_execute(program).await;
1787 let err = result.unwrap_err();
1788 assert!(
1789 err.to_string()
1790 .contains("could not coerce value of type array of number(default units), string with 2 values to type [number(mm)]"),
1791 "Expected error but found {err:?}"
1792 );
1793 }
1794
1795 #[tokio::test(flavor = "multi_thread")]
1796 async fn neg_plane() {
1797 let program = r#"
1798p = {
1799 origin = { x = 0, y = 0, z = 0 },
1800 xAxis = { x = 1, y = 0, z = 0 },
1801 yAxis = { x = 0, y = 1, z = 0 },
1802}: Plane
1803p2 = -p
1804"#;
1805
1806 let result = parse_execute(program).await.unwrap();
1807 let mem = result.exec_state.stack();
1808 match mem
1809 .memory
1810 .get_from("p2", result.mem_env, SourceRange::default(), 0)
1811 .unwrap()
1812 {
1813 KclValue::Plane { value } => {
1814 assert_eq!(value.info.x_axis.x, -1.0);
1815 assert_eq!(value.info.x_axis.y, 0.0);
1816 assert_eq!(value.info.x_axis.z, 0.0);
1817 }
1818 _ => unreachable!(),
1819 }
1820 }
1821
1822 #[tokio::test(flavor = "multi_thread")]
1823 async fn multiple_returns() {
1824 let program = r#"fn foo() {
1825 return 0
1826 return 42
1827}
1828
1829a = foo()
1830"#;
1831
1832 let result = parse_execute(program).await;
1833 assert!(result.unwrap_err().to_string().contains("return"));
1834 }
1835
1836 #[tokio::test(flavor = "multi_thread")]
1837 async fn load_all_modules() {
1838 let program_a_kcl = r#"
1840export a = 1
1841"#;
1842 let program_b_kcl = r#"
1844import a from 'a.kcl'
1845
1846export b = a + 1
1847"#;
1848 let program_c_kcl = r#"
1850import a from 'a.kcl'
1851
1852export c = a + 2
1853"#;
1854
1855 let main_kcl = r#"
1857import b from 'b.kcl'
1858import c from 'c.kcl'
1859
1860d = b + c
1861"#;
1862
1863 let main = crate::parsing::parse_str(main_kcl, ModuleId::default())
1864 .parse_errs_as_err()
1865 .unwrap();
1866
1867 let tmpdir = tempfile::TempDir::with_prefix("zma_kcl_load_all_modules").unwrap();
1868
1869 tokio::fs::File::create(tmpdir.path().join("main.kcl"))
1870 .await
1871 .unwrap()
1872 .write_all(main_kcl.as_bytes())
1873 .await
1874 .unwrap();
1875
1876 tokio::fs::File::create(tmpdir.path().join("a.kcl"))
1877 .await
1878 .unwrap()
1879 .write_all(program_a_kcl.as_bytes())
1880 .await
1881 .unwrap();
1882
1883 tokio::fs::File::create(tmpdir.path().join("b.kcl"))
1884 .await
1885 .unwrap()
1886 .write_all(program_b_kcl.as_bytes())
1887 .await
1888 .unwrap();
1889
1890 tokio::fs::File::create(tmpdir.path().join("c.kcl"))
1891 .await
1892 .unwrap()
1893 .write_all(program_c_kcl.as_bytes())
1894 .await
1895 .unwrap();
1896
1897 let exec_ctxt = ExecutorContext {
1898 engine: Arc::new(Box::new(
1899 crate::engine::conn_mock::EngineConnection::new()
1900 .await
1901 .map_err(|err| {
1902 KclError::new_internal(KclErrorDetails::new(
1903 format!("Failed to create mock engine connection: {}", err),
1904 vec![SourceRange::default()],
1905 ))
1906 })
1907 .unwrap(),
1908 )),
1909 fs: Arc::new(crate::fs::FileManager::new()),
1910 settings: ExecutorSettings {
1911 project_directory: Some(crate::TypedPath(tmpdir.path().into())),
1912 ..Default::default()
1913 },
1914 context_type: ContextType::Mock,
1915 };
1916 let mut exec_state = ExecState::new(&exec_ctxt);
1917
1918 exec_ctxt
1919 .run(
1920 &crate::Program {
1921 ast: main.clone(),
1922 original_file_contents: "".to_owned(),
1923 },
1924 &mut exec_state,
1925 )
1926 .await
1927 .unwrap();
1928 }
1929
1930 #[tokio::test(flavor = "multi_thread")]
1931 async fn user_coercion() {
1932 let program = r#"fn foo(x: Axis2d) {
1933 return 0
1934}
1935
1936foo(x = { direction = [0, 0], origin = [0, 0]})
1937"#;
1938
1939 parse_execute(program).await.unwrap();
1940
1941 let program = r#"fn foo(x: Axis3d) {
1942 return 0
1943}
1944
1945foo(x = { direction = [0, 0], origin = [0, 0]})
1946"#;
1947
1948 parse_execute(program).await.unwrap_err();
1949 }
1950
1951 #[tokio::test(flavor = "multi_thread")]
1952 async fn coerce_return() {
1953 let program = r#"fn foo(): number(mm) {
1954 return 42
1955}
1956
1957a = foo()
1958"#;
1959
1960 parse_execute(program).await.unwrap();
1961
1962 let program = r#"fn foo(): number(mm) {
1963 return { bar: 42 }
1964}
1965
1966a = foo()
1967"#;
1968
1969 parse_execute(program).await.unwrap_err();
1970 }
1971
1972 #[tokio::test(flavor = "multi_thread")]
1973 async fn test_sensible_error_when_missing_equals_in_kwarg() {
1974 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)"]
1975 .into_iter()
1976 .enumerate()
1977 {
1978 let program = format!(
1979 "fn foo() {{ return 0 }}
1980z = 0
1981fn f(x, y, z) {{ return 0 }}
1982{call}"
1983 );
1984 let err = parse_execute(&program).await.unwrap_err();
1985 let msg = err.message();
1986 assert!(
1987 msg.contains("This argument needs a label, but it doesn't have one"),
1988 "failed test {i}: {msg}"
1989 );
1990 assert!(msg.contains("`y`"), "failed test {i}, missing `y`: {msg}");
1991 if i == 0 {
1992 assert!(msg.contains("`z`"), "failed test {i}, missing `z`: {msg}");
1993 }
1994 }
1995 }
1996
1997 #[tokio::test(flavor = "multi_thread")]
1998 async fn default_param_for_unlabeled() {
1999 let ast = r#"fn myExtrude(@sk, length) {
2002 return extrude(sk, length)
2003}
2004sketch001 = startSketchOn(XY)
2005 |> circle(center = [0, 0], radius = 93.75)
2006 |> myExtrude(length = 40)
2007"#;
2008
2009 parse_execute(ast).await.unwrap();
2010 }
2011
2012 #[tokio::test(flavor = "multi_thread")]
2013 async fn dont_use_unlabelled_as_input() {
2014 let ast = r#"length = 10
2016startSketchOn(XY)
2017 |> circle(center = [0, 0], radius = 93.75)
2018 |> extrude(length)
2019"#;
2020
2021 parse_execute(ast).await.unwrap();
2022 }
2023
2024 #[tokio::test(flavor = "multi_thread")]
2025 async fn ascription_in_binop() {
2026 let ast = r#"foo = tan(0): number(rad) - 4deg"#;
2027 parse_execute(ast).await.unwrap();
2028 }
2029
2030 #[tokio::test(flavor = "multi_thread")]
2031 async fn neg_sqrt() {
2032 let ast = r#"bad = sqrt(-2)"#;
2033
2034 let e = parse_execute(ast).await.unwrap_err();
2035 assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
2037 }
2038
2039 #[tokio::test(flavor = "multi_thread")]
2040 async fn non_array_fns() {
2041 let ast = r#"push(1, item = 2)
2042pop(1)
2043map(1, f = fn(@x) { return x + 1 })
2044reduce(1, f = fn(@x, accum) { return accum + x}, initial = 0)"#;
2045
2046 parse_execute(ast).await.unwrap();
2047 }
2048
2049 #[tokio::test(flavor = "multi_thread")]
2050 async fn non_array_indexing() {
2051 let good = r#"a = 42
2052good = a[0]
2053"#;
2054 let result = parse_execute(good).await.unwrap();
2055 let mem = result.exec_state.stack();
2056 let num = mem
2057 .memory
2058 .get_from("good", result.mem_env, SourceRange::default(), 0)
2059 .unwrap()
2060 .as_ty_f64()
2061 .unwrap();
2062 assert_eq!(num.n, 42.0);
2063
2064 let bad = r#"a = 42
2065bad = a[1]
2066"#;
2067
2068 parse_execute(bad).await.unwrap_err();
2069 }
2070
2071 #[tokio::test(flavor = "multi_thread")]
2072 async fn coerce_unknown_to_length() {
2073 let ast = r#"x = 2mm * 2mm
2074y = x: number(Length)"#;
2075 let e = parse_execute(ast).await.unwrap_err();
2076 assert!(
2077 e.message().contains("could not coerce"),
2078 "Error message: '{}'",
2079 e.message()
2080 );
2081
2082 let ast = r#"x = 2mm
2083y = x: number(Length)"#;
2084 let result = parse_execute(ast).await.unwrap();
2085 let mem = result.exec_state.stack();
2086 let num = mem
2087 .memory
2088 .get_from("y", result.mem_env, SourceRange::default(), 0)
2089 .unwrap()
2090 .as_ty_f64()
2091 .unwrap();
2092 assert_eq!(num.n, 2.0);
2093 assert_eq!(num.ty, NumericType::mm());
2094 }
2095}