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 let mut value = value.clone();
740
741 if let KclValue::Number { value: n, meta, .. } = &value {
744 if let RuntimeType::Primitive(PrimitiveType::Number(num)) = &ty {
745 if num.is_fully_specified() {
746 value = KclValue::Number {
747 ty: NumericType::Any,
748 value: *n,
749 meta: meta.clone(),
750 };
751 }
752 }
753 }
754
755 value.coerce(&ty, exec_state).map_err(|_| {
756 let suggestion = if ty == RuntimeType::length() {
757 ", you might try coercing to a fully specified numeric type such as `number(mm)`"
758 } else if ty == RuntimeType::angle() {
759 ", you might try coercing to a fully specified numeric type such as `number(deg)`"
760 } else {
761 ""
762 };
763 KclError::Semantic(KclErrorDetails {
764 message: format!(
765 "could not coerce {} value to type {ty}{suggestion}",
766 value.human_friendly_type()
767 ),
768 source_ranges: vec![source_range],
769 })
770 })
771}
772
773impl BinaryPart {
774 #[async_recursion]
775 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
776 match self {
777 BinaryPart::Literal(literal) => Ok(KclValue::from_literal((**literal).clone(), exec_state)),
778 BinaryPart::Name(name) => name.get_result(exec_state, ctx).await.cloned(),
779 BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
780 BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await,
781 BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
782 BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
783 BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
784 BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
785 }
786 }
787}
788
789impl Node<Name> {
790 async fn get_result<'a>(
791 &self,
792 exec_state: &'a mut ExecState,
793 ctx: &ExecutorContext,
794 ) -> Result<&'a KclValue, KclError> {
795 if self.abs_path {
796 return Err(KclError::Semantic(KclErrorDetails {
797 message: "Absolute paths (names beginning with `::` are not yet supported)".to_owned(),
798 source_ranges: self.as_source_ranges(),
799 }));
800 }
801
802 if self.path.is_empty() {
803 return exec_state.stack().get(&self.name.name, self.into());
804 }
805
806 let mut mem_spec: Option<(EnvironmentRef, Vec<String>)> = None;
807 for p in &self.path {
808 let value = match mem_spec {
809 Some((env, exports)) => {
810 if !exports.contains(&p.name) {
811 return Err(KclError::Semantic(KclErrorDetails {
812 message: format!("Item {} not found in module's exported items", p.name),
813 source_ranges: p.as_source_ranges(),
814 }));
815 }
816
817 exec_state
818 .stack()
819 .memory
820 .get_from(&p.name, env, p.as_source_range(), 0)?
821 }
822 None => exec_state.stack().get(&p.name, self.into())?,
823 };
824
825 let KclValue::Module { value: module_id, .. } = value else {
826 return Err(KclError::Semantic(KclErrorDetails {
827 message: format!(
828 "Identifier in path must refer to a module, found {}",
829 value.human_friendly_type()
830 ),
831 source_ranges: p.as_source_ranges(),
832 }));
833 };
834
835 mem_spec = Some(
836 ctx.exec_module_for_items(*module_id, exec_state, p.as_source_range())
837 .await?,
838 );
839 }
840
841 let (env, exports) = mem_spec.unwrap();
842 if !exports.contains(&self.name.name) {
843 return Err(KclError::Semantic(KclErrorDetails {
844 message: format!("Item {} not found in module's exported items", self.name.name),
845 source_ranges: self.name.as_source_ranges(),
846 }));
847 }
848
849 exec_state
850 .stack()
851 .memory
852 .get_from(&self.name.name, env, self.name.as_source_range(), 0)
853 }
854}
855
856impl Node<MemberExpression> {
857 fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
858 let property = Property::try_from(self.computed, self.property.clone(), exec_state, self.into())?;
859 let object = match &self.object {
860 MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
862 MemberObject::Identifier(identifier) => {
863 let value = exec_state.stack().get(&identifier.name, identifier.into())?;
864 value.clone()
865 }
866 };
867
868 match (object, property, self.computed) {
870 (KclValue::Object { value: map, meta: _ }, Property::String(property), false) => {
871 if let Some(value) = map.get(&property) {
872 Ok(value.to_owned())
873 } else {
874 Err(KclError::UndefinedValue(KclErrorDetails {
875 message: format!("Property '{property}' not found in object"),
876 source_ranges: vec![self.clone().into()],
877 }))
878 }
879 }
880 (KclValue::Object { .. }, Property::String(property), true) => Err(KclError::Semantic(KclErrorDetails {
881 message: format!("Cannot index object with string; use dot notation instead, e.g. `obj.{property}`"),
882 source_ranges: vec![self.clone().into()],
883 })),
884 (KclValue::Object { .. }, p, _) => {
885 let t = p.type_name();
886 let article = article_for(t);
887 Err(KclError::Semantic(KclErrorDetails {
888 message: format!(
889 "Only strings can be used as the property of an object, but you're using {article} {t}",
890 ),
891 source_ranges: vec![self.clone().into()],
892 }))
893 }
894 (KclValue::HomArray { value: arr, .. }, Property::UInt(index), _) => {
895 let value_of_arr = arr.get(index);
896 if let Some(value) = value_of_arr {
897 Ok(value.to_owned())
898 } else {
899 Err(KclError::UndefinedValue(KclErrorDetails {
900 message: format!("The array doesn't have any item at index {index}"),
901 source_ranges: vec![self.clone().into()],
902 }))
903 }
904 }
905 (KclValue::HomArray { .. }, p, _) => {
906 let t = p.type_name();
907 let article = article_for(t);
908 Err(KclError::Semantic(KclErrorDetails {
909 message: format!(
910 "Only integers >= 0 can be used as the index of an array, but you're using {article} {t}",
911 ),
912 source_ranges: vec![self.clone().into()],
913 }))
914 }
915 (KclValue::Solid { value }, Property::String(prop), false) if prop == "sketch" => Ok(KclValue::Sketch {
916 value: Box::new(value.sketch),
917 }),
918 (KclValue::Sketch { value: sk }, Property::String(prop), false) if prop == "tags" => Ok(KclValue::Object {
919 meta: vec![Metadata {
920 source_range: SourceRange::from(self.clone()),
921 }],
922 value: sk
923 .tags
924 .iter()
925 .map(|(k, tag)| (k.to_owned(), KclValue::TagIdentifier(Box::new(tag.to_owned()))))
926 .collect(),
927 }),
928 (being_indexed, _, _) => {
929 let t = being_indexed.human_friendly_type();
930 let article = article_for(&t);
931 Err(KclError::Semantic(KclErrorDetails {
932 message: format!("Only arrays can be indexed, but you're trying to index {article} {t}"),
933 source_ranges: vec![self.clone().into()],
934 }))
935 }
936 }
937 }
938}
939
940impl Node<BinaryExpression> {
941 #[async_recursion]
942 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
943 let left_value = self.left.get_result(exec_state, ctx).await?;
944 let right_value = self.right.get_result(exec_state, ctx).await?;
945 let mut meta = left_value.metadata();
946 meta.extend(right_value.metadata());
947
948 if self.operator == BinaryOperator::Add {
950 if let (KclValue::String { value: left, meta: _ }, KclValue::String { value: right, meta: _ }) =
951 (&left_value, &right_value)
952 {
953 return Ok(KclValue::String {
954 value: format!("{}{}", left, right),
955 meta,
956 });
957 }
958 }
959
960 if self.operator == BinaryOperator::Add || self.operator == BinaryOperator::Or {
962 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
963 let args = Args::new(Default::default(), self.into(), ctx.clone(), None);
964 let result = crate::std::csg::inner_union(
965 vec![*left.clone(), *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::Sub {
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_subtract(
978 vec![*left.clone()],
979 vec![*right.clone()],
980 Default::default(),
981 exec_state,
982 args,
983 )
984 .await?;
985 return Ok(result.into());
986 }
987 } else if self.operator == BinaryOperator::And {
988 if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
990 let args = Args::new(Default::default(), self.into(), ctx.clone(), None);
991 let result = crate::std::csg::inner_intersect(
992 vec![*left.clone(), *right.clone()],
993 Default::default(),
994 exec_state,
995 args,
996 )
997 .await?;
998 return Ok(result.into());
999 }
1000 }
1001
1002 if self.operator == BinaryOperator::Or || self.operator == BinaryOperator::And {
1004 let KclValue::Bool {
1005 value: left_value,
1006 meta: _,
1007 } = left_value
1008 else {
1009 return Err(KclError::Semantic(KclErrorDetails {
1010 message: format!(
1011 "Cannot apply logical operator to non-boolean value: {}",
1012 left_value.human_friendly_type()
1013 ),
1014 source_ranges: vec![self.left.clone().into()],
1015 }));
1016 };
1017 let KclValue::Bool {
1018 value: right_value,
1019 meta: _,
1020 } = right_value
1021 else {
1022 return Err(KclError::Semantic(KclErrorDetails {
1023 message: format!(
1024 "Cannot apply logical operator to non-boolean value: {}",
1025 right_value.human_friendly_type()
1026 ),
1027 source_ranges: vec![self.right.clone().into()],
1028 }));
1029 };
1030 let raw_value = match self.operator {
1031 BinaryOperator::Or => left_value || right_value,
1032 BinaryOperator::And => left_value && right_value,
1033 _ => unreachable!(),
1034 };
1035 return Ok(KclValue::Bool { value: raw_value, meta });
1036 }
1037
1038 let left = number_as_f64(&left_value, self.left.clone().into())?;
1039 let right = number_as_f64(&right_value, self.right.clone().into())?;
1040
1041 let value = match self.operator {
1042 BinaryOperator::Add => {
1043 let (l, r, ty) = NumericType::combine_eq_coerce(left, right);
1044 self.warn_on_unknown(&ty, "Adding", exec_state);
1045 KclValue::Number { value: l + r, meta, ty }
1046 }
1047 BinaryOperator::Sub => {
1048 let (l, r, ty) = NumericType::combine_eq_coerce(left, right);
1049 self.warn_on_unknown(&ty, "Subtracting", exec_state);
1050 KclValue::Number { value: l - r, meta, ty }
1051 }
1052 BinaryOperator::Mul => {
1053 let (l, r, ty) = NumericType::combine_mul(left, right);
1054 self.warn_on_unknown(&ty, "Multiplying", exec_state);
1055 KclValue::Number { value: l * r, meta, ty }
1056 }
1057 BinaryOperator::Div => {
1058 let (l, r, ty) = NumericType::combine_div(left, right);
1059 self.warn_on_unknown(&ty, "Dividing", exec_state);
1060 KclValue::Number { value: l / r, meta, ty }
1061 }
1062 BinaryOperator::Mod => {
1063 let (l, r, ty) = NumericType::combine_div(left, right);
1064 self.warn_on_unknown(&ty, "Modulo of", exec_state);
1065 KclValue::Number { value: l % r, meta, ty }
1066 }
1067 BinaryOperator::Pow => KclValue::Number {
1068 value: left.n.powf(right.n),
1069 meta,
1070 ty: exec_state.current_default_units(),
1071 },
1072 BinaryOperator::Neq => {
1073 let (l, r, ty) = NumericType::combine_eq(left, right);
1074 self.warn_on_unknown(&ty, "Comparing", exec_state);
1075 KclValue::Bool { value: l != r, meta }
1076 }
1077 BinaryOperator::Gt => {
1078 let (l, r, ty) = NumericType::combine_eq(left, right);
1079 self.warn_on_unknown(&ty, "Comparing", exec_state);
1080 KclValue::Bool { value: l > r, meta }
1081 }
1082 BinaryOperator::Gte => {
1083 let (l, r, ty) = NumericType::combine_eq(left, right);
1084 self.warn_on_unknown(&ty, "Comparing", exec_state);
1085 KclValue::Bool { value: l >= r, meta }
1086 }
1087 BinaryOperator::Lt => {
1088 let (l, r, ty) = NumericType::combine_eq(left, right);
1089 self.warn_on_unknown(&ty, "Comparing", exec_state);
1090 KclValue::Bool { value: l < r, meta }
1091 }
1092 BinaryOperator::Lte => {
1093 let (l, r, ty) = NumericType::combine_eq(left, right);
1094 self.warn_on_unknown(&ty, "Comparing", exec_state);
1095 KclValue::Bool { value: l <= r, meta }
1096 }
1097 BinaryOperator::Eq => {
1098 let (l, r, ty) = NumericType::combine_eq(left, right);
1099 self.warn_on_unknown(&ty, "Comparing", exec_state);
1100 KclValue::Bool { value: l == r, meta }
1101 }
1102 BinaryOperator::And | BinaryOperator::Or => unreachable!(),
1103 };
1104
1105 Ok(value)
1106 }
1107
1108 fn warn_on_unknown(&self, ty: &NumericType, verb: &str, exec_state: &mut ExecState) {
1109 if ty == &NumericType::Unknown {
1110 exec_state.warn(CompilationError::err(
1112 self.as_source_range(),
1113 format!("{} numbers which have unknown or incompatible units.", verb),
1114 ));
1115 }
1116 }
1117}
1118
1119impl Node<UnaryExpression> {
1120 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1121 if self.operator == UnaryOperator::Not {
1122 let value = self.argument.get_result(exec_state, ctx).await?;
1123 let KclValue::Bool {
1124 value: bool_value,
1125 meta: _,
1126 } = value
1127 else {
1128 return Err(KclError::Semantic(KclErrorDetails {
1129 message: format!(
1130 "Cannot apply unary operator ! to non-boolean value: {}",
1131 value.human_friendly_type()
1132 ),
1133 source_ranges: vec![self.into()],
1134 }));
1135 };
1136 let meta = vec![Metadata {
1137 source_range: self.into(),
1138 }];
1139 let negated = KclValue::Bool {
1140 value: !bool_value,
1141 meta,
1142 };
1143
1144 return Ok(negated);
1145 }
1146
1147 let value = &self.argument.get_result(exec_state, ctx).await?;
1148 let err = || {
1149 KclError::Semantic(KclErrorDetails {
1150 message: format!(
1151 "You can only negate numbers, planes, or lines, but this is a {}",
1152 value.human_friendly_type()
1153 ),
1154 source_ranges: vec![self.into()],
1155 })
1156 };
1157 match value {
1158 KclValue::Number { value, ty, .. } => {
1159 let meta = vec![Metadata {
1160 source_range: self.into(),
1161 }];
1162 Ok(KclValue::Number {
1163 value: -value,
1164 meta,
1165 ty: ty.clone(),
1166 })
1167 }
1168 KclValue::Plane { value } => {
1169 let mut plane = value.clone();
1170 if plane.info.x_axis.x != 0.0 {
1171 plane.info.x_axis.x *= -1.0;
1172 }
1173 if plane.info.x_axis.y != 0.0 {
1174 plane.info.x_axis.y *= -1.0;
1175 }
1176 if plane.info.x_axis.z != 0.0 {
1177 plane.info.x_axis.z *= -1.0;
1178 }
1179
1180 plane.value = PlaneType::Uninit;
1181 plane.id = exec_state.next_uuid();
1182 Ok(KclValue::Plane { value: plane })
1183 }
1184 KclValue::Object { value: values, meta } => {
1185 let Some(direction) = values.get("direction") else {
1187 return Err(err());
1188 };
1189
1190 let direction = match direction {
1191 KclValue::Tuple { value: values, meta } => {
1192 let values = values
1193 .iter()
1194 .map(|v| match v {
1195 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
1196 value: *value * -1.0,
1197 ty: ty.clone(),
1198 meta: meta.clone(),
1199 }),
1200 _ => Err(err()),
1201 })
1202 .collect::<Result<Vec<_>, _>>()?;
1203
1204 KclValue::Tuple {
1205 value: values,
1206 meta: meta.clone(),
1207 }
1208 }
1209 KclValue::HomArray {
1210 value: values,
1211 ty: ty @ RuntimeType::Primitive(PrimitiveType::Number(_)),
1212 } => {
1213 let values = values
1214 .iter()
1215 .map(|v| match v {
1216 KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
1217 value: *value * -1.0,
1218 ty: ty.clone(),
1219 meta: meta.clone(),
1220 }),
1221 _ => Err(err()),
1222 })
1223 .collect::<Result<Vec<_>, _>>()?;
1224
1225 KclValue::HomArray {
1226 value: values,
1227 ty: ty.clone(),
1228 }
1229 }
1230 _ => return Err(err()),
1231 };
1232
1233 let mut value = values.clone();
1234 value.insert("direction".to_owned(), direction);
1235 Ok(KclValue::Object {
1236 value,
1237 meta: meta.clone(),
1238 })
1239 }
1240 _ => Err(err()),
1241 }
1242 }
1243}
1244
1245pub(crate) async fn execute_pipe_body(
1246 exec_state: &mut ExecState,
1247 body: &[Expr],
1248 source_range: SourceRange,
1249 ctx: &ExecutorContext,
1250) -> Result<KclValue, KclError> {
1251 let Some((first, body)) = body.split_first() else {
1252 return Err(KclError::Semantic(KclErrorDetails {
1253 message: "Pipe expressions cannot be empty".to_owned(),
1254 source_ranges: vec![source_range],
1255 }));
1256 };
1257 let meta = Metadata {
1262 source_range: SourceRange::from(first),
1263 };
1264 let output = ctx
1265 .execute_expr(first, exec_state, &meta, &[], StatementKind::Expression)
1266 .await?;
1267
1268 let previous_pipe_value = std::mem::replace(&mut exec_state.mod_local.pipe_value, Some(output));
1272 let result = inner_execute_pipe_body(exec_state, body, ctx).await;
1274 exec_state.mod_local.pipe_value = previous_pipe_value;
1276
1277 result
1278}
1279
1280#[async_recursion]
1283async fn inner_execute_pipe_body(
1284 exec_state: &mut ExecState,
1285 body: &[Expr],
1286 ctx: &ExecutorContext,
1287) -> Result<KclValue, KclError> {
1288 for expression in body {
1289 if let Expr::TagDeclarator(_) = expression {
1290 return Err(KclError::Semantic(KclErrorDetails {
1291 message: format!("This cannot be in a PipeExpression: {:?}", expression),
1292 source_ranges: vec![expression.into()],
1293 }));
1294 }
1295 let metadata = Metadata {
1296 source_range: SourceRange::from(expression),
1297 };
1298 let output = ctx
1299 .execute_expr(expression, exec_state, &metadata, &[], StatementKind::Expression)
1300 .await?;
1301 exec_state.mod_local.pipe_value = Some(output);
1302 }
1303 let final_output = exec_state.mod_local.pipe_value.take().unwrap();
1305 Ok(final_output)
1306}
1307
1308impl Node<CallExpressionKw> {
1309 #[async_recursion]
1310 pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1311 let fn_name = &self.callee;
1312 let callsite: SourceRange = self.into();
1313
1314 let mut fn_args = IndexMap::with_capacity(self.arguments.len());
1316 let mut errors = Vec::new();
1317 for arg_expr in &self.arguments {
1318 let source_range = SourceRange::from(arg_expr.arg.clone());
1319 let metadata = Metadata { source_range };
1320 let value = ctx
1321 .execute_expr(&arg_expr.arg, exec_state, &metadata, &[], StatementKind::Expression)
1322 .await?;
1323 let arg = Arg::new(value, source_range);
1324 match &arg_expr.label {
1325 Some(l) => {
1326 fn_args.insert(l.name.clone(), arg);
1327 }
1328 None => {
1329 if let Some(id) = arg_expr.arg.ident_name() {
1330 fn_args.insert(id.to_owned(), arg);
1331 } else {
1332 errors.push(arg);
1333 }
1334 }
1335 }
1336 }
1337
1338 let unlabeled = if let Some(ref arg_expr) = self.unlabeled {
1340 let source_range = SourceRange::from(arg_expr.clone());
1341 let metadata = Metadata { source_range };
1342 let value = ctx
1343 .execute_expr(arg_expr, exec_state, &metadata, &[], StatementKind::Expression)
1344 .await?;
1345
1346 let label = arg_expr.ident_name().map(str::to_owned);
1347
1348 Some((label, Arg::new(value, source_range)))
1349 } else {
1350 None
1351 };
1352
1353 let mut args = Args::new_kw(
1354 KwArgs {
1355 unlabeled,
1356 labeled: fn_args,
1357 errors,
1358 },
1359 self.into(),
1360 ctx.clone(),
1361 exec_state.pipe_value().map(|v| Arg::new(v.clone(), callsite)),
1362 );
1363 match ctx.stdlib.get_either(fn_name) {
1364 FunctionKind::Core(func) => {
1365 if func.deprecated() {
1366 exec_state.warn(CompilationError::err(
1367 self.callee.as_source_range(),
1368 format!("`{fn_name}` is deprecated, see the docs for a recommended replacement"),
1369 ));
1370 }
1371
1372 let formals = func.args(false);
1373
1374 if let Some((Some(label), _)) = &args.kw_args.unlabeled {
1377 if (formals.iter().all(|a| a.label_required) || exec_state.pipe_value().is_some())
1378 && formals.iter().any(|a| &a.name == label && a.label_required)
1379 && !args.kw_args.labeled.contains_key(label)
1380 {
1381 let (label, arg) = args.kw_args.unlabeled.take().unwrap();
1382 args.kw_args.labeled.insert(label.unwrap(), arg);
1383 }
1384 }
1385
1386 #[cfg(feature = "artifact-graph")]
1387 let op = if func.feature_tree_operation() {
1388 let op_labeled_args = args
1389 .kw_args
1390 .labeled
1391 .iter()
1392 .map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
1393 .collect();
1394 Some(Operation::StdLibCall {
1395 std_lib_fn: (&func).into(),
1396 unlabeled_arg: args
1397 .unlabeled_kw_arg_unconverted()
1398 .map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)),
1399 labeled_args: op_labeled_args,
1400 source_range: callsite,
1401 is_error: false,
1402 })
1403 } else {
1404 None
1405 };
1406
1407 for (label, arg) in &args.kw_args.labeled {
1408 match formals.iter().find(|p| &p.name == label) {
1409 Some(p) => {
1410 if !p.label_required {
1411 exec_state.err(CompilationError::err(
1412 arg.source_range,
1413 format!(
1414 "The function `{fn_name}` expects an unlabeled first parameter (`{label}`), but it is labelled in the call"
1415 ),
1416 ));
1417 }
1418 }
1419 None => {
1420 exec_state.err(CompilationError::err(
1421 arg.source_range,
1422 format!("`{label}` is not an argument of `{fn_name}`"),
1423 ));
1424 }
1425 }
1426 }
1427
1428 let mut return_value = {
1430 exec_state.mut_stack().push_new_env_for_rust_call();
1432 let result = func.std_lib_fn()(exec_state, args).await;
1433 exec_state.mut_stack().pop_env();
1434
1435 #[cfg(feature = "artifact-graph")]
1436 if let Some(mut op) = op {
1437 op.set_std_lib_call_is_error(result.is_err());
1438 exec_state.global.operations.push(op);
1444 }
1445
1446 result
1447 }?;
1448
1449 update_memory_for_tags_of_geometry(&mut return_value, exec_state)?;
1450
1451 Ok(return_value)
1452 }
1453 FunctionKind::UserDefined => {
1454 let func = fn_name.get_result(exec_state, ctx).await?.clone();
1457
1458 let Some(fn_src) = func.as_fn() else {
1459 return Err(KclError::Semantic(KclErrorDetails {
1460 message: "cannot call this because it isn't a function".to_string(),
1461 source_ranges: vec![callsite],
1462 }));
1463 };
1464
1465 let return_value = fn_src
1466 .call_kw(Some(fn_name.to_string()), exec_state, ctx, args, callsite)
1467 .await
1468 .map_err(|e| {
1469 e.add_source_ranges(vec![callsite])
1471 })?;
1472
1473 let result = return_value.ok_or_else(move || {
1474 let mut source_ranges: Vec<SourceRange> = vec![callsite];
1475 if let KclValue::Function { meta, .. } = func {
1477 source_ranges = meta.iter().map(|m| m.source_range).collect();
1478 };
1479 KclError::UndefinedValue(KclErrorDetails {
1480 message: format!("Result of user-defined function {} is undefined", fn_name),
1481 source_ranges,
1482 })
1483 })?;
1484
1485 Ok(result)
1486 }
1487 }
1488 }
1489}
1490
1491fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut ExecState) -> Result<(), KclError> {
1492 match result {
1497 KclValue::Sketch { value } => {
1498 for (name, tag) in value.tags.iter() {
1499 if exec_state.stack().cur_frame_contains(name) {
1500 exec_state.mut_stack().update(name, |v, _| {
1501 v.as_mut_tag().unwrap().merge_info(tag);
1502 });
1503 } else {
1504 exec_state
1505 .mut_stack()
1506 .add(
1507 name.to_owned(),
1508 KclValue::TagIdentifier(Box::new(tag.clone())),
1509 SourceRange::default(),
1510 )
1511 .unwrap();
1512 }
1513 }
1514 }
1515 KclValue::Solid { ref mut value } => {
1516 for v in &value.value {
1517 if let Some(tag) = v.get_tag() {
1518 let tag_id = if let Some(t) = value.sketch.tags.get(&tag.name) {
1520 let mut t = t.clone();
1521 let Some(info) = t.get_cur_info() else {
1522 return Err(KclError::Internal(KclErrorDetails {
1523 message: format!("Tag {} does not have path info", tag.name),
1524 source_ranges: vec![tag.into()],
1525 }));
1526 };
1527
1528 let mut info = info.clone();
1529 info.surface = Some(v.clone());
1530 info.sketch = value.id;
1531 t.info.push((exec_state.stack().current_epoch(), info));
1532 t
1533 } else {
1534 TagIdentifier {
1537 value: tag.name.clone(),
1538 info: vec![(
1539 exec_state.stack().current_epoch(),
1540 TagEngineInfo {
1541 id: v.get_id(),
1542 surface: Some(v.clone()),
1543 path: None,
1544 sketch: value.id,
1545 },
1546 )],
1547 meta: vec![Metadata {
1548 source_range: tag.clone().into(),
1549 }],
1550 }
1551 };
1552
1553 value.sketch.merge_tags(Some(&tag_id).into_iter());
1555
1556 if exec_state.stack().cur_frame_contains(&tag.name) {
1557 exec_state.mut_stack().update(&tag.name, |v, _| {
1558 v.as_mut_tag().unwrap().merge_info(&tag_id);
1559 });
1560 } else {
1561 exec_state
1562 .mut_stack()
1563 .add(
1564 tag.name.clone(),
1565 KclValue::TagIdentifier(Box::new(tag_id)),
1566 SourceRange::default(),
1567 )
1568 .unwrap();
1569 }
1570 }
1571 }
1572
1573 if !value.sketch.tags.is_empty() {
1575 let sketches_to_update: Vec<_> = exec_state
1576 .stack()
1577 .find_keys_in_current_env(|v| match v {
1578 KclValue::Sketch { value: sk } => sk.original_id == value.sketch.original_id,
1579 _ => false,
1580 })
1581 .cloned()
1582 .collect();
1583
1584 for k in sketches_to_update {
1585 exec_state.mut_stack().update(&k, |v, _| {
1586 let sketch = v.as_mut_sketch().unwrap();
1587 sketch.merge_tags(value.sketch.tags.values());
1588 });
1589 }
1590 }
1591 }
1592 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
1593 for v in value {
1594 update_memory_for_tags_of_geometry(v, exec_state)?;
1595 }
1596 }
1597 _ => {}
1598 }
1599 Ok(())
1600}
1601
1602impl Node<TagDeclarator> {
1603 pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
1604 let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
1605 value: self.name.clone(),
1606 info: Vec::new(),
1607 meta: vec![Metadata {
1608 source_range: self.into(),
1609 }],
1610 }));
1611
1612 exec_state
1613 .mut_stack()
1614 .add(self.name.clone(), memory_item.clone(), self.into())?;
1615
1616 Ok(self.into())
1617 }
1618}
1619
1620impl Node<ArrayExpression> {
1621 #[async_recursion]
1622 pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1623 let mut results = Vec::with_capacity(self.elements.len());
1624
1625 for element in &self.elements {
1626 let metadata = Metadata::from(element);
1627 let value = ctx
1630 .execute_expr(element, exec_state, &metadata, &[], StatementKind::Expression)
1631 .await?;
1632
1633 results.push(value);
1634 }
1635
1636 Ok(KclValue::HomArray {
1637 value: results,
1638 ty: RuntimeType::Primitive(PrimitiveType::Any),
1639 })
1640 }
1641}
1642
1643impl Node<ArrayRangeExpression> {
1644 #[async_recursion]
1645 pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1646 let metadata = Metadata::from(&self.start_element);
1647 let start_val = ctx
1648 .execute_expr(
1649 &self.start_element,
1650 exec_state,
1651 &metadata,
1652 &[],
1653 StatementKind::Expression,
1654 )
1655 .await?;
1656 let (start, start_ty) = start_val.as_int_with_ty().ok_or(KclError::Semantic(KclErrorDetails {
1657 source_ranges: vec![self.into()],
1658 message: format!("Expected int but found {}", start_val.human_friendly_type()),
1659 }))?;
1660 let metadata = Metadata::from(&self.end_element);
1661 let end_val = ctx
1662 .execute_expr(&self.end_element, exec_state, &metadata, &[], StatementKind::Expression)
1663 .await?;
1664 let (end, end_ty) = end_val.as_int_with_ty().ok_or(KclError::Semantic(KclErrorDetails {
1665 source_ranges: vec![self.into()],
1666 message: format!("Expected int but found {}", end_val.human_friendly_type()),
1667 }))?;
1668
1669 if start_ty != end_ty {
1670 let start = start_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: start_ty });
1671 let start = fmt::human_display_number(start.n, start.ty);
1672 let end = end_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: end_ty });
1673 let end = fmt::human_display_number(end.n, end.ty);
1674 return Err(KclError::Semantic(KclErrorDetails {
1675 source_ranges: vec![self.into()],
1676 message: format!("Range start and end must be of the same type, but found {start} and {end}"),
1677 }));
1678 }
1679
1680 if end < start {
1681 return Err(KclError::Semantic(KclErrorDetails {
1682 source_ranges: vec![self.into()],
1683 message: format!("Range start is greater than range end: {start} .. {end}"),
1684 }));
1685 }
1686
1687 let range: Vec<_> = if self.end_inclusive {
1688 (start..=end).collect()
1689 } else {
1690 (start..end).collect()
1691 };
1692
1693 let meta = vec![Metadata {
1694 source_range: self.into(),
1695 }];
1696
1697 Ok(KclValue::HomArray {
1698 value: range
1699 .into_iter()
1700 .map(|num| KclValue::Number {
1701 value: num as f64,
1702 ty: start_ty.clone(),
1703 meta: meta.clone(),
1704 })
1705 .collect(),
1706 ty: RuntimeType::Primitive(PrimitiveType::Number(start_ty)),
1707 })
1708 }
1709}
1710
1711impl Node<ObjectExpression> {
1712 #[async_recursion]
1713 pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1714 let mut object = HashMap::with_capacity(self.properties.len());
1715 for property in &self.properties {
1716 let metadata = Metadata::from(&property.value);
1717 let result = ctx
1718 .execute_expr(&property.value, exec_state, &metadata, &[], StatementKind::Expression)
1719 .await?;
1720
1721 object.insert(property.key.name.clone(), result);
1722 }
1723
1724 Ok(KclValue::Object {
1725 value: object,
1726 meta: vec![Metadata {
1727 source_range: self.into(),
1728 }],
1729 })
1730 }
1731}
1732
1733fn article_for<S: AsRef<str>>(s: S) -> &'static str {
1734 if s.as_ref().starts_with(['a', 'e', 'i', 'o', 'u', '[']) {
1736 "an"
1737 } else {
1738 "a"
1739 }
1740}
1741
1742fn number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<TyF64, KclError> {
1743 v.as_ty_f64().ok_or_else(|| {
1744 let actual_type = v.human_friendly_type();
1745 KclError::Semantic(KclErrorDetails {
1746 source_ranges: vec![source_range],
1747 message: format!("Expected a number, but found {actual_type}",),
1748 })
1749 })
1750}
1751
1752impl Node<IfExpression> {
1753 #[async_recursion]
1754 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1755 let cond = ctx
1757 .execute_expr(
1758 &self.cond,
1759 exec_state,
1760 &Metadata::from(self),
1761 &[],
1762 StatementKind::Expression,
1763 )
1764 .await?
1765 .get_bool()?;
1766 if cond {
1767 let block_result = ctx.exec_block(&self.then_val, exec_state, BodyType::Block).await?;
1768 return Ok(block_result.unwrap());
1772 }
1773
1774 for else_if in &self.else_ifs {
1776 let cond = ctx
1777 .execute_expr(
1778 &else_if.cond,
1779 exec_state,
1780 &Metadata::from(self),
1781 &[],
1782 StatementKind::Expression,
1783 )
1784 .await?
1785 .get_bool()?;
1786 if cond {
1787 let block_result = ctx.exec_block(&else_if.then_val, exec_state, BodyType::Block).await?;
1788 return Ok(block_result.unwrap());
1792 }
1793 }
1794
1795 ctx.exec_block(&self.final_else, exec_state, BodyType::Block)
1797 .await
1798 .map(|expr| expr.unwrap())
1799 }
1800}
1801
1802#[derive(Debug)]
1803enum Property {
1804 UInt(usize),
1805 String(String),
1806}
1807
1808impl Property {
1809 fn try_from(
1810 computed: bool,
1811 value: LiteralIdentifier,
1812 exec_state: &ExecState,
1813 sr: SourceRange,
1814 ) -> Result<Self, KclError> {
1815 let property_sr = vec![sr];
1816 let property_src: SourceRange = value.clone().into();
1817 match value {
1818 LiteralIdentifier::Identifier(identifier) => {
1819 let name = &identifier.name;
1820 if !computed {
1821 Ok(Property::String(name.to_string()))
1823 } else {
1824 let prop = exec_state.stack().get(name, property_src)?;
1827 jvalue_to_prop(prop, property_sr, name)
1828 }
1829 }
1830 LiteralIdentifier::Literal(literal) => {
1831 let value = literal.value.clone();
1832 match value {
1833 LiteralValue::Number { value, .. } => {
1834 if let Some(x) = crate::try_f64_to_usize(value) {
1835 Ok(Property::UInt(x))
1836 } else {
1837 Err(KclError::Semantic(KclErrorDetails {
1838 source_ranges: property_sr,
1839 message: format!("{value} is not a valid index, indices must be whole numbers >= 0"),
1840 }))
1841 }
1842 }
1843 _ => Err(KclError::Semantic(KclErrorDetails {
1844 source_ranges: vec![sr],
1845 message: "Only numbers (>= 0) can be indexes".to_owned(),
1846 })),
1847 }
1848 }
1849 }
1850 }
1851}
1852
1853fn jvalue_to_prop(value: &KclValue, property_sr: Vec<SourceRange>, name: &str) -> Result<Property, KclError> {
1854 let make_err = |message: String| {
1855 Err::<Property, _>(KclError::Semantic(KclErrorDetails {
1856 source_ranges: property_sr,
1857 message,
1858 }))
1859 };
1860 match value {
1861 KclValue::Number{value: num, .. } => {
1862 let num = *num;
1863 if num < 0.0 {
1864 return make_err(format!("'{num}' is negative, so you can't index an array with it"))
1865 }
1866 let nearest_int = crate::try_f64_to_usize(num);
1867 if let Some(nearest_int) = nearest_int {
1868 Ok(Property::UInt(nearest_int))
1869 } else {
1870 make_err(format!("'{num}' is not an integer, so you can't index an array with it"))
1871 }
1872 }
1873 KclValue::String{value: x, meta:_} => Ok(Property::String(x.to_owned())),
1874 _ => {
1875 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"))
1876 }
1877 }
1878}
1879
1880impl Property {
1881 fn type_name(&self) -> &'static str {
1882 match self {
1883 Property::UInt(_) => "number",
1884 Property::String(_) => "string",
1885 }
1886 }
1887}
1888
1889impl Node<PipeExpression> {
1890 #[async_recursion]
1891 pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
1892 execute_pipe_body(exec_state, &self.body, self.into(), ctx).await
1893 }
1894}
1895
1896fn type_check_params_kw(
1897 fn_name: Option<&str>,
1898 function_expression: NodeRef<'_, FunctionExpression>,
1899 args: &mut KwArgs,
1900 exec_state: &mut ExecState,
1901) -> Result<(), KclError> {
1902 if let Some((Some(label), _)) = &args.unlabeled {
1905 if (function_expression.params.iter().all(|p| p.labeled) || exec_state.pipe_value().is_some())
1906 && function_expression
1907 .params
1908 .iter()
1909 .any(|p| &p.identifier.name == label && p.labeled)
1910 && !args.labeled.contains_key(label)
1911 {
1912 let (label, arg) = args.unlabeled.take().unwrap();
1913 args.labeled.insert(label.unwrap(), arg);
1914 }
1915 }
1916
1917 for (label, arg) in &mut args.labeled {
1918 match function_expression.params.iter().find(|p| &p.identifier.name == label) {
1919 Some(p) => {
1920 if !p.labeled {
1921 exec_state.err(CompilationError::err(
1922 arg.source_range,
1923 format!(
1924 "{} expects an unlabeled first parameter (`{label}`), but it is labelled in the call",
1925 fn_name
1926 .map(|n| format!("The function `{}`", n))
1927 .unwrap_or_else(|| "This function".to_owned()),
1928 ),
1929 ));
1930 }
1931
1932 if let Some(ty) = &p.type_ {
1933 arg.value = arg
1934 .value
1935 .coerce(
1936 &RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).map_err(|e| KclError::Semantic(e.into()))?,
1937 exec_state,
1938 )
1939 .map_err(|e| {
1940 let mut message = format!(
1941 "{label} requires a value with type `{}`, but found {}",
1942 ty.inner,
1943 arg.value.human_friendly_type(),
1944 );
1945 if let Some(ty) = e.explicit_coercion {
1946 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})`");
1948 }
1949 KclError::Semantic(KclErrorDetails {
1950 message,
1951 source_ranges: vec![arg.source_range],
1952 })
1953 })?;
1954 }
1955 }
1956 None => {
1957 exec_state.err(CompilationError::err(
1958 arg.source_range,
1959 format!(
1960 "`{label}` is not an argument of {}",
1961 fn_name
1962 .map(|n| format!("`{}`", n))
1963 .unwrap_or_else(|| "this function".to_owned()),
1964 ),
1965 ));
1966 }
1967 }
1968 }
1969
1970 if !args.errors.is_empty() {
1971 let actuals = args.labeled.keys();
1972 let formals: Vec<_> = function_expression
1973 .params
1974 .iter()
1975 .filter_map(|p| {
1976 if !p.labeled {
1977 return None;
1978 }
1979
1980 let name = &p.identifier.name;
1981 if actuals.clone().any(|a| a == name) {
1982 return None;
1983 }
1984
1985 Some(format!("`{name}`"))
1986 })
1987 .collect();
1988
1989 let suggestion = if formals.is_empty() {
1990 String::new()
1991 } else {
1992 format!("; suggested labels: {}", formals.join(", "))
1993 };
1994
1995 let mut errors = args.errors.iter().map(|e| {
1996 CompilationError::err(
1997 e.source_range,
1998 format!("This argument needs a label, but it doesn't have one{suggestion}"),
1999 )
2000 });
2001
2002 let first = errors.next().unwrap();
2003 errors.for_each(|e| exec_state.err(e));
2004
2005 return Err(KclError::Semantic(first.into()));
2006 }
2007
2008 if let Some(arg) = &mut args.unlabeled {
2009 if let Some(p) = function_expression.params.iter().find(|p| !p.labeled) {
2010 if let Some(ty) = &p.type_ {
2011 arg.1.value = arg
2012 .1
2013 .value
2014 .coerce(
2015 &RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.1.source_range)
2016 .map_err(|e| KclError::Semantic(e.into()))?,
2017 exec_state,
2018 )
2019 .map_err(|_| {
2020 KclError::Semantic(KclErrorDetails {
2021 message: format!(
2022 "The input argument of {} requires a value with type `{}`, but found {}",
2023 fn_name
2024 .map(|n| format!("`{}`", n))
2025 .unwrap_or_else(|| "this function".to_owned()),
2026 ty.inner,
2027 arg.1.value.human_friendly_type()
2028 ),
2029 source_ranges: vec![arg.1.source_range],
2030 })
2031 })?;
2032 }
2033 }
2034 }
2035
2036 Ok(())
2037}
2038
2039fn assign_args_to_params_kw(
2040 fn_name: Option<&str>,
2041 function_expression: NodeRef<'_, FunctionExpression>,
2042 mut args: Args,
2043 exec_state: &mut ExecState,
2044) -> Result<(), KclError> {
2045 type_check_params_kw(fn_name, function_expression, &mut args.kw_args, exec_state)?;
2046
2047 let source_ranges = vec![function_expression.into()];
2050
2051 for param in function_expression.params.iter() {
2052 if param.labeled {
2053 let arg = args.kw_args.labeled.get(¶m.identifier.name);
2054 let arg_val = match arg {
2055 Some(arg) => arg.value.clone(),
2056 None => match param.default_value {
2057 Some(ref default_val) => KclValue::from_default_param(default_val.clone(), exec_state),
2058 None => {
2059 return Err(KclError::Semantic(KclErrorDetails {
2060 source_ranges,
2061 message: format!(
2062 "This function requires a parameter {}, but you haven't passed it one.",
2063 param.identifier.name
2064 ),
2065 }));
2066 }
2067 },
2068 };
2069 exec_state
2070 .mut_stack()
2071 .add(param.identifier.name.clone(), arg_val, (¶m.identifier).into())?;
2072 } else {
2073 let unlabelled = args.unlabeled_kw_arg_unconverted();
2074
2075 let Some(unlabeled) = unlabelled else {
2076 let param_name = ¶m.identifier.name;
2077 return Err(if args.kw_args.labeled.contains_key(param_name) {
2078 KclError::Semantic(KclErrorDetails {
2079 source_ranges,
2080 message: format!("The function does declare a parameter named '{param_name}', but this parameter doesn't use a label. Try removing the `{param_name}:`"),
2081 })
2082 } else {
2083 KclError::Semantic(KclErrorDetails {
2084 source_ranges,
2085 message: "This function expects an unlabeled first parameter, but you haven't passed it one."
2086 .to_owned(),
2087 })
2088 });
2089 };
2090 exec_state.mut_stack().add(
2091 param.identifier.name.clone(),
2092 unlabeled.value.clone(),
2093 (¶m.identifier).into(),
2094 )?;
2095 }
2096 }
2097
2098 Ok(())
2099}
2100
2101fn coerce_result_type(
2102 result: Result<Option<KclValue>, KclError>,
2103 function_expression: NodeRef<'_, FunctionExpression>,
2104 exec_state: &mut ExecState,
2105) -> Result<Option<KclValue>, KclError> {
2106 if let Ok(Some(val)) = result {
2107 if let Some(ret_ty) = &function_expression.return_type {
2108 let ty = RuntimeType::from_parsed(ret_ty.inner.clone(), exec_state, ret_ty.as_source_range())
2109 .map_err(|e| KclError::Semantic(e.into()))?;
2110 let val = val.coerce(&ty, exec_state).map_err(|_| {
2111 KclError::Semantic(KclErrorDetails {
2112 message: format!(
2113 "This function requires its result to be of type `{}`, but found {}",
2114 ty.human_friendly_type(),
2115 val.human_friendly_type(),
2116 ),
2117 source_ranges: ret_ty.as_source_ranges(),
2118 })
2119 })?;
2120 Ok(Some(val))
2121 } else {
2122 Ok(Some(val))
2123 }
2124 } else {
2125 result
2126 }
2127}
2128
2129async fn call_user_defined_function_kw(
2130 fn_name: Option<&str>,
2131 args: Args,
2132 memory: EnvironmentRef,
2133 function_expression: NodeRef<'_, FunctionExpression>,
2134 exec_state: &mut ExecState,
2135 ctx: &ExecutorContext,
2136) -> Result<Option<KclValue>, KclError> {
2137 exec_state.mut_stack().push_new_env_for_call(memory);
2141 if let Err(e) = assign_args_to_params_kw(fn_name, function_expression, args, exec_state) {
2142 exec_state.mut_stack().pop_env();
2143 return Err(e);
2144 }
2145
2146 let result = ctx
2148 .exec_block(&function_expression.body, exec_state, BodyType::Block)
2149 .await;
2150 let mut result = result.map(|_| {
2151 exec_state
2152 .stack()
2153 .get(memory::RETURN_NAME, function_expression.as_source_range())
2154 .ok()
2155 .cloned()
2156 });
2157
2158 result = coerce_result_type(result, function_expression, exec_state);
2159
2160 exec_state.mut_stack().pop_env();
2162
2163 result
2164}
2165
2166impl FunctionSource {
2167 pub async fn call_kw(
2168 &self,
2169 fn_name: Option<String>,
2170 exec_state: &mut ExecState,
2171 ctx: &ExecutorContext,
2172 mut args: Args,
2173 callsite: SourceRange,
2174 ) -> Result<Option<KclValue>, KclError> {
2175 match self {
2176 FunctionSource::Std { func, ast, props } => {
2177 if props.deprecated {
2178 exec_state.warn(CompilationError::err(
2179 callsite,
2180 format!(
2181 "`{}` is deprecated, see the docs for a recommended replacement",
2182 props.name
2183 ),
2184 ));
2185 }
2186
2187 type_check_params_kw(Some(&props.name), ast, &mut args.kw_args, exec_state)?;
2188
2189 if let Some(arg) = &mut args.kw_args.unlabeled {
2190 if let Some(p) = ast.params.iter().find(|p| !p.labeled) {
2191 if let Some(ty) = &p.type_ {
2192 arg.1.value = arg
2193 .1
2194 .value
2195 .coerce(
2196 &RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.1.source_range)
2197 .map_err(|e| KclError::Semantic(e.into()))?,
2198 exec_state,
2199 )
2200 .map_err(|_| {
2201 KclError::Semantic(KclErrorDetails {
2202 message: format!(
2203 "The input argument of {} requires a value with type `{}`, but found {}",
2204 props.name,
2205 ty.inner,
2206 arg.1.value.human_friendly_type(),
2207 ),
2208 source_ranges: vec![callsite],
2209 })
2210 })?;
2211 }
2212 }
2213 }
2214
2215 #[cfg(feature = "artifact-graph")]
2216 let op = if props.include_in_feature_tree {
2217 let op_labeled_args = args
2218 .kw_args
2219 .labeled
2220 .iter()
2221 .map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
2222 .collect();
2223 Some(Operation::KclStdLibCall {
2224 name: fn_name.unwrap_or_default(),
2225 unlabeled_arg: args
2226 .unlabeled_kw_arg_unconverted()
2227 .map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)),
2228 labeled_args: op_labeled_args,
2229 source_range: callsite,
2230 is_error: false,
2231 })
2232 } else {
2233 None
2234 };
2235
2236 exec_state.mut_stack().push_new_env_for_rust_call();
2238 let mut result = {
2239 let result = func(exec_state, args).await;
2241 exec_state.mut_stack().pop_env();
2242
2243 #[cfg(feature = "artifact-graph")]
2244 if let Some(mut op) = op {
2245 op.set_std_lib_call_is_error(result.is_err());
2246 exec_state.global.operations.push(op);
2252 }
2253 result
2254 }?;
2255
2256 update_memory_for_tags_of_geometry(&mut result, exec_state)?;
2257
2258 Ok(Some(result))
2259 }
2260 FunctionSource::User { ast, memory, .. } => {
2261 #[cfg(feature = "artifact-graph")]
2263 {
2264 let op_labeled_args = args
2265 .kw_args
2266 .labeled
2267 .iter()
2268 .map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
2269 .collect();
2270 exec_state.global.operations.push(Operation::GroupBegin {
2271 group: Group::FunctionCall {
2272 name: fn_name.clone(),
2273 function_source_range: ast.as_source_range(),
2274 unlabeled_arg: args
2275 .kw_args
2276 .unlabeled
2277 .as_ref()
2278 .map(|arg| OpArg::new(OpKclValue::from(&arg.1.value), arg.1.source_range)),
2279 labeled_args: op_labeled_args,
2280 },
2281 source_range: callsite,
2282 });
2283 }
2284
2285 let result =
2286 call_user_defined_function_kw(fn_name.as_deref(), args, *memory, ast, exec_state, ctx).await;
2287
2288 #[cfg(feature = "artifact-graph")]
2290 exec_state.global.operations.push(Operation::GroupEnd);
2291
2292 result
2293 }
2294 FunctionSource::None => unreachable!(),
2295 }
2296 }
2297}
2298
2299#[cfg(test)]
2300mod test {
2301 use std::sync::Arc;
2302
2303 use tokio::io::AsyncWriteExt;
2304
2305 use super::*;
2306 use crate::{
2307 exec::UnitType,
2308 execution::{memory::Stack, parse_execute, ContextType},
2309 parsing::ast::types::{DefaultParamVal, Identifier, Parameter},
2310 ExecutorSettings, UnitLen,
2311 };
2312
2313 #[tokio::test(flavor = "multi_thread")]
2314 async fn test_assign_args_to_params() {
2315 fn mem(number: usize) -> KclValue {
2317 KclValue::Number {
2318 value: number as f64,
2319 ty: NumericType::count(),
2320 meta: Default::default(),
2321 }
2322 }
2323 fn ident(s: &'static str) -> Node<Identifier> {
2324 Node::no_src(Identifier {
2325 name: s.to_owned(),
2326 digest: None,
2327 })
2328 }
2329 fn opt_param(s: &'static str) -> Parameter {
2330 Parameter {
2331 identifier: ident(s),
2332 type_: None,
2333 default_value: Some(DefaultParamVal::none()),
2334 labeled: true,
2335 digest: None,
2336 }
2337 }
2338 fn req_param(s: &'static str) -> Parameter {
2339 Parameter {
2340 identifier: ident(s),
2341 type_: None,
2342 default_value: None,
2343 labeled: true,
2344 digest: None,
2345 }
2346 }
2347 fn additional_program_memory(items: &[(String, KclValue)]) -> Stack {
2348 let mut program_memory = Stack::new_for_tests();
2349 for (name, item) in items {
2350 program_memory
2351 .add(name.clone(), item.clone(), SourceRange::default())
2352 .unwrap();
2353 }
2354 program_memory
2355 }
2356 for (test_name, params, args, expected) in [
2358 ("empty", Vec::new(), Vec::new(), Ok(additional_program_memory(&[]))),
2359 (
2360 "all params required, and all given, should be OK",
2361 vec![req_param("x")],
2362 vec![("x", mem(1))],
2363 Ok(additional_program_memory(&[("x".to_owned(), mem(1))])),
2364 ),
2365 (
2366 "all params required, none given, should error",
2367 vec![req_param("x")],
2368 vec![],
2369 Err(KclError::Semantic(KclErrorDetails {
2370 source_ranges: vec![SourceRange::default()],
2371 message: "This function requires a parameter x, but you haven't passed it one.".to_owned(),
2372 })),
2373 ),
2374 (
2375 "all params optional, none given, should be OK",
2376 vec![opt_param("x")],
2377 vec![],
2378 Ok(additional_program_memory(&[("x".to_owned(), KclValue::none())])),
2379 ),
2380 (
2381 "mixed params, too few given",
2382 vec![req_param("x"), opt_param("y")],
2383 vec![],
2384 Err(KclError::Semantic(KclErrorDetails {
2385 source_ranges: vec![SourceRange::default()],
2386 message: "This function requires a parameter x, but you haven't passed it one.".to_owned(),
2387 })),
2388 ),
2389 (
2390 "mixed params, minimum given, should be OK",
2391 vec![req_param("x"), opt_param("y")],
2392 vec![("x", mem(1))],
2393 Ok(additional_program_memory(&[
2394 ("x".to_owned(), mem(1)),
2395 ("y".to_owned(), KclValue::none()),
2396 ])),
2397 ),
2398 (
2399 "mixed params, maximum given, should be OK",
2400 vec![req_param("x"), opt_param("y")],
2401 vec![("x", mem(1)), ("y", mem(2))],
2402 Ok(additional_program_memory(&[
2403 ("x".to_owned(), mem(1)),
2404 ("y".to_owned(), mem(2)),
2405 ])),
2406 ),
2407 ] {
2408 let func_expr = &Node::no_src(FunctionExpression {
2410 params,
2411 body: Program::empty(),
2412 return_type: None,
2413 digest: None,
2414 });
2415 let labeled = args
2416 .iter()
2417 .map(|(name, value)| {
2418 let arg = Arg::new(value.clone(), SourceRange::default());
2419 ((*name).to_owned(), arg)
2420 })
2421 .collect::<IndexMap<_, _>>();
2422 let exec_ctxt = ExecutorContext {
2423 engine: Arc::new(Box::new(
2424 crate::engine::conn_mock::EngineConnection::new().await.unwrap(),
2425 )),
2426 fs: Arc::new(crate::fs::FileManager::new()),
2427 stdlib: Arc::new(crate::std::StdLib::new()),
2428 settings: Default::default(),
2429 context_type: ContextType::Mock,
2430 };
2431 let mut exec_state = ExecState::new(&exec_ctxt);
2432 exec_state.mod_local.stack = Stack::new_for_tests();
2433
2434 let args = Args::new_kw(
2435 KwArgs {
2436 unlabeled: None,
2437 labeled,
2438 errors: Vec::new(),
2439 },
2440 SourceRange::default(),
2441 exec_ctxt,
2442 None,
2443 );
2444 let actual =
2445 assign_args_to_params_kw(None, func_expr, args, &mut exec_state).map(|_| exec_state.mod_local.stack);
2446 assert_eq!(
2447 actual, expected,
2448 "failed test '{test_name}':\ngot {actual:?}\nbut expected\n{expected:?}"
2449 );
2450 }
2451 }
2452
2453 #[tokio::test(flavor = "multi_thread")]
2454 async fn ascription() {
2455 let program = r#"
2456a = 42: number
2457b = a: number
2458p = {
2459 origin = { x = 0, y = 0, z = 0 },
2460 xAxis = { x = 1, y = 0, z = 0 },
2461 yAxis = { x = 0, y = 1, z = 0 },
2462 zAxis = { x = 0, y = 0, z = 1 }
2463}: Plane
2464arr1 = [42]: [number(cm)]
2465"#;
2466
2467 let result = parse_execute(program).await.unwrap();
2468 let mem = result.exec_state.stack();
2469 assert!(matches!(
2470 mem.memory
2471 .get_from("p", result.mem_env, SourceRange::default(), 0)
2472 .unwrap(),
2473 KclValue::Plane { .. }
2474 ));
2475 let arr1 = mem
2476 .memory
2477 .get_from("arr1", result.mem_env, SourceRange::default(), 0)
2478 .unwrap();
2479 if let KclValue::HomArray { value, ty } = arr1 {
2480 assert_eq!(value.len(), 1, "Expected Vec with specific length: found {:?}", value);
2481 assert_eq!(*ty, RuntimeType::known_length(UnitLen::Cm));
2482 if let KclValue::Number { value, ty, .. } = &value[0] {
2484 assert_eq!(*value, 4.2);
2486 assert_eq!(*ty, NumericType::Known(UnitType::Length(UnitLen::Cm)));
2487 } else {
2488 panic!("Expected a number; found {:?}", value[0]);
2489 }
2490 } else {
2491 panic!("Expected HomArray; found {arr1:?}");
2492 }
2493
2494 let program = r#"
2495a = 42: string
2496"#;
2497 let result = parse_execute(program).await;
2498 let err = result.unwrap_err();
2499 assert!(
2500 err.to_string()
2501 .contains("could not coerce number(default units) value to type string"),
2502 "Expected error but found {err:?}"
2503 );
2504
2505 let program = r#"
2506a = 42: Plane
2507"#;
2508 let result = parse_execute(program).await;
2509 let err = result.unwrap_err();
2510 assert!(
2511 err.to_string()
2512 .contains("could not coerce number(default units) value to type Plane"),
2513 "Expected error but found {err:?}"
2514 );
2515
2516 let program = r#"
2517arr = [0]: [string]
2518"#;
2519 let result = parse_execute(program).await;
2520 let err = result.unwrap_err();
2521 assert!(
2522 err.to_string()
2523 .contains("could not coerce [any; 1] value to type [string]"),
2524 "Expected error but found {err:?}"
2525 );
2526
2527 let program = r#"
2528mixedArr = [0, "a"]: [number(mm)]
2529"#;
2530 let result = parse_execute(program).await;
2531 let err = result.unwrap_err();
2532 assert!(
2533 err.to_string()
2534 .contains("could not coerce [any; 2] value to type [number(mm)]"),
2535 "Expected error but found {err:?}"
2536 );
2537 }
2538
2539 #[tokio::test(flavor = "multi_thread")]
2540 async fn neg_plane() {
2541 let program = r#"
2542p = {
2543 origin = { x = 0, y = 0, z = 0 },
2544 xAxis = { x = 1, y = 0, z = 0 },
2545 yAxis = { x = 0, y = 1, z = 0 },
2546}: Plane
2547p2 = -p
2548"#;
2549
2550 let result = parse_execute(program).await.unwrap();
2551 let mem = result.exec_state.stack();
2552 match mem
2553 .memory
2554 .get_from("p2", result.mem_env, SourceRange::default(), 0)
2555 .unwrap()
2556 {
2557 KclValue::Plane { value } => {
2558 assert_eq!(value.info.x_axis.x, -1.0);
2559 assert_eq!(value.info.x_axis.y, 0.0);
2560 assert_eq!(value.info.x_axis.z, 0.0);
2561 }
2562 _ => unreachable!(),
2563 }
2564 }
2565
2566 #[tokio::test(flavor = "multi_thread")]
2567 async fn multiple_returns() {
2568 let program = r#"fn foo() {
2569 return 0
2570 return 42
2571}
2572
2573a = foo()
2574"#;
2575
2576 let result = parse_execute(program).await;
2577 assert!(result.unwrap_err().to_string().contains("return"));
2578 }
2579
2580 #[tokio::test(flavor = "multi_thread")]
2581 async fn load_all_modules() {
2582 let program_a_kcl = r#"
2584export a = 1
2585"#;
2586 let program_b_kcl = r#"
2588import a from 'a.kcl'
2589
2590export b = a + 1
2591"#;
2592 let program_c_kcl = r#"
2594import a from 'a.kcl'
2595
2596export c = a + 2
2597"#;
2598
2599 let main_kcl = r#"
2601import b from 'b.kcl'
2602import c from 'c.kcl'
2603
2604d = b + c
2605"#;
2606
2607 let main = crate::parsing::parse_str(main_kcl, ModuleId::default())
2608 .parse_errs_as_err()
2609 .unwrap();
2610
2611 let tmpdir = tempfile::TempDir::with_prefix("zma_kcl_load_all_modules").unwrap();
2612
2613 tokio::fs::File::create(tmpdir.path().join("main.kcl"))
2614 .await
2615 .unwrap()
2616 .write_all(main_kcl.as_bytes())
2617 .await
2618 .unwrap();
2619
2620 tokio::fs::File::create(tmpdir.path().join("a.kcl"))
2621 .await
2622 .unwrap()
2623 .write_all(program_a_kcl.as_bytes())
2624 .await
2625 .unwrap();
2626
2627 tokio::fs::File::create(tmpdir.path().join("b.kcl"))
2628 .await
2629 .unwrap()
2630 .write_all(program_b_kcl.as_bytes())
2631 .await
2632 .unwrap();
2633
2634 tokio::fs::File::create(tmpdir.path().join("c.kcl"))
2635 .await
2636 .unwrap()
2637 .write_all(program_c_kcl.as_bytes())
2638 .await
2639 .unwrap();
2640
2641 let exec_ctxt = ExecutorContext {
2642 engine: Arc::new(Box::new(
2643 crate::engine::conn_mock::EngineConnection::new()
2644 .await
2645 .map_err(|err| {
2646 KclError::Internal(crate::errors::KclErrorDetails {
2647 message: format!("Failed to create mock engine connection: {}", err),
2648 source_ranges: vec![SourceRange::default()],
2649 })
2650 })
2651 .unwrap(),
2652 )),
2653 fs: Arc::new(crate::fs::FileManager::new()),
2654 settings: ExecutorSettings {
2655 project_directory: Some(crate::TypedPath(tmpdir.path().into())),
2656 ..Default::default()
2657 },
2658 stdlib: Arc::new(crate::std::StdLib::new()),
2659 context_type: ContextType::Mock,
2660 };
2661 let mut exec_state = ExecState::new(&exec_ctxt);
2662
2663 exec_ctxt
2664 .run(
2665 &crate::Program {
2666 ast: main.clone(),
2667 original_file_contents: "".to_owned(),
2668 },
2669 &mut exec_state,
2670 )
2671 .await
2672 .unwrap();
2673 }
2674
2675 #[tokio::test(flavor = "multi_thread")]
2676 async fn user_coercion() {
2677 let program = r#"fn foo(x: Axis2d) {
2678 return 0
2679}
2680
2681foo(x = { direction = [0, 0], origin = [0, 0]})
2682"#;
2683
2684 parse_execute(program).await.unwrap();
2685
2686 let program = r#"fn foo(x: Axis3d) {
2687 return 0
2688}
2689
2690foo(x = { direction = [0, 0], origin = [0, 0]})
2691"#;
2692
2693 parse_execute(program).await.unwrap_err();
2694 }
2695
2696 #[tokio::test(flavor = "multi_thread")]
2697 async fn coerce_return() {
2698 let program = r#"fn foo(): number(mm) {
2699 return 42
2700}
2701
2702a = foo()
2703"#;
2704
2705 parse_execute(program).await.unwrap();
2706
2707 let program = r#"fn foo(): number(mm) {
2708 return { bar: 42 }
2709}
2710
2711a = foo()
2712"#;
2713
2714 parse_execute(program).await.unwrap_err();
2715 }
2716
2717 #[tokio::test(flavor = "multi_thread")]
2718 async fn test_sensible_error_when_missing_equals_in_kwarg() {
2719 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)"]
2720 .into_iter()
2721 .enumerate()
2722 {
2723 let program = format!(
2724 "fn foo() {{ return 0 }}
2725z = 0
2726fn f(x, y, z) {{ return 0 }}
2727{call}"
2728 );
2729 let err = parse_execute(&program).await.unwrap_err();
2730 let msg = err.message();
2731 assert!(
2732 msg.contains("This argument needs a label, but it doesn't have one"),
2733 "failed test {i}: {msg}"
2734 );
2735 assert!(msg.contains("`y`"), "failed test {i}, missing `y`: {msg}");
2736 if i == 0 {
2737 assert!(msg.contains("`z`"), "failed test {i}, missing `z`: {msg}");
2738 }
2739 }
2740 }
2741
2742 #[tokio::test(flavor = "multi_thread")]
2743 async fn default_param_for_unlabeled() {
2744 let ast = r#"fn myExtrude(@sk, length) {
2747 return extrude(sk, length)
2748}
2749sketch001 = startSketchOn(XY)
2750 |> circle(center = [0, 0], radius = 93.75)
2751 |> myExtrude(length = 40)
2752"#;
2753
2754 parse_execute(ast).await.unwrap();
2755 }
2756
2757 #[tokio::test(flavor = "multi_thread")]
2758 async fn dont_use_unlabelled_as_input() {
2759 let ast = r#"length = 10
2761startSketchOn(XY)
2762 |> circle(center = [0, 0], radius = 93.75)
2763 |> extrude(length)
2764"#;
2765
2766 parse_execute(ast).await.unwrap();
2767 }
2768
2769 #[tokio::test(flavor = "multi_thread")]
2770 async fn ascription_in_binop() {
2771 let ast = r#"foo = tan(0): number(rad) - 4deg"#;
2772 parse_execute(ast).await.unwrap();
2773 }
2774
2775 #[tokio::test(flavor = "multi_thread")]
2776 async fn neg_sqrt() {
2777 let ast = r#"bad = sqrt(-2)"#;
2778
2779 let e = parse_execute(ast).await.unwrap_err();
2780 assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
2782 }
2783
2784 #[tokio::test(flavor = "multi_thread")]
2785 async fn coerce_unknown_to_length() {
2786 let ast = r#"x = 2mm * 2mm
2787y = x: number(Length)"#;
2788 let e = parse_execute(ast).await.unwrap_err();
2789 assert!(
2790 e.message().contains("could not coerce"),
2791 "Error message: '{}'",
2792 e.message()
2793 );
2794
2795 let ast = r#"x = 2mm
2796y = x: number(Length)"#;
2797 let result = parse_execute(ast).await.unwrap();
2798 let mem = result.exec_state.stack();
2799 let num = mem
2800 .memory
2801 .get_from("y", result.mem_env, SourceRange::default(), 0)
2802 .unwrap()
2803 .as_ty_f64()
2804 .unwrap();
2805 assert_eq!(num.n, 2.0);
2806 assert_eq!(num.ty, NumericType::mm());
2807 }
2808}