diff --git a/docs/kcl/loft.md b/docs/kcl/loft.md index bdc912630f..e73aa69cfa 100644 --- a/docs/kcl/loft.md +++ b/docs/kcl/loft.md @@ -9,7 +9,7 @@ Create a 3D surface or solid by interpolating between two or more sketches. The sketches need to closed and on the same plane. ```js -loft(sketches: [Sketch], v_degree: NonZeroU32, bez_approximate_rational: bool, base_curve_index?: u32, tolerance?: number) -> Solid +loft(sketches: [Sketch], v_degree: NonZeroU32, bez_approximate_rational: bool, base_curve_index?: integer, tolerance?: number) -> Solid ``` @@ -20,7 +20,7 @@ loft(sketches: [Sketch], v_degree: NonZeroU32, bez_approximate_rational: bool, b | `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | Which sketches to loft. Must include at least 2 sketches. | Yes | | `v_degree` | `NonZeroU32` | Degree of the interpolation. Must be greater than zero. For example, use 2 for quadratic, or 3 for cubic interpolation in the V direction. This defaults to 2, if not specified. | Yes | | `bez_approximate_rational` | `bool` | Attempt to approximate rational curves (such as arcs) using a bezier. This will remove banding around interpolations between arcs and non-arcs. It may produce errors in other scenarios Over time, this field won't be necessary. | Yes | -| `base_curve_index` | `u32` | This can be set to override the automatically determined topological base curve, which is usually the first section encountered. | No | +| `base_curve_index` | `integer` | This can be set to override the automatically determined topological base curve, which is usually the first section encountered. | No | | `tolerance` | `number` | Tolerance for the loft operation. | No | ### Returns diff --git a/docs/kcl/patternTransform.md b/docs/kcl/patternTransform.md index 6cba339bb0..2b4854ba70 100644 --- a/docs/kcl/patternTransform.md +++ b/docs/kcl/patternTransform.md @@ -35,7 +35,7 @@ The transform function returns a transform object. All properties of the object - `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local") ```js -patternTransform(total_instances: u32, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid] +patternTransform(total_instances: integer, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid] ``` @@ -43,7 +43,7 @@ patternTransform(total_instances: u32, transform_function: FunctionParam, solid_ | Name | Type | Description | Required | |----------|------|-------------|----------| -| `total_instances` | `u32` | | Yes | +| `total_instances` | `integer` | | Yes | | `transform_function` | `FunctionParam` | | Yes | | `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | A solid or a group of solids. | Yes | diff --git a/docs/kcl/patternTransform2d.md b/docs/kcl/patternTransform2d.md index 749246bd9b..353ce75b5d 100644 --- a/docs/kcl/patternTransform2d.md +++ b/docs/kcl/patternTransform2d.md @@ -9,7 +9,7 @@ Just like patternTransform, but works on 2D sketches not 3D solids. ```js -patternTransform2d(total_instances: u32, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch] +patternTransform2d(total_instances: integer, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch] ``` @@ -17,7 +17,7 @@ patternTransform2d(total_instances: u32, transform_function: FunctionParam, soli | Name | Type | Description | Required | |----------|------|-------------|----------| -| `total_instances` | `u32` | | Yes | +| `total_instances` | `integer` | | Yes | | `transform_function` | `FunctionParam` | | Yes | | `solid_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes | diff --git a/docs/kcl/std.json b/docs/kcl/std.json index d66b087a7d..2e71fda040 100644 --- a/docs/kcl/std.json +++ b/docs/kcl/std.json @@ -97135,7 +97135,7 @@ }, { "name": "base_curve_index", - "type": "u32", + "type": "integer", "schema": { "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", "title": "Nullable_uint32", @@ -101932,6 +101932,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -103313,6 +103338,12 @@ } } }, + "ModuleId": { + "description": "Identifier of a source file. Uses a u32 to keep the size small.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "KclNone": { "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", @@ -103931,6 +103962,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -105312,6 +105368,12 @@ } } }, + "ModuleId": { + "description": "Identifier of a source file. Uses a u32 to keep the size small.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "KclNone": { "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", @@ -105934,6 +105996,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -107315,6 +107402,12 @@ } } }, + "ModuleId": { + "description": "Identifier of a source file. Uses a u32 to keep the size small.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "KclNone": { "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", @@ -122705,7 +122798,7 @@ "args": [ { "name": "total_instances", - "type": "u32", + "type": "integer", "schema": { "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", "title": "uint32", @@ -125482,7 +125575,7 @@ "args": [ { "name": "total_instances", - "type": "u32", + "type": "integer", "schema": { "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", "title": "uint32", @@ -137258,6 +137351,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -138639,6 +138757,12 @@ } } }, + "ModuleId": { + "description": "Identifier of a source file. Uses a u32 to keep the size small.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "KclNone": { "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", @@ -139254,6 +139378,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -139869,6 +140018,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -141250,6 +141424,12 @@ } } }, + "ModuleId": { + "description": "Identifier of a source file. Uses a u32 to keep the size small.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "KclNone": { "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", @@ -141866,6 +142046,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -142508,6 +142713,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -143862,6 +144092,12 @@ } } }, + "ModuleId": { + "description": "Identifier of a source file. Uses a u32 to keep the size small.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "KclNone": { "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", @@ -144496,6 +144732,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -145877,6 +146138,12 @@ } } }, + "ModuleId": { + "description": "Identifier of a source file. Uses a u32 to keep the size small.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "KclNone": { "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", @@ -146492,6 +146759,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -147107,6 +147399,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -148488,6 +148805,12 @@ } } }, + "ModuleId": { + "description": "Identifier of a source file. Uses a u32 to keep the size small.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "KclNone": { "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", @@ -149106,6 +149429,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -150487,6 +150835,12 @@ } } }, + "ModuleId": { + "description": "Identifier of a source file. Uses a u32 to keep the size small.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "KclNone": { "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", @@ -151103,6 +151457,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -151745,6 +152124,31 @@ } } }, + { + "type": "object", + "required": [ + "__meta", + "type", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Module" + ] + }, + "value": { + "$ref": "#/components/schemas/ModuleId" + }, + "__meta": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Metadata" + } + } + } + }, { "type": "object", "required": [ @@ -153099,6 +153503,12 @@ } } }, + "ModuleId": { + "description": "Identifier of a source file. Uses a u32 to keep the size small.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, "KclNone": { "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", diff --git a/docs/kcl/types/AngledLineData.md b/docs/kcl/types/AngledLineData.md index 8b75d8b486..f49fc8c5c9 100644 --- a/docs/kcl/types/AngledLineData.md +++ b/docs/kcl/types/AngledLineData.md @@ -13,13 +13,18 @@ Data to draw an angled line. An angle and length with explicitly named parameters -[`PolarCoordsData`](/docs/kcl/types/PolarCoordsData) +**Type:** `object` +## Properties +| Property | Type | Description | Required | +|----------|------|-------------|----------| +| `angle` |`number`| The angle of the line (in degrees). | No | +| `length` |`number`| The length of the line. | No | ---- diff --git a/docs/kcl/types/KclValue.md b/docs/kcl/types/KclValue.md index f71daf6ab9..cabf63161f 100644 --- a/docs/kcl/types/KclValue.md +++ b/docs/kcl/types/KclValue.md @@ -329,6 +329,23 @@ Data for an imported geometry. +## Properties + +| Property | Type | Description | Required | +|----------|------|-------------|----------| +| `type` |enum: `Module`| | No | +| `value` |[`ModuleId`](/docs/kcl/types/ModuleId)| Any KCL value. | No | +| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No | + + +---- + +**Type:** `object` + + + + + ## Properties | Property | Type | Description | Required | diff --git a/docs/kcl/types/ModuleId.md b/docs/kcl/types/ModuleId.md new file mode 100644 index 0000000000..9ed25fcfe4 --- /dev/null +++ b/docs/kcl/types/ModuleId.md @@ -0,0 +1,16 @@ +--- +title: "ModuleId" +excerpt: "Identifier of a source file. Uses a u32 to keep the size small." +layout: manual +--- + +Identifier of a source file. Uses a u32 to keep the size small. + +**Type:** `integer` (`uint32`) + + + + + + + diff --git a/src/lang/queryAst.ts b/src/lang/queryAst.ts index 1509e843d8..08b5032da2 100644 --- a/src/lang/queryAst.ts +++ b/src/lang/queryAst.ts @@ -397,6 +397,7 @@ function moreNodePathFromSourceRange( } return path } + return path } console.error('not implemented: ' + node.type) diff --git a/src/lang/wasm.ts b/src/lang/wasm.ts index 3c2d3f6cfa..3b1ed0de65 100644 --- a/src/lang/wasm.ts +++ b/src/lang/wasm.ts @@ -259,7 +259,7 @@ export function emptyExecState(): ExecState { function execStateFromRaw(raw: RawExecState): ExecState { return { - memory: ProgramMemory.fromRaw(raw.memory), + memory: ProgramMemory.fromRaw(raw.modLocal.memory), } } diff --git a/src/wasm-lib/derive-docs/src/lib.rs b/src/wasm-lib/derive-docs/src/lib.rs index ce79611270..5a1d30c237 100644 --- a/src/wasm-lib/derive-docs/src/lib.rs +++ b/src/wasm-lib/derive-docs/src/lib.rs @@ -779,6 +779,8 @@ fn rust_type_to_openapi_type(t: &str) -> String { if t == "f64" { return "number".to_string(); + } else if t == "u32" { + return "integer".to_string(); } else if t == "str" { return "string".to_string(); } else { @@ -813,7 +815,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()).await.unwrap(); + ctx.run(program.into(), &mut crate::ExecState::new()).await.unwrap(); } #[tokio::test(flavor = "multi_thread", worker_threads = 5)] diff --git a/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen b/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen index 33132ca66c..0b93b33a85 100644 --- a/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen +++ b/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen @@ -14,7 +14,7 @@ mod test_examples_someFn { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/args_with_refs.gen b/src/wasm-lib/derive-docs/tests/args_with_refs.gen index 102fe4202b..08b80d4b21 100644 --- a/src/wasm-lib/derive-docs/tests/args_with_refs.gen +++ b/src/wasm-lib/derive-docs/tests/args_with_refs.gen @@ -14,7 +14,7 @@ mod test_examples_someFn { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/array.gen b/src/wasm-lib/derive-docs/tests/array.gen index 7769ef97ab..b4016e3b5f 100644 --- a/src/wasm-lib/derive-docs/tests/array.gen +++ b/src/wasm-lib/derive-docs/tests/array.gen @@ -15,7 +15,7 @@ mod test_examples_show { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } @@ -52,7 +52,7 @@ mod test_examples_show { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/box.gen b/src/wasm-lib/derive-docs/tests/box.gen index 88f22ad596..fc220c90d6 100644 --- a/src/wasm-lib/derive-docs/tests/box.gen +++ b/src/wasm-lib/derive-docs/tests/box.gen @@ -15,7 +15,7 @@ mod test_examples_show { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen b/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen index a2e12d706d..01292dabd5 100644 --- a/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen +++ b/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen @@ -16,7 +16,7 @@ mod test_examples_my_func { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } @@ -53,7 +53,7 @@ mod test_examples_my_func { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/lineTo.gen b/src/wasm-lib/derive-docs/tests/lineTo.gen index c486ebe695..52b909dcad 100644 --- a/src/wasm-lib/derive-docs/tests/lineTo.gen +++ b/src/wasm-lib/derive-docs/tests/lineTo.gen @@ -16,7 +16,7 @@ mod test_examples_line_to { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } @@ -53,7 +53,7 @@ mod test_examples_line_to { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/min.gen b/src/wasm-lib/derive-docs/tests/min.gen index 1f264dd5c2..f42a101295 100644 --- a/src/wasm-lib/derive-docs/tests/min.gen +++ b/src/wasm-lib/derive-docs/tests/min.gen @@ -15,7 +15,7 @@ mod test_examples_min { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } @@ -52,7 +52,7 @@ mod test_examples_min { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/option.gen b/src/wasm-lib/derive-docs/tests/option.gen index 22d5b76f8f..edbec4ca4c 100644 --- a/src/wasm-lib/derive-docs/tests/option.gen +++ b/src/wasm-lib/derive-docs/tests/option.gen @@ -15,7 +15,7 @@ mod test_examples_show { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/option_input_format.gen b/src/wasm-lib/derive-docs/tests/option_input_format.gen index 014bbabc52..26f56a3abc 100644 --- a/src/wasm-lib/derive-docs/tests/option_input_format.gen +++ b/src/wasm-lib/derive-docs/tests/option_input_format.gen @@ -15,7 +15,7 @@ mod test_examples_import { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen b/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen index 41c376e56d..31c5155ff5 100644 --- a/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen +++ b/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen @@ -15,7 +15,7 @@ mod test_examples_import { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen b/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen index 3156e6db92..2f2d87f1c6 100644 --- a/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen +++ b/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen @@ -15,7 +15,7 @@ mod test_examples_import { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/show.gen b/src/wasm-lib/derive-docs/tests/show.gen index a5fde2bc57..8ca23679da 100644 --- a/src/wasm-lib/derive-docs/tests/show.gen +++ b/src/wasm-lib/derive-docs/tests/show.gen @@ -15,7 +15,7 @@ mod test_examples_show { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen b/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen index e9f681ab84..428784f1e9 100644 --- a/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen +++ b/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen @@ -14,7 +14,7 @@ mod test_examples_some_function { settings: Default::default(), context_type: crate::execution::ContextType::Mock, }; - ctx.run(program.into(), &mut crate::ExecState::default()) + ctx.run(program.into(), &mut crate::ExecState::new()) .await .unwrap(); } diff --git a/src/wasm-lib/kcl-test-server/src/lib.rs b/src/wasm-lib/kcl-test-server/src/lib.rs index dc7c34244d..35de21defc 100644 --- a/src/wasm-lib/kcl-test-server/src/lib.rs +++ b/src/wasm-lib/kcl-test-server/src/lib.rs @@ -164,7 +164,7 @@ async fn snapshot_endpoint(body: Bytes, state: ExecutorContext) -> Response Result { let ctx = ExecutorContext::new_forwarded_mock(Arc::new(Box::new( crate::conn_mock_core::EngineConnection::new(ref_result).await?, ))); - ctx.run(program.into(), &mut ExecState::default()).await?; + ctx.run(program.into(), &mut ExecState::new()).await?; let result = result.lock().expect("mutex lock").clone(); Ok(result) diff --git a/src/wasm-lib/kcl/src/docs/gen_std_tests.rs b/src/wasm-lib/kcl/src/docs/gen_std_tests.rs index 2024724e3e..b110293a2d 100644 --- a/src/wasm-lib/kcl/src/docs/gen_std_tests.rs +++ b/src/wasm-lib/kcl/src/docs/gen_std_tests.rs @@ -604,24 +604,6 @@ fn clean_function_name(name: &str) -> String { fn_name } -/// Check if a schema is the same as another schema, but don't check the description. -fn is_same_schema(sa: &schemars::schema::Schema, sb: &schemars::schema::Schema) -> bool { - let schemars::schema::Schema::Object(a) = sa else { - return sa == sb; - }; - - let schemars::schema::Schema::Object(b) = sb else { - return sa == sb; - }; - - let mut a = a.clone(); - a.metadata = None; - let mut b = b.clone(); - b.metadata = None; - - a == b -} - /// Recursively create references for types we already know about. fn recurse_and_create_references( name: &str, @@ -655,24 +637,6 @@ fn recurse_and_create_references( return Ok(schemars::schema::Schema::Object(obj)); } - // Check if this is the type we already know about. - for (n, s) in types { - if is_same_schema(schema, s) && name != n && !n.starts_with("[") { - // Return a reference to the type. - let sref = schemars::schema::Schema::new_ref(n.to_string()); - // Add the existing metadata to the reference. - let schemars::schema::Schema::Object(ro) = sref else { - return Err(anyhow::anyhow!( - "Failed to get object schema, should have not been a primitive" - )); - }; - let mut ro = ro.clone(); - ro.metadata = o.metadata.clone(); - - return Ok(schemars::schema::Schema::Object(ro)); - } - } - let mut obj = o.clone(); // If we have an object iterate over the properties and recursively create references. diff --git a/src/wasm-lib/kcl/src/execution/exec_ast.rs b/src/wasm-lib/kcl/src/execution/exec_ast.rs index e484517104..e9fd2c1445 100644 --- a/src/wasm-lib/kcl/src/execution/exec_ast.rs +++ b/src/wasm-lib/kcl/src/execution/exec_ast.rs @@ -29,7 +29,7 @@ impl BinaryPart { match self { BinaryPart::Literal(literal) => Ok(literal.into()), BinaryPart::Identifier(identifier) => { - let value = exec_state.memory.get(&identifier.name, identifier.into())?; + let value = exec_state.memory().get(&identifier.name, identifier.into())?; Ok(value.clone()) } BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await, @@ -47,7 +47,7 @@ impl Node { let array = match &self.object { MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?, MemberObject::Identifier(identifier) => { - let value = exec_state.memory.get(&identifier.name, identifier.into())?; + let value = exec_state.memory().get(&identifier.name, identifier.into())?; value.clone() } }; @@ -75,7 +75,7 @@ impl Node { // TODO: Don't use recursion here, use a loop. MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?, MemberObject::Identifier(identifier) => { - let value = exec_state.memory.get(&identifier.name, identifier.into())?; + let value = exec_state.memory().get(&identifier.name, identifier.into())?; value.clone() } }; @@ -310,11 +310,11 @@ pub(crate) async fn execute_pipe_body( // Now that we've evaluated the first child expression in the pipeline, following child expressions // should use the previous child expression for %. // This means there's no more need for the previous pipe_value from the parent AST node above this one. - let previous_pipe_value = std::mem::replace(&mut exec_state.pipe_value, Some(output)); + let previous_pipe_value = std::mem::replace(&mut exec_state.mod_local.pipe_value, Some(output)); // Evaluate remaining elements. let result = inner_execute_pipe_body(exec_state, body, ctx).await; // Restore the previous pipe value. - exec_state.pipe_value = previous_pipe_value; + exec_state.mod_local.pipe_value = previous_pipe_value; result } @@ -340,10 +340,10 @@ async fn inner_execute_pipe_body( let output = ctx .execute_expr(expression, exec_state, &metadata, StatementKind::Expression) .await?; - exec_state.pipe_value = Some(output); + exec_state.mod_local.pipe_value = Some(output); } // Safe to unwrap here, because pipe_value always has something pushed in when the `match first` executes. - let final_output = exec_state.pipe_value.take().unwrap(); + let final_output = exec_state.mod_local.pipe_value.take().unwrap(); Ok(final_output) } @@ -417,7 +417,7 @@ impl Node { // before running, and we will likely want to use the // return value. The call takes ownership of the args, // so we need to build the op before the call. - exec_state.operations.push(op); + exec_state.mod_local.operations.push(op); } result }; @@ -431,8 +431,8 @@ impl Node { let source_range = SourceRange::from(self); // Clone the function so that we can use a mutable reference to // exec_state. - let func = exec_state.memory.get(fn_name, source_range)?.clone(); - let fn_dynamic_state = exec_state.dynamic_state.merge(&exec_state.memory); + let func = exec_state.memory().get(fn_name, source_range)?.clone(); + let fn_dynamic_state = exec_state.mod_local.dynamic_state.merge(exec_state.memory()); // Track call operation. let op_labeled_args = args @@ -441,16 +441,20 @@ impl Node { .iter() .map(|(k, v)| (k.clone(), OpArg::new(v.source_range))) .collect(); - exec_state.operations.push(Operation::UserDefinedFunctionCall { - name: Some(fn_name.clone()), - function_source_range: func.function_def_source_range().unwrap_or_default(), - unlabeled_arg: args.kw_args.unlabeled.as_ref().map(|arg| OpArg::new(arg.source_range)), - labeled_args: op_labeled_args, - source_range: callsite, - }); + exec_state + .mod_local + .operations + .push(Operation::UserDefinedFunctionCall { + name: Some(fn_name.clone()), + function_source_range: func.function_def_source_range().unwrap_or_default(), + unlabeled_arg: args.kw_args.unlabeled.as_ref().map(|arg| OpArg::new(arg.source_range)), + labeled_args: op_labeled_args, + source_range: callsite, + }); let return_value = { - let previous_dynamic_state = std::mem::replace(&mut exec_state.dynamic_state, fn_dynamic_state); + let previous_dynamic_state = + std::mem::replace(&mut exec_state.mod_local.dynamic_state, fn_dynamic_state); let result = func .call_fn_kw(args, exec_state, ctx.clone(), callsite) .await @@ -459,7 +463,7 @@ impl Node { // TODO currently ignored by the frontend e.add_source_ranges(vec![source_range]) }); - exec_state.dynamic_state = previous_dynamic_state; + exec_state.mod_local.dynamic_state = previous_dynamic_state; result? }; @@ -476,7 +480,10 @@ impl Node { })?; // Track return operation. - exec_state.operations.push(Operation::UserDefinedFunctionReturn); + exec_state + .mod_local + .operations + .push(Operation::UserDefinedFunctionReturn); Ok(result) } @@ -537,7 +544,7 @@ impl Node { // before running, and we will likely want to use the // return value. The call takes ownership of the args, // so we need to build the op before the call. - exec_state.operations.push(op); + exec_state.mod_local.operations.push(op); } result }; @@ -551,27 +558,31 @@ impl Node { let source_range = SourceRange::from(self); // Clone the function so that we can use a mutable reference to // exec_state. - let func = exec_state.memory.get(fn_name, source_range)?.clone(); - let fn_dynamic_state = exec_state.dynamic_state.merge(&exec_state.memory); + let func = exec_state.memory().get(fn_name, source_range)?.clone(); + let fn_dynamic_state = exec_state.mod_local.dynamic_state.merge(exec_state.memory()); // Track call operation. - exec_state.operations.push(Operation::UserDefinedFunctionCall { - name: Some(fn_name.clone()), - function_source_range: func.function_def_source_range().unwrap_or_default(), - unlabeled_arg: None, - // TODO: Add the arguments for legacy positional parameters. - labeled_args: Default::default(), - source_range: callsite, - }); + exec_state + .mod_local + .operations + .push(Operation::UserDefinedFunctionCall { + name: Some(fn_name.clone()), + function_source_range: func.function_def_source_range().unwrap_or_default(), + unlabeled_arg: None, + // TODO: Add the arguments for legacy positional parameters. + labeled_args: Default::default(), + source_range: callsite, + }); let return_value = { - let previous_dynamic_state = std::mem::replace(&mut exec_state.dynamic_state, fn_dynamic_state); + let previous_dynamic_state = + std::mem::replace(&mut exec_state.mod_local.dynamic_state, fn_dynamic_state); let result = func.call_fn(fn_args, exec_state, ctx.clone()).await.map_err(|e| { // Add the call expression to the source ranges. // TODO currently ignored by the frontend e.add_source_ranges(vec![source_range]) }); - exec_state.dynamic_state = previous_dynamic_state; + exec_state.mod_local.dynamic_state = previous_dynamic_state; result? }; @@ -588,7 +599,10 @@ impl Node { })?; // Track return operation. - exec_state.operations.push(Operation::UserDefinedFunctionReturn); + exec_state + .mod_local + .operations + .push(Operation::UserDefinedFunctionReturn); Ok(result) } @@ -604,7 +618,7 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex match result { KclValue::Sketch { value: ref mut sketch } => { for (_, tag) in sketch.tags.iter() { - exec_state.memory.update_tag(&tag.value, tag.clone())?; + exec_state.mut_memory().update_tag(&tag.value, tag.clone())?; } } KclValue::Solid(ref mut solid) => { @@ -642,7 +656,7 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex info.sketch = solid.id; t.info = Some(info); - exec_state.memory.update_tag(&tag.name, t.clone())?; + exec_state.mut_memory().update_tag(&tag.name, t.clone())?; // update the sketch tags. solid.sketch.tags.insert(tag.name.clone(), t); @@ -650,11 +664,8 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex } // Find the stale sketch in memory and update it. - if let Some(current_env) = exec_state - .memory - .environments - .get_mut(exec_state.memory.current_env.index()) - { + let cur_env_index = exec_state.memory().current_env.index(); + if let Some(current_env) = exec_state.mut_memory().environments.get_mut(cur_env_index) { current_env.update_sketch_tags(&solid.sketch); } } @@ -673,7 +684,9 @@ impl Node { }], })); - exec_state.memory.add(&self.name, memory_item.clone(), self.into())?; + exec_state + .mut_memory() + .add(&self.name, memory_item.clone(), self.into())?; Ok(self.into()) } @@ -868,7 +881,7 @@ impl Property { Ok(Property::String(name.to_string())) } else { // Actually evaluate memory to compute the property. - let prop = exec_state.memory.get(name, property_src)?; + let prop = exec_state.memory().get(name, property_src)?; jvalue_to_prop(prop, property_sr, name) } } diff --git a/src/wasm-lib/kcl/src/execution/kcl_value.rs b/src/wasm-lib/kcl/src/execution/kcl_value.rs index 7a3d1648c3..756dffb29d 100644 --- a/src/wasm-lib/kcl/src/execution/kcl_value.rs +++ b/src/wasm-lib/kcl/src/execution/kcl_value.rs @@ -10,7 +10,7 @@ use crate::{ execution::{Face, ImportedGeometry, MemoryFunction, Metadata, Plane, SketchSet, Solid, SolidSet, TagIdentifier}, parsing::ast::types::{FunctionExpression, KclNone, LiteralValue, TagDeclarator, TagNode}, std::{args::Arg, FnAsArg}, - ExecState, ExecutorContext, KclError, SourceRange, + ExecState, ExecutorContext, KclError, ModuleId, SourceRange, }; pub type KclObjectFields = HashMap; @@ -84,6 +84,11 @@ pub enum KclValue { #[serde(rename = "__meta")] meta: Vec, }, + Module { + value: ModuleId, + #[serde(rename = "__meta")] + meta: Vec, + }, KclNone { value: KclNone, #[serde(rename = "__meta")] @@ -143,6 +148,7 @@ impl From for Vec { KclValue::String { meta, .. } => to_vec_sr(&meta), KclValue::Array { meta, .. } => to_vec_sr(&meta), KclValue::Object { meta, .. } => to_vec_sr(&meta), + KclValue::Module { meta, .. } => to_vec_sr(&meta), KclValue::Uuid { meta, .. } => to_vec_sr(&meta), KclValue::KclNone { meta, .. } => to_vec_sr(&meta), } @@ -173,6 +179,7 @@ impl From<&KclValue> for Vec { KclValue::Uuid { meta, .. } => to_vec_sr(meta), KclValue::Array { meta, .. } => to_vec_sr(meta), KclValue::Object { meta, .. } => to_vec_sr(meta), + KclValue::Module { meta, .. } => to_vec_sr(meta), KclValue::KclNone { meta, .. } => to_vec_sr(meta), } } @@ -198,6 +205,7 @@ impl KclValue { KclValue::Solids { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(), KclValue::ImportedGeometry(x) => x.meta.clone(), KclValue::Function { meta, .. } => meta.clone(), + KclValue::Module { meta, .. } => meta.clone(), KclValue::KclNone { meta, .. } => meta.clone(), } } @@ -263,6 +271,7 @@ impl KclValue { KclValue::String { .. } => "string (text)", KclValue::Array { .. } => "array (list)", KclValue::Object { .. } => "object", + KclValue::Module { .. } => "module", KclValue::KclNone { .. } => "None", } } diff --git a/src/wasm-lib/kcl/src/execution/mod.rs b/src/wasm-lib/kcl/src/execution/mod.rs index be8a231b15..b1b3c12b42 100644 --- a/src/wasm-lib/kcl/src/execution/mod.rs +++ b/src/wasm-lib/kcl/src/execution/mod.rs @@ -22,6 +22,7 @@ type Point3D = kcmc::shared::Point3d; pub use function_param::FunctionParam; pub use kcl_value::{KclObjectFields, KclValue}; +use uuid::Uuid; pub(crate) mod cache; mod cad_op; @@ -35,7 +36,8 @@ use crate::{ execution::cache::{CacheInformation, CacheResult}, fs::{FileManager, FileSystem}, parsing::ast::types::{ - BodyItem, Expr, FunctionExpression, ImportSelector, ItemVisibility, Node, NodeRef, TagDeclarator, TagNode, + BodyItem, Expr, FunctionExpression, ImportSelector, ItemVisibility, Node, NodeRef, Program as AstProgram, + TagDeclarator, TagNode, }, settings::types::UnitLength, source_range::{ModuleId, SourceRange}, @@ -47,14 +49,32 @@ use crate::{ pub use cad_op::Operation; /// State for executing a program. -#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[ts(export)] #[serde(rename_all = "camelCase")] pub struct ExecState { - /// Program variable bindings. - pub memory: ProgramMemory, + pub global: GlobalState, + pub mod_local: ModuleState, +} + +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +pub struct GlobalState { /// The stable artifact ID generator. pub id_generator: IdGenerator, + /// Map from source file absolute path to module ID. + pub path_to_source_id: IndexMap, + /// Map from module ID to module info. + pub module_infos: IndexMap, +} + +#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +pub struct ModuleState { + /// Program variable bindings. + pub memory: ProgramMemory, /// Dynamic state that follows dynamic flow of the program. pub dynamic_state: DynamicState, /// The current value of the pipe operator returned from the previous @@ -65,29 +85,104 @@ pub struct ExecState { /// The stack of import statements for detecting circular module imports. /// If this is empty, we're not currently executing an import statement. pub import_stack: Vec, - /// Map from source file absolute path to module ID. - pub path_to_source_id: IndexMap, - /// Map from module ID to module info. - pub module_infos: IndexMap, /// Operations that have been performed in execution order, for display in /// the Feature Tree. pub operations: Vec, } +impl Default for ExecState { + fn default() -> Self { + Self::new() + } +} + impl ExecState { - fn add_module(&mut self, path: std::path::PathBuf) -> ModuleId { + pub fn new() -> Self { + ExecState { + global: GlobalState::new(), + mod_local: ModuleState::default(), + } + } + + fn reset(&mut self) { + let mut id_generator = self.global.id_generator.clone(); + // We do not pop the ids, since we want to keep the same id generator. + // This is for the front end to keep track of the ids. + id_generator.next_id = 0; + + let mut global = GlobalState::new(); + global.id_generator = id_generator; + + *self = ExecState { + global, + mod_local: ModuleState::default(), + }; + } + + pub fn memory(&self) -> &ProgramMemory { + &self.mod_local.memory + } + + pub fn mut_memory(&mut self) -> &mut ProgramMemory { + &mut self.mod_local.memory + } + + pub fn next_uuid(&mut self) -> Uuid { + self.global.id_generator.next_uuid() + } + + async fn add_module( + &mut self, + path: std::path::PathBuf, + ctxt: &ExecutorContext, + source_range: SourceRange, + ) -> Result { // Need to avoid borrowing self in the closure. - let new_module_id = ModuleId::from_usize(self.path_to_source_id.len()); + let new_module_id = ModuleId::from_usize(self.global.path_to_source_id.len()); let mut is_new = false; - let id = *self.path_to_source_id.entry(path.clone()).or_insert_with(|| { + let id = *self.global.path_to_source_id.entry(path.clone()).or_insert_with(|| { is_new = true; new_module_id }); + if is_new { - let module_info = ModuleInfo { id, path }; - self.module_infos.insert(id, module_info); + let source = ctxt.fs.read_to_string(&path, source_range).await?; + // TODO handle parsing errors properly + let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err()?; + + let module_info = ModuleInfo { + id, + path, + parsed: Some(parsed), + }; + self.global.module_infos.insert(id, module_info); } - id + + Ok(id) + } +} + +impl GlobalState { + fn new() -> Self { + let mut global = GlobalState { + id_generator: Default::default(), + path_to_source_id: Default::default(), + module_infos: Default::default(), + }; + + // TODO(#4434): Use the top-level file's path. + let root_path = PathBuf::new(); + let root_id = ModuleId::default(); + global.module_infos.insert( + root_id, + ModuleInfo { + id: root_id, + path: root_path.clone(), + parsed: None, + }, + ); + global.path_to_source_id.insert(root_path, root_id); + global } } @@ -159,6 +254,13 @@ impl ProgramMemory { })) } + /// Returns all bindings in the current scope. + #[allow(dead_code)] + fn get_all_cur_scope(&self) -> IndexMap { + let env = &self.environments[self.current_env.index()]; + env.bindings.clone() + } + /// Find all solids in the memory that are on a specific sketch id. /// This does not look inside closures. But as long as we do not allow /// mutation of variables in KCL, closure memory should be a subset of this. @@ -274,18 +376,14 @@ pub struct DynamicState { } impl DynamicState { - pub fn new() -> Self { - Self::default() - } - #[must_use] - pub fn merge(&self, memory: &ProgramMemory) -> Self { + fn merge(&self, memory: &ProgramMemory) -> Self { let mut merged = self.clone(); merged.append(memory); merged } - pub fn append(&mut self, memory: &ProgramMemory) { + fn append(&mut self, memory: &ProgramMemory) { for env in &memory.environments { for item in env.bindings.values() { if let KclValue::Solid(eg) = item { @@ -295,7 +393,7 @@ impl DynamicState { } } - pub fn edge_cut_ids_on_sketch(&self, sketch_id: uuid::Uuid) -> Vec { + pub(crate) fn edge_cut_ids_on_sketch(&self, sketch_id: uuid::Uuid) -> Vec { self.solid_ids .iter() .flat_map(|eg| { @@ -553,7 +651,7 @@ pub struct Plane { impl Plane { pub(crate) fn from_plane_data(value: crate::std::sketch::PlaneData, exec_state: &mut ExecState) -> Self { - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.global.id_generator.next_uuid(); match value { crate::std::sketch::PlaneData::XY => Plane { id, @@ -1001,13 +1099,14 @@ pub enum BodyType { /// Info about a module. Right now, this is pretty minimal. We hope to cache /// modules here in the future. -#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)] #[ts(export)] pub struct ModuleInfo { /// The ID of the module. id: ModuleId, /// Absolute path of the module's source file. path: std::path::PathBuf, + parsed: Option>, } #[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy, ts_rs::TS, JsonSchema)] @@ -1796,7 +1895,7 @@ impl ExecutorContext { source_range: crate::execution::SourceRange, ) -> Result<(), KclError> { self.engine - .clear_scene(&mut exec_state.id_generator, source_range) + .clear_scene(&mut exec_state.global.id_generator, source_range) .await?; // We do not create the planes here as the post hook in wasm will do that @@ -1897,23 +1996,13 @@ impl ExecutorContext { if cache_result.clear_scene && !self.is_mock() { // Pop the execution state, since we are starting fresh. - let mut id_generator = exec_state.id_generator.clone(); - // We do not pop the ids, since we want to keep the same id generator. - // This is for the front end to keep track of the ids. - id_generator.next_id = 0; - *exec_state = ExecState { - id_generator, - ..Default::default() - }; + exec_state.reset(); // We don't do this in mock mode since there is no engine connection // anyways and from the TS side we override memory and don't want to clear it. self.reset_scene(exec_state, Default::default()).await?; } - // TODO: Use the top-level file's path. - exec_state.add_module(std::path::PathBuf::from("")); - // Re-apply the settings, in case the cache was busted. self.engine.reapply_settings(&self.settings, Default::default()).await?; @@ -1937,11 +2026,13 @@ impl ExecutorContext { match statement { BodyItem::ImportStatement(import_stmt) => { let source_range = SourceRange::from(import_stmt); - let (module_memory, module_exports) = - self.open_module(&import_stmt.path, exec_state, source_range).await?; + let module_id = self.open_module(&import_stmt.path, exec_state, source_range).await?; match &import_stmt.selector { ImportSelector::List { items } => { + let (_, module_memory, module_exports) = self + .exec_module(module_id, exec_state, ExecutionKind::Isolated, source_range) + .await?; for import_item in items { // Extract the item from the module. let item = @@ -1965,18 +2056,24 @@ impl ExecutorContext { } // Add the item to the current module. - exec_state.memory.add( + exec_state.mut_memory().add( import_item.identifier(), item.clone(), SourceRange::from(&import_item.name), )?; if let ItemVisibility::Export = import_stmt.visibility { - exec_state.module_exports.push(import_item.identifier().to_owned()); + exec_state + .mod_local + .module_exports + .push(import_item.identifier().to_owned()); } } } ImportSelector::Glob(_) => { + let (_, module_memory, module_exports) = self + .exec_module(module_id, exec_state, ExecutionKind::Isolated, source_range) + .await?; for name in module_exports.iter() { let item = module_memory.get(name, source_range).map_err(|_err| { KclError::Internal(KclErrorDetails { @@ -1984,18 +2081,20 @@ impl ExecutorContext { source_ranges: vec![source_range], }) })?; - exec_state.memory.add(name, item.clone(), source_range)?; + exec_state.mut_memory().add(name, item.clone(), source_range)?; if let ItemVisibility::Export = import_stmt.visibility { - exec_state.module_exports.push(name.clone()); + exec_state.mod_local.module_exports.push(name.clone()); } } } - ImportSelector::None(_) => { - return Err(KclError::Semantic(KclErrorDetails { - message: "Importing whole module is not yet implemented, sorry.".to_owned(), - source_ranges: vec![source_range], - })); + ImportSelector::None { .. } => { + let name = import_stmt.module_name().unwrap(); + let item = KclValue::Module { + value: module_id, + meta: vec![source_range.into()], + }; + exec_state.mut_memory().add(&name, item, source_range)?; } } last_expr = None; @@ -2025,11 +2124,11 @@ impl ExecutorContext { StatementKind::Declaration { name: &var_name }, ) .await?; - exec_state.memory.add(&var_name, memory_item, source_range)?; + exec_state.mut_memory().add(&var_name, memory_item, source_range)?; // Track exports. if let ItemVisibility::Export = variable_declaration.visibility { - exec_state.module_exports.push(var_name); + exec_state.mod_local.module_exports.push(var_name); } last_expr = None; } @@ -2043,7 +2142,7 @@ impl ExecutorContext { StatementKind::Expression, ) .await?; - exec_state.memory.return_ = Some(value); + exec_state.mut_memory().return_ = Some(value); last_expr = None; } } @@ -2069,18 +2168,19 @@ impl ExecutorContext { path: &str, exec_state: &mut ExecState, source_range: SourceRange, - ) -> Result<(ProgramMemory, Vec), KclError> { + ) -> Result { let resolved_path = if let Some(project_dir) = &self.settings.project_directory { project_dir.join(path) } else { std::path::PathBuf::from(&path) }; - if exec_state.import_stack.contains(&resolved_path) { + if exec_state.mod_local.import_stack.contains(&resolved_path) { return Err(KclError::ImportCycle(KclErrorDetails { message: format!( "circular import of modules is not allowed: {} -> {}", exec_state + .mod_local .import_stack .iter() .map(|p| p.as_path().to_string_lossy()) @@ -2091,31 +2191,44 @@ impl ExecutorContext { source_ranges: vec![source_range], })); } - let module_id = exec_state.add_module(resolved_path.clone()); - let source = self.fs.read_to_string(&resolved_path, source_range).await?; - // TODO handle parsing errors properly - let program = crate::parsing::parse_str(&source, module_id).parse_errs_as_err()?; + exec_state.add_module(resolved_path.clone(), self, source_range).await + } + + async fn exec_module( + &self, + module_id: ModuleId, + exec_state: &mut ExecState, + exec_kind: ExecutionKind, + source_range: SourceRange, + ) -> Result<(Option, ProgramMemory, Vec), KclError> { + // TODO It sucks that we have to clone the whole module AST here + let info = exec_state.global.module_infos[&module_id].clone(); - exec_state.import_stack.push(resolved_path.clone()); - let original_execution = self.engine.replace_execution_kind(ExecutionKind::Isolated); - let original_memory = std::mem::take(&mut exec_state.memory); - let original_exports = std::mem::take(&mut exec_state.module_exports); + let mut local_state = ModuleState { + import_stack: exec_state.mod_local.import_stack.clone(), + ..Default::default() + }; + local_state.import_stack.push(info.path.clone()); + std::mem::swap(&mut exec_state.mod_local, &mut local_state); + let original_execution = self.engine.replace_execution_kind(exec_kind); + + // The unwrap here is safe since we only elide the AST for the top module. let result = self - .inner_execute(&program, exec_state, crate::execution::BodyType::Root) + .inner_execute(&info.parsed.unwrap(), exec_state, crate::execution::BodyType::Root) .await; - let module_exports = std::mem::replace(&mut exec_state.module_exports, original_exports); - let module_memory = std::mem::replace(&mut exec_state.memory, original_memory); + + std::mem::swap(&mut exec_state.mod_local, &mut local_state); self.engine.replace_execution_kind(original_execution); - exec_state.import_stack.pop(); - result.map_err(|err| { + let result = result.map_err(|err| { if let KclError::ImportCycle(_) = err { // It was an import cycle. Keep the original message. err.override_source_ranges(vec![source_range]) } else { KclError::Semantic(KclErrorDetails { message: format!( - "Error loading imported file. Open it to view more details. {path}: {}", + "Error loading imported file. Open it to view more details. {}: {}", + info.path.display(), err.message() ), source_ranges: vec![source_range], @@ -2123,7 +2236,7 @@ impl ExecutorContext { } })?; - Ok((module_memory, module_exports)) + Ok((result, local_state.memory, local_state.module_exports)) } #[async_recursion] @@ -2139,8 +2252,23 @@ impl ExecutorContext { Expr::Literal(literal) => KclValue::from(literal), Expr::TagDeclarator(tag) => tag.execute(exec_state).await?, Expr::Identifier(identifier) => { - let value = exec_state.memory.get(&identifier.name, identifier.into())?; - value.clone() + let value = exec_state.memory().get(&identifier.name, identifier.into())?.clone(); + if let KclValue::Module { value: module_id, meta } = value { + let (result, _, _) = self + .exec_module(module_id, exec_state, ExecutionKind::Normal, metadata.source_range) + .await?; + result.ok_or_else(|| { + KclError::Semantic(KclErrorDetails { + message: format!( + "Evaluating module `{}` as part of an assembly did not produce a result", + identifier.name + ), + source_ranges: vec![metadata.source_range, meta[0].source_range], + }) + })? + } else { + value + } } Expr::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, self).await?, Expr::FunctionExpression(function_expression) => { @@ -2151,7 +2279,7 @@ impl ExecutorContext { expression: function_expression.clone(), meta: vec![metadata.to_owned()], func: None, - memory: Box::new(exec_state.memory.clone()), + memory: Box::new(exec_state.memory().clone()), } } Expr::CallExpression(call_expression) => call_expression.execute(exec_state, self).await?, @@ -2168,7 +2296,7 @@ impl ExecutorContext { source_ranges: vec![pipe_substitution.into()], })); } - StatementKind::Expression => match exec_state.pipe_value.clone() { + StatementKind::Expression => match exec_state.mod_local.pipe_value.clone() { Some(x) => x, None => { return Err(KclError::Semantic(KclErrorDetails { @@ -2188,7 +2316,9 @@ impl ExecutorContext { let result = self .execute_expr(&expr.expr, exec_state, metadata, statement_kind) .await?; - exec_state.memory.add(&expr.label.name, result.clone(), init.into())?; + exec_state + .mut_memory() + .add(&expr.label.name, result.clone(), init.into())?; // TODO this lets us use the label as a variable name, but not as a tag in most cases result } @@ -2373,12 +2503,12 @@ pub(crate) async fn call_user_defined_function( // Execute the function body using the memory we just created. let (result, fn_memory) = { - let previous_memory = std::mem::replace(&mut exec_state.memory, fn_memory); + let previous_memory = std::mem::replace(&mut exec_state.mod_local.memory, fn_memory); let result = ctx .inner_execute(&function_expression.body, exec_state, BodyType::Block) .await; // Restore the previous memory. - let fn_memory = std::mem::replace(&mut exec_state.memory, previous_memory); + let fn_memory = std::mem::replace(&mut exec_state.mod_local.memory, previous_memory); (result, fn_memory) }; @@ -2403,12 +2533,12 @@ pub(crate) async fn call_user_defined_function_kw( // Execute the function body using the memory we just created. let (result, fn_memory) = { - let previous_memory = std::mem::replace(&mut exec_state.memory, fn_memory); + let previous_memory = std::mem::replace(&mut exec_state.mod_local.memory, fn_memory); let result = ctx .inner_execute(&function_expression.body, exec_state, BodyType::Block) .await; // Restore the previous memory. - let fn_memory = std::mem::replace(&mut exec_state.memory, previous_memory); + let fn_memory = std::mem::replace(&mut exec_state.mod_local.memory, previous_memory); (result, fn_memory) }; @@ -2433,7 +2563,7 @@ mod tests { OldAstState, }; - pub async fn parse_execute(code: &str) -> Result<(Program, ExecutorContext, ExecState)> { + async fn parse_execute(code: &str) -> Result<(Program, ExecutorContext, ExecState)> { let program = Program::parse_no_errs(code)?; let ctx = ExecutorContext { @@ -2450,6 +2580,7 @@ mod tests { } /// Convenience function to get a JSON value from memory and unwrap. + #[track_caller] fn mem_get_json(memory: &ProgramMemory, name: &str) -> KclValue { memory.get(name, SourceRange::default()).unwrap().to_owned() } @@ -2880,21 +3011,21 @@ let shape = layer() |> patternTransform(10, transform, %) async fn test_math_execute_with_functions() { let ast = r#"const myVar = 2 + min(100, -1 + legLen(5, 3))"#; let (_, _, exec_state) = parse_execute(ast).await.unwrap(); - assert_eq!(5.0, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap()); + assert_eq!(5.0, mem_get_json(exec_state.memory(), "myVar").as_f64().unwrap()); } #[tokio::test(flavor = "multi_thread")] async fn test_math_execute() { let ast = r#"const myVar = 1 + 2 * (3 - 4) / -5 + 6"#; let (_, _, exec_state) = parse_execute(ast).await.unwrap(); - assert_eq!(7.4, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap()); + assert_eq!(7.4, mem_get_json(exec_state.memory(), "myVar").as_f64().unwrap()); } #[tokio::test(flavor = "multi_thread")] async fn test_math_execute_start_negative() { let ast = r#"const myVar = -5 + 6"#; let (_, _, exec_state) = parse_execute(ast).await.unwrap(); - assert_eq!(1.0, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap()); + assert_eq!(1.0, mem_get_json(exec_state.memory(), "myVar").as_f64().unwrap()); } #[tokio::test(flavor = "multi_thread")] @@ -2903,7 +3034,7 @@ let shape = layer() |> patternTransform(10, transform, %) let (_, _, exec_state) = parse_execute(ast).await.unwrap(); assert_eq!( std::f64::consts::TAU, - mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap() + mem_get_json(exec_state.memory(), "myVar").as_f64().unwrap() ); } @@ -2911,7 +3042,7 @@ let shape = layer() |> patternTransform(10, transform, %) async fn test_math_define_decimal_without_leading_zero() { let ast = r#"let thing = .4 + 7"#; let (_, _, exec_state) = parse_execute(ast).await.unwrap(); - assert_eq!(7.4, mem_get_json(&exec_state.memory, "thing").as_f64().unwrap()); + assert_eq!(7.4, mem_get_json(exec_state.memory(), "thing").as_f64().unwrap()); } #[tokio::test(flavor = "multi_thread")] @@ -2951,10 +3082,10 @@ fn check = (x) => { check(false) "#; let (_, _, exec_state) = parse_execute(ast).await.unwrap(); - assert_eq!(false, mem_get_json(&exec_state.memory, "notTrue").as_bool().unwrap()); - assert_eq!(true, mem_get_json(&exec_state.memory, "notFalse").as_bool().unwrap()); - assert_eq!(true, mem_get_json(&exec_state.memory, "c").as_bool().unwrap()); - assert_eq!(false, mem_get_json(&exec_state.memory, "d").as_bool().unwrap()); + assert_eq!(false, mem_get_json(exec_state.memory(), "notTrue").as_bool().unwrap()); + assert_eq!(true, mem_get_json(exec_state.memory(), "notFalse").as_bool().unwrap()); + assert_eq!(true, mem_get_json(exec_state.memory(), "c").as_bool().unwrap()); + assert_eq!(false, mem_get_json(exec_state.memory(), "d").as_bool().unwrap()); } #[tokio::test(flavor = "multi_thread")] @@ -3635,4 +3766,65 @@ shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#; assert_eq!(result, None); } + + #[tokio::test(flavor = "multi_thread")] + async fn kcl_test_ids_stable_between_executions() { + let code = r#"sketch001 = startSketchOn('XZ') +|> startProfileAt([61.74, 206.13], %) +|> xLine(305.11, %, $seg01) +|> yLine(-291.85, %) +|> xLine(-segLen(seg01), %) +|> lineTo([profileStartX(%), profileStartY(%)], %) +|> close(%) +|> extrude(40.14, %) +|> shell({ + faces: [seg01], + thickness: 3.14, +}, %) +"#; + + let ctx = crate::test_server::new_context(UnitLength::Mm, true, None) + .await + .unwrap(); + let old_program = crate::Program::parse_no_errs(code).unwrap(); + // Execute the program. + let mut exec_state = Default::default(); + let cache_info = crate::CacheInformation { + old: None, + new_ast: old_program.ast.clone(), + }; + ctx.run(cache_info, &mut exec_state).await.unwrap(); + + // Get the id_generator from the first execution. + let id_generator = exec_state.global.id_generator.clone(); + + let code = r#"sketch001 = startSketchOn('XZ') +|> startProfileAt([62.74, 206.13], %) +|> xLine(305.11, %, $seg01) +|> yLine(-291.85, %) +|> xLine(-segLen(seg01), %) +|> lineTo([profileStartX(%), profileStartY(%)], %) +|> close(%) +|> extrude(40.14, %) +|> shell({ + faces: [seg01], + thickness: 3.14, +}, %) +"#; + + // Execute a slightly different program again. + let program: Program = crate::Program::parse_no_errs(code).unwrap(); + let cache_info = crate::CacheInformation { + old: Some(crate::OldAstState { + ast: old_program.ast.clone(), + exec_state: exec_state.clone(), + settings: ctx.settings.clone(), + }), + new_ast: program.ast.clone(), + }; + // Execute the program. + ctx.run(cache_info, &mut exec_state).await.unwrap(); + + assert_eq!(id_generator, exec_state.global.id_generator); + } } diff --git a/src/wasm-lib/kcl/src/lsp/kcl/mod.rs b/src/wasm-lib/kcl/src/lsp/kcl/mod.rs index cbdd2f3fd9..a22447b0a9 100644 --- a/src/wasm-lib/kcl/src/lsp/kcl/mod.rs +++ b/src/wasm-lib/kcl/src/lsp/kcl/mod.rs @@ -726,11 +726,11 @@ impl Backend { drop(last_successful_ast_state); self.memory_map - .insert(params.uri.to_string(), exec_state.memory.clone()); + .insert(params.uri.to_string(), exec_state.memory().clone()); // Send the notification to the client that the memory was updated. self.client - .send_notification::(exec_state.memory) + .send_notification::(exec_state.mod_local.memory) .await; Ok(()) diff --git a/src/wasm-lib/kcl/src/lsp/tests.rs b/src/wasm-lib/kcl/src/lsp/tests.rs index 81dd275bf7..9a242f046d 100644 --- a/src/wasm-lib/kcl/src/lsp/tests.rs +++ b/src/wasm-lib/kcl/src/lsp/tests.rs @@ -2344,6 +2344,7 @@ async fn kcl_test_kcl_lsp_diagnostics_on_execution_error() { .await; // Get the diagnostics. + // TODO warnings being stomped by execution errors? assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1); // Update the text. diff --git a/src/wasm-lib/kcl/src/parsing/ast/digest.rs b/src/wasm-lib/kcl/src/parsing/ast/digest.rs index 463fc0b978..2943c98186 100644 --- a/src/wasm-lib/kcl/src/parsing/ast/digest.rs +++ b/src/wasm-lib/kcl/src/parsing/ast/digest.rs @@ -59,8 +59,8 @@ impl ImportStatement { } } ImportSelector::Glob(_) => hasher.update(b"ImportSelector::Glob"), - ImportSelector::None(None) => hasher.update(b"ImportSelector::None"), - ImportSelector::None(Some(alias)) => { + ImportSelector::None { alias: None } => hasher.update(b"ImportSelector::None"), + ImportSelector::None { alias: Some(alias) } => { hasher.update(b"ImportSelector::None"); hasher.update(alias.compute_digest()); } diff --git a/src/wasm-lib/kcl/src/parsing/ast/types/mod.rs b/src/wasm-lib/kcl/src/parsing/ast/types/mod.rs index 99af323315..1a7ef8bcb5 100644 --- a/src/wasm-lib/kcl/src/parsing/ast/types/mod.rs +++ b/src/wasm-lib/kcl/src/parsing/ast/types/mod.rs @@ -1225,7 +1225,7 @@ pub enum ImportSelector { Glob(Node<()>), /// Import the module itself (the param is an optional alias). /// E.g., `import "foo.kcl" as bar` - None(Option>), + None { alias: Option> }, } impl ImportSelector { @@ -1244,8 +1244,8 @@ impl ImportSelector { None } ImportSelector::Glob(_) => None, - ImportSelector::None(None) => None, - ImportSelector::None(Some(alias)) => { + ImportSelector::None { alias: None } => None, + ImportSelector::None { alias: Some(alias) } => { let alias_source_range = SourceRange::from(&*alias); if !alias_source_range.contains(pos) { return None; @@ -1264,8 +1264,8 @@ impl ImportSelector { } } ImportSelector::Glob(_) => {} - ImportSelector::None(None) => {} - ImportSelector::None(Some(alias)) => alias.rename(old_name, new_name), + ImportSelector::None { alias: None } => {} + ImportSelector::None { alias: Some(alias) } => alias.rename(old_name, new_name), } } } @@ -1296,7 +1296,7 @@ impl Node { false } ImportSelector::Glob(_) => false, - ImportSelector::None(_) => name == self.module_name().unwrap(), + ImportSelector::None { .. } => name == self.module_name().unwrap(), } } @@ -1304,7 +1304,7 @@ impl Node { /// Validated during parsing and guaranteed to return `Some` if the statement imports /// the module itself (i.e., self.selector is ImportSelector::None). pub fn module_name(&self) -> Option { - if let ImportSelector::None(Some(alias)) = &self.selector { + if let ImportSelector::None { alias: Some(alias) } = &self.selector { return Some(alias.name.clone()); } diff --git a/src/wasm-lib/kcl/src/parsing/parser.rs b/src/wasm-lib/kcl/src/parsing/parser.rs index 26ec22767b..587542f7ab 100644 --- a/src/wasm-lib/kcl/src/parsing/parser.rs +++ b/src/wasm-lib/kcl/src/parsing/parser.rs @@ -1444,7 +1444,7 @@ fn import_stmt(i: &mut TokenSlice) -> PResult> { require_whitespace(i)?; let (mut selector, path) = alt(( - string_literal.map(|s| (ImportSelector::None(None), Some(s))), + string_literal.map(|s| (ImportSelector::None { alias: None }, Some(s))), glob.map(|t| { let s = t.as_source_range(); ( @@ -1507,7 +1507,7 @@ fn import_stmt(i: &mut TokenSlice) -> PResult> { )); } - if let ImportSelector::None(ref mut a) = selector { + if let ImportSelector::None { alias: ref mut a } = selector { if let Some(alias) = opt(preceded( (whitespace, import_as_keyword, whitespace), identifier.context(expected("an identifier to alias the import")), diff --git a/src/wasm-lib/kcl/src/parsing/token/tokeniser.rs b/src/wasm-lib/kcl/src/parsing/token/tokeniser.rs index 074514f574..6eb73f11ac 100644 --- a/src/wasm-lib/kcl/src/parsing/token/tokeniser.rs +++ b/src/wasm-lib/kcl/src/parsing/token/tokeniser.rs @@ -366,6 +366,7 @@ mod tests { use super::*; use crate::parsing::token::TokenSlice; + fn assert_parse_err<'i, P, O, E>(mut p: P, s: &'i str) where O: std::fmt::Debug, diff --git a/src/wasm-lib/kcl/src/simulation_tests.rs b/src/wasm-lib/kcl/src/simulation_tests.rs index c6bf7a707d..b9099607ca 100644 --- a/src/wasm-lib/kcl/src/simulation_tests.rs +++ b/src/wasm-lib/kcl/src/simulation_tests.rs @@ -127,7 +127,7 @@ async fn execute(test_name: &str, render_to_png: bool) { }); assert_snapshot(test_name, "Operations executed", || { - insta::assert_json_snapshot!("ops", e.exec_state.operations); + insta::assert_json_snapshot!("ops", e.exec_state.mod_local.operations); }); } e => { @@ -711,6 +711,27 @@ mod import_glob { super::execute(TEST_NAME, false).await } } +mod import_whole { + const TEST_NAME: &str = "import_whole"; + + /// Test parsing KCL. + #[test] + fn parse() { + super::parse(TEST_NAME) + } + + /// Test that parsing and unparsing KCL produces the original KCL input. + #[test] + fn unparse() { + super::unparse(TEST_NAME) + } + + /// Test that KCL is executed correctly. + #[tokio::test(flavor = "multi_thread")] + async fn kcl_test_execute() { + super::execute(TEST_NAME, false).await + } +} mod import_side_effect { const TEST_NAME: &str = "import_side_effect"; diff --git a/src/wasm-lib/kcl/src/std/args.rs b/src/wasm-lib/kcl/src/std/args.rs index f8a48349ea..0c5c432f87 100644 --- a/src/wasm-lib/kcl/src/std/args.rs +++ b/src/wasm-lib/kcl/src/std/args.rs @@ -185,7 +185,7 @@ impl Args { exec_state: &'e mut ExecState, tag: &'a TagIdentifier, ) -> Result<&'e crate::execution::TagEngineInfo, KclError> { - if let KclValue::TagIdentifier(t) = exec_state.memory.get(&tag.value, self.source_range)? { + if let KclValue::TagIdentifier(t) = exec_state.memory().get(&tag.value, self.source_range)? { Ok(t.info.as_ref().ok_or_else(|| { KclError::Type(KclErrorDetails { message: format!("Tag `{}` does not have engine info", tag.value), @@ -251,12 +251,12 @@ impl Args { // Find all the solids on the same shared sketch. ids.extend( exec_state - .memory + .memory() .find_solids_on_sketch(solid.sketch.id) .iter() .flat_map(|eg| eg.get_all_edge_cut_ids()), ); - ids.extend(exec_state.dynamic_state.edge_cut_ids_on_sketch(sketch_id)); + ids.extend(exec_state.mod_local.dynamic_state.edge_cut_ids_on_sketch(sketch_id)); traversed_sketches.push(sketch_id); } diff --git a/src/wasm-lib/kcl/src/std/chamfer.rs b/src/wasm-lib/kcl/src/std/chamfer.rs index bc69587870..9c4d6c864f 100644 --- a/src/wasm-lib/kcl/src/std/chamfer.rs +++ b/src/wasm-lib/kcl/src/std/chamfer.rs @@ -134,7 +134,7 @@ async fn inner_chamfer( EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(exec_state, &edge_tag)?.id, }; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.global.id_generator.next_uuid(); args.batch_end_cmd( id, ModelingCmd::from(mcmd::Solid3dFilletEdge { diff --git a/src/wasm-lib/kcl/src/std/extrude.rs b/src/wasm-lib/kcl/src/std/extrude.rs index d28fea9029..e8a672d125 100644 --- a/src/wasm-lib/kcl/src/std/extrude.rs +++ b/src/wasm-lib/kcl/src/std/extrude.rs @@ -84,7 +84,7 @@ async fn inner_extrude( exec_state: &mut ExecState, args: Args, ) -> Result { - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); // Extrude the element(s). let sketches: Vec = sketch_set.into(); @@ -93,7 +93,7 @@ async fn inner_extrude( // Before we extrude, we need to enable the sketch mode. // We do this here in case extrude is called out of order. args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::EnableSketchMode { animated: false, ortho: false, @@ -121,7 +121,7 @@ async fn inner_extrude( // Disable the sketch mode. args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}), ) .await?; @@ -140,7 +140,7 @@ pub(crate) async fn do_post_extrude( // Bring the object to the front of the scene. // See: https://github.com/KittyCAD/modeling-app/issues/806 args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::ObjectBringToFront { object_id: sketch.id }), ) .await?; @@ -163,7 +163,7 @@ pub(crate) async fn do_post_extrude( let solid3d_info = args .send_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::Solid3dGetExtrusionFaceInfo { edge_id: any_edge_id, object_id: sketch.id, @@ -196,7 +196,7 @@ pub(crate) async fn do_post_extrude( // Instead, the Typescript codebases (which handles WebSocket sends when compiled via Wasm) // uses this to build the artifact graph, which the UI needs. args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::Solid3dGetOppositeEdge { edge_id: curve_id, object_id: sketch.id, @@ -206,7 +206,7 @@ pub(crate) async fn do_post_extrude( .await?; args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::Solid3dGetNextAdjacentEdge { edge_id: curve_id, object_id: sketch.id, @@ -259,7 +259,7 @@ pub(crate) async fn do_post_extrude( let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::execution::ExtrudePlane { // pushing this values with a fake face_id to make extrudes mock-execute safe - face_id: exec_state.id_generator.next_uuid(), + face_id: exec_state.next_uuid(), tag: path.get_base().tag.clone(), geo_meta: GeoMeta { id: path.get_base().geo_meta.id, @@ -305,8 +305,8 @@ fn analyze_faces(exec_state: &mut ExecState, args: &Args, face_infos: Vec Result }] async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result { if args.ctx.is_mock() { - return Ok(exec_state.id_generator.next_uuid()); + return Ok(exec_state.next_uuid()); } let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); let tagged_path = args.get_tag_engine_info(exec_state, &tag)?; let resp = args @@ -302,11 +302,11 @@ async fn inner_get_next_adjacent_edge( args: Args, ) -> Result { if args.ctx.is_mock() { - return Ok(exec_state.id_generator.next_uuid()); + return Ok(exec_state.next_uuid()); } let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); let tagged_path = args.get_tag_engine_info(exec_state, &tag)?; let resp = args @@ -387,11 +387,11 @@ async fn inner_get_previous_adjacent_edge( args: Args, ) -> Result { if args.ctx.is_mock() { - return Ok(exec_state.id_generator.next_uuid()); + return Ok(exec_state.next_uuid()); } let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); let tagged_path = args.get_tag_engine_info(exec_state, &tag)?; let resp = args diff --git a/src/wasm-lib/kcl/src/std/helix.rs b/src/wasm-lib/kcl/src/std/helix.rs index 62c5cf7161..e4834e0799 100644 --- a/src/wasm-lib/kcl/src/std/helix.rs +++ b/src/wasm-lib/kcl/src/std/helix.rs @@ -61,7 +61,7 @@ async fn inner_helix( exec_state: &mut ExecState, args: Args, ) -> Result, KclError> { - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, ModelingCmd::from(mcmd::EntityMakeHelix { diff --git a/src/wasm-lib/kcl/src/std/import.rs b/src/wasm-lib/kcl/src/std/import.rs index eb535defeb..89ad712a20 100644 --- a/src/wasm-lib/kcl/src/std/import.rs +++ b/src/wasm-lib/kcl/src/std/import.rs @@ -300,13 +300,13 @@ async fn inner_import( if args.ctx.is_mock() { return Ok(ImportedGeometry { - id: exec_state.id_generator.next_uuid(), + id: exec_state.next_uuid(), value: import_files.iter().map(|f| f.path.to_string()).collect(), meta: vec![args.source_range.into()], }); } - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); let resp = args .send_modeling_cmd( id, diff --git a/src/wasm-lib/kcl/src/std/loft.rs b/src/wasm-lib/kcl/src/std/loft.rs index 57606460fe..a43bbba0b4 100644 --- a/src/wasm-lib/kcl/src/std/loft.rs +++ b/src/wasm-lib/kcl/src/std/loft.rs @@ -142,7 +142,7 @@ async fn inner_loft( })); } - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, ModelingCmd::from(mcmd::Loft { diff --git a/src/wasm-lib/kcl/src/std/mirror.rs b/src/wasm-lib/kcl/src/std/mirror.rs index 6c611dacc2..3882e611b6 100644 --- a/src/wasm-lib/kcl/src/std/mirror.rs +++ b/src/wasm-lib/kcl/src/std/mirror.rs @@ -121,7 +121,7 @@ async fn inner_mirror_2d( let (axis, origin) = axis.axis_and_origin()?; args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::EntityMirror { ids: starting_sketches.iter().map(|sketch| sketch.id).collect(), axis, @@ -134,7 +134,7 @@ async fn inner_mirror_2d( let edge_id = edge.get_engine_id(exec_state, &args)?; args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::EntityMirrorAcrossEdge { ids: starting_sketches.iter().map(|sketch| sketch.id).collect(), edge_id, diff --git a/src/wasm-lib/kcl/src/std/patterns.rs b/src/wasm-lib/kcl/src/std/patterns.rs index c2cf56dd47..8833c354fc 100644 --- a/src/wasm-lib/kcl/src/std/patterns.rs +++ b/src/wasm-lib/kcl/src/std/patterns.rs @@ -379,7 +379,7 @@ async fn send_pattern_transform( exec_state: &mut ExecState, args: &Args, ) -> Result, KclError> { - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); let resp = args .send_modeling_cmd( @@ -1032,7 +1032,7 @@ async fn pattern_circular( exec_state: &mut ExecState, args: Args, ) -> Result { - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); let num_repetitions = match data.repetitions() { RepetitionsNeeded::More(n) => n, RepetitionsNeeded::None => { diff --git a/src/wasm-lib/kcl/src/std/planes.rs b/src/wasm-lib/kcl/src/std/planes.rs index 282f04ef7b..bc6c35e51b 100644 --- a/src/wasm-lib/kcl/src/std/planes.rs +++ b/src/wasm-lib/kcl/src/std/planes.rs @@ -209,7 +209,7 @@ async fn make_offset_plane_in_engine(plane: &Plane, exec_state: &mut ExecState, // Set the color. args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::PlaneSetColor { color, plane_id: plane.id, diff --git a/src/wasm-lib/kcl/src/std/revolve.rs b/src/wasm-lib/kcl/src/std/revolve.rs index db7eca9069..e617153e79 100644 --- a/src/wasm-lib/kcl/src/std/revolve.rs +++ b/src/wasm-lib/kcl/src/std/revolve.rs @@ -264,7 +264,7 @@ async fn inner_revolve( let angle = Angle::from_degrees(data.angle.unwrap_or(360.0)); - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); match data.axis { AxisOrEdgeReference::Axis(axis) => { let (axis, origin) = axis.axis_and_origin()?; diff --git a/src/wasm-lib/kcl/src/std/shapes.rs b/src/wasm-lib/kcl/src/std/shapes.rs index 2651cecca1..1aca88cbef 100644 --- a/src/wasm-lib/kcl/src/std/shapes.rs +++ b/src/wasm-lib/kcl/src/std/shapes.rs @@ -100,7 +100,7 @@ async fn inner_circle( let angle_start = Angle::zero(); let angle_end = Angle::turn(); - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, @@ -269,7 +269,7 @@ async fn inner_polygon( // Draw all the lines with unique IDs and modified tags for vertex in vertices.iter().skip(1) { let from = sketch.current_pen_position()?; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, @@ -304,7 +304,7 @@ async fn inner_polygon( // Close the polygon by connecting back to the first vertex with a new ID let from = sketch.current_pen_position()?; - let close_id = exec_state.id_generator.next_uuid(); + let close_id = exec_state.next_uuid(); args.batch_modeling_cmd( close_id, @@ -337,7 +337,7 @@ async fn inner_polygon( sketch.paths.push(current_path); args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::ClosePath { path_id: sketch.id }), ) .await?; diff --git a/src/wasm-lib/kcl/src/std/shell.rs b/src/wasm-lib/kcl/src/std/shell.rs index a5acb8f382..68762ed432 100644 --- a/src/wasm-lib/kcl/src/std/shell.rs +++ b/src/wasm-lib/kcl/src/std/shell.rs @@ -230,7 +230,7 @@ async fn inner_shell( } args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::Solid3dShellFace { hollow: false, face_ids, @@ -316,7 +316,7 @@ async fn inner_hollow( args.flush_batch_for_solid_set(exec_state, solid.clone().into()).await?; args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::Solid3dShellFace { hollow: true, face_ids: Vec::new(), // This is empty because we want to hollow the entire object. diff --git a/src/wasm-lib/kcl/src/std/sketch.rs b/src/wasm-lib/kcl/src/std/sketch.rs index 5060eabf8b..70617263b6 100644 --- a/src/wasm-lib/kcl/src/std/sketch.rs +++ b/src/wasm-lib/kcl/src/std/sketch.rs @@ -124,7 +124,7 @@ async fn inner_line_to( args: Args, ) -> Result { let from = sketch.current_pen_position()?; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, @@ -299,7 +299,7 @@ async fn inner_line( let from = sketch.current_pen_position()?; let to = [from.x + delta[0], from.y + delta[1]]; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, @@ -488,7 +488,7 @@ async fn inner_angled_line( let to: [f64; 2] = [from.x + delta[0], from.y + delta[1]]; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, @@ -1230,7 +1230,7 @@ pub(crate) async fn inner_start_profile_at( // Hide whatever plane we are sketching on. // This is especially helpful for offset planes, which would be visible otherwise. args.batch_end_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::ObjectVisible { object_id: plane.id, hidden: true, @@ -1243,7 +1243,7 @@ pub(crate) async fn inner_start_profile_at( // Enter sketch mode on the surface. // We call this here so you can reuse the sketch surface for multiple sketches. - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, ModelingCmd::from(mcmd::EnableSketchMode { @@ -1261,8 +1261,8 @@ pub(crate) async fn inner_start_profile_at( ) .await?; - let id = exec_state.id_generator.next_uuid(); - let path_id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); + let path_id = exec_state.next_uuid(); args.batch_modeling_cmd(path_id, ModelingCmd::from(mcmd::StartPath {})) .await?; @@ -1427,7 +1427,7 @@ pub(crate) async fn inner_close( let from = sketch.current_pen_position()?; let to: Point2d = sketch.start.from.into(); - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd(id, ModelingCmd::from(mcmd::ClosePath { path_id: sketch.id })) .await?; @@ -1436,7 +1436,7 @@ pub(crate) async fn inner_close( if let SketchSurface::Plane(_) = sketch.on { // We were on a plane, disable the sketch mode. args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}), ) .await?; @@ -1573,7 +1573,7 @@ pub(crate) async fn inner_arc( } let ccw = angle_start < angle_end; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, @@ -1652,7 +1652,7 @@ pub(crate) async fn inner_arc_to( args: Args, ) -> Result { let from: Point2d = sketch.current_pen_position()?; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); // The start point is taken from the path you are extending. args.batch_modeling_cmd( @@ -1798,7 +1798,7 @@ async fn inner_tangential_arc( let tangent_info = sketch.get_tangential_info_from_paths(); //this function desperately needs some documentation let tan_previous_point = tangent_info.tan_previous_point(from.into()); - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); let (center, to, ccw) = match data { TangentialArcData::RadiusAndOffset { radius, offset } => { @@ -1935,7 +1935,7 @@ async fn inner_tangential_arc_to( }); let delta = [to_x - from.x, to_y - from.y]; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd(id, tan_arc_to(&sketch, &delta)).await?; let current_path = Path::TangentialArcTo { @@ -2018,7 +2018,7 @@ async fn inner_tangential_arc_to_relative( })); } - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd(id, tan_arc_to(&sketch, &delta)).await?; let current_path = Path::TangentialArcTo { @@ -2138,7 +2138,7 @@ async fn inner_bezier_curve( let delta = data.to; let to = [from.x + data.to[0], from.y + data.to[1]]; - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, @@ -2230,7 +2230,7 @@ async fn inner_hole( let hole_sketches: Vec = hole_sketch.into(); for hole_sketch in hole_sketches { args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::Solid2dAddHole { object_id: sketch.id, hole_id: hole_sketch.id, @@ -2241,7 +2241,7 @@ async fn inner_hole( // suggestion (mike) // we also hide the source hole since its essentially "consumed" by this operation args.batch_modeling_cmd( - exec_state.id_generator.next_uuid(), + exec_state.next_uuid(), ModelingCmd::from(mcmd::ObjectVisible { object_id: hole_sketch.id, hidden: true, diff --git a/src/wasm-lib/kcl/src/std/sweep.rs b/src/wasm-lib/kcl/src/std/sweep.rs index 4de34c4bc8..02566e2ca2 100644 --- a/src/wasm-lib/kcl/src/std/sweep.rs +++ b/src/wasm-lib/kcl/src/std/sweep.rs @@ -87,7 +87,7 @@ async fn inner_sweep( exec_state: &mut ExecState, args: Args, ) -> Result, KclError> { - let id = exec_state.id_generator.next_uuid(); + let id = exec_state.next_uuid(); args.batch_modeling_cmd( id, ModelingCmd::from(mcmd::Sweep { diff --git a/src/wasm-lib/kcl/src/test_server.rs b/src/wasm-lib/kcl/src/test_server.rs index 5962818f7f..5e00ed66dd 100644 --- a/src/wasm-lib/kcl/src/test_server.rs +++ b/src/wasm-lib/kcl/src/test_server.rs @@ -41,7 +41,7 @@ pub async fn execute_and_snapshot_ast( let ctx = new_context(units, true, project_directory).await?; do_execute_and_snapshot(&ctx, ast) .await - .map(|(state, snap)| (state.memory, state.operations, snap)) + .map(|(state, snap)| (state.mod_local.memory, state.mod_local.operations, snap)) } pub async fn execute_and_snapshot_no_auth( diff --git a/src/wasm-lib/kcl/src/unparser.rs b/src/wasm-lib/kcl/src/unparser.rs index d587c21e83..a02a2135bb 100644 --- a/src/wasm-lib/kcl/src/unparser.rs +++ b/src/wasm-lib/kcl/src/unparser.rs @@ -146,11 +146,11 @@ impl ImportStatement { string.push_str(" from "); } ImportSelector::Glob(_) => string.push_str("* from "), - ImportSelector::None(_) => {} + ImportSelector::None { .. } => {} } string.push_str(&format!("\"{}\"", self.path)); - if let ImportSelector::None(Some(alias)) = &self.selector { + if let ImportSelector::None { alias: Some(alias) } = &self.selector { string.push_str(" as "); string.push_str(&alias.name); } diff --git a/src/wasm-lib/kcl/tests/import_side_effect/execution_error.snap b/src/wasm-lib/kcl/tests/import_side_effect/execution_error.snap index 09cd4bdb64..278cc7eac0 100644 --- a/src/wasm-lib/kcl/tests/import_side_effect/execution_error.snap +++ b/src/wasm-lib/kcl/tests/import_side_effect/execution_error.snap @@ -5,8 +5,9 @@ description: Error from executing import_side_effect.kcl KCL Semantic error × semantic: Error loading imported file. Open it to view more details. - │ export_side_effect.kcl: Cannot send modeling commands while importing. - │ Wrap your code in a function if you want to import the file. + │ tests/import_side_effect/export_side_effect.kcl: Cannot send modeling + │ commands while importing. Wrap your code in a function if you want to + │ import the file. ╭──── 1 │ import foo from "export_side_effect.kcl" · ──────────────────────────────────────── diff --git a/src/wasm-lib/kcl/tests/import_side_effect/ops.snap b/src/wasm-lib/kcl/tests/import_side_effect/ops.snap index 8914f7c0f5..d6327de12f 100644 --- a/src/wasm-lib/kcl/tests/import_side_effect/ops.snap +++ b/src/wasm-lib/kcl/tests/import_side_effect/ops.snap @@ -1,27 +1,5 @@ --- source: kcl/src/simulation_tests.rs description: Operations executed import_side_effect.kcl -snapshot_kind: text --- -[ - { - "isError": true, - "labeledArgs": { - "data": { - "sourceRange": [ - 95, - 99, - 1 - ] - } - }, - "name": "startSketchOn", - "sourceRange": [ - 81, - 100, - 1 - ], - "type": "StdLibCall", - "unlabeledArg": null - } -] +[] diff --git a/src/wasm-lib/kcl/tests/import_whole/ast.snap b/src/wasm-lib/kcl/tests/import_whole/ast.snap new file mode 100644 index 0000000000..123b3996d3 --- /dev/null +++ b/src/wasm-lib/kcl/tests/import_whole/ast.snap @@ -0,0 +1,150 @@ +--- +source: kcl/src/simulation_tests.rs +description: Result of parsing import_whole.kcl +--- +{ + "Ok": { + "body": [ + { + "end": 32, + "path": "exported_mod.kcl", + "selector": { + "type": "None", + "alias": { + "end": 32, + "name": "foo", + "start": 29, + "type": "Identifier" + } + }, + "start": 0, + "type": "ImportStatement", + "type": "ImportStatement" + }, + { + "declaration": { + "end": 96, + "id": { + "end": 37, + "name": "bar", + "start": 34, + "type": "Identifier" + }, + "init": { + "body": [ + { + "end": 43, + "name": "foo", + "start": 40, + "type": "Identifier", + "type": "Identifier" + }, + { + "arguments": [ + { + "end": 92, + "properties": [ + { + "end": 72, + "key": { + "end": 62, + "name": "faces", + "start": 57, + "type": "Identifier" + }, + "start": 57, + "type": "ObjectProperty", + "value": { + "elements": [ + { + "end": 71, + "raw": "'end'", + "start": 66, + "type": "Literal", + "type": "Literal", + "value": "end" + } + ], + "end": 72, + "start": 65, + "type": "ArrayExpression", + "type": "ArrayExpression" + } + }, + { + "end": 90, + "key": { + "end": 83, + "name": "thickness", + "start": 74, + "type": "Identifier" + }, + "start": 74, + "type": "ObjectProperty", + "value": { + "end": 90, + "raw": "0.25", + "start": 86, + "type": "Literal", + "type": "Literal", + "value": 0.25 + } + } + ], + "start": 55, + "type": "ObjectExpression", + "type": "ObjectExpression" + }, + { + "end": 95, + "start": 94, + "type": "PipeSubstitution", + "type": "PipeSubstitution" + } + ], + "callee": { + "end": 54, + "name": "shell", + "start": 49, + "type": "Identifier" + }, + "end": 96, + "start": 49, + "type": "CallExpression", + "type": "CallExpression" + } + ], + "end": 96, + "start": 40, + "type": "PipeExpression", + "type": "PipeExpression" + }, + "start": 34, + "type": "VariableDeclarator" + }, + "end": 96, + "kind": "const", + "start": 34, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + } + ], + "end": 97, + "nonCodeMeta": { + "nonCodeNodes": { + "0": [ + { + "end": 34, + "start": 32, + "type": "NonCodeNode", + "value": { + "type": "newLine" + } + } + ] + }, + "startNodes": [] + }, + "start": 0 + } +} diff --git a/src/wasm-lib/kcl/tests/import_whole/exported_mod.kcl b/src/wasm-lib/kcl/tests/import_whole/exported_mod.kcl new file mode 100644 index 0000000000..a803a59fc3 --- /dev/null +++ b/src/wasm-lib/kcl/tests/import_whole/exported_mod.kcl @@ -0,0 +1,3 @@ +startSketchOn('XY') + |> circle({ center = [5, 5], radius = 10 }, %) + |> extrude(10, %) diff --git a/src/wasm-lib/kcl/tests/import_whole/input.kcl b/src/wasm-lib/kcl/tests/import_whole/input.kcl new file mode 100644 index 0000000000..48ec0647a8 --- /dev/null +++ b/src/wasm-lib/kcl/tests/import_whole/input.kcl @@ -0,0 +1,4 @@ +import "exported_mod.kcl" as foo + +bar = foo + |> shell({ faces = ['end'], thickness = 0.25 }, %) diff --git a/src/wasm-lib/kcl/tests/import_whole/ops.snap b/src/wasm-lib/kcl/tests/import_whole/ops.snap new file mode 100644 index 0000000000..276b9d484d --- /dev/null +++ b/src/wasm-lib/kcl/tests/import_whole/ops.snap @@ -0,0 +1,32 @@ +--- +source: kcl/src/simulation_tests.rs +description: Operations executed import_whole.kcl +--- +[ + { + "labeledArgs": { + "data": { + "sourceRange": [ + 55, + 92, + 0 + ] + }, + "solid_set": { + "sourceRange": [ + 94, + 95, + 0 + ] + } + }, + "name": "shell", + "sourceRange": [ + 49, + 96, + 0 + ], + "type": "StdLibCall", + "unlabeledArg": null + } +] diff --git a/src/wasm-lib/kcl/tests/import_whole/program_memory.snap b/src/wasm-lib/kcl/tests/import_whole/program_memory.snap new file mode 100644 index 0000000000..7795e95ce5 --- /dev/null +++ b/src/wasm-lib/kcl/tests/import_whole/program_memory.snap @@ -0,0 +1,164 @@ +--- +source: kcl/src/simulation_tests.rs +description: Program memory after executing import_whole.kcl +--- +{ + "environments": [ + { + "bindings": { + "HALF_TURN": { + "type": "Number", + "value": 180.0, + "__meta": [] + }, + "QUARTER_TURN": { + "type": "Number", + "value": 90.0, + "__meta": [] + }, + "THREE_QUARTER_TURN": { + "type": "Number", + "value": 270.0, + "__meta": [] + }, + "ZERO": { + "type": "Number", + "value": 0.0, + "__meta": [] + }, + "bar": { + "type": "Solid", + "type": "Solid", + "id": "[uuid]", + "value": [ + { + "faceId": "[uuid]", + "id": "[uuid]", + "sourceRange": [ + 25, + 68, + 1 + ], + "tag": null, + "type": "extrudeArc" + } + ], + "sketch": { + "type": "Sketch", + "id": "[uuid]", + "paths": [ + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [ + 25, + 68, + 1 + ] + }, + "ccw": true, + "center": [ + 5.0, + 5.0 + ], + "from": [ + 15.0, + 5.0 + ], + "radius": 10.0, + "tag": null, + "to": [ + 15.0, + 5.0 + ], + "type": "Circle" + } + ], + "on": { + "type": "plane", + "id": "[uuid]", + "value": "XY", + "origin": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "xAxis": { + "x": 1.0, + "y": 0.0, + "z": 0.0 + }, + "yAxis": { + "x": 0.0, + "y": 1.0, + "z": 0.0 + }, + "zAxis": { + "x": 0.0, + "y": 0.0, + "z": 1.0 + }, + "__meta": [] + }, + "start": { + "from": [ + 15.0, + 5.0 + ], + "to": [ + 15.0, + 5.0 + ], + "tag": null, + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [ + 25, + 68, + 1 + ] + } + }, + "__meta": [ + { + "sourceRange": [ + 25, + 68, + 1 + ] + } + ] + }, + "height": 10.0, + "startCapId": "[uuid]", + "endCapId": "[uuid]", + "__meta": [ + { + "sourceRange": [ + 25, + 68, + 1 + ] + } + ] + }, + "foo": { + "type": "Module", + "value": 1, + "__meta": [ + { + "sourceRange": [ + 0, + 32, + 0 + ] + } + ] + } + }, + "parent": null + } + ], + "currentEnv": 0, + "return": null +} diff --git a/src/wasm-lib/src/wasm.rs b/src/wasm-lib/src/wasm.rs index 33db3f5902..83a2f6168d 100644 --- a/src/wasm-lib/src/wasm.rs +++ b/src/wasm-lib/src/wasm.rs @@ -86,7 +86,7 @@ pub async fn execute( // Populate from the old exec state if it exists. if let Some(program_memory_override) = program_memory_override { - exec_state.memory = program_memory_override; + exec_state.mod_local.memory = program_memory_override; } else { // If we are in mock mode, we don't want to use any cache. if let Some(old) = read_old_ast_memory().await { @@ -110,7 +110,7 @@ pub async fn execute( } // Add additional outputs to the error. - let error = KclErrorWithOutputs::new(err, exec_state.operations.clone()); + let error = KclErrorWithOutputs::new(err, exec_state.mod_local.operations.clone()); // Throw the error. return Err(serde_json::to_string(&error).map_err(|serde_err| serde_err.to_string())?); diff --git a/src/wasm-lib/tests/executor/main.rs b/src/wasm-lib/tests/executor/main.rs index d76094aa2a..3006fcf1c6 100644 --- a/src/wasm-lib/tests/executor/main.rs +++ b/src/wasm-lib/tests/executor/main.rs @@ -1,7 +1,7 @@ mod cache; use kcl_lib::{ - test_server::{execute_and_snapshot, execute_and_snapshot_no_auth, new_context}, + test_server::{execute_and_snapshot, execute_and_snapshot_no_auth}, UnitLength, }; @@ -2001,62 +2001,3 @@ async fn kcl_test_error_no_auth_websocket() { .to_string() .contains("Please send the following object over this websocket")); } - -#[tokio::test(flavor = "multi_thread")] -async fn kcl_test_ids_stable_between_executions() { - let code = r#"sketch001 = startSketchOn('XZ') - |> startProfileAt([61.74, 206.13], %) - |> xLine(305.11, %, $seg01) - |> yLine(-291.85, %) - |> xLine(-segLen(seg01), %) - |> lineTo([profileStartX(%), profileStartY(%)], %) - |> close(%) - |> extrude(40.14, %) - |> shell({ - faces: [seg01], - thickness: 3.14, - }, %) -"#; - - let ctx = new_context(UnitLength::Mm, true, None).await.unwrap(); - let old_program = kcl_lib::Program::parse_no_errs(code).unwrap(); - // Execute the program. - let mut exec_state = Default::default(); - let cache_info = kcl_lib::CacheInformation { - old: None, - new_ast: old_program.ast.clone(), - }; - ctx.run(cache_info, &mut exec_state).await.unwrap(); - - // Get the id_generator from the first execution. - let id_generator = exec_state.id_generator.clone(); - - let code = r#"sketch001 = startSketchOn('XZ') - |> startProfileAt([62.74, 206.13], %) - |> xLine(305.11, %, $seg01) - |> yLine(-291.85, %) - |> xLine(-segLen(seg01), %) - |> lineTo([profileStartX(%), profileStartY(%)], %) - |> close(%) - |> extrude(40.14, %) - |> shell({ - faces: [seg01], - thickness: 3.14, - }, %) -"#; - - // Execute a slightly different program again. - let program = kcl_lib::Program::parse_no_errs(code).unwrap(); - let cache_info = kcl_lib::CacheInformation { - old: Some(kcl_lib::OldAstState { - ast: old_program.ast.clone(), - exec_state: exec_state.clone(), - settings: ctx.settings.clone(), - }), - new_ast: program.ast.clone(), - }; - // Execute the program. - ctx.run(cache_info, &mut exec_state).await.unwrap(); - - assert_eq!(id_generator, exec_state.id_generator); -} diff --git a/src/wasm-lib/tests/modify/main.rs b/src/wasm-lib/tests/modify/main.rs index 677eb895d4..22c813d74b 100644 --- a/src/wasm-lib/tests/modify/main.rs +++ b/src/wasm-lib/tests/modify/main.rs @@ -15,8 +15,8 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, Modu // We need to get the sketch ID. // Get the sketch ID from memory. - let KclValue::Sketch { value: sketch } = exec_state.memory.get(name, SourceRange::default()).unwrap() else { - anyhow::bail!("part001 not found in memory: {:?}", exec_state.memory); + let KclValue::Sketch { value: sketch } = exec_state.memory().get(name, SourceRange::default()).unwrap() else { + anyhow::bail!("part001 not found in memory: {:?}", exec_state.memory()); }; let sketch_id = sketch.id;