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