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