allow nested files imported (#7090)

* allow nested files

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* disallow bad things

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add playwright test on windows

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add playwright test on windows

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Update rust/kcl-lib/tests/nested_windows_main_kcl/unparsed@main.kcl.snap

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
This commit is contained in:
Jess Frazelle
2025-05-19 19:42:25 -07:00
committed by GitHub
parent d0697c24fd
commit 1b4289f93f
29 changed files with 866 additions and 33 deletions

View File

@ -2064,3 +2064,59 @@ test(
}) })
} }
) )
test(
'nested dir import works on windows',
{ tag: ['@electron', '@windows'] },
async ({ scene, cmdBar, context, page }, testInfo) => {
// Skip if on non-windows
if (process.platform !== 'win32') {
test.skip()
}
await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
const nestedDir = path.join(bracketDir, 'nested')
await fsp.mkdir(nestedDir, { recursive: true })
await fsp.copyFile(
executorInputPath('cylinder-inches.kcl'),
path.join(nestedDir, 'main.kcl')
)
await fsp.writeFile(
path.join(bracketDir, 'main.kcl'),
`import 'nested\\main.kcl' as thing
thing`
)
})
await page.setBodyDimensions({ width: 1200, height: 500 })
const u = await getUtils(page)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await scene.settled(cmdBar)
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
}
)

View File

@ -35,31 +35,28 @@ impl Default for TypedPath {
impl From<&String> for TypedPath { impl From<&String> for TypedPath {
fn from(path: &String) -> Self { fn from(path: &String) -> Self {
#[cfg(target_arch = "wasm32")] TypedPath::new(path)
{
TypedPath(typed_path::TypedPath::derive(path).to_path_buf())
}
#[cfg(not(target_arch = "wasm32"))]
{
TypedPath(std::path::PathBuf::from(path))
}
} }
} }
impl From<&str> for TypedPath { impl From<&str> for TypedPath {
fn from(path: &str) -> Self { fn from(path: &str) -> Self {
TypedPath::new(path)
}
}
impl TypedPath {
pub fn new(path: &str) -> Self {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
TypedPath(typed_path::TypedPath::derive(path).to_path_buf()) TypedPath(typed_path::TypedPath::derive(path).to_path_buf())
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ {
TypedPath(std::path::PathBuf::from(path)) TypedPath(normalise_import(path))
} }
} }
}
impl TypedPath {
pub fn extension(&self) -> Option<&str> { pub fn extension(&self) -> Option<&str> {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
@ -85,6 +82,17 @@ impl TypedPath {
} }
} }
pub fn join_typed(&self, path: &TypedPath) -> Self {
#[cfg(target_arch = "wasm32")]
{
TypedPath(self.0.join(path.0.to_path()))
}
#[cfg(not(target_arch = "wasm32"))]
{
TypedPath(self.0.join(&path.0))
}
}
pub fn parent(&self) -> Option<Self> { pub fn parent(&self) -> Option<Self> {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
@ -206,3 +214,19 @@ impl schemars::JsonSchema for TypedPath {
gen.subschema_for::<std::path::PathBuf>() gen.subschema_for::<std::path::PathBuf>()
} }
} }
/// Turn `nested\foo\bar\main.kcl` or `nested/foo/bar/main.kcl`
/// into a PathBuf that works on the host OS.
///
/// * Does **not** touch `..` or symlinks call `canonicalize()` if you need that.
/// * Returns an owned `PathBuf` only when normalisation was required.
fn normalise_import<S: AsRef<str>>(raw: S) -> std::path::PathBuf {
let s = raw.as_ref();
// On Unix we need to swap `\` → `/`. On Windows we leave it alone.
// (Windows happily consumes `/`)
if cfg!(unix) && s.contains('\\') {
std::path::PathBuf::from(s.replace('\\', "/"))
} else {
std::path::Path::new(s).to_path_buf()
}
}

View File

