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