Merge branch 'main' into jtran/units-indicator
This commit is contained in:
		
							
								
								
									
										18
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								Makefile
									
									
									
									
									
								
							@ -20,29 +20,25 @@ $(WASM_PACK):
 | 
			
		||||
###############################################################################
 | 
			
		||||
# BUILD
 | 
			
		||||
 | 
			
		||||
KCL_WASM_LIB_FILES := $(wildcard rust/**/*.rs)
 | 
			
		||||
TS_SRC := $(wildcard src/**/*.tsx) $(wildcard src/**/*.ts)
 | 
			
		||||
XSTATE_TYPEGENS := $(wildcard src/machines/*.typegen.ts)
 | 
			
		||||
RUST_SOURCES := $(wildcard rust/*) $(wildcard rust/**/*)
 | 
			
		||||
TYPESCRIPT_SOURCES := $(wildcard src/**/*.tsx) $(wildcard src/**/*.ts)
 | 
			
		||||
 | 
			
		||||
.PHONY: build
 | 
			
		||||
build: build-web build-desktop
 | 
			
		||||
 | 
			
		||||
.PHONY: build-web
 | 
			
		||||
build-web: public/kcl_wasm_lib_bg.wasm $(XSTATE_TYPEGENS) build/index.html
 | 
			
		||||
build-web: public/kcl_wasm_lib_bg.wasm build/index.html
 | 
			
		||||
 | 
			
		||||
.PHONY: build-desktop
 | 
			
		||||
build-desktop: public/kcl_wasm_lib_bg.wasm $(XSTATE_TYPEGENS) .vite/build/main.js
 | 
			
		||||
build-desktop: public/kcl_wasm_lib_bg.wasm .vite/build/main.js
 | 
			
		||||
 | 
			
		||||
public/kcl_wasm_lib_bg.wasm: $(KCL_WASM_LIB_FILES)
 | 
			
		||||
public/kcl_wasm_lib_bg.wasm: $(RUST_SOURCES)
 | 
			
		||||
	yarn build:wasm
 | 
			
		||||
 | 
			
		||||
$(XSTATE_TYPEGENS): $(TS_SRC)
 | 
			
		||||
	yarn xstate typegen 'src/**/*.ts?(x)'
 | 
			
		||||
 | 
			
		||||
build/index.html: $(TS_SRC)
 | 
			
		||||
build/index.html: $(TYPESCRIPT_SOURCES)
 | 
			
		||||
	yarn build:local
 | 
			
		||||
 | 
			
		||||
.vite/build/main.js: $(TS_SRC)
 | 
			
		||||
.vite/build/main.js: $(TYPESCRIPT_SOURCES)
 | 
			
		||||
	yarn tronb:vite:dev
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
 | 
			
		||||
@ -22,8 +22,12 @@ layout: manual
 | 
			
		||||
  * [`string`](kcl/types/string)
 | 
			
		||||
  * [`tag`](kcl/types/tag)
 | 
			
		||||
* **std**
 | 
			
		||||
  * [`Face`](kcl/types/Face)
 | 
			
		||||
  * [`HALF_TURN`](kcl/consts/std-HALF_TURN)
 | 
			
		||||
  * [`Helix`](kcl/types/Helix)
 | 
			
		||||
  * [`Plane`](kcl/types/Plane)
 | 
			
		||||
  * [`Point2d`](kcl/types/Point2d)
 | 
			
		||||
  * [`Point3d`](kcl/types/Point3d)
 | 
			
		||||
  * [`QUARTER_TURN`](kcl/consts/std-QUARTER_TURN)
 | 
			
		||||
  * [`Sketch`](kcl/types/Sketch)
 | 
			
		||||
  * [`Solid`](kcl/types/Solid)
 | 
			
		||||
 | 
			
		||||
@ -1,28 +1,12 @@
 | 
			
		||||
---
 | 
			
		||||
title: "Face"
 | 
			
		||||
title: "std::Face"
 | 
			
		||||
excerpt: "A face."
 | 
			
		||||
layout: manual
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
A face.
 | 
			
		||||
 | 
			
		||||
**Type:** `object`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Properties
 | 
			
		||||
 | 
			
		||||
| Property | Type | Description | Required |
 | 
			
		||||
|----------|------|-------------|----------|
 | 
			
		||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the face. | No |
 | 
			
		||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
 | 
			
		||||
| `value` |[`string`](/docs/kcl/types/string)| The tag of the face. | No |
 | 
			
		||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's X axis be? | No |
 | 
			
		||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's Y axis be? | No |
 | 
			
		||||
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
 | 
			
		||||
| `solid` |[`Solid`](/docs/kcl/types/Solid)| The solid the face is on. | No |
 | 
			
		||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,26 +1,12 @@
 | 
			
		||||
---
 | 
			
		||||
title: "Helix"
 | 
			
		||||
title: "std::Helix"
 | 
			
		||||
excerpt: "A helix."
 | 
			
		||||
layout: manual
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
A helix.
 | 
			
		||||
 | 
			
		||||
**Type:** `object`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Properties
 | 
			
		||||
 | 
			
		||||
| Property | Type | Description | Required |
 | 
			
		||||
|----------|------|-------------|----------|
 | 
			
		||||
| `value` |[`string`](/docs/kcl/types/string)| The id of the helix. | No |
 | 
			
		||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
 | 
			
		||||
| `revolutions` |[`number`](/docs/kcl/types/number)| Number of revolutions. | No |
 | 
			
		||||
| `angleStart` |[`number`](/docs/kcl/types/number)| Start angle (in degrees). | No |
 | 
			
		||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
 | 
			
		||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -188,7 +188,7 @@ Any KCL value.
 | 
			
		||||
| Property | Type | Description | Required |
 | 
			
		||||
|----------|------|-------------|----------|
 | 
			
		||||
| `type` |enum: [`Face`](/docs/kcl/types/Face)|  | No |
 | 
			
		||||
| `value` |[`Face`](/docs/kcl/types/Face)| A face. | No |
 | 
			
		||||
| `value` |[`Face`](/docs/kcl/types/Face)|  | No |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
@ -236,7 +236,7 @@ Any KCL value.
 | 
			
		||||
| Property | Type | Description | Required |
 | 
			
		||||
|----------|------|-------------|----------|
 | 
			
		||||
| `type` |enum: [`Helix`](/docs/kcl/types/Helix)|  | No |
 | 
			
		||||
| `value` |[`Helix`](/docs/kcl/types/Helix)| A helix. | No |
 | 
			
		||||
| `value` |[`Helix`](/docs/kcl/types/Helix)|  | No |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								docs/kcl/types/Point2d.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								docs/kcl/types/Point2d.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
---
 | 
			
		||||
title: "std::Point2d"
 | 
			
		||||
excerpt: "A point in two dimensional space."
 | 
			
		||||
layout: manual
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
A point in two dimensional space.
 | 
			
		||||
 | 
			
		||||
```kcl
 | 
			
		||||
type Point2d = [number; 2]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`Point2d` is an alias for a two-element array of [number](/docs/kcl/types/number)s. To write a value
 | 
			
		||||
with type `Point2d`, use an array, e.g., `[0, 0]` or `[5.0, 3.14]`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,22 +1,17 @@
 | 
			
		||||
---
 | 
			
		||||
title: "Point3d"
 | 
			
		||||
excerpt: ""
 | 
			
		||||
title: "std::Point3d"
 | 
			
		||||
excerpt: "A point in three dimensional space."
 | 
			
		||||
layout: manual
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
A point in three dimensional space.
 | 
			
		||||
 | 
			
		||||
**Type:** `object`
 | 
			
		||||
```kcl
 | 
			
		||||
type Point3d = [number; 3]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`Point3d` is an alias for a three-element array of [number](/docs/kcl/types/number)s. To write a value
 | 
			
		||||
with type `Point3d`, use an array, e.g., `[0, 0, 0]` or `[5.0, 3.14, 6.8]`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Properties
 | 
			
		||||
 | 
			
		||||
| Property | Type | Description | Required |
 | 
			
		||||
|----------|------|-------------|----------|
 | 
			
		||||
| `x` |[`number`](/docs/kcl/types/number)|  | No |
 | 
			
		||||
| `y` |[`number`](/docs/kcl/types/number)|  | No |
 | 
			
		||||
| `z` |[`number`](/docs/kcl/types/number)|  | No |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,6 @@ A path to sweep along.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
A helix.
 | 
			
		||||
 | 
			
		||||
