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}