@ -185,9 +185,9 @@ impl ModulePath {
match path { match path {
ImportPath::Kcl { filename: path } | ImportPath::Foreign { path } => { ImportPath::Kcl { filename: path } | ImportPath::Foreign { path } => {
let resolved_path = if let Some(project_dir) = project_directory { let resolved_path = if let Some(project_dir) = project_directory {
project_dir.join(path) project_dir.join_typed(path)
} else { } else {
TypedPath::from(path) path.clone()
}; };
ModulePath::Local { value: resolved_path } ModulePath::Local { value: resolved_path }
} }

View File

@ -34,7 +34,7 @@ use crate::{
}, },
parsing::{ast::digest::Digest, token::NumericSuffix, PIPE_OPERATOR}, parsing::{ast::digest::Digest, token::NumericSuffix, PIPE_OPERATOR},
source_range::SourceRange, source_range::SourceRange,
ModuleId, ModuleId, TypedPath,
}; };
mod condition; mod condition;
@ -1741,8 +1741,8 @@ impl ImportSelector {
#[ts(export)] #[ts(export)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum ImportPath { pub enum ImportPath {
Kcl { filename: String }, Kcl { filename: TypedPath },
Foreign { path: String }, Foreign { path: TypedPath },
Std { path: Vec<String> }, Std { path: Vec<String> },
} }
@ -1811,16 +1811,14 @@ impl ImportStatement {
match &self.path { match &self.path {
ImportPath::Kcl { filename: s } | ImportPath::Foreign { path: s } => { ImportPath::Kcl { filename: s } | ImportPath::Foreign { path: s } => {
let mut parts = s.split('.'); let name = s.file_name().map(|f| f.to_string());
let path = parts.next()?; // Remove the extension if it exists.
let _ext = parts.next()?; let extension = s.extension();
let rest = parts.next(); if let Some(extension) = extension {
name.map(|n| n.trim_end_matches(extension).trim_end_matches('.').to_string())
if rest.is_some() { } else {
return None; name
} }
path.rsplit(&['/', '\\']).next().map(str::to_owned)
} }
ImportPath::Std { path } => path.last().cloned(), ImportPath::Std { path } => path.last().cloned(),
} }

View File

@ -35,7 +35,7 @@ use crate::{
token::{Token, TokenSlice, TokenType}, token::{Token, TokenSlice, TokenType},
PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR, PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR,
}, },
SourceRange, IMPORT_FILE_EXTENSIONS, SourceRange, TypedPath, IMPORT_FILE_EXTENSIONS,
}; };
thread_local! { thread_local! {
@ -1862,7 +1862,7 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
let path = if path_string.ends_with(".kcl") { let path = if path_string.ends_with(".kcl") {
if path_string if path_string
.chars() .chars()
.any(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '-' && c != '.') .any(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '-' && c != '.' && c != '/' && c != '\\')
{ {
return Err(ErrMode::Cut( return Err(ErrMode::Cut(
CompilationError::fatal( CompilationError::fatal(
@ -1873,7 +1873,30 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
)); ));
} }
ImportPath::Kcl { filename: path_string } if path_string.starts_with("..") {
return Err(ErrMode::Cut(
CompilationError::fatal(
path_range,
"import path may not start with '..'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
)
.into(),
));
}
// Make sure they are not using an absolute path.
if path_string.starts_with('/') || path_string.starts_with('\\') {
return Err(ErrMode::Cut(
CompilationError::fatal(
path_range,
"import path may not start with '/' or '\\'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
)
.into(),
));
}
ImportPath::Kcl {
filename: TypedPath::new(&path_string),
}
} else if path_string.starts_with("std::") { } else if path_string.starts_with("std::") {
ParseContext::warn(CompilationError::err( ParseContext::warn(CompilationError::err(
path_range, path_range,
@ -1910,7 +1933,9 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
format!("unsupported import path format. KCL files can be imported from the current project, CAD files with the following formats are supported: {}", IMPORT_FILE_EXTENSIONS.join(", ")), format!("unsupported import path format. KCL files can be imported from the current project, CAD files with the following formats are supported: {}", IMPORT_FILE_EXTENSIONS.join(", ")),
)) ))
} }
ImportPath::Foreign { path: path_string } ImportPath::Foreign {
path: TypedPath::new(&path_string),
}
} else { } else {
return Err(ErrMode::Cut( return Err(ErrMode::Cut(
CompilationError::fatal( CompilationError::fatal(
@ -4534,6 +4559,16 @@ e
fn bad_imports() { fn bad_imports() {
assert_err( assert_err(
r#"import cube from "../cube.kcl""#, r#"import cube from "../cube.kcl""#,
"import path may not start with '..'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
[17, 30],
);
assert_err(
r#"import cube from "/cube.kcl""#,
"import path may not start with '/' or '\\'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
[17, 28],
);
assert_err(
r#"import cube from "C:\cube.kcl""#,
"import path may only contain alphanumeric characters, underscore, hyphen, and period. KCL files in other directories are not yet supported.", "import path may only contain alphanumeric characters, underscore, hyphen, and period. KCL files in other directories are not yet supported.",
[17, 30], [17, 30],
); );

View File

@ -3276,3 +3276,45 @@ mod subtract_regression10 {
super::execute(TEST_NAME, true).await super::execute(TEST_NAME, true).await
} }
} }
mod nested_main_kcl {
const TEST_NAME: &str = "nested_main_kcl";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME)
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[tokio::test(flavor = "multi_thread")]
async fn unparse() {
super::unparse(TEST_NAME).await
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await
}
}
mod nested_windows_main_kcl {
const TEST_NAME: &str = "nested_windows_main_kcl";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME)
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[tokio::test(flavor = "multi_thread")]
async fn unparse() {
super::unparse(TEST_NAME).await
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await
}
}

View File

@ -0,0 +1,184 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands nested_main_kcl.kcl
---
[
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "make_plane",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"x_axis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"y_axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"size": 60.0,
"clobber": false,
"hide": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "close_path",
"path_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "enable_sketch_mode",
"entity_id": "[uuid]",
"ortho": false,
"animated": false,
"adjust_camera": false,
"planar_normal": {
"x": 0.0,
"y": 0.0,
"z": 1.0
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "extend_path",
"path": "[uuid]",
"segment": {
"type": "arc",
"center": {
"x": 15.0,
"y": 0.0
},
"radius": 5.0,
"start": {
"unit": "degrees",
"value": 0.0
},
"end": {
"unit": "degrees",
"value": 360.0
},
"relative": false
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "move_path_pen",
"path": "[uuid]",
"to": {
"x": 20.0,
"y": 0.0,
"z": 0.0
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "sketch_mode_disable"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "start_path"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_bring_to_front",
"object_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "revolve",
"target": "[uuid]",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"axis_is_2d": true,
"angle": {
"unit": "degrees",
"value": 360.0
},
"tolerance": 0.0000001,
"opposite": "None"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_adjacency_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_extrusion_face_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
}
]

View File

@ -0,0 +1,6 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact graph flowchart nested_main_kcl.kcl
extension: md
snapshot_kind: binary
---

View File

@ -0,0 +1,23 @@
```mermaid
flowchart LR
subgraph path2 [Path]
2["Path<br>[43, 81, 1]"]
3["Segment<br>[43, 81, 1]"]
4[Solid2d]
end
1["Plane<br>[18, 35, 1]"]
5["Sweep Revolve<br>[89, 142, 1]"]
6[Wall]
%% face_code_ref=Missing NodePath
7["SweepEdge Adjacent"]
1 --- 2
2 --- 3
2 --- 4
2 ---- 5
5 <--x 3
3 --- 6
3 --- 7
5 --- 6
5 --- 7
6 --- 7
```

View File

@ -0,0 +1,73 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing nested_main_kcl.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"end": 0,
"path": {
"type": "Kcl",
"filename": "nested/foo/bar/main.kcl"
},
"selector": {
"type": "None",
"alias": {
"commentStart": 0,
"end": 0,
"name": "bar",
"start": 0,
"type": "Identifier"
}
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "bar",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"commentStart": 0,
"end": 0,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,3 @@
import "nested/foo/bar/main.kcl" as bar
bar

View File

@ -0,0 +1,7 @@
// A donut shape.
startSketchOn(XY)
|> circle( center = [15, 0], radius = 5 )
|> revolve(
angle = 360,
axis = Y,
)

View File

@ -0,0 +1,18 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed nested_main_kcl.kcl
---
[
{
"type": "GroupBegin",
"group": {
"type": "ModuleInstance",
"name": "main.kcl",
"moduleId": 0
},
"sourceRange": []
},
{
"type": "GroupEnd"
}
]

View File

@ -0,0 +1,10 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing nested_main_kcl.kcl
---
{
"bar": {
"type": "Module",
"value": 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

@ -0,0 +1,7 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing nested_main_kcl.kcl
---
import "nested/foo/bar/main.kcl" as bar
bar

View File

@ -0,0 +1,8 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing tests/nested_main_kcl/nested/foo/bar/main.kcl
---
// A donut shape.
startSketchOn(XY)
|> circle(center = [15, 0], radius = 5)
|> revolve(angle = 360, axis = Y)

View File

@ -0,0 +1,184 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands nested_main_kcl.kcl
---
[
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "make_plane",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"x_axis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"y_axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"size": 60.0,
"clobber": false,
"hide": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "close_path",
"path_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "enable_sketch_mode",
"entity_id": "[uuid]",
"ortho": false,
"animated": false,
"adjust_camera": false,
"planar_normal": {
"x": 0.0,
"y": 0.0,
"z": 1.0
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "extend_path",
"path": "[uuid]",
"segment": {
"type": "arc",
"center": {
"x": 15.0,
"y": 0.0
},
"radius": 5.0,
"start": {
"unit": "degrees",
"value": 0.0
},
"end": {
"unit": "degrees",
"value": 360.0
},
"relative": false
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "move_path_pen",
"path": "[uuid]",
"to": {
"x": 20.0,
"y": 0.0,
"z": 0.0
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "sketch_mode_disable"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "start_path"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_bring_to_front",
"object_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "revolve",
"target": "[uuid]",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"axis_is_2d": true,
"angle": {
"unit": "degrees",
"value": 360.0
},
"tolerance": 0.0000001,
"opposite": "None"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_adjacency_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_extrusion_face_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
}
]

View File

@ -0,0 +1,6 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact graph flowchart nested_main_kcl.kcl
extension: md
snapshot_kind: binary
---

View File

@ -0,0 +1,23 @@
```mermaid
flowchart LR
subgraph path2 [Path]
2["Path<br>[43, 81, 1]"]
3["Segment<br>[43, 81, 1]"]
4[Solid2d]
end
1["Plane<br>[18, 35, 1]"]
5["Sweep Revolve<br>[89, 142, 1]"]
6[Wall]
%% face_code_ref=Missing NodePath
7["SweepEdge Adjacent"]
1 --- 2
2 --- 3
2 --- 4
2 ---- 5
5 <--x 3
3 --- 6
3 --- 7
5 --- 6
5 --- 7
6 --- 7
```

View File

@ -0,0 +1,73 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing nested_windows_main_kcl.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"end": 0,
"path": {
"type": "Kcl",
"filename": "nested/foo/bar/main.kcl"
},
"selector": {
"type": "None",
"alias": {
"commentStart": 0,
"end": 0,
"name": "bar",
"start": 0,
"type": "Identifier"
}
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "bar",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"commentStart": 0,
"end": 0,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,3 @@
import "nested\foo\bar\main.kcl" as bar
bar

View File

@ -0,0 +1,7 @@
// A donut shape.
startSketchOn(XY)
|> circle( center = [15, 0], radius = 5 )
|> revolve(
angle = 360,
axis = Y,
)

View File

@ -0,0 +1,18 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed nested_main_kcl.kcl
---
[
{
"type": "GroupBegin",
"group": {
"type": "ModuleInstance",
"name": "main.kcl",
"moduleId": 0
},
"sourceRange": []
},
{
"type": "GroupEnd"
}
]

View File

@ -0,0 +1,10 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing nested_main_kcl.kcl
---
{
"bar": {
"type": "Module",
"value": 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

@ -0,0 +1,7 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing nested_windows_main_kcl.kcl
---
import "nested/foo/bar/main.kcl" as bar
bar

View File

@ -0,0 +1,8 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing tests/nested_windows_main_kcl/nested/foo/bar/main.kcl
---
// A donut shape.
startSketchOn(XY)
|> circle(center = [15, 0], radius = 5)
|> revolve(angle = 360, axis = Y)

View File

@ -86,11 +86,11 @@ flowchart LR
8 --- 18 8 --- 18
8 ---- 20 8 ---- 20
8 --- 21 8 --- 21
12 --- 22 12 <--x 22
12 <--x 23 12 --- 23
12 <--x 24 12 <--x 24
16 --- 25 16 <--x 25
16 <--x 26 16 --- 26
16 <--x 27 16 <--x 27
19 --- 22 19 --- 22
19 --- 23 19 --- 23