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