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