[`Helix`](/docs/kcl/types/Helix)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB  | 
@ -20,7 +20,7 @@ statement[@isGroup=Statement] {
 | 
			
		||||
  ImportStatement { kw<"import"> ImportItems ImportFrom String } |
 | 
			
		||||
  FunctionDeclaration { kw<"export">? kw<"fn"> VariableDefinition Equals? ParamList Arrow? Body } |
 | 
			
		||||
  VariableDeclaration { kw<"export">? (kw<"var"> | kw<"let"> | kw<"const">)? VariableDefinition Equals expression } |
 | 
			
		||||
  TypeDeclaration { kw<"export">? kw<"type"> identifier } |
 | 
			
		||||
  TypeDeclaration { kw<"export">? kw<"type"> identifier ("=" type)? } |
 | 
			
		||||
  ReturnStatement { kw<"return"> expression } |
 | 
			
		||||
  ExpressionStatement { expression } |
 | 
			
		||||
  Annotation { AnnotationName AnnotationList? }
 | 
			
		||||
@ -79,7 +79,7 @@ type[@isGroup=Type] {
 | 
			
		||||
    identifier,
 | 
			
		||||
    "bool" | "number" | "string" | "tag" | "Sketch" | "SketchSurface" | "Solid" | "Plane"
 | 
			
		||||
  > |
 | 
			
		||||
  ArrayType { type !member "[" "]" } |
 | 
			
		||||
  ArrayType { "[" type !member (";" Number "+"?)? "]" } |
 | 
			
		||||
  ObjectType { "{" commaSep<ObjectProperty { PropertyName ":" type }> "}" }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -137,7 +137,7 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
 | 
			
		||||
  "(" ")"
 | 
			
		||||
  "{" "}"
 | 
			
		||||
  "[" "]"
 | 
			
		||||
  "," "?" ":" "." ".."
 | 
			
		||||
  "," "?" ":" "." ".." ";"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@external propSource kclHighlight from "./highlight"
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,9 @@ use crate::{
 | 
			
		||||
const TYPES_DIR: &str = "../../docs/kcl/types";
 | 
			
		||||
const LANG_TOPICS: [&str; 5] = ["Types", "Modules", "Settings", "Known Issues", "Constants"];
 | 
			
		||||
// These types are declared in std.
 | 
			
		||||
const DECLARED_TYPES: [&str; 7] = ["number", "string", "tag", "bool", "Sketch", "Solid", "Plane"];
 | 
			
		||||
const DECLARED_TYPES: [&str; 11] = [
 | 
			
		||||
    "number", "string", "tag", "bool", "Sketch", "Solid", "Plane", "Helix", "Face", "Point2d", "Point3d",
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
fn init_handlebars() -> Result<handlebars::Handlebars<'static>> {
 | 
			
		||||
    let mut hbs = handlebars::Handlebars::new();
 | 
			
		||||
@ -457,6 +459,7 @@ fn generate_type_from_kcl(ty: &TyData, file_name: String, example_name: String)
 | 
			
		||||
 | 
			
		||||
    let data = json!({
 | 
			
		||||
        "name": ty.qual_name(),
 | 
			
		||||
        "definition": ty.alias.as_ref().map(|t| format!("type {} = {t}", ty.name)),
 | 
			
		||||
        "summary": ty.summary,
 | 
			
		||||
        "description": ty.description,
 | 
			
		||||
        "deprecated": ty.properties.deprecated,
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
use std::str::FromStr;
 | 
			
		||||
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
use tower_lsp::lsp_types::{
 | 
			
		||||
    CompletionItem, CompletionItemKind, CompletionItemLabelDetails, Documentation, InsertTextFormat, MarkupContent,
 | 
			
		||||
    MarkupKind, ParameterInformation, ParameterLabel, SignatureHelp, SignatureInformation,
 | 
			
		||||
@ -282,7 +283,7 @@ impl ConstData {
 | 
			
		||||
            documentation: self.short_docs().map(|s| {
 | 
			
		||||
                Documentation::MarkupContent(MarkupContent {
 | 
			
		||||
                    kind: MarkupKind::Markdown,
 | 
			
		||||
                    value: s,
 | 
			
		||||
                    value: remove_md_links(&s),
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            deprecated: Some(self.properties.deprecated),
 | 
			
		||||
@ -335,7 +336,7 @@ impl FnData {
 | 
			
		||||
            name,
 | 
			
		||||
            qual_name,
 | 
			
		||||
            args: expr.params.iter().map(ArgData::from_ast).collect(),
 | 
			
		||||
            return_type: expr.return_type.as_ref().map(|t| t.recast(&Default::default(), 0)),
 | 
			
		||||
            return_type: expr.return_type.as_ref().map(|t| t.to_string()),
 | 
			
		||||
            properties: Properties {
 | 
			
		||||
                exported: !var.visibility.is_default(),
 | 
			
		||||
                deprecated: false,
 | 
			
		||||
@ -393,7 +394,7 @@ impl FnData {
 | 
			
		||||
            documentation: self.short_docs().map(|s| {
 | 
			
		||||
                Documentation::MarkupContent(MarkupContent {
 | 
			
		||||
                    kind: MarkupKind::Markdown,
 | 
			
		||||
                    value: s,
 | 
			
		||||
                    value: remove_md_links(&s),
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            deprecated: Some(self.properties.deprecated),
 | 
			
		||||
@ -496,7 +497,7 @@ impl ArgData {
 | 
			
		||||
    fn from_ast(arg: &crate::parsing::ast::types::Parameter) -> Self {
 | 
			
		||||
        ArgData {
 | 
			
		||||
            name: arg.identifier.name.clone(),
 | 
			
		||||
            ty: arg.type_.as_ref().map(|t| t.recast(&Default::default(), 0)),
 | 
			
		||||
            ty: arg.type_.as_ref().map(|t| t.to_string()),
 | 
			
		||||
            // Doc comments are not yet supported on parameters.
 | 
			
		||||
            docs: None,
 | 
			
		||||
            kind: if arg.labeled {
 | 
			
		||||
@ -560,6 +561,7 @@ pub struct TyData {
 | 
			
		||||
    /// The fully qualified name.
 | 
			
		||||
    pub qual_name: String,
 | 
			
		||||
    pub properties: Properties,
 | 
			
		||||
    pub alias: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// The summary of the function.
 | 
			
		||||
    pub summary: Option<String>,
 | 
			
		||||
@ -583,6 +585,7 @@ impl TyData {
 | 
			
		||||
                doc_hidden: false,
 | 
			
		||||
                impl_kind: annotations::Impl::Kcl,
 | 
			
		||||
            },
 | 
			
		||||
            alias: ty.alias.as_ref().map(|t| t.to_string()),
 | 
			
		||||
            summary: None,
 | 
			
		||||
            description: None,
 | 
			
		||||
            examples: Vec::new(),
 | 
			
		||||
@ -609,13 +612,16 @@ impl TyData {
 | 
			
		||||
    fn to_completion_item(&self) -> CompletionItem {
 | 
			
		||||
        CompletionItem {
 | 
			
		||||
            label: self.name.clone(),
 | 
			
		||||
            label_details: None,
 | 
			
		||||
            label_details: self.alias.as_ref().map(|t| CompletionItemLabelDetails {
 | 
			
		||||
                detail: Some(format!("type {} = {t}", self.name)),
 | 
			
		||||
                description: None,
 | 
			
		||||
            }),
 | 
			
		||||
            kind: Some(CompletionItemKind::FUNCTION),
 | 
			
		||||
            detail: Some(self.qual_name().to_owned()),
 | 
			
		||||
            documentation: self.short_docs().map(|s| {
 | 
			
		||||
                Documentation::MarkupContent(MarkupContent {
 | 
			
		||||
                    kind: MarkupKind::Markdown,
 | 
			
		||||
                    value: s,
 | 
			
		||||
                    value: remove_md_links(&s),
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            deprecated: Some(self.properties.deprecated),
 | 
			
		||||
@ -635,6 +641,11 @@ impl TyData {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn remove_md_links(s: &str) -> String {
 | 
			
		||||
    let re = Regex::new(r"\[([^\]]*)\]\([^\)]*\)").unwrap();
 | 
			
		||||
    re.replace_all(s, "$1").to_string()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
trait ApplyMeta {
 | 
			
		||||
    fn apply_docs(
 | 
			
		||||
        &mut self,
 | 
			
		||||
@ -863,6 +874,24 @@ mod test {
 | 
			
		||||
        panic!("didn't find PI");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_remove_md_links() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            remove_md_links("sdf dsf sd fj sdk fasdfs. asad[sdfs] dfsdf(dsfs, dsf)"),
 | 
			
		||||
            "sdf dsf sd fj sdk fasdfs. asad[sdfs] dfsdf(dsfs, dsf)".to_owned()
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(remove_md_links("[]()"), "".to_owned());
 | 
			
		||||
        assert_eq!(remove_md_links("[foo](bar)"), "foo".to_owned());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            remove_md_links("asdasda dsa[foo](http://www.bar/baz/qux.md). asdasdasdas asdas"),
 | 
			
		||||
            "asdasda dsafoo. asdasdasdas asdas".to_owned()
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            remove_md_links("a [foo](bar) b [2](bar) c [_](bar)"),
 | 
			
		||||
            "a foo b 2 c _".to_owned()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
 | 
			
		||||
    async fn test_examples() -> miette::Result<()> {
 | 
			
		||||
        let std = walk_prelude();
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ use tower_lsp::lsp_types::{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    execution::{kcl_value::NumericType, Sketch},
 | 
			
		||||
    execution::{types::NumericType, Sketch},
 | 
			
		||||
    std::Primitive,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								rust/kcl-lib/src/docs/templates/kclType.hbs
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								rust/kcl-lib/src/docs/templates/kclType.hbs
									
									
									
									
										vendored
									
									
								
							@ -10,6 +10,12 @@ layout: manual
 | 
			
		||||
{{/if}}
 | 
			
		||||
{{{summary}}}
 | 
			
		||||
 | 
			
		||||
{{#if definition}}
 | 
			
		||||
```kcl
 | 
			
		||||
{{{definition}}}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
{{/if}}
 | 
			
		||||
{{{description}}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ use kittycad_modeling_cmds::coord::{System, KITTYCAD, OPENGL, VULKAN};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclErrorDetails,
 | 
			
		||||
    execution::kcl_value::{UnitAngle, UnitLen},
 | 
			
		||||
    execution::types::{UnitAngle, UnitLen},
 | 
			
		||||
    parsing::ast::types::{Annotation, Expr, Node, ObjectProperty},
 | 
			
		||||
    KclError, SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ use indexmap::IndexMap;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use super::{kcl_value::NumericType, ArtifactId, KclValue};
 | 
			
		||||
use super::{types::NumericType, ArtifactId, KclValue};
 | 
			
		||||
use crate::{docs::StdLibFn, std::get_stdlib_fn, SourceRange};
 | 
			
		||||
 | 
			
		||||
/// A CAD modeling operation for display in the feature tree, AKA operations
 | 
			
		||||
 | 
			
		||||
@ -8,9 +8,10 @@ use crate::{
 | 
			
		||||
    execution::{
 | 
			
		||||
        annotations,
 | 
			
		||||
        cad_op::{OpArg, OpKclValue, Operation},
 | 
			
		||||
        kcl_value::{FunctionSource, NumericType, RuntimeType},
 | 
			
		||||
        kcl_value::FunctionSource,
 | 
			
		||||
        memory,
 | 
			
		||||
        state::ModuleState,
 | 
			
		||||
        types::{NumericType, RuntimeType},
 | 
			
		||||
        BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, PlaneType, TagEngineInfo,
 | 
			
		||||
        TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
@ -29,6 +30,8 @@ use crate::{
 | 
			
		||||
    CompilationError,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::kcl_value::TypeDef;
 | 
			
		||||
 | 
			
		||||
enum StatementKind<'a> {
 | 
			
		||||
    Declaration { name: &'a str },
 | 
			
		||||
    Expression,
 | 
			
		||||
@ -299,8 +302,9 @@ impl ExecutorContext {
 | 
			
		||||
                                    }));
 | 
			
		||||
                                }
 | 
			
		||||
                            };
 | 
			
		||||
                            let (t, props) = crate::std::std_ty(std_path, &ty.name.name);
 | 
			
		||||
                            let value = KclValue::Type {
 | 
			
		||||
                                value: Some(crate::std::std_ty(std_path, &ty.name.name)),
 | 
			
		||||
                                value: TypeDef::RustRepr(t, props),
 | 
			
		||||
                                meta: vec![metadata],
 | 
			
		||||
                            };
 | 
			
		||||
                            exec_state
 | 
			
		||||
@ -319,12 +323,40 @@ impl ExecutorContext {
 | 
			
		||||
                        }
 | 
			
		||||
                        // Do nothing for primitive types, they get special treatment and their declarations are just for documentation.
 | 
			
		||||
                        annotations::Impl::Primitive => {}
 | 
			
		||||
                        annotations::Impl::Kcl => {
 | 
			
		||||
                            return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                                message: "User-defined types are not yet supported.".to_owned(),
 | 
			
		||||
                                source_ranges: vec![metadata.source_range],
 | 
			
		||||
                            }));
 | 
			
		||||
                        }
 | 
			
		||||
                        annotations::Impl::Kcl => match &ty.alias {
 | 
			
		||||
                            Some(alias) => {
 | 
			
		||||
                                let value = KclValue::Type {
 | 
			
		||||
                                    value: TypeDef::Alias(
 | 
			
		||||
                                        RuntimeType::from_parsed(
 | 
			
		||||
                                            alias.inner.clone(),
 | 
			
		||||
                                            exec_state,
 | 
			
		||||
                                            metadata.source_range,
 | 
			
		||||
                                        )
 | 
			
		||||
                                        .map_err(|e| KclError::Semantic(e.into()))?,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    meta: vec![metadata],
 | 
			
		||||
                                };
 | 
			
		||||
                                exec_state
 | 
			
		||||
                                    .mut_stack()
 | 
			
		||||
                                    .add(
 | 
			
		||||
                                        format!("{}{}", memory::TYPE_PREFIX, ty.name.name),
 | 
			
		||||
                                        value,
 | 
			
		||||
                                        metadata.source_range,
 | 
			
		||||
                                    )
 | 
			
		||||
                                    .map_err(|_| {
 | 
			
		||||
                                        KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                                            message: format!("Redefinition of type {}.", ty.name.name),
 | 
			
		||||
                                            source_ranges: vec![metadata.source_range],
 | 
			
		||||
                                        })
 | 
			
		||||
                                    })?;
 | 
			
		||||
                            }
 | 
			
		||||
                            None => {
 | 
			
		||||
                                return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                                    message: "User-defined types are not yet supported.".to_owned(),
 | 
			
		||||
                                    source_ranges: vec![metadata.source_range],
 | 
			
		||||
                                }))
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    last_expr = None;
 | 
			
		||||
@ -641,30 +673,28 @@ impl ExecutorContext {
 | 
			
		||||
                let result = self
 | 
			
		||||
                    .execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
 | 
			
		||||
                    .await?;
 | 
			
		||||
                coerce(&result, &expr.ty, exec_state).ok_or_else(|| {
 | 
			
		||||
                    KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                        message: format!(
 | 
			
		||||
                            "could not coerce {} value to type {}",
 | 
			
		||||
                            result.human_friendly_type(),
 | 
			
		||||
                            expr.ty
 | 
			
		||||
                        ),
 | 
			
		||||
                        source_ranges: vec![expr.into()],
 | 
			
		||||
                    })
 | 
			
		||||
                })?
 | 
			
		||||
                coerce(&result, &expr.ty, exec_state, expr.into())?
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        Ok(item)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn coerce(value: &KclValue, ty: &Node<Type>, exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
fn coerce(
 | 
			
		||||
    value: &KclValue,
 | 
			
		||||
    ty: &Node<Type>,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
    source_range: SourceRange,
 | 
			
		||||
) -> Result<KclValue, KclError> {
 | 
			
		||||
    let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
 | 
			
		||||
        .map_err(|e| {
 | 
			
		||||
            exec_state.err(e);
 | 
			
		||||
        })
 | 
			
		||||
        .ok()??;
 | 
			
		||||
        .map_err(|e| KclError::Semantic(e.into()))?;
 | 
			
		||||
 | 
			
		||||
    value.coerce(&ty, exec_state)
 | 
			
		||||
    value.coerce(&ty, exec_state).ok_or_else(|| {
 | 
			
		||||
        KclError::Semantic(KclErrorDetails {
 | 
			
		||||
            message: format!("could not coerce {} value to type {}", value.human_friendly_type(), ty),
 | 
			
		||||
            source_ranges: vec![source_range],
 | 
			
		||||
        })
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BinaryPart {
 | 
			
		||||
 | 
			
		||||
@ -104,7 +104,7 @@ impl From<SolidOrSketchOrImportedGeometry> for crate::execution::KclValue {
 | 
			
		||||
                            .into_iter()
 | 
			
		||||
                            .map(|s| crate::execution::KclValue::Solid { value: Box::new(s) })
 | 
			
		||||
                            .collect(),
 | 
			
		||||
                        ty: crate::execution::PrimitiveType::Solid,
 | 
			
		||||
                        ty: crate::execution::types::RuntimeType::solid(),
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -119,7 +119,7 @@ impl From<SolidOrSketchOrImportedGeometry> for crate::execution::KclValue {
 | 
			
		||||
                            .into_iter()
 | 
			
		||||
                            .map(|s| crate::execution::KclValue::Sketch { value: Box::new(s) })
 | 
			
		||||
                            .collect(),
 | 
			
		||||
                        ty: crate::execution::PrimitiveType::Sketch,
 | 
			
		||||
                        ty: crate::execution::types::RuntimeType::sketch(),
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{annotations, kcl_value::UnitLen, ExecState, ExecutorContext, ImportedGeometry},
 | 
			
		||||
    execution::{annotations, types::UnitLen, ExecState, ExecutorContext, ImportedGeometry},
 | 
			
		||||
    fs::FileSystem,
 | 
			
		||||
    parsing::ast::types::{Annotation, Node},
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
 | 
			
		||||
@ -1,29 +1,20 @@
 | 
			
		||||
use std::{collections::HashMap, fmt};
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
 | 
			
		||||
use super::{
 | 
			
		||||
    memory::{self, EnvironmentRef},
 | 
			
		||||
    MetaSettings, Point3d,
 | 
			
		||||
};
 | 
			
		||||
use super::{memory::EnvironmentRef, MetaSettings};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclErrorDetails,
 | 
			
		||||
    execution::{
 | 
			
		||||
        types::{NumericType, PrimitiveType, RuntimeType},
 | 
			
		||||
        ExecState, ExecutorContext, Face, Helix, ImportedGeometry, Metadata, Plane, Sketch, Solid, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::{
 | 
			
		||||
        ast::types::{
 | 
			
		||||
            DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node,
 | 
			
		||||
            PrimitiveType as AstPrimitiveType, TagDeclarator, TagNode, Type,
 | 
			
		||||
        },
 | 
			
		||||
        token::NumericSuffix,
 | 
			
		||||
    },
 | 
			
		||||
    std::{
 | 
			
		||||
        args::{Arg, FromKclValue},
 | 
			
		||||
        StdFnProps,
 | 
			
		||||
    parsing::ast::types::{
 | 
			
		||||
        DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node, TagDeclarator, TagNode,
 | 
			
		||||
    },
 | 
			
		||||
    std::{args::Arg, StdFnProps},
 | 
			
		||||
    CompilationError, KclError, ModuleId, SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -65,7 +56,7 @@ pub enum KclValue {
 | 
			
		||||
        value: Vec<KclValue>,
 | 
			
		||||
        // The type of values, not the array type.
 | 
			
		||||
        #[serde(skip)]
 | 
			
		||||
        ty: PrimitiveType,
 | 
			
		||||
        ty: RuntimeType,
 | 
			
		||||
    },
 | 
			
		||||
    Object {
 | 
			
		||||
        value: KclObjectFields,
 | 
			
		||||
@ -105,7 +96,7 @@ pub enum KclValue {
 | 
			
		||||
    #[ts(skip)]
 | 
			
		||||
    Type {
 | 
			
		||||
        #[serde(skip)]
 | 
			
		||||
        value: Option<(PrimitiveType, StdFnProps)>,
 | 
			
		||||
        value: TypeDef,
 | 
			
		||||
        #[serde(skip)]
 | 
			
		||||
        meta: Vec<Metadata>,
 | 
			
		||||
    },
 | 
			
		||||
@ -142,6 +133,12 @@ impl JsonSchema for FunctionSource {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub enum TypeDef {
 | 
			
		||||
    RustRepr(PrimitiveType, StdFnProps),
 | 
			
		||||
    Alias(RuntimeType),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Vec<Sketch>> for KclValue {
 | 
			
		||||
    fn from(mut eg: Vec<Sketch>) -> Self {
 | 
			
		||||
        if eg.len() == 1 {
 | 
			
		||||
@ -154,7 +151,7 @@ impl From<Vec<Sketch>> for KclValue {
 | 
			
		||||
                    .into_iter()
 | 
			
		||||
                    .map(|s| KclValue::Sketch { value: Box::new(s) })
 | 
			
		||||
                    .collect(),
 | 
			
		||||
                ty: crate::execution::PrimitiveType::Sketch,
 | 
			
		||||
                ty: RuntimeType::Primitive(PrimitiveType::Sketch),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -169,7 +166,7 @@ impl From<Vec<Solid>> for KclValue {
 | 
			
		||||
        } else {
 | 
			
		||||
            KclValue::HomArray {
 | 
			
		||||
                value: eg.into_iter().map(|s| KclValue::Solid { value: Box::new(s) }).collect(),
 | 
			
		||||
                ty: crate::execution::PrimitiveType::Solid,
 | 
			
		||||
                ty: RuntimeType::Primitive(PrimitiveType::Solid),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -553,259 +550,6 @@ impl KclValue {
 | 
			
		||||
        Ok(*b)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// True if `self` has a type which is a subtype of `ty` without coercion.
 | 
			
		||||
    pub fn has_type(&self, ty: &RuntimeType) -> bool {
 | 
			
		||||
        let Some(self_ty) = self.principal_type() else {
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self_ty.subtype(ty)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Coerce `self` to a new value which has `ty` as it's closest supertype.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If the result is Some, then:
 | 
			
		||||
    ///   - result.principal_type().unwrap().subtype(ty)
 | 
			
		||||
    ///
 | 
			
		||||
    /// If self.principal_type() == ty then result == self
 | 
			
		||||
    pub fn coerce(&self, ty: &RuntimeType, exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        match ty {
 | 
			
		||||
            RuntimeType::Primitive(ty) => self.coerce_to_primitive_type(ty, exec_state),
 | 
			
		||||
            RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, *len, exec_state),
 | 
			
		||||
            RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, exec_state),
 | 
			
		||||
            RuntimeType::Union(tys) => self.coerce_to_union_type(tys, exec_state),
 | 
			
		||||
            RuntimeType::Object(tys) => self.coerce_to_object_type(tys, exec_state),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_primitive_type(&self, ty: &PrimitiveType, exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        let value = match self {
 | 
			
		||||
            KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } if value.len() == 1 => &value[0],
 | 
			
		||||
            _ => self,
 | 
			
		||||
        };
 | 
			
		||||
        match ty {
 | 
			
		||||
            // TODO numeric type coercions
 | 
			
		||||
            PrimitiveType::Number(_ty) => match value {
 | 
			
		||||
                KclValue::Number { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::String => match value {
 | 
			
		||||
                KclValue::String { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Boolean => match value {
 | 
			
		||||
                KclValue::Bool { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Sketch => match value {
 | 
			
		||||
                KclValue::Sketch { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Solid => match value {
 | 
			
		||||
                KclValue::Solid { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Plane => match value {
 | 
			
		||||
                KclValue::Plane { .. } => Some(value.clone()),
 | 
			
		||||
                KclValue::Object { value, meta } => {
 | 
			
		||||
                    let origin = value.get("origin").and_then(Point3d::from_kcl_val)?;
 | 
			
		||||
                    let x_axis = value.get("xAxis").and_then(Point3d::from_kcl_val)?;
 | 
			
		||||
                    let y_axis = value.get("yAxis").and_then(Point3d::from_kcl_val)?;
 | 
			
		||||
                    let z_axis = value.get("zAxis").and_then(Point3d::from_kcl_val)?;
 | 
			
		||||
 | 
			
		||||
                    let id = exec_state.mod_local.id_generator.next_uuid();
 | 
			
		||||
                    let plane = Plane {
 | 
			
		||||
                        id,
 | 
			
		||||
                        artifact_id: id.into(),
 | 
			
		||||
                        origin,
 | 
			
		||||
                        x_axis,
 | 
			
		||||
                        y_axis,
 | 
			
		||||
                        z_axis,
 | 
			
		||||
                        value: super::PlaneType::Uninit,
 | 
			
		||||
                        // TODO use length unit from origin
 | 
			
		||||
                        units: exec_state.length_unit(),
 | 
			
		||||
                        meta: meta.clone(),
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    Some(KclValue::Plane { value: Box::new(plane) })
 | 
			
		||||
                }
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::ImportedGeometry => match value {
 | 
			
		||||
                KclValue::ImportedGeometry { .. } => Some(value.clone()),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_array_type(&self, ty: &PrimitiveType, len: ArrayLen, exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::HomArray { value, ty: aty } => {
 | 
			
		||||
                // TODO could check types of values individually
 | 
			
		||||
                if aty != ty {
 | 
			
		||||
                    return None;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let value = match len {
 | 
			
		||||
                    ArrayLen::None => value.clone(),
 | 
			
		||||
                    ArrayLen::NonEmpty => {
 | 
			
		||||
                        if value.is_empty() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value.clone()
 | 
			
		||||
                    }
 | 
			
		||||
                    ArrayLen::Known(n) => {
 | 
			
		||||
                        if n != value.len() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value[..n].to_vec()
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                Some(KclValue::HomArray { value, ty: ty.clone() })
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::MixedArray { value, .. } => {
 | 
			
		||||
                let value = match len {
 | 
			
		||||
                    ArrayLen::None => value.clone(),
 | 
			
		||||
                    ArrayLen::NonEmpty => {
 | 
			
		||||
                        if value.is_empty() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value.clone()
 | 
			
		||||
                    }
 | 
			
		||||
                    ArrayLen::Known(n) => {
 | 
			
		||||
                        if n != value.len() {
 | 
			
		||||
                            return None;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value[..n].to_vec()
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                let rt = RuntimeType::Primitive(ty.clone());
 | 
			
		||||
                let value = value
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|v| v.coerce(&rt, exec_state))
 | 
			
		||||
                    .collect::<Option<Vec<_>>>()?;
 | 
			
		||||
 | 
			
		||||
                Some(KclValue::HomArray { value, ty: ty.clone() })
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::KclNone { .. } if len.satisfied(0) => Some(KclValue::HomArray {
 | 
			
		||||
                value: Vec::new(),
 | 
			
		||||
                ty: ty.clone(),
 | 
			
		||||
            }),
 | 
			
		||||
            value if len.satisfied(1) => {
 | 
			
		||||
                if value.has_type(&RuntimeType::Primitive(ty.clone())) {
 | 
			
		||||
                    Some(KclValue::HomArray {
 | 
			
		||||
                        value: vec![value.clone()],
 | 
			
		||||
                        ty: ty.clone(),
 | 
			
		||||
                    })
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_tuple_type(&self, tys: &[PrimitiveType], exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
 | 
			
		||||
                if value.len() < tys.len() {
 | 
			
		||||
                    return None;
 | 
			
		||||
                }
 | 
			
		||||
                let mut result = Vec::new();
 | 
			
		||||
                for (i, t) in tys.iter().enumerate() {
 | 
			
		||||
                    result.push(value[i].coerce_to_primitive_type(t, exec_state)?);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Some(KclValue::MixedArray {
 | 
			
		||||
                    value: result,
 | 
			
		||||
                    meta: Vec::new(),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::KclNone { meta, .. } if tys.is_empty() => Some(KclValue::MixedArray {
 | 
			
		||||
                value: Vec::new(),
 | 
			
		||||
                meta: meta.clone(),
 | 
			
		||||
            }),
 | 
			
		||||
            value if tys.len() == 1 => {
 | 
			
		||||
                if value.has_type(&RuntimeType::Primitive(tys[0].clone())) {
 | 
			
		||||
                    Some(KclValue::MixedArray {
 | 
			
		||||
                        value: vec![value.clone()],
 | 
			
		||||
                        meta: Vec::new(),
 | 
			
		||||
                    })
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_union_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        for t in tys {
 | 
			
		||||
            if let Some(v) = self.coerce(t, exec_state) {
 | 
			
		||||
                return Some(v);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn coerce_to_object_type(&self, tys: &[(String, RuntimeType)], _exec_state: &mut ExecState) -> Option<KclValue> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::Object { value, .. } => {
 | 
			
		||||
                for (s, t) in tys {
 | 
			
		||||
                    // TODO coerce fields
 | 
			
		||||
                    if !value.get(s)?.has_type(t) {
 | 
			
		||||
                        return None;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                // TODO remove non-required fields
 | 
			
		||||
                Some(self.clone())
 | 
			
		||||
            }
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn principal_type(&self) -> Option<RuntimeType> {
 | 
			
		||||
        match self {
 | 
			
		||||
            KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
 | 
			
		||||
            KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(ty.clone()))),
 | 
			
		||||
            KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)),
 | 
			
		||||
            KclValue::Object { value, .. } => {
 | 
			
		||||
                let properties = value
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|(k, v)| v.principal_type().map(|t| (k.clone(), t)))
 | 
			
		||||
                    .collect::<Option<Vec<_>>>()?;
 | 
			
		||||
                Some(RuntimeType::Object(properties))
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
 | 
			
		||||
            KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
 | 
			
		||||
            KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
 | 
			
		||||
            KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
 | 
			
		||||
            KclValue::MixedArray { value, .. } => Some(RuntimeType::Tuple(
 | 
			
		||||
                value
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|v| v.principal_type().and_then(RuntimeType::primitive))
 | 
			
		||||
                    .collect::<Option<Vec<_>>>()?,
 | 
			
		||||
            )),
 | 
			
		||||
            KclValue::HomArray { ty, value, .. } => Some(RuntimeType::Array(ty.clone(), ArrayLen::Known(value.len()))),
 | 
			
		||||
            KclValue::Face { .. } => None,
 | 
			
		||||
            KclValue::Helix { .. }
 | 
			
		||||
            | KclValue::Function { .. }
 | 
			
		||||
            | KclValue::Module { .. }
 | 
			
		||||
            | KclValue::TagIdentifier(_)
 | 
			
		||||
            | KclValue::TagDeclarator(_)
 | 
			
		||||
            | KclValue::KclNone { .. }
 | 
			
		||||
            | KclValue::Type { .. }
 | 
			
		||||
            | KclValue::Uuid { .. } => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// If this memory item is a function, call it with the given arguments, return its val as Ok.
 | 
			
		||||
    /// If it's not a function, return Err.
 | 
			
		||||
    pub async fn call_fn(
 | 
			
		||||
@ -919,447 +663,3 @@ impl KclValue {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub enum RuntimeType {
 | 
			
		||||
    Primitive(PrimitiveType),
 | 
			
		||||
    Array(PrimitiveType, ArrayLen),
 | 
			
		||||
    Union(Vec<RuntimeType>),
 | 
			
		||||
    Tuple(Vec<PrimitiveType>),
 | 
			
		||||
    Object(Vec<(String, RuntimeType)>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RuntimeType {
 | 
			
		||||
    pub fn from_parsed(
 | 
			
		||||
        value: Type,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Option<Self>, CompilationError> {
 | 
			
		||||
        Ok(match value {
 | 
			
		||||
            Type::Primitive(pt) => {
 | 
			
		||||
                PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(RuntimeType::Primitive)
 | 
			
		||||
            }
 | 
			
		||||
            Type::Array(pt) => {
 | 
			
		||||
                PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(|t| RuntimeType::Array(t, ArrayLen::None))
 | 
			
		||||
            }
 | 
			
		||||
            Type::Object { properties } => properties
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|p| {
 | 
			
		||||
                    let pt = match p.type_ {
 | 
			
		||||
                        Some(t) => t,
 | 
			
		||||
                        None => return Ok(None),
 | 
			
		||||
                    };
 | 
			
		||||
                    Ok(RuntimeType::from_parsed(pt.inner, exec_state, source_range)?
 | 
			
		||||
                        .map(|ty| (p.identifier.inner.name, ty)))
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Result<Option<Vec<_>>, CompilationError>>()?
 | 
			
		||||
                .map(RuntimeType::Object),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn human_friendly_type(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            RuntimeType::Primitive(ty) => ty.to_string(),
 | 
			
		||||
            RuntimeType::Array(ty, ArrayLen::None) => format!("an array of {}", ty.display_multiple()),
 | 
			
		||||
            RuntimeType::Array(ty, ArrayLen::NonEmpty) => format!("one or more {}", ty.display_multiple()),
 | 
			
		||||
            RuntimeType::Array(ty, ArrayLen::Known(n)) => format!("an array of {n} {}", ty.display_multiple()),
 | 
			
		||||
            RuntimeType::Union(tys) => tys
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(Self::human_friendly_type)
 | 
			
		||||
                .collect::<Vec<_>>()
 | 
			
		||||
                .join(" or "),
 | 
			
		||||
            RuntimeType::Tuple(tys) => format!(
 | 
			
		||||
                "an array with values of types ({})",
 | 
			
		||||
                tys.iter().map(PrimitiveType::to_string).collect::<Vec<_>>().join(", ")
 | 
			
		||||
            ),
 | 
			
		||||
            RuntimeType::Object(_) => format!("an object with fields {}", self),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Subtype with no coercion, including refining numeric types.
 | 
			
		||||
    fn subtype(&self, sup: &RuntimeType) -> bool {
 | 
			
		||||
        use RuntimeType::*;
 | 
			
		||||
 | 
			
		||||
        match (self, sup) {
 | 
			
		||||
            (Primitive(t1), Primitive(t2)) => t1 == t2,
 | 
			
		||||
            // TODO arrays could be covariant
 | 
			
		||||
            (Array(t1, l1), Array(t2, l2)) => t1 == t2 && l1.subtype(*l2),
 | 
			
		||||
            (Tuple(t1), Tuple(t2)) => t1 == t2,
 | 
			
		||||
            (Tuple(t1), Array(t2, l2)) => (l2.satisfied(t1.len())) && t1.iter().all(|t| t == t2),
 | 
			
		||||
            (Union(ts1), Union(ts2)) => ts1.iter().all(|t| ts2.contains(t)),
 | 
			
		||||
            (t1, Union(ts2)) => ts2.contains(t1),
 | 
			
		||||
            // TODO record subtyping - subtype can be larger, fields can be covariant.
 | 
			
		||||
            (Object(t1), Object(t2)) => t1 == t2,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn primitive(self) -> Option<PrimitiveType> {
 | 
			
		||||
        match self {
 | 
			
		||||
            RuntimeType::Primitive(t) => Some(t),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for RuntimeType {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            RuntimeType::Primitive(t) => t.fmt(f),
 | 
			
		||||
            RuntimeType::Array(t, l) => match l {
 | 
			
		||||
                ArrayLen::None => write!(f, "[{t}]"),
 | 
			
		||||
                ArrayLen::NonEmpty => write!(f, "[{t}; 1+]"),
 | 
			
		||||
                ArrayLen::Known(n) => write!(f, "[{t}; {n}]"),
 | 
			
		||||
            },
 | 
			
		||||
            RuntimeType::Tuple(ts) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "[{}]",
 | 
			
		||||
                ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
 | 
			
		||||
            ),
 | 
			
		||||
            RuntimeType::Union(ts) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "{}",
 | 
			
		||||
                ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(" | ")
 | 
			
		||||
            ),
 | 
			
		||||
            RuntimeType::Object(items) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "{{ {} }}",
 | 
			
		||||
                items
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|(n, t)| format!("{n}: {t}"))
 | 
			
		||||
                    .collect::<Vec<_>>()
 | 
			
		||||
                    .join(", ")
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq)]
 | 
			
		||||
pub enum ArrayLen {
 | 
			
		||||
    None,
 | 
			
		||||
    NonEmpty,
 | 
			
		||||
    Known(usize),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ArrayLen {
 | 
			
		||||
    pub fn subtype(self, other: ArrayLen) -> bool {
 | 
			
		||||
        match (self, other) {
 | 
			
		||||
            (_, ArrayLen::None) => true,
 | 
			
		||||
            (ArrayLen::NonEmpty, ArrayLen::NonEmpty) => true,
 | 
			
		||||
            (ArrayLen::Known(size), ArrayLen::NonEmpty) if size > 0 => true,
 | 
			
		||||
            (ArrayLen::Known(s1), ArrayLen::Known(s2)) if s1 == s2 => true,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// True if the length constraint is satisfied by the supplied length.
 | 
			
		||||
    fn satisfied(self, len: usize) -> bool {
 | 
			
		||||
        match self {
 | 
			
		||||
            ArrayLen::None => true,
 | 
			
		||||
            ArrayLen::NonEmpty => len > 0,
 | 
			
		||||
            ArrayLen::Known(s) => len == s,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub enum PrimitiveType {
 | 
			
		||||
    Number(NumericType),
 | 
			
		||||
    String,
 | 
			
		||||
    Boolean,
 | 
			
		||||
    Sketch,
 | 
			
		||||
    Solid,
 | 
			
		||||
    Plane,
 | 
			
		||||
    ImportedGeometry,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PrimitiveType {
 | 
			
		||||
    fn from_parsed(
 | 
			
		||||
        value: AstPrimitiveType,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Option<Self>, CompilationError> {
 | 
			
		||||
        Ok(match value {
 | 
			
		||||
            AstPrimitiveType::String => Some(PrimitiveType::String),
 | 
			
		||||
            AstPrimitiveType::Boolean => Some(PrimitiveType::Boolean),
 | 
			
		||||
            AstPrimitiveType::Number(suffix) => Some(PrimitiveType::Number(NumericType::from_parsed(
 | 
			
		||||
                suffix,
 | 
			
		||||
                &exec_state.mod_local.settings,
 | 
			
		||||
            ))),
 | 
			
		||||
            AstPrimitiveType::Named(name) => {
 | 
			
		||||
                let ty_val = exec_state
 | 
			
		||||
                    .stack()
 | 
			
		||||
                    .get(&format!("{}{}", memory::TYPE_PREFIX, name.name), source_range)
 | 
			
		||||
                    .map_err(|_| CompilationError::err(source_range, format!("Unknown type: {}", name.name)))?;
 | 
			
		||||
 | 
			
		||||
                let (ty, _) = match ty_val {
 | 
			
		||||
                    KclValue::Type { value: Some(ty), .. } => ty,
 | 
			
		||||
                    _ => unreachable!(),
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                Some(ty.clone())
 | 
			
		||||
            }
 | 
			
		||||
            _ => None,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn display_multiple(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
 | 
			
		||||
            PrimitiveType::Number(_) => "numbers".to_owned(),
 | 
			
		||||
            PrimitiveType::String => "strings".to_owned(),
 | 
			
		||||
            PrimitiveType::Boolean => "bools".to_owned(),
 | 
			
		||||
            PrimitiveType::Sketch => "Sketches".to_owned(),
 | 
			
		||||
            PrimitiveType::Solid => "Solids".to_owned(),
 | 
			
		||||
            PrimitiveType::Plane => "Planes".to_owned(),
 | 
			
		||||
            PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for PrimitiveType {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
 | 
			
		||||
            PrimitiveType::Number(_) => write!(f, "number"),
 | 
			
		||||
            PrimitiveType::String => write!(f, "string"),
 | 
			
		||||
            PrimitiveType::Boolean => write!(f, "bool"),
 | 
			
		||||
            PrimitiveType::Sketch => write!(f, "Sketch"),
 | 
			
		||||
            PrimitiveType::Solid => write!(f, "Solid"),
 | 
			
		||||
            PrimitiveType::Plane => write!(f, "Plane"),
 | 
			
		||||
            PrimitiveType::ImportedGeometry => write!(f, "imported geometry"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum NumericType {
 | 
			
		||||
    // Specified by the user (directly or indirectly)
 | 
			
		||||
    Known(UnitType),
 | 
			
		||||
    // Unspecified, using defaults
 | 
			
		||||
    Default { len: UnitLen, angle: UnitAngle },
 | 
			
		||||
    // Exceeded the ability of the type system to track.
 | 
			
		||||
    Unknown,
 | 
			
		||||
    // Type info has been explicitly cast away.
 | 
			
		||||
    Any,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NumericType {
 | 
			
		||||
    pub fn count() -> Self {
 | 
			
		||||
        NumericType::Known(UnitType::Count)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Combine two types when we expect them to be equal.
 | 
			
		||||
    pub fn combine_eq(self, other: &NumericType) -> NumericType {
 | 
			
		||||
        if &self == other {
 | 
			
		||||
            self
 | 
			
		||||
        } else {
 | 
			
		||||
            NumericType::Unknown
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Combine n types when we expect them to be equal.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Precondition: tys.len() > 0
 | 
			
		||||
    pub fn combine_n_eq(tys: &[NumericType]) -> NumericType {
 | 
			
		||||
        let ty0 = tys[0].clone();
 | 
			
		||||
        for t in &tys[1..] {
 | 
			
		||||
            if t != &ty0 {
 | 
			
		||||
                return NumericType::Unknown;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ty0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Combine two types in addition-like operations.
 | 
			
		||||
    pub fn combine_add(a: NumericType, b: NumericType) -> NumericType {
 | 
			
		||||
        if a == b {
 | 
			
		||||
            return a;
 | 
			
		||||
        }
 | 
			
		||||
        NumericType::Unknown
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Combine two types in multiplication-like operations.
 | 
			
		||||
    pub fn combine_mul(a: NumericType, b: NumericType) -> NumericType {
 | 
			
		||||
        if a == NumericType::count() {
 | 
			
		||||
            return b;
 | 
			
		||||
        }
 | 
			
		||||
        if b == NumericType::count() {
 | 
			
		||||
            return a;
 | 
			
		||||
        }
 | 
			
		||||
        NumericType::Unknown
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Combine two types in division-like operations.
 | 
			
		||||
    pub fn combine_div(a: NumericType, b: NumericType) -> NumericType {
 | 
			
		||||
        if b == NumericType::count() {
 | 
			
		||||
            return a;
 | 
			
		||||
        }
 | 
			
		||||
        NumericType::Unknown
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn from_parsed(suffix: NumericSuffix, settings: &super::MetaSettings) -> Self {
 | 
			
		||||
        match suffix {
 | 
			
		||||
            NumericSuffix::None => NumericType::Default {
 | 
			
		||||
                len: settings.default_length_units,
 | 
			
		||||
                angle: settings.default_angle_units,
 | 
			
		||||
            },
 | 
			
		||||
            NumericSuffix::Count => NumericType::Known(UnitType::Count),
 | 
			
		||||
            NumericSuffix::Mm => NumericType::Known(UnitType::Length(UnitLen::Mm)),
 | 
			
		||||
            NumericSuffix::Cm => NumericType::Known(UnitType::Length(UnitLen::Cm)),
 | 
			
		||||
            NumericSuffix::M => NumericType::Known(UnitType::Length(UnitLen::M)),
 | 
			
		||||
            NumericSuffix::Inch => NumericType::Known(UnitType::Length(UnitLen::Inches)),
 | 
			
		||||
            NumericSuffix::Ft => NumericType::Known(UnitType::Length(UnitLen::Feet)),
 | 
			
		||||
            NumericSuffix::Yd => NumericType::Known(UnitType::Length(UnitLen::Yards)),
 | 
			
		||||
            NumericSuffix::Deg => NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
 | 
			
		||||
            NumericSuffix::Rad => NumericType::Known(UnitType::Angle(UnitAngle::Radians)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitLen> for NumericType {
 | 
			
		||||
    fn from(value: UnitLen) -> Self {
 | 
			
		||||
        NumericType::Known(UnitType::Length(value))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitAngle> for NumericType {
 | 
			
		||||
    fn from(value: UnitAngle) -> Self {
 | 
			
		||||
        NumericType::Known(UnitType::Angle(value))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum UnitType {
 | 
			
		||||
    Count,
 | 
			
		||||
    Length(UnitLen),
 | 
			
		||||
    Angle(UnitAngle),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for UnitType {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            UnitType::Count => write!(f, "_"),
 | 
			
		||||
            UnitType::Length(l) => l.fmt(f),
 | 
			
		||||
            UnitType::Angle(a) => a.fmt(f),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO called UnitLen so as not to clash with UnitLength in settings)
 | 
			
		||||
/// A unit of length.
 | 
			
		||||
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum UnitLen {
 | 
			
		||||
    #[default]
 | 
			
		||||
    Mm,
 | 
			
		||||
    Cm,
 | 
			
		||||
    M,
 | 
			
		||||
    Inches,
 | 
			
		||||
    Feet,
 | 
			
		||||
    Yards,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for UnitLen {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            UnitLen::Mm => write!(f, "mm"),
 | 
			
		||||
            UnitLen::Cm => write!(f, "cm"),
 | 
			
		||||
            UnitLen::M => write!(f, "m"),
 | 
			
		||||
            UnitLen::Inches => write!(f, "in"),
 | 
			
		||||
            UnitLen::Feet => write!(f, "ft"),
 | 
			
		||||
            UnitLen::Yards => write!(f, "yd"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TryFrom<NumericSuffix> for UnitLen {
 | 
			
		||||
    type Error = ();
 | 
			
		||||
 | 
			
		||||
    fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
 | 
			
		||||
        match suffix {
 | 
			
		||||
            NumericSuffix::Mm => Ok(Self::Mm),
 | 
			
		||||
            NumericSuffix::Cm => Ok(Self::Cm),
 | 
			
		||||
            NumericSuffix::M => Ok(Self::M),
 | 
			
		||||
            NumericSuffix::Inch => Ok(Self::Inches),
 | 
			
		||||
            NumericSuffix::Ft => Ok(Self::Feet),
 | 
			
		||||
            NumericSuffix::Yd => Ok(Self::Yards),
 | 
			
		||||
            _ => Err(()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<crate::UnitLength> for UnitLen {
 | 
			
		||||
    fn from(unit: crate::UnitLength) -> Self {
 | 
			
		||||
        match unit {
 | 
			
		||||
            crate::UnitLength::Cm => UnitLen::Cm,
 | 
			
		||||
            crate::UnitLength::Ft => UnitLen::Feet,
 | 
			
		||||
            crate::UnitLength::In => UnitLen::Inches,
 | 
			
		||||
            crate::UnitLength::M => UnitLen::M,
 | 
			
		||||
            crate::UnitLength::Mm => UnitLen::Mm,
 | 
			
		||||
            crate::UnitLength::Yd => UnitLen::Yards,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitLen> for crate::UnitLength {
 | 
			
		||||
    fn from(unit: UnitLen) -> Self {
 | 
			
		||||
        match unit {
 | 
			
		||||
            UnitLen::Cm => crate::UnitLength::Cm,
 | 
			
		||||
            UnitLen::Feet => crate::UnitLength::Ft,
 | 
			
		||||
            UnitLen::Inches => crate::UnitLength::In,
 | 
			
		||||
            UnitLen::M => crate::UnitLength::M,
 | 
			
		||||
            UnitLen::Mm => crate::UnitLength::Mm,
 | 
			
		||||
            UnitLen::Yards => crate::UnitLength::Yd,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UnitLen> for kittycad_modeling_cmds::units::UnitLength {
 | 
			
		||||
    fn from(unit: UnitLen) -> Self {
 | 
			
		||||
        match unit {
 | 
			
		||||
            UnitLen::Cm => kittycad_modeling_cmds::units::UnitLength::Centimeters,
 | 
			
		||||
            UnitLen::Feet => kittycad_modeling_cmds::units::UnitLength::Feet,
 | 
			
		||||
            UnitLen::Inches => kittycad_modeling_cmds::units::UnitLength::Inches,
 | 
			
		||||
            UnitLen::M => kittycad_modeling_cmds::units::UnitLength::Meters,
 | 
			
		||||
            UnitLen::Mm => kittycad_modeling_cmds::units::UnitLength::Millimeters,
 | 
			
		||||
            UnitLen::Yards => kittycad_modeling_cmds::units::UnitLength::Yards,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A unit of angle.
 | 
			
		||||
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum UnitAngle {
 | 
			
		||||
    #[default]
 | 
			
		||||
    Degrees,
 | 
			
		||||
    Radians,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for UnitAngle {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            UnitAngle::Degrees => write!(f, "deg"),
 | 
			
		||||
            UnitAngle::Radians => write!(f, "rad"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TryFrom<NumericSuffix> for UnitAngle {
 | 
			
		||||
    type Error = ();
 | 
			
		||||
 | 
			
		||||
    fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
 | 
			
		||||
        match suffix {
 | 
			
		||||
            NumericSuffix::Deg => Ok(Self::Degrees),
 | 
			
		||||
            NumericSuffix::Rad => Ok(Self::Radians),
 | 
			
		||||
            _ => Err(()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1055,7 +1055,7 @@ mod env {
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::execution::kcl_value::{FunctionSource, NumericType};
 | 
			
		||||
    use crate::execution::{kcl_value::FunctionSource, types::NumericType};
 | 
			
		||||
 | 
			
		||||
    fn sr() -> SourceRange {
 | 
			
		||||
        SourceRange::default()
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ pub(crate) use import::{
 | 
			
		||||
    import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
 | 
			
		||||
};
 | 
			
		||||
use indexmap::IndexMap;
 | 
			
		||||
pub use kcl_value::{KclObjectFields, KclValue, PrimitiveType, UnitAngle, UnitLen};
 | 
			
		||||
pub use kcl_value::{KclObjectFields, KclValue};
 | 
			
		||||
use kcmc::{
 | 
			
		||||
    each_cmd as mcmd,
 | 
			
		||||
    ok_response::{output::TakeSnapshot, OkModelingCmdResponse},
 | 
			
		||||
@ -34,6 +34,7 @@ use crate::{
 | 
			
		||||
    execution::{
 | 
			
		||||
        artifact::build_artifact_graph,
 | 
			
		||||
        cache::{CacheInformation, CacheResult},
 | 
			
		||||
        types::{UnitAngle, UnitLen},
 | 
			
		||||
    },
 | 
			
		||||
    fs::FileManager,
 | 
			
		||||
    modules::{ModuleId, ModulePath},
 | 
			
		||||
@ -54,6 +55,7 @@ mod import;
 | 
			
		||||
pub(crate) mod kcl_value;
 | 
			
		||||
mod memory;
 | 
			
		||||
mod state;
 | 
			
		||||
pub(crate) mod types;
 | 
			
		||||
 | 
			
		||||
/// Outcome of executing a program.  This is used in TS.
 | 
			
		||||
#[derive(Debug, Clone, Serialize, ts_rs::TS)]
 | 
			
		||||
@ -1375,6 +1377,22 @@ const answer = returnX()"#;
 | 
			
		||||
        assert!(errs.is_empty());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn type_aliases() {
 | 
			
		||||
        let text = r#"type MyTy = [number; 2]
 | 
			
		||||
fn foo(x: MyTy) {
 | 
			
		||||
    return x[0]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
foo([0, 1])
 | 
			
		||||
 | 
			
		||||
type Other = MyTy | Helix
 | 
			
		||||
"#;
 | 
			
		||||
        let result = parse_execute(text).await.unwrap();
 | 
			
		||||
        let errs = result.exec_state.errors();
 | 
			
		||||
        assert!(errs.is_empty());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_cannot_shebang_in_fn() {
 | 
			
		||||
        let ast = r#"
 | 
			
		||||
 | 
			
		||||
@ -12,10 +12,9 @@ use crate::{
 | 
			
		||||
    execution::{
 | 
			
		||||
        annotations,
 | 
			
		||||
        id_generator::IdGenerator,
 | 
			
		||||
        kcl_value,
 | 
			
		||||
        memory::{ProgramMemory, Stack},
 | 
			
		||||
        Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, EnvironmentRef, ExecOutcome, ExecutorSettings, KclValue,
 | 
			
		||||
        Operation, UnitAngle, UnitLen,
 | 
			
		||||
        types, Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, EnvironmentRef, ExecOutcome, ExecutorSettings,
 | 
			
		||||
        KclValue, Operation, UnitAngle, UnitLen,
 | 
			
		||||
    },
 | 
			
		||||
    modules::{ModuleId, ModuleInfo, ModuleLoader, ModulePath, ModuleRepr, ModuleSource},
 | 
			
		||||
    parsing::ast::types::Annotation,
 | 
			
		||||
@ -305,8 +304,8 @@ impl ModuleState {
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct MetaSettings {
 | 
			
		||||
    pub default_length_units: kcl_value::UnitLen,
 | 
			
		||||
    pub default_angle_units: kcl_value::UnitAngle,
 | 
			
		||||
    pub default_length_units: types::UnitLen,
 | 
			
		||||
    pub default_angle_units: types::UnitAngle,
 | 
			
		||||
    pub std_path: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -321,12 +320,12 @@ impl MetaSettings {
 | 
			
		||||
            match &*p.inner.key.name {
 | 
			
		||||
                annotations::SETTINGS_UNIT_LENGTH => {
 | 
			
		||||
                    let value = annotations::expect_ident(&p.inner.value)?;
 | 
			
		||||
                    let value = kcl_value::UnitLen::from_str(value, annotation.as_source_range())?;
 | 
			
		||||
                    let value = types::UnitLen::from_str(value, annotation.as_source_range())?;
 | 
			
		||||
                    self.default_length_units = value;
 | 
			
		||||
                }
 | 
			
		||||
                annotations::SETTINGS_UNIT_ANGLE => {
 | 
			
		||||
                    let value = annotations::expect_ident(&p.inner.value)?;
 | 
			
		||||
                    let value = kcl_value::UnitAngle::from_str(value, annotation.as_source_range())?;
 | 
			
		||||
                    let value = types::UnitAngle::from_str(value, annotation.as_source_range())?;
 | 
			
		||||
                    self.default_angle_units = value;
 | 
			
		||||
                }
 | 
			
		||||
                name => {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1254
									
								
								rust/kcl-lib/src/execution/types.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1254
									
								
								rust/kcl-lib/src/execution/types.rs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -344,8 +344,8 @@ impl Node<Type> {
 | 
			
		||||
        let range = self.as_source_range();
 | 
			
		||||
        if range.contains(pos) {
 | 
			
		||||
            match &self.inner {
 | 
			
		||||
                Type::Array(t) | Type::Primitive(t) => {
 | 
			
		||||
                    let mut name = t.to_string();
 | 
			
		||||
                Type::Array { ty, .. } | Type::Primitive(ty) => {
 | 
			
		||||
                    let mut name = ty.to_string();
 | 
			
		||||
                    if name.ends_with(')') {
 | 
			
		||||
                        name.truncate(name.find('(').unwrap());
 | 
			
		||||
                    }
 | 
			
		||||
@ -379,7 +379,7 @@ impl FunctionExpression {
 | 
			
		||||
        if let Some(value) = self.body.get_expr_for_position(pos) {
 | 
			
		||||
            let mut vars = opts.vars.clone().unwrap_or_default();
 | 
			
		||||
            for arg in &self.params {
 | 
			
		||||
                let ty = arg.type_.as_ref().map(|ty| ty.recast(&FormatOptions::default(), 0));
 | 
			
		||||
                let ty = arg.type_.as_ref().map(|ty| ty.to_string());
 | 
			
		||||
                vars.insert(arg.identifier.inner.name.clone(), ty);
 | 
			
		||||
            }
 | 
			
		||||
            return value.get_hover_value_for_position(
 | 
			
		||||
 | 
			
		||||
@ -1900,7 +1900,7 @@ async fn test_kcl_lsp_diagnostic_has_errors() {
 | 
			
		||||
            assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                diagnostics.full_document_diagnostic_report.items[0].message,
 | 
			
		||||
                "lexical: found unknown token ';'"
 | 
			
		||||
                "Unexpected token: ;"
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            panic!("Expected full diagnostics");
 | 
			
		||||
 | 
			
		||||
@ -194,9 +194,21 @@ impl Type {
 | 
			
		||||
                hasher.update(b"FnArgType::Primitive");
 | 
			
		||||
                hasher.update(prim.compute_digest())
 | 
			
		||||
            }
 | 
			
		||||
            Type::Array(prim) => {
 | 
			
		||||
            Type::Array { ty, len } => {
 | 
			
		||||
                hasher.update(b"FnArgType::Array");
 | 
			
		||||
                hasher.update(prim.compute_digest())
 | 
			
		||||
                hasher.update(ty.compute_digest());
 | 
			
		||||
                match len {
 | 
			
		||||
                    crate::execution::types::ArrayLen::None => {}
 | 
			
		||||
                    crate::execution::types::ArrayLen::NonEmpty => hasher.update(usize::MAX.to_ne_bytes()),
 | 
			
		||||
                    crate::execution::types::ArrayLen::Known(n) => hasher.update(n.to_ne_bytes()),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Type::Union { tys } => {
 | 
			
		||||
                hasher.update(b"FnArgType::Union");
 | 
			
		||||
                hasher.update(tys.len().to_ne_bytes());
 | 
			
		||||
                for t in tys.iter_mut() {
 | 
			
		||||
                    hasher.update(t.compute_digest());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Type::Object { properties } => {
 | 
			
		||||
                hasher.update(b"FnArgType::Object");
 | 
			
		||||
@ -300,6 +312,9 @@ impl TypeDeclaration {
 | 
			
		||||
                hasher.update(a.compute_digest());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(alias) = &mut slf.alias {
 | 
			
		||||
            hasher.update(alias.compute_digest());
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ pub use crate::parsing::ast::types::{
 | 
			
		||||
use crate::{
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    execution::{annotations, KclValue, Metadata, TagIdentifier},
 | 
			
		||||
    execution::{annotations, types::ArrayLen, KclValue, Metadata, TagIdentifier},
 | 
			
		||||
    parsing::{ast::digest::Digest, token::NumericSuffix, PIPE_OPERATOR},
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
    ModuleId,
 | 
			
		||||
@ -150,7 +150,7 @@ impl<T> Node<T> {
 | 
			
		||||
        self.start <= pos && pos <= self.end
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn map<U>(self, f: fn(T) -> U) -> Node<U> {
 | 
			
		||||
    pub fn map<U>(self, f: impl Fn(T) -> U) -> Node<U> {
 | 
			
		||||
        Node {
 | 
			
		||||
            inner: f(self.inner),
 | 
			
		||||
            start: self.start,
 | 
			
		||||
@ -1914,6 +1914,7 @@ pub struct TypeDeclaration {
 | 
			
		||||
    pub args: Option<NodeList<Identifier>>,
 | 
			
		||||
    #[serde(default, skip_serializing_if = "ItemVisibility::is_default")]
 | 
			
		||||
    pub visibility: ItemVisibility,
 | 
			
		||||
    pub alias: Option<Node<Type>>,
 | 
			
		||||
 | 
			
		||||
    #[serde(default, skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    #[ts(optional)]
 | 
			
		||||
@ -3043,7 +3044,14 @@ pub enum Type {
 | 
			
		||||
    /// A primitive type.
 | 
			
		||||
    Primitive(PrimitiveType),
 | 
			
		||||
    // An array of a primitive type.
 | 
			
		||||
    Array(PrimitiveType),
 | 
			
		||||
    Array {
 | 
			
		||||
        ty: PrimitiveType,
 | 
			
		||||
        len: ArrayLen,
 | 
			
		||||
    },
 | 
			
		||||
    // Union/enum types
 | 
			
		||||
    Union {
 | 
			
		||||
        tys: NodeList<PrimitiveType>,
 | 
			
		||||
    },
 | 
			
		||||
    // An object type.
 | 
			
		||||
    Object {
 | 
			
		||||
        properties: Vec<Parameter>,
 | 
			
		||||
@ -3054,7 +3062,22 @@ impl fmt::Display for Type {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            Type::Primitive(primitive_type) => primitive_type.fmt(f),
 | 
			
		||||
            Type::Array(primitive_type) => write!(f, "[{primitive_type}]"),
 | 
			
		||||
            Type::Array { ty, len } => {
 | 
			
		||||
                write!(f, "[{ty}")?;
 | 
			
		||||
                match len {
 | 
			
		||||
                    ArrayLen::None => {}
 | 
			
		||||
                    ArrayLen::NonEmpty => write!(f, "; 1+")?,
 | 
			
		||||
                    ArrayLen::Known(n) => write!(f, "; {n}")?,
 | 
			
		||||
                }
 | 
			
		||||
                write!(f, "]")
 | 
			
		||||
            }
 | 
			
		||||
            Type::Union { tys } => {
 | 
			
		||||
                write!(
 | 
			
		||||
                    f,
 | 
			
		||||
                    "{}",
 | 
			
		||||
                    tys.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(" | ")
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            Type::Object { properties } => {
 | 
			
		||||
                write!(f, "{{")?;
 | 
			
		||||
                let mut first = true;
 | 
			
		||||
@ -3671,11 +3694,17 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
        assert_eq!(params.len(), 3);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            params[0].type_.as_ref().unwrap().inner,
 | 
			
		||||
            Type::Array(PrimitiveType::Number(NumericSuffix::None))
 | 
			
		||||
            Type::Array {
 | 
			
		||||
                ty: PrimitiveType::Number(NumericSuffix::None),
 | 
			
		||||
                len: ArrayLen::None
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            params[1].type_.as_ref().unwrap().inner,
 | 
			
		||||
            Type::Array(PrimitiveType::String)
 | 
			
		||||
            Type::Array {
 | 
			
		||||
                ty: PrimitiveType::String,
 | 
			
		||||
                len: ArrayLen::None
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            params[2].type_.as_ref().unwrap().inner,
 | 
			
		||||
@ -3703,7 +3732,10 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
        assert_eq!(params.len(), 3);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            params[0].type_.as_ref().unwrap().inner,
 | 
			
		||||
            Type::Array(PrimitiveType::Number(NumericSuffix::None))
 | 
			
		||||
            Type::Array {
 | 
			
		||||
                ty: PrimitiveType::Number(NumericSuffix::None),
 | 
			
		||||
                len: ArrayLen::None
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            params[1].type_.as_ref().unwrap().inner,
 | 
			
		||||
@ -3739,7 +3771,15 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                            56,
 | 
			
		||||
                            module_id,
 | 
			
		||||
                        ),
 | 
			
		||||
                        type_: Some(Node::new(Type::Array(PrimitiveType::String), 59, 65, module_id)),
 | 
			
		||||
                        type_: Some(Node::new(
 | 
			
		||||
                            Type::Array {
 | 
			
		||||
                                ty: PrimitiveType::String,
 | 
			
		||||
                                len: ArrayLen::None
 | 
			
		||||
                            },
 | 
			
		||||
                            59,
 | 
			
		||||
                            65,
 | 
			
		||||
                            module_id
 | 
			
		||||
                        )),
 | 
			
		||||
                        default_value: None,
 | 
			
		||||
                        labeled: true,
 | 
			
		||||
                        digest: None
 | 
			
		||||
@ -3820,7 +3860,15 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                            34,
 | 
			
		||||
                            module_id,
 | 
			
		||||
                        ),
 | 
			
		||||
                        type_: Some(Node::new(Type::Array(PrimitiveType::String), 37, 43, module_id)),
 | 
			
		||||
                        type_: Some(Node::new(
 | 
			
		||||
                            Type::Array {
 | 
			
		||||
                                ty: PrimitiveType::String,
 | 
			
		||||
                                len: ArrayLen::None
 | 
			
		||||
                            },
 | 
			
		||||
                            37,
 | 
			
		||||
                            43,
 | 
			
		||||
                            module_id
 | 
			
		||||
                        )),
 | 
			
		||||
                        default_value: None,
 | 
			
		||||
                        labeled: true,
 | 
			
		||||
                        digest: None
 | 
			
		||||
@ -3993,7 +4041,7 @@ startSketchOn('XY')"#;
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            meta_settings.default_length_units,
 | 
			
		||||
            crate::execution::kcl_value::UnitLen::Inches
 | 
			
		||||
            crate::execution::types::UnitLen::Inches
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -4009,13 +4057,13 @@ startSketchOn('XY')"#;
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            meta_settings.default_length_units,
 | 
			
		||||
            crate::execution::kcl_value::UnitLen::Inches
 | 
			
		||||
            crate::execution::types::UnitLen::Inches
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Edit the ast.
 | 
			
		||||
        let new_program = program
 | 
			
		||||
            .change_meta_settings(crate::execution::MetaSettings {
 | 
			
		||||
                default_length_units: crate::execution::kcl_value::UnitLen::Mm,
 | 
			
		||||
                default_length_units: crate::execution::types::UnitLen::Mm,
 | 
			
		||||
                ..Default::default()
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap();
 | 
			
		||||
@ -4024,10 +4072,7 @@ startSketchOn('XY')"#;
 | 
			
		||||
        assert!(result.is_some());
 | 
			
		||||
        let meta_settings = result.unwrap();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            meta_settings.default_length_units,
 | 
			
		||||
            crate::execution::kcl_value::UnitLen::Mm
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(meta_settings.default_length_units, crate::execution::types::UnitLen::Mm);
 | 
			
		||||
 | 
			
		||||
        let formatted = new_program.recast(&Default::default(), 0);
 | 
			
		||||
 | 
			
		||||
@ -4050,7 +4095,7 @@ startSketchOn('XY')
 | 
			
		||||
        // Edit the ast.
 | 
			
		||||
        let new_program = program
 | 
			
		||||
            .change_meta_settings(crate::execution::MetaSettings {
 | 
			
		||||
                default_length_units: crate::execution::kcl_value::UnitLen::Mm,
 | 
			
		||||
                default_length_units: crate::execution::types::UnitLen::Mm,
 | 
			
		||||
                ..Default::default()
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap();
 | 
			
		||||
@ -4059,10 +4104,7 @@ startSketchOn('XY')
 | 
			
		||||
        assert!(result.is_some());
 | 
			
		||||
        let meta_settings = result.unwrap();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            meta_settings.default_length_units,
 | 
			
		||||
            crate::execution::kcl_value::UnitLen::Mm
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(meta_settings.default_length_units, crate::execution::types::UnitLen::Mm);
 | 
			
		||||
 | 
			
		||||
        let formatted = new_program.recast(&Default::default(), 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ use super::{
 | 
			
		||||
use crate::{
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::{CompilationError, Severity, Tag},
 | 
			
		||||
    execution::types::ArrayLen,
 | 
			
		||||
    parsing::{
 | 
			
		||||
        ast::types::{
 | 
			
		||||
            Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
 | 
			
		||||
@ -2215,7 +2216,7 @@ fn ty_decl(i: &mut TokenSlice) -> PResult<BoxNode<TypeDeclaration>> {
 | 
			
		||||
    let name = identifier(i)?;
 | 
			
		||||
    let mut end = name.end;
 | 
			
		||||
 | 
			
		||||
    let args = if peek(open_paren).parse_next(i).is_ok() {
 | 
			
		||||
    let args = if peek((opt(whitespace), open_paren)).parse_next(i).is_ok() {
 | 
			
		||||
        ignore_whitespace(i);
 | 
			
		||||
        open_paren(i)?;
 | 
			
		||||
        ignore_whitespace(i);
 | 
			
		||||
@ -2228,11 +2229,28 @@ fn ty_decl(i: &mut TokenSlice) -> PResult<BoxNode<TypeDeclaration>> {
 | 
			
		||||
        None
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let alias = if peek((opt(whitespace), equals)).parse_next(i).is_ok() {
 | 
			
		||||
        ignore_whitespace(i);
 | 
			
		||||
        equals(i)?;
 | 
			
		||||
        ignore_whitespace(i);
 | 
			
		||||
        let ty = argument_type(i)?;
 | 
			
		||||
 | 
			
		||||
        ParseContext::warn(CompilationError::err(
 | 
			
		||||
            ty.as_source_range(),
 | 
			
		||||
            "Type aliases are experimental, likely to change in the future, and likely to not work properly.",
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        Some(ty)
 | 
			
		||||
    } else {
 | 
			
		||||
        None
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let module_id = name.module_id;
 | 
			
		||||
    let result = Node::boxed(
 | 
			
		||||
        TypeDeclaration {
 | 
			
		||||
            name,
 | 
			
		||||
            args,
 | 
			
		||||
            alias,
 | 
			
		||||
            visibility,
 | 
			
		||||
            digest: None,
 | 
			
		||||
        },
 | 
			
		||||
@ -2336,21 +2354,7 @@ impl TryFrom<Token> for Node<TagDeclarator> {
 | 
			
		||||
                format!("Cannot assign a tag to a reserved keyword: {}", token.value.as_str()),
 | 
			
		||||
            )),
 | 
			
		||||
 | 
			
		||||
            TokenType::Bang
 | 
			
		||||
            | TokenType::At
 | 
			
		||||
            | TokenType::Hash
 | 
			
		||||
            | TokenType::Colon
 | 
			
		||||
            | TokenType::Period
 | 
			
		||||
            | TokenType::Operator
 | 
			
		||||
            | TokenType::DoublePeriod
 | 
			
		||||
            | TokenType::QuestionMark
 | 
			
		||||
            | TokenType::BlockComment
 | 
			
		||||
            | TokenType::Function
 | 
			
		||||
            | TokenType::String
 | 
			
		||||
            | TokenType::Dollar
 | 
			
		||||
            | TokenType::Keyword
 | 
			
		||||
            | TokenType::Unknown
 | 
			
		||||
            | TokenType::LineComment => Err(CompilationError::fatal(
 | 
			
		||||
            _ => Err(CompilationError::fatal(
 | 
			
		||||
                token.as_source_range(),
 | 
			
		||||
                // this is `start with` because if most of these cases are in the middle, it ends
 | 
			
		||||
                // up hitting a different error path(e.g. including a bang) or being valid(e.g. including a comment) since it will get broken up into
 | 
			
		||||
@ -2617,6 +2621,14 @@ fn colon(i: &mut TokenSlice) -> PResult<Token> {
 | 
			
		||||
    TokenType::Colon.parse_from(i)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn semi_colon(i: &mut TokenSlice) -> PResult<Token> {
 | 
			
		||||
    TokenType::SemiColon.parse_from(i)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn plus(i: &mut TokenSlice) -> PResult<Token> {
 | 
			
		||||
    one_of((TokenType::Operator, "+")).parse_next(i)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn equals(i: &mut TokenSlice) -> PResult<Token> {
 | 
			
		||||
    one_of((TokenType::Operator, "="))
 | 
			
		||||
        .context(expected("the equals operator, ="))
 | 
			
		||||
@ -2659,6 +2671,12 @@ fn comma_sep(i: &mut TokenSlice) -> PResult<()> {
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Parse a `|`, optionally followed by some whitespace.
 | 
			
		||||
fn pipe_sep(i: &mut TokenSlice) -> PResult<()> {
 | 
			
		||||
    (opt(whitespace), one_of((TokenType::Operator, "|")), opt(whitespace)).parse_next(i)?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Arguments are passed into a function.
 | 
			
		||||
fn arguments(i: &mut TokenSlice) -> PResult<Vec<Expr>> {
 | 
			
		||||
    separated(0.., expression, comma_sep)
 | 
			
		||||
@ -2685,7 +2703,15 @@ fn argument_type(i: &mut TokenSlice) -> PResult<Node<Type>> {
 | 
			
		||||
    let type_ = alt((
 | 
			
		||||
        // Object types
 | 
			
		||||
        // TODO it is buggy to treat object fields like parameters since the parameters parser assumes a terminating `)`.
 | 
			
		||||
        (open_brace, parameters, close_brace).map(|(open, params, close)| {
 | 
			
		||||
        (open_brace, parameters, close_brace).try_map(|(open, params, close)| {
 | 
			
		||||
            for p in ¶ms {
 | 
			
		||||
                if p.type_.is_none() {
 | 
			
		||||
                    return Err(CompilationError::fatal(
 | 
			
		||||
                        p.identifier.as_source_range(),
 | 
			
		||||
                        "Missing type for field in record type",
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Ok(Node::new(
 | 
			
		||||
                Type::Object { properties: params },
 | 
			
		||||
                open.start,
 | 
			
		||||
@ -2694,12 +2720,21 @@ fn argument_type(i: &mut TokenSlice) -> PResult<Node<Type>> {
 | 
			
		||||
            ))
 | 
			
		||||
        }),
 | 
			
		||||
        // Array types
 | 
			
		||||
        (open_bracket, primitive_type, close_bracket).map(|(_, t, _)| Ok(t.map(Type::Array))),
 | 
			
		||||
        // Primitive types
 | 
			
		||||
        primitive_type.map(|t| Ok(t.map(Type::Primitive))),
 | 
			
		||||
        array_type,
 | 
			
		||||
        // Primitive or union types
 | 
			
		||||
        separated(1.., primitive_type, pipe_sep).map(|mut tys: Vec<_>| {
 | 
			
		||||
            if tys.len() == 1 {
 | 
			
		||||
                tys.pop().unwrap().map(Type::Primitive)
 | 
			
		||||
            } else {
 | 
			
		||||
                let start = tys[0].start;
 | 
			
		||||
                let module_id = tys[0].module_id;
 | 
			
		||||
                let end = tys.last().unwrap().end;
 | 
			
		||||
                Node::new(Type::Union { tys }, start, end, module_id)
 | 
			
		||||
            }
 | 
			
		||||
        }),
 | 
			
		||||
    ))
 | 
			
		||||
    .parse_next(i)?
 | 
			
		||||
    .map_err(|e: CompilationError| ErrMode::Backtrack(ContextError::from(e)))?;
 | 
			
		||||
    .parse_next(i)?;
 | 
			
		||||
 | 
			
		||||
    Ok(type_)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2721,6 +2756,55 @@ fn primitive_type(i: &mut TokenSlice) -> PResult<Node<PrimitiveType>> {
 | 
			
		||||
    Ok(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn array_type(i: &mut TokenSlice) -> PResult<Node<Type>> {
 | 
			
		||||
    fn opt_whitespace(i: &mut TokenSlice) -> PResult<()> {
 | 
			
		||||
        ignore_whitespace(i);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open_bracket(i)?;
 | 
			
		||||
    let ty = primitive_type(i)?;
 | 
			
		||||
    let len = opt((
 | 
			
		||||
        semi_colon,
 | 
			
		||||
        opt_whitespace,
 | 
			
		||||
        any.try_map(|token: Token| match token.token_type {
 | 
			
		||||
            TokenType::Number => {
 | 
			
		||||
                let value = token.uint_value().ok_or_else(|| {
 | 
			
		||||
                    CompilationError::fatal(
 | 
			
		||||
                        token.as_source_range(),
 | 
			
		||||
                        format!("Expected unsigned integer literal, found: {}", token.value),
 | 
			
		||||
                    )
 | 
			
		||||
                })?;
 | 
			
		||||
 | 
			
		||||
                Ok(value as usize)
 | 
			
		||||
            }
 | 
			
		||||
            _ => Err(CompilationError::fatal(token.as_source_range(), "invalid array length")),
 | 
			
		||||
        }),
 | 
			
		||||
        opt(plus),
 | 
			
		||||
    ))
 | 
			
		||||
    .parse_next(i)?;
 | 
			
		||||
    close_bracket(i)?;
 | 
			
		||||
 | 
			
		||||
    let len = if let Some((tok, _, n, plus)) = len {
 | 
			
		||||
        if plus.is_some() {
 | 
			
		||||
            if n != 1 {
 | 
			
		||||
                return Err(ErrMode::Cut(ContextError::from(CompilationError::fatal(
 | 
			
		||||
                    tok.as_source_range(),
 | 
			
		||||
                    "Non-empty arrays are specified using `1+`, for a fixed-size array use just an integer",
 | 
			
		||||
                ))));
 | 
			
		||||
            } else {
 | 
			
		||||
                ArrayLen::NonEmpty
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            ArrayLen::Known(n)
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        ArrayLen::None
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Ok(ty.map(|ty| Type::Array { ty, len }))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn uom_for_type(i: &mut TokenSlice) -> PResult<NumericSuffix> {
 | 
			
		||||
    any.try_map(|t: Token| t.value.parse()).parse_next(i)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -367,6 +367,8 @@ pub enum TokenType {
 | 
			
		||||
    QuestionMark,
 | 
			
		||||
    /// The @ symbol.
 | 
			
		||||
    At,
 | 
			
		||||
    /// `;`
 | 
			
		||||
    SemiColon,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Most KCL tokens correspond to LSP semantic tokens (but not all).
 | 
			
		||||
@ -396,6 +398,7 @@ impl TryFrom<TokenType> for SemanticTokenType {
 | 
			
		||||
            | TokenType::Hash
 | 
			
		||||
            | TokenType::Dollar
 | 
			
		||||
            | TokenType::At
 | 
			
		||||
            | TokenType::SemiColon
 | 
			
		||||
            | TokenType::Unknown => {
 | 
			
		||||
                anyhow::bail!("unsupported token type: {:?}", token_type)
 | 
			
		||||
            }
 | 
			
		||||
@ -488,6 +491,18 @@ impl Token {
 | 
			
		||||
        value.parse().ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn uint_value(&self) -> Option<u32> {
 | 
			
		||||
        if self.token_type != TokenType::Number {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        let value = &self.value;
 | 
			
		||||
        let value = value
 | 
			
		||||
            .split_once(|c: char| c == '_' || c.is_ascii_alphabetic())
 | 
			
		||||
            .map(|(s, _)| s)
 | 
			
		||||
            .unwrap_or(value);
 | 
			
		||||
        value.parse().ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn numeric_suffix(&self) -> NumericSuffix {
 | 
			
		||||
        if self.token_type != TokenType::Number {
 | 
			
		||||
            return NumericSuffix::None;
 | 
			
		||||
 | 
			
		||||
@ -88,6 +88,7 @@ pub(super) fn token(i: &mut Input<'_>) -> PResult<Token> {
 | 
			
		||||
        '@' => at,
 | 
			
		||||
        '0'..='9' => number,
 | 
			
		||||
        ':' => colon,
 | 
			
		||||
        ';' => semi_colon,
 | 
			
		||||
        '.' => alt((number, double_period, period)),
 | 
			
		||||
        '#' => hash,
 | 
			
		||||
        '$' => dollar,
 | 
			
		||||
@ -282,6 +283,16 @@ fn colon(i: &mut Input<'_>) -> PResult<Token> {
 | 
			
		||||
    ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn semi_colon(i: &mut Input<'_>) -> PResult<Token> {
 | 
			
		||||
    let (value, range) = ';'.with_span().parse_next(i)?;
 | 
			
		||||
    Ok(Token::from_range(
 | 
			
		||||
        range,
 | 
			
		||||
        i.state.module_id,
 | 
			
		||||
        TokenType::SemiColon,
 | 
			
		||||
        value.to_string(),
 | 
			
		||||
    ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn period(i: &mut Input<'_>) -> PResult<Token> {
 | 
			
		||||
    let (value, range) = '.'.with_span().parse_next(i)?;
 | 
			
		||||
    Ok(Token::from_range(
 | 
			
		||||
@ -689,7 +700,7 @@ const things = "things"
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_unrecognized_token() {
 | 
			
		||||
        let module_id = ModuleId::from_usize(1);
 | 
			
		||||
        let actual = lex("12 ; 8", module_id).unwrap();
 | 
			
		||||
        let actual = lex("12 ~ 8", module_id).unwrap();
 | 
			
		||||
 | 
			
		||||
        use TokenType::*;
 | 
			
		||||
        assert_tokens(&[(Number, 0, 2), (Unknown, 3, 4), (Number, 5, 6)], actual.as_slice());
 | 
			
		||||
 | 
			
		||||
@ -12,10 +12,7 @@ use validator::Validate;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{ArrayLen, RuntimeType},
 | 
			
		||||
        ExecState, KclValue, PrimitiveType, Solid,
 | 
			
		||||
    },
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, KclValue, Solid},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -42,11 +39,7 @@ struct AppearanceData {
 | 
			
		||||
 | 
			
		||||
/// Set the appearance of a solid. This only works on solids, not sketches or individual paths.
 | 
			
		||||
pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let solids = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "solids",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
 | 
			
		||||
 | 
			
		||||
    let color: String = args.get_kw_arg("color")?;
 | 
			
		||||
    let metalness: Option<f64> = args.get_kw_arg_opt("metalness")?;
 | 
			
		||||
 | 
			
		||||
@ -12,9 +12,10 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{ArrayLen, FunctionSource, NumericType, RuntimeType},
 | 
			
		||||
        ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, PrimitiveType, Sketch,
 | 
			
		||||
        SketchSurface, Solid, TagIdentifier,
 | 
			
		||||
        kcl_value::FunctionSource,
 | 
			
		||||
        types::{NumericType, PrimitiveType, RuntimeType},
 | 
			
		||||
        ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, Sketch, SketchSurface,
 | 
			
		||||
        Solid, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
@ -188,8 +189,10 @@ impl Args {
 | 
			
		||||
                ty.human_friendly_type(),
 | 
			
		||||
            );
 | 
			
		||||
            let suggestion = match (ty, actual_type_name) {
 | 
			
		||||
                (RuntimeType::Primitive(PrimitiveType::Solid), "Sketch")
 | 
			
		||||
                | (RuntimeType::Array(PrimitiveType::Solid, _), "Sketch") => Some(
 | 
			
		||||
                (RuntimeType::Primitive(PrimitiveType::Solid), "Sketch") => Some(
 | 
			
		||||
                    "You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
 | 
			
		||||
                ),
 | 
			
		||||
                (RuntimeType::Array(t, _), "Sketch") if **t == RuntimeType::Primitive(PrimitiveType::Solid) => Some(
 | 
			
		||||
                    "You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
 | 
			
		||||
                ),
 | 
			
		||||
                _ => None,
 | 
			
		||||
@ -309,8 +312,10 @@ impl Args {
 | 
			
		||||
                ty.human_friendly_type(),
 | 
			
		||||
            );
 | 
			
		||||
            let suggestion = match (ty, actual_type_name) {
 | 
			
		||||
                (RuntimeType::Primitive(PrimitiveType::Solid), "Sketch")
 | 
			
		||||
                | (RuntimeType::Array(PrimitiveType::Solid, _), "Sketch") => Some(
 | 
			
		||||
                (RuntimeType::Primitive(PrimitiveType::Solid), "Sketch") => Some(
 | 
			
		||||
                    "You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
 | 
			
		||||
                ),
 | 
			
		||||
                (RuntimeType::Array(ty, _), "Sketch") if **ty == RuntimeType::Primitive(PrimitiveType::Solid) => Some(
 | 
			
		||||
                    "You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
 | 
			
		||||
                ),
 | 
			
		||||
                _ => None,
 | 
			
		||||
@ -597,7 +602,7 @@ impl Args {
 | 
			
		||||
        };
 | 
			
		||||
        let sarg = arg0
 | 
			
		||||
            .value
 | 
			
		||||
            .coerce(&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::None), exec_state)
 | 
			
		||||
            .coerce(&RuntimeType::sketches(), exec_state)
 | 
			
		||||
            .ok_or(KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!(
 | 
			
		||||
                    "Expected an array of sketches, found {}",
 | 
			
		||||
@ -685,7 +690,7 @@ impl Args {
 | 
			
		||||
        };
 | 
			
		||||
        let sarg = arg1
 | 
			
		||||
            .value
 | 
			
		||||
            .coerce(&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::None), exec_state)
 | 
			
		||||
            .coerce(&RuntimeType::sketches(), exec_state)
 | 
			
		||||
            .ok_or(KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!(
 | 
			
		||||
                    "Expected one or more sketches for second argument, found {}",
 | 
			
		||||
 | 
			
		||||
@ -8,8 +8,8 @@ use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::RuntimeType, ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, PrimitiveType,
 | 
			
		||||
        Solid,
 | 
			
		||||
        types::{PrimitiveType, RuntimeType},
 | 
			
		||||
        ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, Solid,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    std::{fillet::EdgeReference, Args},
 | 
			
		||||
 | 
			
		||||
@ -5,20 +5,14 @@ use kcl_derive_docs::stdlib;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{ArrayLen, RuntimeType},
 | 
			
		||||
        ExecState, KclValue, PrimitiveType, Solid,
 | 
			
		||||
    },
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, KclValue, Solid},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Union two or more solids into a single solid.
 | 
			
		||||
pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let solids: Vec<Solid> = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "objects",
 | 
			
		||||
        &RuntimeType::Union(vec![RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty)]),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let solids: Vec<Solid> =
 | 
			
		||||
        args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::Union(vec![RuntimeType::solids()]), exec_state)?;
 | 
			
		||||
 | 
			
		||||
    if solids.len() < 2 {
 | 
			
		||||
        return Err(KclError::UndefinedValue(KclErrorDetails {
 | 
			
		||||
@ -74,11 +68,7 @@ async fn inner_union(solids: Vec<Solid>, exec_state: &mut ExecState, args: Args)
 | 
			
		||||
/// Intersect returns the shared volume between multiple solids, preserving only
 | 
			
		||||
/// overlapping regions.
 | 
			
		||||
pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let solids: Vec<Solid> = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "objects",
 | 
			
		||||
        &RuntimeType::Union(vec![RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty)]),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let solids: Vec<Solid> = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
 | 
			
		||||
 | 
			
		||||
    if solids.len() < 2 {
 | 
			
		||||
        return Err(KclError::UndefinedValue(KclErrorDetails {
 | 
			
		||||
@ -139,16 +129,8 @@ async fn inner_intersect(solids: Vec<Solid>, exec_state: &mut ExecState, args: A
 | 
			
		||||
 | 
			
		||||
/// Subtract removes tool solids from base solids, leaving the remaining material.
 | 
			
		||||
pub async fn subtract(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let solids: Vec<Solid> = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "objects",
 | 
			
		||||
        &RuntimeType::Union(vec![RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty)]),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let tools: Vec<Solid> = args.get_kw_arg_typed(
 | 
			
		||||
        "tools",
 | 
			
		||||
        &RuntimeType::Union(vec![RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty)]),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let solids: Vec<Solid> = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
 | 
			
		||||
    let tools: Vec<Solid> = args.get_kw_arg_typed("tools", &RuntimeType::solids(), exec_state)?;
 | 
			
		||||
 | 
			
		||||
    let solids = inner_subtract(solids, tools, exec_state, args).await?;
 | 
			
		||||
    Ok(solids.into())
 | 
			
		||||
 | 
			
		||||
@ -19,8 +19,8 @@ use uuid::Uuid;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{ArrayLen, RuntimeType},
 | 
			
		||||
        ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, PrimitiveType, Sketch, SketchSurface, Solid,
 | 
			
		||||
        types::RuntimeType, ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, Sketch, SketchSurface,
 | 
			
		||||
        Solid,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    std::Args,
 | 
			
		||||
@ -28,11 +28,7 @@ use crate::{
 | 
			
		||||
 | 
			
		||||
/// Extrudes by a given amount.
 | 
			
		||||
pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "sketches",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
 | 
			
		||||
    let length = args.get_kw_arg("length")?;
 | 
			
		||||
    let tag_start = args.get_kw_arg_opt("tagStart")?;
 | 
			
		||||
    let tag_end = args.get_kw_arg_opt("tagEnd")?;
 | 
			
		||||
 | 
			
		||||
@ -11,8 +11,8 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::RuntimeType, EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, PrimitiveType,
 | 
			
		||||
        Solid, TagIdentifier,
 | 
			
		||||
        types::{PrimitiveType, RuntimeType},
 | 
			
		||||
        EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    settings::types::UnitLength,
 | 
			
		||||
 | 
			
		||||
@ -9,10 +9,7 @@ use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{ArrayLen, RuntimeType},
 | 
			
		||||
        ExecState, KclValue, PrimitiveType, Sketch, Solid,
 | 
			
		||||
    },
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, KclValue, Sketch, Solid},
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
 | 
			
		||||
};
 | 
			
		||||
@ -21,11 +18,7 @@ const DEFAULT_V_DEGREE: u32 = 2;
 | 
			
		||||
 | 
			
		||||
/// Create a 3D surface or solid by interpolating between two or more sketches.
 | 
			
		||||
pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "sketches",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
 | 
			
		||||
    let v_degree: NonZeroU32 = args
 | 
			
		||||
        .get_kw_arg_opt("vDegree")?
 | 
			
		||||
        .unwrap_or(NonZeroU32::new(DEFAULT_V_DEGREE).unwrap());
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    execution::{ExecState, KclValue},
 | 
			
		||||
    execution::{types::PrimitiveType, ExecState, KclValue},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub type StdFn = fn(
 | 
			
		||||
@ -207,20 +207,13 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn std_ty(path: &str, fn_name: &str) -> (crate::execution::PrimitiveType, StdFnProps) {
 | 
			
		||||
pub(crate) fn std_ty(path: &str, fn_name: &str) -> (PrimitiveType, StdFnProps) {
 | 
			
		||||
    match (path, fn_name) {
 | 
			
		||||
        ("prelude", "Sketch") => (
 | 
			
		||||
            crate::execution::PrimitiveType::Sketch,
 | 
			
		||||
            StdFnProps::default("std::Sketch"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("prelude", "Solid") => (
 | 
			
		||||
            crate::execution::PrimitiveType::Solid,
 | 
			
		||||
            StdFnProps::default("std::Solid"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("prelude", "Plane") => (
 | 
			
		||||
            crate::execution::PrimitiveType::Plane,
 | 
			
		||||
            StdFnProps::default("std::Plane"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("prelude", "Sketch") => (PrimitiveType::Sketch, StdFnProps::default("std::Sketch")),
 | 
			
		||||
        ("prelude", "Solid") => (PrimitiveType::Solid, StdFnProps::default("std::Solid")),
 | 
			
		||||
        ("prelude", "Plane") => (PrimitiveType::Plane, StdFnProps::default("std::Plane")),
 | 
			
		||||
        ("prelude", "Face") => (PrimitiveType::Face, StdFnProps::default("std::Face")),
 | 
			
		||||
        ("prelude", "Helix") => (PrimitiveType::Helix, StdFnProps::default("std::Helix")),
 | 
			
		||||
        _ => unreachable!(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -20,8 +20,9 @@ use super::args::Arg;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{ArrayLen, FunctionSource, NumericType, RuntimeType},
 | 
			
		||||
        ExecState, Geometries, Geometry, KclObjectFields, KclValue, Point2d, Point3d, PrimitiveType, Sketch, Solid,
 | 
			
		||||
        kcl_value::FunctionSource,
 | 
			
		||||
        types::{NumericType, RuntimeType},
 | 
			
		||||
        ExecState, Geometries, Geometry, KclObjectFields, KclValue, Point2d, Point3d, Sketch, Solid,
 | 
			
		||||
    },
 | 
			
		||||
    std::Args,
 | 
			
		||||
    ExecutorContext, SourceRange,
 | 
			
		||||
@ -47,11 +48,7 @@ pub struct LinearPattern3dData {
 | 
			
		||||
 | 
			
		||||
/// Repeat some 3D solid, changing each repetition slightly.
 | 
			
		||||
pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let solids = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "solids",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
 | 
			
		||||
    let instances: u32 = args.get_kw_arg("instances")?;
 | 
			
		||||
    let transform: &FunctionSource = args.get_kw_arg("transform")?;
 | 
			
		||||
    let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
 | 
			
		||||
@ -62,11 +59,7 @@ pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result
 | 
			
		||||
 | 
			
		||||
/// Repeat some 2D sketch, changing each repetition slightly.
 | 
			
		||||
pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "sketches",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
 | 
			
		||||
    let instances: u32 = args.get_kw_arg("instances")?;
 | 
			
		||||
    let transform: &FunctionSource = args.get_kw_arg("transform")?;
 | 
			
		||||
    let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
 | 
			
		||||
@ -664,7 +657,7 @@ impl GeometryTrait for Solid {
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::execution::kcl_value::NumericType;
 | 
			
		||||
    use crate::execution::types::NumericType;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_array_to_point3d() {
 | 
			
		||||
@ -696,11 +689,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
/// A linear pattern on a 2D sketch.
 | 
			
		||||
pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "sketches",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
 | 
			
		||||
    let instances: u32 = args.get_kw_arg("instances")?;
 | 
			
		||||
    let distance: f64 = args.get_kw_arg("distance")?;
 | 
			
		||||
    let axis: [f64; 2] = args.get_kw_arg("axis")?;
 | 
			
		||||
@ -779,11 +768,7 @@ async fn inner_pattern_linear_2d(
 | 
			
		||||
 | 
			
		||||
/// A linear pattern on a 3D model.
 | 
			
		||||
pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let solids = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "solids",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
 | 
			
		||||
    let instances: u32 = args.get_kw_arg("instances")?;
 | 
			
		||||
    let distance: f64 = args.get_kw_arg("distance")?;
 | 
			
		||||
    let axis: [f64; 3] = args.get_kw_arg("axis")?;
 | 
			
		||||
@ -1028,11 +1013,7 @@ impl CircularPattern {
 | 
			
		||||
 | 
			
		||||
/// A circular pattern on a 2D sketch.
 | 
			
		||||
pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "sketches",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
 | 
			
		||||
    let instances: u32 = args.get_kw_arg("instances")?;
 | 
			
		||||
    let center: [f64; 2] = args.get_kw_arg("center")?;
 | 
			
		||||
    let arc_degrees: f64 = args.get_kw_arg("arcDegrees")?;
 | 
			
		||||
@ -1136,11 +1117,7 @@ async fn inner_pattern_circular_2d(
 | 
			
		||||
 | 
			
		||||
/// A circular pattern on a 3D model.
 | 
			
		||||
pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let solids = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "solids",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
 | 
			
		||||
    // The number of total instances. Must be greater than or equal to 1.
 | 
			
		||||
    // This includes the original entity. For example, if instances is 2,
 | 
			
		||||
    // there will be two copies -- the original, and one new copy.
 | 
			
		||||
 | 
			
		||||
@ -7,21 +7,14 @@ use kittycad_modeling_cmds::{self as kcmc};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{ArrayLen, RuntimeType},
 | 
			
		||||
        ExecState, KclValue, PrimitiveType, Sketch, Solid,
 | 
			
		||||
    },
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, KclValue, Sketch, Solid},
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    std::{axis_or_reference::Axis2dOrEdgeReference, extrude::do_post_extrude, fillet::default_tolerance, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Revolve a sketch or set of sketches around an axis.
 | 
			
		||||
pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "sketches",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
 | 
			
		||||
    let axis: Axis2dOrEdgeReference = args.get_kw_arg("axis")?;
 | 
			
		||||
    let angle = args.get_kw_arg_opt("angle")?;
 | 
			
		||||
    let tolerance = args.get_kw_arg_opt("tolerance")?;
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,10 @@ use kittycad_modeling_cmds::shared::Angle;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{kcl_value::RuntimeType, ExecState, KclValue, Point2d, PrimitiveType, Sketch, TagIdentifier},
 | 
			
		||||
    execution::{
 | 
			
		||||
        types::{PrimitiveType, RuntimeType},
 | 
			
		||||
        ExecState, KclValue, Point2d, Sketch, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    std::{utils::between, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,20 +7,13 @@ use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{ArrayLen, RuntimeType},
 | 
			
		||||
        ExecState, KclValue, PrimitiveType, Solid,
 | 
			
		||||
    },
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, KclValue, Solid},
 | 
			
		||||
    std::{sketch::FaceTag, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Create a shell.
 | 
			
		||||
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let solids = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "solids",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
 | 
			
		||||
    let thickness = args.get_kw_arg("thickness")?;
 | 
			
		||||
    let faces = args.get_kw_arg("faces")?;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,9 +14,9 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::RuntimeType, Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path,
 | 
			
		||||
        Plane, Point2d, Point3d, PrimitiveType, Sketch, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane,
 | 
			
		||||
        TagEngineInfo, TagIdentifier,
 | 
			
		||||
        types::{PrimitiveType, RuntimeType},
 | 
			
		||||
        Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d,
 | 
			
		||||
        Sketch, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane, TagEngineInfo, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    std::{
 | 
			
		||||
 | 
			
		||||
@ -9,10 +9,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{ArrayLen, RuntimeType},
 | 
			
		||||
        ExecState, Helix, KclValue, PrimitiveType, Sketch, Solid,
 | 
			
		||||
    },
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, Helix, KclValue, Sketch, Solid},
 | 
			
		||||
    parsing::ast::types::TagNode,
 | 
			
		||||
    std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
 | 
			
		||||
};
 | 
			
		||||
@ -28,11 +25,7 @@ pub enum SweepPath {
 | 
			
		||||
 | 
			
		||||
/// Extrude a sketch along a path.
 | 
			
		||||
pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "sketches",
 | 
			
		||||
        &RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
    let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
 | 
			
		||||
    let path: SweepPath = args.get_kw_arg("path")?;
 | 
			
		||||
    let sectional = args.get_kw_arg_opt("sectional")?;
 | 
			
		||||
    let tolerance = args.get_kw_arg_opt("tolerance")?;
 | 
			
		||||
 | 
			
		||||
@ -13,10 +13,7 @@ use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    execution::{
 | 
			
		||||
        kcl_value::{ArrayLen, RuntimeType},
 | 
			
		||||
        ExecState, KclValue, PrimitiveType, SolidOrSketchOrImportedGeometry,
 | 
			
		||||
    },
 | 
			
		||||
    execution::{types::RuntimeType, ExecState, KclValue, SolidOrSketchOrImportedGeometry},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -25,9 +22,9 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
 | 
			
		||||
    let objects = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "objects",
 | 
			
		||||
        &RuntimeType::Union(vec![
 | 
			
		||||
            RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
 | 
			
		||||
            RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
 | 
			
		||||
            RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
 | 
			
		||||
            RuntimeType::sketches(),
 | 
			
		||||
            RuntimeType::solids(),
 | 
			
		||||
            RuntimeType::imported(),
 | 
			
		||||
        ]),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
@ -187,9 +184,9 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
 | 
			
		||||
    let objects = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "objects",
 | 
			
		||||
        &RuntimeType::Union(vec![
 | 
			
		||||
            RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
 | 
			
		||||
            RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
 | 
			
		||||
            RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
 | 
			
		||||
            RuntimeType::sketches(),
 | 
			
		||||
            RuntimeType::solids(),
 | 
			
		||||
            RuntimeType::imported(),
 | 
			
		||||
        ]),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
@ -390,9 +387,9 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
 | 
			
		||||
    let objects = args.get_unlabeled_kw_arg_typed(
 | 
			
		||||
        "objects",
 | 
			
		||||
        &RuntimeType::Union(vec![
 | 
			
		||||
            RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
 | 
			
		||||
            RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
 | 
			
		||||
            RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
 | 
			
		||||
            RuntimeType::sketches(),
 | 
			
		||||
            RuntimeType::solids(),
 | 
			
		||||
            RuntimeType::imported(),
 | 
			
		||||
        ]),
 | 
			
		||||
        exec_state,
 | 
			
		||||
    )?;
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ use kcl_derive_docs::stdlib;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    execution::{ExecState, KclValue, UnitLen},
 | 
			
		||||
    execution::{types::UnitLen, ExecState, KclValue},
 | 
			
		||||
    std::Args,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,8 +6,7 @@ use crate::parsing::{
 | 
			
		||||
        CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions, FunctionExpression,
 | 
			
		||||
        IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier,
 | 
			
		||||
        LiteralValue, MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter,
 | 
			
		||||
        PipeExpression, Program, TagDeclarator, Type, TypeDeclaration, UnaryExpression, VariableDeclaration,
 | 
			
		||||
        VariableKind,
 | 
			
		||||
        PipeExpression, Program, TagDeclarator, TypeDeclaration, UnaryExpression, VariableDeclaration, VariableKind,
 | 
			
		||||
    },
 | 
			
		||||
    token::NumericSuffix,
 | 
			
		||||
    PIPE_OPERATOR,
 | 
			
		||||
@ -308,7 +307,7 @@ impl Expr {
 | 
			
		||||
            Expr::AscribedExpression(e) => {
 | 
			
		||||
                let mut result = e.expr.recast(options, indentation_level, ctxt);
 | 
			
		||||
                result += ": ";
 | 
			
		||||
                result += &e.ty.recast(options, indentation_level);
 | 
			
		||||
                result += &e.ty.to_string();
 | 
			
		||||
                result
 | 
			
		||||
            }
 | 
			
		||||
            Expr::None(_) => {
 | 
			
		||||
@ -457,6 +456,10 @@ impl TypeDeclaration {
 | 
			
		||||
            }
 | 
			
		||||
            arg_str.push(')');
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(alias) = &self.alias {
 | 
			
		||||
            arg_str.push_str(" = ");
 | 
			
		||||
            arg_str.push_str(&alias.to_string());
 | 
			
		||||
        }
 | 
			
		||||
        format!("{}type {}{}", vis, self.name.name, arg_str)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -812,7 +815,7 @@ impl FunctionExpression {
 | 
			
		||||
        let tab0 = options.get_indentation(indentation_level);
 | 
			
		||||
        let tab1 = options.get_indentation(indentation_level + 1);
 | 
			
		||||
        let return_type = match &self.return_type {
 | 
			
		||||
            Some(rt) => format!(": {}", rt.recast(&new_options, indentation_level)),
 | 
			
		||||
            Some(rt) => format!(": {rt}"),
 | 
			
		||||
            None => String::new(),
 | 
			
		||||
        };
 | 
			
		||||
        let body = self.body.recast(&new_options, indentation_level + 1);
 | 
			
		||||
@ -822,14 +825,14 @@ impl FunctionExpression {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Parameter {
 | 
			
		||||
    pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
 | 
			
		||||
    pub fn recast(&self, _options: &FormatOptions, _indentation_level: usize) -> String {
 | 
			
		||||
        let at_sign = if self.labeled { "" } else { "@" };
 | 
			
		||||
        let identifier = &self.identifier.name;
 | 
			
		||||
        let question_mark = if self.default_value.is_some() { "?" } else { "" };
 | 
			
		||||
        let mut result = format!("{at_sign}{identifier}{question_mark}");
 | 
			
		||||
        if let Some(ty) = &self.type_ {
 | 
			
		||||
            result += ": ";
 | 
			
		||||
            result += &ty.recast(options, indentation_level);
 | 
			
		||||
            result += &ty.to_string();
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(DefaultParamVal::Literal(ref literal)) = self.default_value {
 | 
			
		||||
            let lit = literal.recast();
 | 
			
		||||
@ -840,31 +843,6 @@ impl Parameter {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Type {
 | 
			
		||||
    pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            Type::Primitive(t) => t.to_string(),
 | 
			
		||||
            Type::Array(t) => format!("[{t}]"),
 | 
			
		||||
            Type::Object { properties } => {
 | 
			
		||||
                let mut result = "{".to_owned();
 | 
			
		||||
                for p in properties {
 | 
			
		||||
                    result += " ";
 | 
			
		||||
                    result += &p.recast(options, indentation_level);
 | 
			
		||||
                    result += ",";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if result.ends_with(',') {
 | 
			
		||||
                    result.pop();
 | 
			
		||||
                    result += " ";
 | 
			
		||||
                }
 | 
			
		||||
                result += "}";
 | 
			
		||||
 | 
			
		||||
                result
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Collect all the kcl files in a directory, recursively.
 | 
			
		||||
#[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
#[async_recursion::async_recursion]
 | 
			
		||||
@ -1414,6 +1392,21 @@ thing(1)
 | 
			
		||||
        assert_eq!(recasted, some_program_string);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_recast_typed_consts() {
 | 
			
		||||
        let some_program_string = r#"a = 42: number
 | 
			
		||||
export b = 3.2: number(ft)
 | 
			
		||||
c = "dsfds": A | B | C
 | 
			
		||||
d = [1]: [number]
 | 
			
		||||
e = foo: [number; 3]
 | 
			
		||||
f = [1, 2, 3]: [number; 1+]
 | 
			
		||||
"#;
 | 
			
		||||
        let program = crate::parsing::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted, some_program_string);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_recast_object_fn_in_array_weird_bracket() {
 | 
			
		||||
        let some_program_string = r#"bing = { yo = 55 }
 | 
			
		||||
@ -2438,6 +2431,7 @@ thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
 | 
			
		||||
// A comment
 | 
			
		||||
@(impl = primitive)
 | 
			
		||||
export type bar(unit, baz)
 | 
			
		||||
type baz = Foo | Bar
 | 
			
		||||
"#;
 | 
			
		||||
        let program = crate::parsing::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
 | 
			
		||||
@ -223,6 +223,26 @@ export type Sketch
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export type Solid
 | 
			
		||||
 | 
			
		||||
/// A face.
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export type Face
 | 
			
		||||
 | 
			
		||||
/// A helix.
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export type Helix
 | 
			
		||||
 | 
			
		||||
/// A point in two dimensional space.
 | 
			
		||||
///
 | 
			
		||||
/// `Point2d` is an alias for a two-element array of [number](/docs/kcl/types/number)s. To write a value
 | 
			
		||||
/// with type `Point2d`, use an array, e.g., `[0, 0]` or `[5.0, 3.14]`.
 | 
			
		||||
export type Point2d = [number; 2]
 | 
			
		||||
 | 
			
		||||
/// A point in three dimensional space.
 | 
			
		||||
///
 | 
			
		||||
/// `Point3d` is an alias for a three-element array of [number](/docs/kcl/types/number)s. To write a value
 | 
			
		||||
/// with type `Point3d`, use an array, e.g., `[0, 0, 0]` or `[5.0, 3.14, 6.8]`.
 | 
			
		||||
export type Point3d = [number; 3]
 | 
			
		||||
 | 
			
		||||
export ZERO = 0
 | 
			
		||||
export QUARTER_TURN = 90deg
 | 
			
		||||
export HALF_TURN = 180deg
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user