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