Compare commits

...

17 Commits

Author SHA1 Message Date
57e030a0ad Check settings file exists before checking its contents 2024-10-31 10:32:58 -04:00
0adecf4f58 Merge branch 'main' into franknoirot/adhoc/await-settings-write-test 2024-10-31 07:08:00 -07:00
26e995dc3f Snap to origin and axis behavior for profile starts and segments (#4344)
* Visualize draft point when near axes (only works on XY rn due to quaternion rotation issue)

* Slightly better quaternion rotation

* Actually snap new profiles to the X and Y axis

* Add snapping behavior while dragging

* Fix flickering on non-XY planes

* Add some fixture additions to support click-and-drag tests

* Add new test to verify snapping behavior

* Make the editor test fixture auto-open and close as needed

* All feedback except absolute lines

* Use `lineTo` for lines that have snapped

* Get other existing tests passing after switching to `lineTo` when snapping

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)

* Re-run CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-31 10:04:38 -04:00
08e411e1a3 Test fix: Don't click the settings close button until we confirm a write 2024-10-31 10:01:48 -04:00
a8b816a3e2 Added test to ensure array push is immutable (#4361)
added test to ensure array push is immutable
2024-10-30 23:04:26 +00:00
43bec115c0 Refactor source ranges into a generic node type (#4350)
* WIP

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Fix formatting

* Fix yarn build:wasm

* Fix ts_rs bindings

* Fix tsc errors

* Fix wasm TS types

* Add minimal failing test

* Rename field to avoid name collisions

* Remove node wrapper around NonCodeMeta

Trying to fix TS unit test errors deserializing JSON AST in Rust.

* Rename Node to BoxNode

* Fix lints

* Fix lint by boxing literals

* Rename UnboxedNode to Node

* Look at this (photo)Graph *in the voice of Nickelback*

* Update docs

* Update snapshots

* initial trait

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

* update docs

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

* updates

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

* gross hack for TagNode

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

* extend gross hack

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

* fix EnvRef bullshit

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

* Fix to fail parsing when a tag declarator matches a stdlib function name

* Fix test errors after merging main

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)

* Confirm

* Change to use simpler map_err

* Add comment

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
2024-10-30 20:52:17 +00:00
0c6c646fe7 KCL: New simulation test pipeline (#4351)
The idea behind this is to test all the various stages of executing KCL
separately, i.e.

 - Start with a program
 - Tokenize it
 - Parse those tokens into an AST
 - Recast the AST
 - Execute the AST, outputting
   - a PNG of the rendered model
   - serialized program memory

Each of these steps reads some input and writes some output to disk.
The output of one step becomes the input to the next step. These
intermediate artifacts are also snapshotted (like expectorate or 2020)
to ensure we're aware of any changes to how KCL works. A change could
be a bug, or it could be harmless, or deliberate, but keeping it checked
into the repo means we can easily track changes.

Note: UUIDs sent back by the engine are currently nondeterministic, so
they would break all the snapshot tests. So, the snapshots use a regex
filter and replace anything that looks like a uuid with [uuid] when
writing program memory to a snapshot. In the future I hope our UUIDs will
be seedable and easy to make deterministic. At that point, we can stop
filtering the UUIDs.

We run this pipeline on many different KCL programs. Each keeps its
inputs (KCL programs), outputs (PNG, program memory snapshot) and
intermediate artifacts (AST, token lists, etc) in that directory.

I also added a new `just` command to easily generate these tests.
You can run `just new-sim-test gear $(cat gear.kcl)` to set up a new
gear test directory and generate all the intermediate artifacts for the
first time. This doesn't need any macros, it just appends some new lines
of normal Rust source code to `tests.rs`, so it's easy to see exactly
what the code is doing.

This uses `cargo insta` for convenient snapshot testing of artifacts
as JSON, and `twenty-twenty` for snapshotting PNGs.

This was heavily inspired by Predrag Gruevski's talk at EuroRust 2024
about deterministic simulation testing, and how it can both reduce bugs
and also reduce testing/CI time. Very grateful to him for chatting with
me about this over the last couple of weeks.
2024-10-30 12:14:17 -05:00
0d52851da2 Bump serde from 1.0.213 to 1.0.214 in /src/wasm-lib (#4345)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.213 to 1.0.214.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.213...v1.0.214)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 49fl <ircsurfer33@gmail.com>
2024-10-29 23:12:44 -04:00
6b105897f7 Bump handlebars from 6.1.0 to 6.2.0 in /src/wasm-lib (#4330)
Bumps [handlebars](https://github.com/sunng87/handlebars-rust) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/sunng87/handlebars-rust/releases)
- [Changelog](https://github.com/sunng87/handlebars-rust/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sunng87/handlebars-rust/compare/v6.1.0...v6.2.0)

---
updated-dependencies:
- dependency-name: handlebars
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 49fl <ircsurfer33@gmail.com>
2024-10-29 23:01:31 -04:00
9ff51de301 fix auth test in engine (#4354)
* fix auth test in engine

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

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* emoty

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-29 19:24:24 -07:00
c161f578fd KCL test for subset of poop chute (#4343)
This would have caught the regression in https://github.com/KittyCAD/modeling-app/pull/4333

which had to be reverted in https://github.com/KittyCAD/modeling-app/pull/4339
2024-10-30 02:17:48 +00:00
4804eedf3e Bump react-router-dom from 6.26.1 to 6.27.0 (#4286)
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.26.1 to 6.27.0.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/react-router-dom@6.27.0/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.27.0/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-29 22:17:19 -04:00
99db31a6a4 Tests: remove all timeouts and pasting into editor from file name collision PW test (#4352)
remove all timeouts and pasting into editor from file name collision PW test
2024-10-29 21:42:53 -04:00
90b57ec202 Add lsystem.kcl to tests (#4146)
* Add lsystem.kcl to tests

* Reduce iterations

* Fix the user settings flake shit (NOTE TO ALL FUTURE PEOPLE MODELING-APP DOES NOT WAIT FOR I/O IN SOME CASES BEFORE ROUTER NAVIGATION)
2024-10-29 21:40:31 -04:00
3f86f99f5e Fix just lint and yarn script to check all targets (#4348)
* Fix just lint to check all targets

* Fix yarn test:rust to lint all targets

* Remove redundant options

* Change cargo --all to --workspace

* Update readme to use just command
2024-10-29 19:46:59 +00:00
83e2b093a6 Deflake settings persistence desktop test by verifying we have written to the disk before continuing (#4349) 2024-10-29 12:31:52 -04:00
58f7e0086d Fix CI docs generation after #4329 (#4347)
Fix CI
2024-10-29 14:39:50 +00:00
194 changed files with 30714 additions and 26865 deletions

View File

@ -37,4 +37,4 @@ jobs:
# We specifically want to test the disable-println feature # We specifically want to test the disable-println feature
# Since it is not enabled by default, we need to specify it # Since it is not enabled by default, we need to specify it
# This is used in kcl-lsp # This is used in kcl-lsp
cargo check --all --features disable-println --features pyo3 --features cli cargo check --workspace --features disable-println --features pyo3 --features cli

File diff suppressed because it is too large Load Diff

View File

@ -23,11 +23,11 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `Literal`| | No | | `type` |enum: `Literal`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No | | `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
| `raw` |`string`| | No | | `raw` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -43,10 +43,10 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No | | `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `name` |`string`| | No | | `name` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -62,12 +62,12 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `BinaryExpression`| | No | | `type` |enum: `BinaryExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| | No | | `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| | No |
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No | | `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No | | `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -83,12 +83,12 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `CallExpression`| | No | | `type` |enum: `CallExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No | | `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No | | `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
| `optional` |`boolean`| | No | | `optional` |`boolean`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -104,11 +104,11 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `UnaryExpression`| | No | | `type` |enum: `UnaryExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| | No | | `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| | No |
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No | | `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -124,12 +124,12 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `MemberExpression`| | No | | `type` |enum: `MemberExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No | | `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No | | `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
| `computed` |`boolean`| | No | | `computed` |`boolean`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -145,13 +145,13 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `IfExpression`| | No | | `type` |enum: `IfExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No | | `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No | | `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No | | `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
| `final_else` |[`Program`](/docs/kcl/types/Program)| | No | | `final_else` |[`Program`](/docs/kcl/types/Program)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----

View File

@ -23,12 +23,12 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `ImportStatement`| | No | | `type` |enum: `ImportStatement`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No | | `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No |
| `path` |`string`| | No | | `path` |`string`| | No |
| `raw_path` |`string`| | No | | `raw_path` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -44,10 +44,10 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `ExpressionStatement`| | No | | `type` |enum: `ExpressionStatement`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `expression` |[`Expr`](/docs/kcl/types/Expr)| | No | | `expression` |[`Expr`](/docs/kcl/types/Expr)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -63,12 +63,12 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `VariableDeclaration`| | No | | `type` |enum: `VariableDeclaration`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No | | `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No | | `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No |
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No | | `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -84,10 +84,10 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `ReturnStatement`| | No | | `type` |enum: `ReturnStatement`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `argument` |[`Expr`](/docs/kcl/types/Expr)| | No | | `argument` |[`Expr`](/docs/kcl/types/Expr)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----

View File

@ -15,10 +15,10 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No | | `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No | | `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

View File

@ -16,6 +16,6 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `bindings` |`object`| | No | | `bindings` |`object`| | No |
| `parent` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No | | `parent` |`integer`| | No |

View File

@ -24,11 +24,11 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `Literal`| | No | | `type` |enum: `Literal`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| An expression can be evaluated to yield a single KCL value. | No | | `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| An expression can be evaluated to yield a single KCL value. | No |
| `raw` |`string`| | No | | `raw` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -44,10 +44,10 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No | | `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `name` |`string`| | No | | `name` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -63,10 +63,10 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No | | `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `value` |`string`| | No | | `value` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -82,12 +82,12 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `BinaryExpression`| | No | | `type` |enum: `BinaryExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| An expression can be evaluated to yield a single KCL value. | No | | `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No | | `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No | | `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -103,11 +103,11 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No | | `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No | | `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
| `body` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No | | `body` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -123,12 +123,12 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `CallExpression`| | No | | `type` |enum: `CallExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No | | `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No |
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No | | `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
| `optional` |`boolean`| | No | | `optional` |`boolean`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -144,11 +144,11 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `PipeExpression`| | No | | `type` |enum: `PipeExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `body` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No | | `body` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No | | `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -164,9 +164,9 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `PipeSubstitution`| | No | | `type` |enum: `PipeSubstitution`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -182,11 +182,11 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `ArrayExpression`| | No | | `type` |enum: `ArrayExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `elements` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No | | `elements` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No | | `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -202,12 +202,12 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `ArrayRangeExpression`| | No | | `type` |enum: `ArrayRangeExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `startElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No | | `startElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
| `endElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No | | `endElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
| `endInclusive` |`boolean`| Is the `end_element` included in the range? | No | | `endInclusive` |`boolean`| Is the `end_element` included in the range? | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -223,11 +223,11 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `ObjectExpression`| | No | | `type` |enum: `ObjectExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `properties` |`[` [`ObjectProperty`](/docs/kcl/types/ObjectProperty) `]`| | No | | `properties` |`[` [`ObjectProperty`](/docs/kcl/types/ObjectProperty) `]`| | No |
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No | | `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -243,12 +243,12 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `MemberExpression`| | No | | `type` |enum: `MemberExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| An expression can be evaluated to yield a single KCL value. | No | | `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| An expression can be evaluated to yield a single KCL value. | No |
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| An expression can be evaluated to yield a single KCL value. | No | | `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| An expression can be evaluated to yield a single KCL value. | No |
| `computed` |`boolean`| | No | | `computed` |`boolean`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -264,11 +264,11 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `UnaryExpression`| | No | | `type` |enum: `UnaryExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| An expression can be evaluated to yield a single KCL value. | No | | `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No | | `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -284,13 +284,13 @@ An expression can be evaluated to yield a single KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `IfExpression`| | No | | `type` |enum: `IfExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `cond` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No | | `cond` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
| `then_val` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No | | `then_val` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No | | `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
| `final_else` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No | | `final_else` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -307,8 +307,8 @@ KCL value for an optional parameter which was not given an argument. (remember,
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `None`| | No | | `type` |enum: `None`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No | | `start` |`integer`| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No | | `end` |`integer`| | No |
---- ----

View File

@ -15,10 +15,10 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No | | `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
| `body` |[`Program`](/docs/kcl/types/Program)| | No | | `body` |[`Program`](/docs/kcl/types/Program)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

View File

@ -15,9 +15,9 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `name` |`string`| | No | | `name` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

View File

@ -17,8 +17,8 @@ layout: manual
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No | | `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No |
| `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No | | `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

View File

@ -59,10 +59,10 @@ Any KCL value.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No | | `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `value` |`string`| | No | | `value` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----

View File

@ -23,10 +23,10 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No | | `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `name` |`string`| | No | | `name` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -42,11 +42,11 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `Literal`| | No | | `type` |enum: `Literal`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No | | `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
| `raw` |`string`| | No | | `raw` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----

View File

@ -23,12 +23,12 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `MemberExpression`| | No | | `type` |enum: `MemberExpression`| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No | | `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No | | `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
| `computed` |`boolean`| | No | | `computed` |`boolean`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----
@ -44,10 +44,10 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No | | `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `name` |`string`| | No | | `name` |`string`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |
---- ----

View File

@ -16,7 +16,7 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `nonCodeNodes` |`object`| | No | | `nonCodeNodes` |`object`| | No |
| `start` |`[` [`NonCodeNode`](/docs/kcl/types/NonCodeNode) `]`| | No | | `startNodes` |`[` [`NonCodeNode`](/docs/kcl/types/NonCodeNode) `]`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |

View File

@ -15,9 +15,9 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `value` |[`NonCodeValue`](/docs/kcl/types/NonCodeValue)| | No | | `value` |[`NonCodeValue`](/docs/kcl/types/NonCodeValue)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

View File

@ -15,10 +15,10 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `key` |[`Identifier`](/docs/kcl/types/Identifier)| | No | | `key` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
| `value` |[`Expr`](/docs/kcl/types/Expr)| | No | | `value` |[`Expr`](/docs/kcl/types/Expr)| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

View File

@ -16,10 +16,10 @@ A KCL program top level, or function body.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No | | `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No | | `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

View File

@ -16,7 +16,7 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `environments` |`[` [`Environment`](/docs/kcl/types/Environment) `]`| | No | | `environments` |`[` [`Environment`](/docs/kcl/types/Environment) `]`| | No |
| `currentEnv` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No | | `currentEnv` |`integer`| | No |
| `return` |[`KclValue`](/docs/kcl/types/KclValue)| | No | | `return` |[`KclValue`](/docs/kcl/types/KclValue)| | No |

View File

@ -15,10 +15,10 @@ layout: manual
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `id` |[`Identifier`](/docs/kcl/types/Identifier)| The identifier of the variable. | No | | `id` |[`Identifier`](/docs/kcl/types/Identifier)| The identifier of the variable. | No |
| `init` |[`Expr`](/docs/kcl/types/Expr)| The value of the variable. | No | | `init` |[`Expr`](/docs/kcl/types/Expr)| The value of the variable. | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No | | `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

View File

@ -86,7 +86,7 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %) |> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1 + 0.01}], %) |> line([0, ${commonPoints.num1 + 0.01}], %)
|> line([-${commonPoints.num2}, 0], %)`) |> lineTo([0, ${commonPoints.num3}], %)`)
} }
// deselect line tool // deselect line tool

View File

@ -193,61 +193,74 @@ test.describe('when using the file tree to', () => {
test( test(
'create a new file with the same name as an existing file cancels the operation', 'create a new file with the same name as an existing file cancels the operation',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browser: _, tronApp }, testInfo) => { async (
await tronApp.initialise() { browser: _, tronApp, homePage, scene, editor, toolbar },
testInfo
) => {
const projectName = 'cube'
const mainFile = 'main.kcl'
const secondFile = 'cylinder.kcl'
const kclCube = await fsp.readFile(executorInputPath('cube.kcl'), 'utf-8')
const kclCylinder = await fsp.readFile(
executorInputPath('cylinder.kcl'),
'utf-8'
)
await tronApp.initialise({
fixtures: { homePage, scene, editor, toolbar },
folderSetupFn: async (dir) => {
const cubeDir = join(dir, projectName)
await fsp.mkdir(cubeDir, { recursive: true })
await fsp.copyFile(
executorInputPath('cube.kcl'),
join(cubeDir, mainFile)
)
await fsp.copyFile(
executorInputPath('cylinder.kcl'),
join(cubeDir, secondFile)
)
},
})
const { const {
openKclCodePanel,
openFilePanel, openFilePanel,
pasteCodeInEditor,
createNewFileAndSelect,
renameFile, renameFile,
selectFile, selectFile,
editorTextMatches, editorTextMatches,
waitForPageLoad,
} = await getUtils(tronApp.page, _test) } = await getUtils(tronApp.page, _test)
await tronApp.page.setViewportSize({ width: 1200, height: 500 }) await test.step(`Setup: Open project and navigate to ${secondFile}`, async () => {
tronApp.page.on('console', console.log) await homePage.expectState({
projectCards: [
await createProject({ name: 'project-000', page: tronApp.page }) {
await openKclCodePanel() title: projectName,
fileCount: 2,
folderCount: 2, // TODO: This is a pre-existing bug, there are no folders within the project
},
],
sortBy: 'last-modified-desc',
})
await homePage.openProject(projectName)
await waitForPageLoad()
await openFilePanel() await openFilePanel()
// File the main.kcl with contents await selectFile(secondFile)
const kclCube = await fsp.readFile( })
'src/wasm-lib/tests/executor/inputs/cube.kcl',
'utf-8'
)
await pasteCodeInEditor(kclCube)
// TODO: We have a timeout of 1s between edits to write to disk. If you reload the page too quickly it won't write to disk. await test.step(`Attempt to rename ${secondFile} to ${mainFile}`, async () => {
await tronApp.page.waitForTimeout(2000) await renameFile(secondFile, mainFile)
})
const kcl1 = 'main.kcl' await test.step(`Postcondition: ${mainFile} still has the original content`, async () => {
const kcl2 = '2.kcl' await selectFile(mainFile)
await createNewFileAndSelect(kcl2)
const kclCylinder = await fsp.readFile(
'src/wasm-lib/tests/executor/inputs/cylinder.kcl',
'utf-8'
)
await pasteCodeInEditor(kclCylinder)
// TODO: We have a timeout of 1s between edits to write to disk. If you reload the page too quickly it won't write to disk.
await tronApp.page.waitForTimeout(2000)
await renameFile(kcl2, kcl1)
await test.step(`Postcondition: ${kcl1} still has the original content`, async () => {
await selectFile(kcl1)
await editorTextMatches(kclCube) await editorTextMatches(kclCube)
}) })
await tronApp.page.waitForTimeout(500)
await test.step(`Postcondition: ${kcl2} still exists with the original content`, async () => { await test.step(`Postcondition: ${secondFile} still exists with the original content`, async () => {
await selectFile(kcl2) await selectFile(secondFile)
await editorTextMatches(kclCylinder) await editorTextMatches(kclCylinder)
}) })
await tronApp?.close?.() await tronApp.close()
} }
) )
@ -273,11 +286,11 @@ test.describe('when using the file tree to', () => {
) )
await pasteCodeInEditor(kclCube) await pasteCodeInEditor(kclCube)
const kcl1 = 'main.kcl' const mainFile = 'main.kcl'
await deleteFile(kcl1) await deleteFile(mainFile)
await test.step(`Postcondition: ${kcl1} is recreated but has no content`, async () => { await test.step(`Postcondition: ${mainFile} is recreated but has no content`, async () => {
await editorTextMatches('') await editorTextMatches('')
}) })

View File

@ -1,6 +1,11 @@
import type { Page, Locator } from '@playwright/test' import type { Page, Locator } from '@playwright/test'
import { expect } from '@playwright/test' import { expect } from '@playwright/test'
import { sansWhitespace } from '../test-utils' import {
closePane,
checkIfPaneIsOpen,
openPane,
sansWhitespace,
} from '../test-utils'
interface EditorState { interface EditorState {
activeLines: Array<string> activeLines: Array<string>
@ -11,6 +16,7 @@ interface EditorState {
export class EditorFixture { export class EditorFixture {
public page: Page public page: Page
private paneButtonTestId = 'code-pane-button'
private diagnosticsTooltip!: Locator private diagnosticsTooltip!: Locator
private diagnosticsGutterIcon!: Locator private diagnosticsGutterIcon!: Locator
private codeContent!: Locator private codeContent!: Locator
@ -31,19 +37,32 @@ export class EditorFixture {
private _expectEditorToContain = private _expectEditorToContain =
(not = false) => (not = false) =>
( async (
code: string, code: string,
{ {
shouldNormalise = false, shouldNormalise = false,
timeout = 5_000, timeout = 5_000,
}: { shouldNormalise?: boolean; timeout?: number } = {} }: { shouldNormalise?: boolean; timeout?: number } = {}
) => { ) => {
const wasPaneOpen = await this.checkIfPaneIsOpen()
if (!wasPaneOpen) {
await this.openPane()
}
const resetPane = async () => {
if (!wasPaneOpen) {
await this.closePane()
}
}
if (!shouldNormalise) { if (!shouldNormalise) {
const expectStart = expect(this.codeContent) const expectStart = expect(this.codeContent)
if (not) { if (not) {
return expectStart.not.toContainText(code, { timeout }) const result = await expectStart.not.toContainText(code, { timeout })
await resetPane()
return result
} }
return expectStart.toContainText(code, { timeout }) const result = await expectStart.toContainText(code, { timeout })
await resetPane()
return result
} }
const normalisedCode = code.replaceAll(/\s+/g, '').trim() const normalisedCode = code.replaceAll(/\s+/g, '').trim()
const expectStart = expect.poll( const expectStart = expect.poll(
@ -56,9 +75,13 @@ export class EditorFixture {
} }
) )
if (not) { if (not) {
return expectStart.not.toContain(normalisedCode) const result = await expectStart.not.toContain(normalisedCode)
await resetPane()
return result
} }
return expectStart.toContain(normalisedCode) const result = await expectStart.toContain(normalisedCode)
await resetPane()
return result
} }
expectEditor = { expectEditor = {
toContain: this._expectEditorToContain(), toContain: this._expectEditorToContain(),
@ -115,4 +138,13 @@ export class EditorFixture {
code = code.replace(findCode, replaceCode) code = code.replace(findCode, replaceCode)
await this.codeContent.fill(code) await this.codeContent.fill(code)
} }
checkIfPaneIsOpen() {
return checkIfPaneIsOpen(this.page, this.paneButtonTestId)
}
closePane() {
return closePane(this.page, this.paneButtonTestId)
}
openPane() {
return openPane(this.page, this.paneButtonTestId)
}
} }

View File

@ -20,6 +20,7 @@ export class AuthenticatedApp {
public readonly page: Page public readonly page: Page
public readonly context: BrowserContext public readonly context: BrowserContext
public readonly testInfo: TestInfo public readonly testInfo: TestInfo
public readonly viewPortSize = { width: 1000, height: 500 }
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) { constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
this.page = page this.page = page
@ -36,7 +37,7 @@ export class AuthenticatedApp {
;(window as any).playwrightSkipFilePicker = true ;(window as any).playwrightSkipFilePicker = true
}, code) }, code)
await this.page.setViewportSize({ width: 1000, height: 500 }) await this.page.setViewportSize(this.viewPortSize)
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
} }

View File

@ -10,7 +10,13 @@ import {
} from '../test-utils' } from '../test-utils'
type mouseParams = { type mouseParams = {
pixelDiff: number pixelDiff?: number
}
type mouseDragToParams = mouseParams & {
fromPoint: { x: number; y: number }
}
type mouseDragFromParams = mouseParams & {
toPoint: { x: number; y: number }
} }
type SceneSerialised = { type SceneSerialised = {
@ -20,6 +26,13 @@ type SceneSerialised = {
} }
} }
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
type DragFromHandler = (
dragParams: mouseDragFromParams
) => Promise<void | boolean>
export class SceneFixture { export class SceneFixture {
public page: Page public page: Page
@ -55,7 +68,7 @@ export class SceneFixture {
x: number, x: number,
y: number, y: number,
{ steps }: { steps: number } = { steps: 20 } { steps }: { steps: number } = { steps: 20 }
) => ): [ClickHandler, MoveHandler] =>
[ [
(clickParams?: mouseParams) => { (clickParams?: mouseParams) => {
if (clickParams?.pixelDiff) { if (clickParams?.pixelDiff) {
@ -78,6 +91,47 @@ export class SceneFixture {
return this.page.mouse.move(x, y, { steps }) return this.page.mouse.move(x, y, { steps })
}, },
] as const ] as const
makeDragHelpers = (
x: number,
y: number,
{ steps }: { steps: number } = { steps: 20 }
): [DragToHandler, DragFromHandler] =>
[
(dragToParams: mouseDragToParams) => {
if (dragToParams?.pixelDiff) {
return doAndWaitForImageDiff(
this.page,
() =>
this.page.dragAndDrop('#stream', '#stream', {
sourcePosition: dragToParams.fromPoint,
targetPosition: { x, y },
}),
dragToParams.pixelDiff
)
}
return this.page.dragAndDrop('#stream', '#stream', {
sourcePosition: dragToParams.fromPoint,
targetPosition: { x, y },
})
},
(dragFromParams: mouseDragFromParams) => {
if (dragFromParams?.pixelDiff) {
return doAndWaitForImageDiff(
this.page,
() =>
this.page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x, y },
targetPosition: dragFromParams.toPoint,
}),
dragFromParams.pixelDiff
)
}
return this.page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x, y },
targetPosition: dragFromParams.toPoint,
})
},
] as const
/** Likely no where, there's a chance it will click something in the scene, depending what you have in the scene. /** Likely no where, there's a chance it will click something in the scene, depending what you have in the scene.
* *

View File

@ -7,6 +7,7 @@ export class ToolbarFixture {
extrudeButton!: Locator extrudeButton!: Locator
startSketchBtn!: Locator startSketchBtn!: Locator
lineBtn!: Locator
rectangleBtn!: Locator rectangleBtn!: Locator
exitSketchBtn!: Locator exitSketchBtn!: Locator
editSketchBtn!: Locator editSketchBtn!: Locator
@ -24,6 +25,7 @@ export class ToolbarFixture {
this.page = page this.page = page
this.extrudeButton = page.getByTestId('extrude') this.extrudeButton = page.getByTestId('extrude')
this.startSketchBtn = page.getByTestId('sketch') this.startSketchBtn = page.getByTestId('sketch')
this.lineBtn = page.getByTestId('line')
this.rectangleBtn = page.getByTestId('corner-rectangle') this.rectangleBtn = page.getByTestId('corner-rectangle')
this.exitSketchBtn = page.getByTestId('sketch-exit') this.exitSketchBtn = page.getByTestId('sketch-exit')
this.editSketchBtn = page.getByText('Edit Sketch') this.editSketchBtn = page.getByText('Edit Sketch')

View File

@ -451,3 +451,103 @@ sketch002 = startSketchOn(extrude001, seg03)
} }
) )
}) })
test(`Verify axis and origin snapping`, async ({
app,
editor,
toolbar,
scene,
}) => {
// Constants and locators
// These are mappings from screenspace to KCL coordinates,
// until we merge in our coordinate system helpers
const xzPlane = [
app.viewPortSize.width * 0.65,
app.viewPortSize.height * 0.3,
] as const
const originSloppy = {
screen: [
app.viewPortSize.width / 2 + 3, // 3px off the center of the screen
app.viewPortSize.height / 2,
],
kcl: [0, 0],
} as const
const xAxisSloppy = {
screen: [
app.viewPortSize.width * 0.75,
app.viewPortSize.height / 2 - 3, // 3px off the X-axis
],
kcl: [16.95, 0],
} as const
const offYAxis = {
screen: [
app.viewPortSize.width * 0.6, // Well off the Y-axis, out of snapping range
app.viewPortSize.height * 0.3,
],
kcl: [6.78, 6.78],
} as const
const yAxisSloppy = {
screen: [
app.viewPortSize.width / 2 + 5, // 5px off the Y-axis
app.viewPortSize.height * 0.3,
],
kcl: [0, 6.78],
} as const
const [clickOnXzPlane, moveToXzPlane] = scene.makeMouseHelpers(...xzPlane)
const [clickOriginSloppy] = scene.makeMouseHelpers(...originSloppy.screen)
const [clickXAxisSloppy, moveXAxisSloppy] = scene.makeMouseHelpers(
...xAxisSloppy.screen
)
const [dragToOffYAxis, dragFromOffAxis] = scene.makeDragHelpers(
...offYAxis.screen
)
const expectedCodeSnippets = {
sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`,
pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], %)`,
segmentOnXAxis: `lineTo([${xAxisSloppy.kcl[0]}, ${xAxisSloppy.kcl[1]}], %)`,
afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], %)`,
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
}
await app.initialise()
await test.step(`Start a sketch on the XZ plane`, async () => {
await editor.closePane()
await toolbar.startSketchPlaneSelection()
await moveToXzPlane()
await clickOnXzPlane()
// timeout wait for engine animation is unavoidable
await app.page.waitForTimeout(600)
await editor.expectEditor.toContain(expectedCodeSnippets.sketchOnXzPlane)
})
await test.step(`Place a point a few pixels off the middle, verify it still snaps to 0,0`, async () => {
await clickOriginSloppy()
await editor.expectEditor.toContain(expectedCodeSnippets.pointAtOrigin)
})
await test.step(`Add a segment on x-axis after moving the mouse a bit, verify it snaps`, async () => {
await moveXAxisSloppy()
await clickXAxisSloppy()
await editor.expectEditor.toContain(expectedCodeSnippets.segmentOnXAxis)
})
await test.step(`Unequip line tool`, async () => {
await toolbar.lineBtn.click()
await expect(toolbar.lineBtn).not.toHaveAttribute('aria-pressed', 'true')
})
await test.step(`Drag the origin point up and to the right, verify it's past snapping`, async () => {
await dragToOffYAxis({
fromPoint: { x: originSloppy.screen[0], y: originSloppy.screen[1] },
})
await editor.expectEditor.toContain(
expectedCodeSnippets.afterSegmentDraggedOffYAxis
)
})
await test.step(`Drag the origin point left to the y-axis, verify it snaps back`, async () => {
await dragFromOffAxis({
toPoint: { x: yAxisSloppy.screen[0], y: yAxisSloppy.screen[1] },
})
await editor.expectEditor.toContain(
expectedCodeSnippets.afterSegmentDraggedOnYAxis
)
})
})

View File

@ -1497,6 +1497,7 @@ test(
'i_shape.kcl', 'i_shape.kcl',
'kittycad_svg.kcl', 'kittycad_svg.kcl',
'lego.kcl', 'lego.kcl',
'lsystem.kcl',
'math.kcl', 'math.kcl',
'member_expression_sketch.kcl', 'member_expression_sketch.kcl',
'mike_stress_test.kcl', 'mike_stress_test.kcl',

View File

@ -637,7 +637,6 @@ test.describe('Sketch tests', () => {
|> revolve({ axis: "X" }, %)`) |> revolve({ axis: "X" }, %)`)
}) })
test('Can add multiple sketches', async ({ page }) => { test('Can add multiple sketches', async ({ page }) => {
test.skip(process.platform === 'darwin', 'Can add multiple sketches')
const u = await getUtils(page) const u = await getUtils(page)
const viewportSize = { width: 1200, height: 500 } const viewportSize = { width: 1200, height: 500 }
await page.setViewportSize(viewportSize) await page.setViewportSize(viewportSize)
@ -675,15 +674,16 @@ test.describe('Sketch tests', () => {
await click00r(50, 0) await click00r(50, 0)
await page.waitForTimeout(100) await page.waitForTimeout(100)
codeStr += ` |> line(${toSU([50, 0])}, %)` codeStr += ` |> lineTo(${toSU([50, 0])}, %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
await click00r(0, 50) await click00r(0, 50)
codeStr += ` |> line(${toSU([0, 50])}, %)` codeStr += ` |> line(${toSU([0, 50])}, %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
await click00r(-50, 0) let clickCoords = await click00r(-50, 0)
codeStr += ` |> line(${toSU([-50, 0])}, %)` expect(clickCoords).not.toBeUndefined()
codeStr += ` |> lineTo(${toSU(clickCoords!)}, %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
// exit the sketch, reset relative clicker // exit the sketch, reset relative clicker
@ -709,8 +709,10 @@ test.describe('Sketch tests', () => {
codeStr += ` |> startProfileAt([2.03, 0], %)` codeStr += ` |> startProfileAt([2.03, 0], %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
// TODO: I couldn't use `toSU` here because of some rounding error causing
// it to be off by 0.01
await click00r(30, 0) await click00r(30, 0)
codeStr += ` |> line([2.04, 0], %)` codeStr += ` |> lineTo([4.07, 0], %)`
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
await click00r(0, 30) await click00r(0, 30)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -219,7 +219,7 @@ test.describe('Test network and connection issues', () => {
|> startProfileAt([12.34, -12.34], %) |> startProfileAt([12.34, -12.34], %)
|> line([12.34, 0], %) |> line([12.34, 0], %)
|> line([-12.34, 12.34], %) |> line([-12.34, 12.34], %)
|> line([-12.34, 0], %) |> lineTo([0, -12.34], %)
`) `)

View File

@ -45,7 +45,9 @@ export const commonPoints = {
startAt: '[7.19, -9.7]', startAt: '[7.19, -9.7]',
num1: 7.25, num1: 7.25,
num2: 14.44, num2: 14.44,
} /** The Y-value of a common lineTo move we perform in tests */
num3: -2.44,
} as const
/** A semi-reliable color to check the default XZ plane on /** A semi-reliable color to check the default XZ plane on
* in dark mode in the default camera position * in dark mode in the default camera position
@ -118,15 +120,32 @@ async function waitForDefaultPlanesToBeVisible(page: Page) {
) )
} }
async function openPane(page: Page, testId: string) { export async function checkIfPaneIsOpen(page: Page, testId: string) {
const locator = page.getByTestId(testId) const paneButtonLocator = page.getByTestId(testId)
await expect(locator).toBeVisible() await expect(paneButtonLocator).toBeVisible()
const isOpen = (await locator?.getAttribute('aria-pressed')) === 'true' return (await paneButtonLocator?.getAttribute('aria-pressed')) === 'true'
}
export async function openPane(page: Page, testId: string) {
const paneButtonLocator = page.getByTestId(testId)
await expect(paneButtonLocator).toBeVisible()
const isOpen = await checkIfPaneIsOpen(page, testId)
if (!isOpen) { if (!isOpen) {
await locator.click() await paneButtonLocator.click()
await expect(locator).toHaveAttribute('aria-pressed', 'true')
} }
await expect(paneButtonLocator).toHaveAttribute('aria-pressed', 'true')
}
export async function closePane(page: Page, testId: string) {
const paneButtonLocator = page.getByTestId(testId)
await expect(paneButtonLocator).toBeVisible()
const isOpen = await checkIfPaneIsOpen(page, testId)
if (isOpen) {
await paneButtonLocator.click()
}
await expect(paneButtonLocator).toHaveAttribute('aria-pressed', 'false')
} }
async function openKclCodePanel(page: Page) { async function openKclCodePanel(page: Page) {
@ -506,6 +525,9 @@ export async function getUtils(page: Page, test_?: typeof test) {
.locator('[data-testid="file-pane-scroll-container"] button') .locator('[data-testid="file-pane-scroll-container"] button')
.filter({ hasText: name }) .filter({ hasText: name })
.click() .click()
await expect(page.getByTestId('project-sidebar-toggle')).toContainText(
name
)
}) })
}, },

View File

@ -96,7 +96,7 @@ test.describe('Testing selections', () => {
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %) |> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1 + 0.01}], %) |> line([0, ${commonPoints.num1 + 0.01}], %)
|> line([-${commonPoints.num2}, 0], %)`) |> lineTo([0, ${commonPoints.num3}], %)`)
// deselect line tool // deselect line tool
await page.getByRole('button', { name: 'line Line', exact: true }).click() await page.getByRole('button', { name: 'line Line', exact: true }).click()
@ -157,7 +157,9 @@ test.describe('Testing selections', () => {
await emptySpaceClick() await emptySpaceClick()
// check the same selection again by putting cursor in code first then selecting axis // check the same selection again by putting cursor in code first then selecting axis
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click() await page
.getByText(` |> lineTo([0, ${commonPoints.num3}], %)`)
.click()
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
await constrainButton.click() await constrainButton.click()
await expect(absYButton).toBeDisabled() await expect(absYButton).toBeDisabled()
@ -180,7 +182,9 @@ test.describe('Testing selections', () => {
process.platform === 'linux' ? 'Control' : 'Meta' process.platform === 'linux' ? 'Control' : 'Meta'
) )
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click() await page
.getByText(` |> lineTo([0, ${commonPoints.num3}], %)`)
.click()
await expect(page.locator('.cm-cursor')).toHaveCount(2) await expect(page.locator('.cm-cursor')).toHaveCount(2)
await page.waitForTimeout(500) await page.waitForTimeout(500)

View File

@ -1,5 +1,6 @@
import { test, expect } from '@playwright/test' import { test, expect } from '@playwright/test'
import * as fsp from 'fs/promises' import * as fsp from 'fs/promises'
import * as fs from 'fs'
import { join } from 'path' import { join } from 'path'
import { import {
getUtils, getUtils,
@ -266,10 +267,15 @@ test.describe('Testing settings', () => {
process.platform === 'win32', process.platform === 'win32',
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557' 'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
) )
const { electronApp, page } = await setupElectron({ const projectName = 'bracket'
const {
electronApp,
page,
dir: projectDirName,
} = await setupElectron({
testInfo, testInfo,
folderSetupFn: async (dir) => { folderSetupFn: async (dir) => {
const bracketDir = join(dir, 'bracket') const bracketDir = join(dir, projectName)
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile( await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'), executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
@ -281,6 +287,12 @@ test.describe('Testing settings', () => {
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
// Selectors and constants // Selectors and constants
const tempProjectSettingsFilePath = join(
projectDirName,
projectName,
PROJECT_SETTINGS_FILE_NAME
)
const tempUserSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
const userThemeColor = '120' const userThemeColor = '120'
const projectThemeColor = '50' const projectThemeColor = '50'
const settingsOpenButton = page.getByRole('link', { const settingsOpenButton = page.getByRole('link', {
@ -293,6 +305,21 @@ test.describe('Testing settings', () => {
const projectLink = page.getByText('bracket') const projectLink = page.getByText('bracket')
const logoLink = page.getByTestId('app-logo') const logoLink = page.getByTestId('app-logo')
async function confirmThemeWasWritten(filePath: string, value: string) {
return expect
.poll(
async () => {
const fileExists = await fs.existsSync(filePath)
return fileExists ? fsp.readFile(filePath, 'utf-8') : ''
},
{
message: 'Setting should now be written to the file',
timeout: 5_000,
}
)
.toContain(`themeColor = "${value}"`)
}
await test.step('Set user theme color on home', async () => { await test.step('Set user theme color on home', async () => {
await expect(settingsOpenButton).toBeVisible() await expect(settingsOpenButton).toBeVisible()
await settingsOpenButton.click() await settingsOpenButton.click()
@ -300,6 +327,7 @@ test.describe('Testing settings', () => {
await expect(userSettingsTab).toBeChecked() await expect(userSettingsTab).toBeChecked()
await themeColorSetting.fill(userThemeColor) await themeColorSetting.fill(userThemeColor)
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor) await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
await confirmThemeWasWritten(tempUserSettingsFilePath, userThemeColor)
await settingsCloseButton.click() await settingsCloseButton.click()
}) })
@ -311,6 +339,11 @@ test.describe('Testing settings', () => {
await expect(projectSettingsTab).toBeChecked() await expect(projectSettingsTab).toBeChecked()
await themeColorSetting.fill(projectThemeColor) await themeColorSetting.fill(projectThemeColor)
await expect(logoLink).toHaveCSS('--primary-hue', projectThemeColor) await expect(logoLink).toHaveCSS('--primary-hue', projectThemeColor)
// Make sure that the project settings file has been written to before continuing
await confirmThemeWasWritten(
tempProjectSettingsFilePath,
projectThemeColor
)
await settingsCloseButton.click() await settingsCloseButton.click()
}) })
@ -324,6 +357,7 @@ test.describe('Testing settings', () => {
await test.step(`Navigate back to the home view and see user setting applied`, async () => { await test.step(`Navigate back to the home view and see user setting applied`, async () => {
await logoLink.click() await logoLink.click()
await page.screenshot({ path: 'out.png' })
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor) await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
}) })

View File

@ -56,7 +56,7 @@
"react-json-view": "^1.21.3", "react-json-view": "^1.21.3",
"react-modal": "^3.16.1", "react-modal": "^3.16.1",
"react-modal-promise": "^1.0.2", "react-modal-promise": "^1.0.2",
"react-router-dom": "^6.26.1", "react-router-dom": "^6.27.0",
"sketch-helpers": "^0.0.4", "sketch-helpers": "^0.0.4",
"three": "^0.166.1", "three": "^0.166.1",
"ua-parser-js": "^1.0.37", "ua-parser-js": "^1.0.37",
@ -76,7 +76,7 @@
"build:both": "vite build", "build:both": "vite build",
"build:both:local": "yarn build:wasm && vite build", "build:both:local": "yarn build:wasm && vite build",
"pretest": "yarn remove-importmeta", "pretest": "yarn remove-importmeta",
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)", "test:rust": "(cd src/wasm-lib && cargo test --workspace && cargo clippy --workspace --all-targets)",
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000", "simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &", "simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &", "simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",

View File

@ -34,6 +34,11 @@
"title": "Car Wheel Assembly", "title": "Car Wheel Assembly",
"description": "A car wheel assembly with a rotor, tire, and lug nuts." "description": "A car wheel assembly with a rotor, tire, and lug nuts."
}, },
{
"file": "dodecahedron.kcl",
"title": "Hollow Dodecahedron",
"description": "A regular dodecahedron or pentagonal dodecahedron is a dodecahedron composed of regular pentagonal faces, three meeting at each vertex. This example shows constructing the individual faces of the dodecahedron and extruding inwards."
},
{ {
"file": "enclosure.kcl", "file": "enclosure.kcl",
"title": "Enclosure", "title": "Enclosure",
@ -54,6 +59,11 @@
"title": "A mounting bracket for the Focusrite Scarlett Solo audio interface", "title": "A mounting bracket for the Focusrite Scarlett Solo audio interface",
"description": "This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material" "description": "This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material"
}, },
{
"file": "food-service-spatula.kcl",
"title": "Food Service Spatula",
"description": "Use these spatulas for mixing, flipping, and scraping."
},
{ {
"file": "french-press.kcl", "file": "french-press.kcl",
"title": "French Press", "title": "French Press",
@ -61,7 +71,7 @@
}, },
{ {
"file": "gear.kcl", "file": "gear.kcl",
"title": "Gear", "title": "Spur Gear",
"description": "A rotating machine part having cut teeth or, in the case of a cogwheel, inserted teeth (called cogs), which mesh with another toothed part to transmit torque. Geared devices can change the speed, torque, and direction of a power source. The two elements that define a gear are its circular shape and the teeth that are integrated into its outer edge, which are designed to fit into the teeth of another gear." "description": "A rotating machine part having cut teeth or, in the case of a cogwheel, inserted teeth (called cogs), which mesh with another toothed part to transmit torque. Geared devices can change the speed, torque, and direction of a power source. The two elements that define a gear are its circular shape and the teeth that are integrated into its outer edge, which are designed to fit into the teeth of another gear."
}, },
{ {

View File

@ -44,6 +44,7 @@ import {
import { ActionButton } from 'components/ActionButton' import { ActionButton } from 'components/ActionButton'
import { err, reportRejection, trap } from 'lib/trap' import { err, reportRejection, trap } from 'lib/trap'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { Node } from 'wasm-lib/kcl/bindings/Node'
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } { function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
const [isCamMoving, setIsCamMoving] = useState(false) const [isCamMoving, setIsCamMoving] = useState(false)
@ -201,7 +202,7 @@ const Overlay = ({
let xAlignment = overlay.angle < 0 ? '0%' : '-100%' let xAlignment = overlay.angle < 0 ? '0%' : '-100%'
let yAlignment = overlay.angle < -90 || overlay.angle >= 90 ? '0%' : '-100%' let yAlignment = overlay.angle < -90 || overlay.angle >= 90 ? '0%' : '-100%'
const _node1 = getNodeFromPath<CallExpression>( const _node1 = getNodeFromPath<Node<CallExpression>>(
kclManager.ast, kclManager.ast,
overlay.pathToNode, overlay.pathToNode,
'CallExpression' 'CallExpression'
@ -381,7 +382,7 @@ export async function deleteSegment({
pathToNode: PathToNode pathToNode: PathToNode
sketchDetails: SketchDetails | null sketchDetails: SketchDetails | null
}) { }) {
let modifiedAst: Program | Error = kclManager.ast let modifiedAst: Node<Program> | Error = kclManager.ast
const dependentRanges = findUsesOfTagInPipe(modifiedAst, pathToNode) const dependentRanges = findUsesOfTagInPipe(modifiedAst, pathToNode)
const shouldContinueSegDelete = dependentRanges.length const shouldContinueSegDelete = dependentRanges.length

View File

@ -19,6 +19,8 @@ import {
import { import {
ARROWHEAD, ARROWHEAD,
AXIS_GROUP, AXIS_GROUP,
DRAFT_POINT,
DRAFT_POINT_GROUP,
getSceneScale, getSceneScale,
INTERSECTION_PLANE_LAYER, INTERSECTION_PLANE_LAYER,
OnClickCallbackArgs, OnClickCallbackArgs,
@ -53,7 +55,7 @@ import {
editorManager, editorManager,
} from 'lib/singletons' } from 'lib/singletons'
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst' import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { executeAst } from 'lang/langHelpers' import { executeAst, ToolTip } from 'lang/langHelpers'
import { import {
createProfileStartHandle, createProfileStartHandle,
SegmentUtils, SegmentUtils,
@ -92,6 +94,7 @@ import { err, reportRejection, trap } from 'lib/trap'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { Point3d } from 'wasm-lib/kcl/bindings/Point3d' import { Point3d } from 'wasm-lib/kcl/bindings/Point3d'
import { SegmentInputs } from 'lang/std/stdTypes' import { SegmentInputs } from 'lang/std/stdTypes'
import { Node } from 'wasm-lib/kcl/bindings/Node'
type DraftSegment = 'line' | 'tangentialArcTo' type DraftSegment = 'line' | 'tangentialArcTo'
@ -313,6 +316,27 @@ export class SceneEntities {
const intersectionPlane = this.scene.getObjectByName(RAYCASTABLE_PLANE) const intersectionPlane = this.scene.getObjectByName(RAYCASTABLE_PLANE)
if (intersectionPlane) this.scene.remove(intersectionPlane) if (intersectionPlane) this.scene.remove(intersectionPlane)
} }
getDraftPoint() {
return this.scene.getObjectByName(DRAFT_POINT)
}
createDraftPoint({ point, group }: { point: Vector2; group: Group }) {
const dummy = new Mesh()
dummy.position.set(0, 0, 0)
const scale = sceneInfra.getClientSceneScaleFactor(dummy)
const draftPoint = createProfileStartHandle({
isDraft: true,
from: [point.x, point.y],
scale,
theme: sceneInfra._theme,
})
draftPoint.layers.set(SKETCH_LAYER)
group.add(draftPoint)
}
removeDraftPoint() {
const draftPoint = this.getDraftPoint()
if (draftPoint) draftPoint.removeFromParent()
}
setupNoPointsListener({ setupNoPointsListener({
sketchDetails, sketchDetails,
@ -321,22 +345,78 @@ export class SceneEntities {
sketchDetails: SketchDetails sketchDetails: SketchDetails
afterClick: (args: OnClickCallbackArgs) => void afterClick: (args: OnClickCallbackArgs) => void
}) { }) {
// Create a THREEjs plane to raycast clicks onto // TODO: Consolidate shared logic between this and setupSketch
// Which should just fire when the sketch mode is entered,
// instead of in these two separate XState states.
this.createIntersectionPlane() this.createIntersectionPlane()
const draftPointGroup = new Group()
draftPointGroup.name = DRAFT_POINT_GROUP
sketchDetails.origin &&
draftPointGroup.position.set(...sketchDetails.origin)
if (!(sketchDetails.yAxis && sketchDetails)) {
console.error('No sketch quaternion or sketch details found')
return
}
this.currentSketchQuaternion = quaternionFromUpNForward(
new Vector3(...sketchDetails.yAxis),
new Vector3(...sketchDetails.zAxis)
)
draftPointGroup.setRotationFromQuaternion(this.currentSketchQuaternion)
this.scene.add(draftPointGroup)
const quaternion = quaternionFromUpNForward( const quaternion = quaternionFromUpNForward(
new Vector3(...sketchDetails.yAxis), new Vector3(...sketchDetails.yAxis),
new Vector3(...sketchDetails.zAxis) new Vector3(...sketchDetails.zAxis)
) )
// Position the click raycast plane // Position the click raycast plane
if (this.intersectionPlane) { this.intersectionPlane!.setRotationFromQuaternion(quaternion)
this.intersectionPlane.setRotationFromQuaternion(quaternion) this.intersectionPlane!.position.copy(
this.intersectionPlane.position.copy(
new Vector3(...(sketchDetails?.origin || [0, 0, 0])) new Vector3(...(sketchDetails?.origin || [0, 0, 0]))
) )
}
sceneInfra.setCallbacks({ sceneInfra.setCallbacks({
onMove: (args) => {
if (!args.intersects.length) return
const axisIntersection = args.intersects.find(
(sceneObject) =>
sceneObject.object.name === X_AXIS ||
sceneObject.object.name === Y_AXIS
)
if (!axisIntersection) return
const { intersectionPoint } = args
// We're hovering over an axis, so we should show a draft point
const snappedPoint = intersectionPoint.twoD.clone()
if (axisIntersection.object.name === X_AXIS) {
snappedPoint.setComponent(1, 0)
} else {
snappedPoint.setComponent(0, 0)
}
// Either create a new one or update the existing one
const draftPoint = this.getDraftPoint()
if (!draftPoint) {
this.createDraftPoint({
point: snappedPoint,
group: draftPointGroup,
})
} else {
// Ignore if there are huge jumps in the mouse position,
// that is likely a strange behavior
if (
draftPoint.position.distanceTo(
new Vector3(snappedPoint.x, snappedPoint.y, 0)
) > 100
) {
return
}
draftPoint.position.set(snappedPoint.x, snappedPoint.y, 0)
}
},
onMouseLeave: () => {
this.removeDraftPoint()
},
onClick: async (args) => { onClick: async (args) => {
this.removeDraftPoint()
if (!args) return if (!args) return
// If there is a valid camera interaction that matches, do that instead // If there is a valid camera interaction that matches, do that instead
const interaction = sceneInfra.camControls.getInteractionType( const interaction = sceneInfra.camControls.getInteractionType(
@ -346,10 +426,25 @@ export class SceneEntities {
if (args.mouseEvent.which !== 1) return if (args.mouseEvent.which !== 1) return
const { intersectionPoint } = args const { intersectionPoint } = args
if (!intersectionPoint?.twoD || !sketchDetails?.sketchPathToNode) return if (!intersectionPoint?.twoD || !sketchDetails?.sketchPathToNode) return
// Snap to either or both axes
// if the click intersects their meshes
const yAxisIntersection = args.intersects.find(
(sceneObject) => sceneObject.object.name === Y_AXIS
)
const xAxisIntersection = args.intersects.find(
(sceneObject) => sceneObject.object.name === X_AXIS
)
const snappedClickPoint = {
x: yAxisIntersection ? 0 : intersectionPoint.twoD.x,
y: xAxisIntersection ? 0 : intersectionPoint.twoD.y,
}
const addStartProfileAtRes = addStartProfileAt( const addStartProfileAtRes = addStartProfileAt(
kclManager.ast, kclManager.ast,
sketchDetails.sketchPathToNode, sketchDetails.sketchPathToNode,
[intersectionPoint.twoD.x, intersectionPoint.twoD.y] [snappedClickPoint.x, snappedClickPoint.y]
) )
if (trap(addStartProfileAtRes)) return if (trap(addStartProfileAtRes)) return
@ -357,6 +452,7 @@ export class SceneEntities {
await kclManager.updateAst(modifiedAst, false) await kclManager.updateAst(modifiedAst, false)
this.removeIntersectionPlane() this.removeIntersectionPlane()
this.scene.remove(draftPointGroup)
// Now perform the caller-specified action // Now perform the caller-specified action
afterClick(args) afterClick(args)
@ -374,14 +470,14 @@ export class SceneEntities {
selectionRanges, selectionRanges,
}: { }: {
sketchPathToNode: PathToNode sketchPathToNode: PathToNode
maybeModdedAst: Program maybeModdedAst: Node<Program>
draftExpressionsIndices?: { start: number; end: number } draftExpressionsIndices?: { start: number; end: number }
forward: [number, number, number] forward: [number, number, number]
up: [number, number, number] up: [number, number, number]
position?: [number, number, number] position?: [number, number, number]
selectionRanges?: Selections selectionRanges?: Selections
}): Promise<{ }): Promise<{
truncatedAst: Program truncatedAst: Node<Program>
programMemoryOverride: ProgramMemory programMemoryOverride: ProgramMemory
sketch: Sketch sketch: Sketch
variableDeclarationName: string variableDeclarationName: string
@ -429,12 +525,7 @@ export class SceneEntities {
const dummy = new Mesh() const dummy = new Mesh()
// TODO: When we actually have sketch positions and rotations we can use them here. // TODO: When we actually have sketch positions and rotations we can use them here.
dummy.position.set(0, 0, 0) dummy.position.set(0, 0, 0)
const orthoFactor = orthoScale(sceneInfra.camControls.camera) const scale = sceneInfra.getClientSceneScaleFactor(dummy)
const factor =
(sceneInfra.camControls.camera instanceof OrthographicCamera
? orthoFactor
: perspScale(sceneInfra.camControls.camera, dummy)) /
sceneInfra._baseUnitMultiplier
const segPathToNode = getNodePathFromSourceRange( const segPathToNode = getNodePathFromSourceRange(
maybeModdedAst, maybeModdedAst,
@ -445,8 +536,9 @@ export class SceneEntities {
from: sketch.start.from, from: sketch.start.from,
id: sketch.start.__geoMeta.id, id: sketch.start.__geoMeta.id,
pathToNode: segPathToNode, pathToNode: segPathToNode,
scale: factor, scale,
theme: sceneInfra._theme, theme: sceneInfra._theme,
isDraft: false,
}) })
_profileStart.layers.set(SKETCH_LAYER) _profileStart.layers.set(SKETCH_LAYER)
_profileStart.traverse((child) => { _profileStart.traverse((child) => {
@ -522,7 +614,7 @@ export class SceneEntities {
id: segment.__geoMeta.id, id: segment.__geoMeta.id,
pathToNode: segPathToNode, pathToNode: segPathToNode,
isDraftSegment, isDraftSegment,
scale: factor, scale,
texture: sceneInfra.extraSegmentTexture, texture: sceneInfra.extraSegmentTexture,
theme: sceneInfra._theme, theme: sceneInfra._theme,
isSelected, isSelected,
@ -566,7 +658,7 @@ export class SceneEntities {
} }
updateAstAndRejigSketch = async ( updateAstAndRejigSketch = async (
sketchPathToNode: PathToNode, sketchPathToNode: PathToNode,
modifiedAst: Program | Error, modifiedAst: Node<Program> | Error,
forward: [number, number, number], forward: [number, number, number],
up: [number, number, number], up: [number, number, number],
origin: [number, number, number] origin: [number, number, number]
@ -659,12 +751,14 @@ export class SceneEntities {
const { intersectionPoint } = args const { intersectionPoint } = args
let intersection2d = intersectionPoint?.twoD let intersection2d = intersectionPoint?.twoD
const profileStart = args.intersects const intersectsProfileStart = args.intersects
.map(({ object }) => getParentGroup(object, [PROFILE_START])) .map(({ object }) => getParentGroup(object, [PROFILE_START]))
.find((a) => a?.name === PROFILE_START) .find((a) => a?.name === PROFILE_START)
let modifiedAst let modifiedAst
if (profileStart) {
// Snapping logic for the profile start handle
if (intersectsProfileStart) {
const lastSegment = sketch.paths.slice(-1)[0] const lastSegment = sketch.paths.slice(-1)[0]
modifiedAst = addCallExpressionsToPipe({ modifiedAst = addCallExpressionsToPipe({
node: kclManager.ast, node: kclManager.ast,
@ -697,19 +791,39 @@ export class SceneEntities {
}) })
if (trap(modifiedAst)) return Promise.reject(modifiedAst) if (trap(modifiedAst)) return Promise.reject(modifiedAst)
} else if (intersection2d) { } else if (intersection2d) {
const intersectsYAxis = args.intersects.find(
(sceneObject) => sceneObject.object.name === Y_AXIS
)
const intersectsXAxis = args.intersects.find(
(sceneObject) => sceneObject.object.name === X_AXIS
)
const lastSegment = sketch.paths.slice(-1)[0] const lastSegment = sketch.paths.slice(-1)[0]
const snappedPoint = {
x: intersectsYAxis ? 0 : intersection2d.x,
y: intersectsXAxis ? 0 : intersection2d.y,
}
let resolvedFunctionName: ToolTip = 'line'
// This might need to become its own function if we want more
// case-based logic for different segment types
if (lastSegment.type === 'TangentialArcTo') {
resolvedFunctionName = 'tangentialArcTo'
} else if (snappedPoint.x === 0 || snappedPoint.y === 0) {
// We consider a point placed on axes or origin to be absolute
resolvedFunctionName = 'lineTo'
}
const tmp = addNewSketchLn({ const tmp = addNewSketchLn({
node: kclManager.ast, node: kclManager.ast,
programMemory: kclManager.programMemory, programMemory: kclManager.programMemory,
input: { input: {
type: 'straight-segment', type: 'straight-segment',
from: [lastSegment.to[0], lastSegment.to[1]], from: [lastSegment.to[0], lastSegment.to[1]],
to: [intersection2d.x, intersection2d.y], to: [snappedPoint.x, snappedPoint.y],
}, },
fnName: fnName: resolvedFunctionName,
lastSegment.type === 'TangentialArcTo'
? 'tangentialArcTo'
: 'line',
pathToNode: sketchPathToNode, pathToNode: sketchPathToNode,
}) })
if (trap(tmp)) return Promise.reject(tmp) if (trap(tmp)) return Promise.reject(tmp)
@ -721,7 +835,7 @@ export class SceneEntities {
} }
await kclManager.executeAstMock(modifiedAst) await kclManager.executeAstMock(modifiedAst)
if (profileStart) { if (intersectsProfileStart) {
sceneInfra.modelingSend({ type: 'CancelSketch' }) sceneInfra.modelingSend({ type: 'CancelSketch' })
} else { } else {
await this.setUpDraftSegment( await this.setUpDraftSegment(
@ -1202,7 +1316,7 @@ export class SceneEntities {
} }
prepareTruncatedMemoryAndAst = ( prepareTruncatedMemoryAndAst = (
sketchPathToNode: PathToNode, sketchPathToNode: PathToNode,
ast?: Program, ast?: Node<Program>,
draftSegment?: DraftSegment draftSegment?: DraftSegment
) => ) =>
prepareTruncatedMemoryAndAst( prepareTruncatedMemoryAndAst(
@ -1223,20 +1337,35 @@ export class SceneEntities {
sketchPathToNode: PathToNode sketchPathToNode: PathToNode
intersects: Intersection<Object3D<Object3DEventMap>>[] intersects: Intersection<Object3D<Object3DEventMap>>[]
draftInfo?: { draftInfo?: {
truncatedAst: Program truncatedAst: Node<Program>
programMemoryOverride: ProgramMemory programMemoryOverride: ProgramMemory
variableDeclarationName: string variableDeclarationName: string
} }
}) { }) {
const profileStart = const intersectsProfileStart =
draftInfo && draftInfo &&
intersects intersects
.map(({ object }) => getParentGroup(object, [PROFILE_START])) .map(({ object }) => getParentGroup(object, [PROFILE_START]))
.find((a) => a?.name === PROFILE_START) .find((a) => a?.name === PROFILE_START)
const intersection2d = profileStart const intersection2d = intersectsProfileStart
? new Vector2(profileStart.position.x, profileStart.position.y) ? new Vector2(
intersectsProfileStart.position.x,
intersectsProfileStart.position.y
)
: _intersection2d : _intersection2d
const intersectsYAxis = intersects.find(
(sceneObject) => sceneObject.object.name === Y_AXIS
)
const intersectsXAxis = intersects.find(
(sceneObject) => sceneObject.object.name === X_AXIS
)
const snappedPoint = new Vector2(
intersectsYAxis ? 0 : intersection2d.x,
intersectsXAxis ? 0 : intersection2d.y
)
const group = getParentGroup(object, SEGMENT_BODIES_PLUS_PROFILE_START) const group = getParentGroup(object, SEGMENT_BODIES_PLUS_PROFILE_START)
const subGroup = getParentGroup(object, [ARROWHEAD, CIRCLE_CENTER_HANDLE]) const subGroup = getParentGroup(object, [ARROWHEAD, CIRCLE_CENTER_HANDLE])
if (!group) return if (!group) return
@ -1256,10 +1385,10 @@ export class SceneEntities {
group.userData.from[0], group.userData.from[0],
group.userData.from[1], group.userData.from[1],
] ]
const dragTo: [number, number] = [intersection2d.x, intersection2d.y] const dragTo: [number, number] = [snappedPoint.x, snappedPoint.y]
let modifiedAst = draftInfo ? draftInfo.truncatedAst : { ...kclManager.ast } let modifiedAst = draftInfo ? draftInfo.truncatedAst : { ...kclManager.ast }
const _node = getNodeFromPath<CallExpression>( const _node = getNodeFromPath<Node<CallExpression>>(
modifiedAst, modifiedAst,
pathToNode, pathToNode,
'CallExpression' 'CallExpression'
@ -1271,7 +1400,7 @@ export class SceneEntities {
let modded: let modded:
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNode: PathToNode pathToNode: PathToNode
} }
| Error | Error
@ -1566,7 +1695,7 @@ export class SceneEntities {
if (parent?.userData?.pathToNode) { if (parent?.userData?.pathToNode) {
const updatedAst = parse(recast(kclManager.ast)) const updatedAst = parse(recast(kclManager.ast))
if (trap(updatedAst)) return if (trap(updatedAst)) return
const _node = getNodeFromPath<CallExpression>( const _node = getNodeFromPath<Node<CallExpression>>(
updatedAst, updatedAst,
parent.userData.pathToNode, parent.userData.pathToNode,
'CallExpression' 'CallExpression'
@ -1701,12 +1830,12 @@ export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ'
function prepareTruncatedMemoryAndAst( function prepareTruncatedMemoryAndAst(
sketchPathToNode: PathToNode, sketchPathToNode: PathToNode,
ast: Program, ast: Node<Program>,
programMemory: ProgramMemory, programMemory: ProgramMemory,
draftSegment?: DraftSegment draftSegment?: DraftSegment
): ):
| { | {
truncatedAst: Program truncatedAst: Node<Program>
programMemoryOverride: ProgramMemory programMemoryOverride: ProgramMemory
variableDeclarationName: string variableDeclarationName: string
} }
@ -1714,7 +1843,7 @@ function prepareTruncatedMemoryAndAst(
const bodyIndex = Number(sketchPathToNode?.[1]?.[0]) || 0 const bodyIndex = Number(sketchPathToNode?.[1]?.[0]) || 0
const _ast = structuredClone(ast) const _ast = structuredClone(ast)
const _node = getNodeFromPath<VariableDeclaration>( const _node = getNodeFromPath<Node<VariableDeclaration>>(
_ast, _ast,
sketchPathToNode || [], sketchPathToNode || [],
'VariableDeclaration' 'VariableDeclaration'
@ -1764,15 +1893,15 @@ function prepareTruncatedMemoryAndAst(
).body.slice(-1)[0].start = lastPipeItem.start ).body.slice(-1)[0].start = lastPipeItem.start
_ast.end = lastPipeItem.end _ast.end = lastPipeItem.end
const varDec = _ast.body[bodyIndex] as VariableDeclaration const varDec = _ast.body[bodyIndex] as Node<VariableDeclaration>
varDec.end = lastPipeItem.end varDec.end = lastPipeItem.end
const declarator = varDec.declarations[0] const declarator = varDec.declarations[0]
declarator.end = lastPipeItem.end declarator.end = lastPipeItem.end
const init = declarator.init as PipeExpression const init = declarator.init as Node<PipeExpression>
init.end = lastPipeItem.end init.end = lastPipeItem.end
init.body.slice(-1)[0].end = lastPipeItem.end init.body.slice(-1)[0].end = lastPipeItem.end
} }
const truncatedAst: Program = { const truncatedAst: Node<Program> = {
..._ast, ..._ast,
body: [structuredClone(_ast.body[bodyIndex])], body: [structuredClone(_ast.body[bodyIndex])],
} }

View File

@ -30,6 +30,7 @@ import { MouseState, SegmentOverlayPayload } from 'machines/modelingMachine'
import { getAngle, throttle } from 'lib/utils' import { getAngle, throttle } from 'lib/utils'
import { Themes } from 'lib/theme' import { Themes } from 'lib/theme'
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer' import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { orthoScale, perspScale } from './helpers'
type SendType = ReturnType<typeof useModelingContext>['send'] type SendType = ReturnType<typeof useModelingContext>['send']
@ -49,6 +50,10 @@ export const RAYCASTABLE_PLANE = 'raycastable-plane'
export const X_AXIS = 'xAxis' export const X_AXIS = 'xAxis'
export const Y_AXIS = 'yAxis' export const Y_AXIS = 'yAxis'
/** the THREEjs representation of the group surrounding a "snapped" point that is not yet placed */
export const DRAFT_POINT_GROUP = 'draft-point-group'
/** the THREEjs representation of a "snapped" point that is not yet placed */
export const DRAFT_POINT = 'draft-point'
export const AXIS_GROUP = 'axisGroup' export const AXIS_GROUP = 'axisGroup'
export const SKETCH_GROUP_SEGMENTS = 'sketch-group-segments' export const SKETCH_GROUP_SEGMENTS = 'sketch-group-segments'
export const ARROWHEAD = 'arrowhead' export const ARROWHEAD = 'arrowhead'
@ -60,6 +65,11 @@ export interface OnMouseEnterLeaveArgs {
selected: Object3D<Object3DEventMap> selected: Object3D<Object3DEventMap>
dragSelected?: Object3D<Object3DEventMap> dragSelected?: Object3D<Object3DEventMap>
mouseEvent: MouseEvent mouseEvent: MouseEvent
/** The intersection of the mouse with the THREEjs raycast plane */
intersectionPoint?: {
twoD?: Vector2
threeD?: Vector3
}
} }
interface OnDragCallbackArgs extends OnMouseEnterLeaveArgs { interface OnDragCallbackArgs extends OnMouseEnterLeaveArgs {
@ -348,29 +358,42 @@ export class SceneInfra {
window.removeEventListener('resize', this.onWindowResize) window.removeEventListener('resize', this.onWindowResize)
// Dispose of any other resources like geometries, materials, textures // Dispose of any other resources like geometries, materials, textures
} }
getClientSceneScaleFactor(meshOrGroup: Mesh | Group) {
const orthoFactor = orthoScale(this.camControls.camera)
const factor =
(this.camControls.camera instanceof OrthographicCamera
? orthoFactor
: perspScale(this.camControls.camera, meshOrGroup)) /
this._baseUnitMultiplier
return factor
}
getPlaneIntersectPoint = (): { getPlaneIntersectPoint = (): {
twoD?: Vector2 twoD?: Vector2
threeD?: Vector3 threeD?: Vector3
intersection: Intersection<Object3D<Object3DEventMap>> intersection: Intersection<Object3D<Object3DEventMap>>
} | null => { } | null => {
// Get the orientations from the camera and mouse position
this.planeRaycaster.setFromCamera( this.planeRaycaster.setFromCamera(
this.currentMouseVector, this.currentMouseVector,
this.camControls.camera this.camControls.camera
) )
// Get the intersection of the ray with the default planes
const planeIntersects = this.planeRaycaster.intersectObjects( const planeIntersects = this.planeRaycaster.intersectObjects(
this.scene.children, this.scene.children,
true true
) )
const recastablePlaneIntersect = planeIntersects.find( if (!planeIntersects.length) return null
// Find the intersection with the raycastable (or sketch) plane
const raycastablePlaneIntersection = planeIntersects.find(
(intersect) => intersect.object.name === RAYCASTABLE_PLANE (intersect) => intersect.object.name === RAYCASTABLE_PLANE
) )
if (!planeIntersects.length) return null if (!raycastablePlaneIntersection)
if (!recastablePlaneIntersect) return { intersection: planeIntersects[0] } return { intersection: planeIntersects[0] }
const planePosition = planeIntersects[0].object.position const planePosition = raycastablePlaneIntersection.object.position
const inversePlaneQuaternion = planeIntersects[0].object.quaternion const inversePlaneQuaternion =
.clone() raycastablePlaneIntersection.object.quaternion.clone().invert()
.invert() const intersectPoint = raycastablePlaneIntersection.point
const intersectPoint = planeIntersects[0].point
let transformedPoint = intersectPoint.clone() let transformedPoint = intersectPoint.clone()
if (transformedPoint) { if (transformedPoint) {
transformedPoint.applyQuaternion(inversePlaneQuaternion) transformedPoint.applyQuaternion(inversePlaneQuaternion)
@ -447,18 +470,26 @@ export class SceneInfra {
if (intersects[0]) { if (intersects[0]) {
const firstIntersectObject = intersects[0].object const firstIntersectObject = intersects[0].object
const planeIntersectPoint = this.getPlaneIntersectPoint()
const intersectionPoint = {
twoD: planeIntersectPoint?.twoD,
threeD: planeIntersectPoint?.threeD,
}
if (this.hoveredObject !== firstIntersectObject) { if (this.hoveredObject !== firstIntersectObject) {
const hoveredObj = this.hoveredObject const hoveredObj = this.hoveredObject
this.hoveredObject = null this.hoveredObject = null
await this.onMouseLeave({ await this.onMouseLeave({
selected: hoveredObj, selected: hoveredObj,
mouseEvent: mouseEvent, mouseEvent: mouseEvent,
intersectionPoint,
}) })
this.hoveredObject = firstIntersectObject this.hoveredObject = firstIntersectObject
await this.onMouseEnter({ await this.onMouseEnter({
selected: this.hoveredObject, selected: this.hoveredObject,
dragSelected: this.selected?.object, dragSelected: this.selected?.object,
mouseEvent: mouseEvent, mouseEvent: mouseEvent,
intersectionPoint,
}) })
if (!this.selected) if (!this.selected)
this.updateMouseState({ this.updateMouseState({

View File

@ -45,6 +45,7 @@ import {
import { getTangentPointFromPreviousArc } from 'lib/utils2d' import { getTangentPointFromPreviousArc } from 'lib/utils2d'
import { import {
ARROWHEAD, ARROWHEAD,
DRAFT_POINT,
SceneInfra, SceneInfra,
SEGMENT_LENGTH_LABEL, SEGMENT_LENGTH_LABEL,
SEGMENT_LENGTH_LABEL_OFFSET_PX, SEGMENT_LENGTH_LABEL_OFFSET_PX,
@ -686,19 +687,20 @@ class CircleSegment implements SegmentUtils {
export function createProfileStartHandle({ export function createProfileStartHandle({
from, from,
id, isDraft = false,
pathToNode,
scale = 1, scale = 1,
theme, theme,
isSelected, isSelected,
...rest
}: { }: {
from: Coords2d from: Coords2d
id: string
pathToNode: PathToNode
scale?: number scale?: number
theme: Themes theme: Themes
isSelected?: boolean isSelected?: boolean
}) { } & (
| { isDraft: true }
| { isDraft: false; id: string; pathToNode: PathToNode }
)) {
const group = new Group() const group = new Group()
const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later
@ -711,13 +713,12 @@ export function createProfileStartHandle({
group.userData = { group.userData = {
type: PROFILE_START, type: PROFILE_START,
id,
from, from,
pathToNode,
isSelected, isSelected,
baseColor, baseColor,
...rest,
} }
group.name = PROFILE_START group.name = isDraft ? DRAFT_POINT : PROFILE_START
group.position.set(from[0], from[1], 0) group.position.set(from[0], from[1], 0)
group.scale.set(scale, scale, scale) group.scale.set(scale, scale, scale)
return group return group

View File

@ -91,6 +91,7 @@ import { submitAndAwaitTextToKcl } from 'lib/textToCad'
import { useFileContext } from 'hooks/useFileContext' import { useFileContext } from 'hooks/useFileContext'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'
import { IndexLoaderData } from 'lib/types' import { IndexLoaderData } from 'lib/types'
import { Node } from 'wasm-lib/kcl/bindings/Node'
type MachineContext<T extends AnyStateMachine> = { type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T> state: StateFrom<T>
@ -971,7 +972,7 @@ export const ModelingMachineProvider = ({
}) })
let parsed = parse(recast(kclManager.ast)) let parsed = parse(recast(kclManager.ast))
if (trap(parsed)) return Promise.reject(parsed) if (trap(parsed)) return Promise.reject(parsed)
parsed = parsed as Program parsed = parsed as Node<Program>
const { modifiedAst: _modifiedAst, pathToReplacedNode } = const { modifiedAst: _modifiedAst, pathToReplacedNode } =
moveValueIntoNewVariablePath( moveValueIntoNewVariablePath(
@ -982,7 +983,7 @@ export const ModelingMachineProvider = ({
) )
parsed = parse(recast(_modifiedAst)) parsed = parse(recast(_modifiedAst))
if (trap(parsed)) return Promise.reject(parsed) if (trap(parsed)) return Promise.reject(parsed)
parsed = parsed as Program parsed = parsed as Node<Program>
if (!pathToReplacedNode) if (!pathToReplacedNode)
return Promise.reject(new Error('No path to replaced node')) return Promise.reject(new Error('No path to replaced node'))

View File

@ -14,6 +14,7 @@ import {
import { TransformInfo } from 'lang/std/stdTypes' import { TransformInfo } from 'lang/std/stdTypes'
import { kclManager } from 'lib/singletons' import { kclManager } from 'lib/singletons'
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { Node } from 'wasm-lib/kcl/bindings/Node'
export function setEqualLengthInfo({ export function setEqualLengthInfo({
selectionRanges, selectionRanges,
@ -86,7 +87,7 @@ export function applyConstraintEqualLength({
selectionRanges: Selections selectionRanges: Selections
}): }):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNodeMap: PathToNodeMap pathToNodeMap: PathToNodeMap
} }
| Error { | Error {

View File

@ -13,6 +13,7 @@ import {
import { TransformInfo } from 'lang/std/stdTypes' import { TransformInfo } from 'lang/std/stdTypes'
import { kclManager } from 'lib/singletons' import { kclManager } from 'lib/singletons'
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { Node } from 'wasm-lib/kcl/bindings/Node'
export function horzVertInfo( export function horzVertInfo(
selectionRanges: Selections, selectionRanges: Selections,
@ -55,11 +56,11 @@ export function horzVertInfo(
export function applyConstraintHorzVert( export function applyConstraintHorzVert(
selectionRanges: Selections, selectionRanges: Selections,
horOrVert: 'vertical' | 'horizontal', horOrVert: 'vertical' | 'horizontal',
ast: Program, ast: Node<Program>,
programMemory: ProgramMemory programMemory: ProgramMemory
): ):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNodeMap: PathToNodeMap pathToNodeMap: PathToNodeMap
} }
| Error { | Error {

View File

@ -19,6 +19,7 @@ import { createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lib/singletons' import { kclManager } from 'lib/singletons'
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { Node } from 'wasm-lib/kcl/bindings/Node'
const getModalInfo = createInfoModal(GetInfoModal) const getModalInfo = createInfoModal(GetInfoModal)
@ -136,7 +137,7 @@ export async function applyConstraintIntersect({
}: { }: {
selectionRanges: Selections selectionRanges: Selections
}): Promise<{ }): Promise<{
modifiedAst: Program modifiedAst: Node<Program>
pathToNodeMap: PathToNodeMap pathToNodeMap: PathToNodeMap
}> { }> {
const info = intersectInfo({ const info = intersectInfo({

View File

@ -13,6 +13,7 @@ import {
import { TransformInfo } from 'lang/std/stdTypes' import { TransformInfo } from 'lang/std/stdTypes'
import { kclManager } from 'lib/singletons' import { kclManager } from 'lib/singletons'
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { Node } from 'wasm-lib/kcl/bindings/Node'
export function removeConstrainingValuesInfo({ export function removeConstrainingValuesInfo({
selectionRanges, selectionRanges,
@ -77,7 +78,7 @@ export function applyRemoveConstrainingValues({
pathToNodes?: Array<PathToNode> pathToNodes?: Array<PathToNode>
}): }):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNodeMap: PathToNodeMap pathToNodeMap: PathToNodeMap
} }
| Error { | Error {

View File

@ -23,6 +23,7 @@ import {
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lib/singletons' import { kclManager } from 'lib/singletons'
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { Node } from 'wasm-lib/kcl/bindings/Node'
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal) const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
@ -161,7 +162,7 @@ export function applyConstraintAxisAlign({
constraint: 'snapToYAxis' | 'snapToXAxis' constraint: 'snapToYAxis' | 'snapToXAxis'
}): }):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNodeMap: PathToNodeMap pathToNodeMap: PathToNodeMap
} }
| Error { | Error {

View File

@ -18,6 +18,7 @@ import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lib/singletons' import { kclManager } from 'lib/singletons'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { cleanErrs, err } from 'lib/trap' import { cleanErrs, err } from 'lib/trap'
import { Node } from 'wasm-lib/kcl/bindings/Node'
const getModalInfo = createInfoModal(GetInfoModal) const getModalInfo = createInfoModal(GetInfoModal)
@ -185,7 +186,7 @@ export function applyConstraintHorzVertAlign({
constraint: 'setHorzDistance' | 'setVertDistance' constraint: 'setHorzDistance' | 'setVertDistance'
}): }):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNodeMap: PathToNodeMap pathToNodeMap: PathToNodeMap
} }
| Error { | Error {

View File

@ -21,9 +21,10 @@ import {
import { getNodeFromPath } from './queryAst' import { getNodeFromPath } from './queryAst'
import { codeManager, editorManager, sceneInfra } from 'lib/singletons' import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
import { Diagnostic } from '@codemirror/lint' import { Diagnostic } from '@codemirror/lint'
import { Node } from 'wasm-lib/kcl/bindings/Node'
interface ExecuteArgs { interface ExecuteArgs {
ast?: Program ast?: Node<Program>
zoomToFit?: boolean zoomToFit?: boolean
executionId?: number executionId?: number
zoomOnRangeAndType?: { zoomOnRangeAndType?: {
@ -33,13 +34,13 @@ interface ExecuteArgs {
} }
export class KclManager { export class KclManager {
private _ast: Program = { private _ast: Node<Program> = {
body: [], body: [],
start: 0, start: 0,
end: 0, end: 0,
nonCodeMeta: { nonCodeMeta: {
nonCodeNodes: {}, nonCodeNodes: {},
start: [], startNodes: [],
}, },
} }
private _execState: ExecState = emptyExecState() private _execState: ExecState = emptyExecState()
@ -55,7 +56,7 @@ export class KclManager {
engineCommandManager: EngineCommandManager engineCommandManager: EngineCommandManager
private _isExecutingCallback: (arg: boolean) => void = () => {} private _isExecutingCallback: (arg: boolean) => void = () => {}
private _astCallBack: (arg: Program) => void = () => {} private _astCallBack: (arg: Node<Program>) => void = () => {}
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {} private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
private _logsCallBack: (arg: string[]) => void = () => {} private _logsCallBack: (arg: string[]) => void = () => {}
private _kclErrorsCallBack: (arg: KCLError[]) => void = () => {} private _kclErrorsCallBack: (arg: KCLError[]) => void = () => {}
@ -181,7 +182,7 @@ export class KclManager {
setWasmInitFailed, setWasmInitFailed,
}: { }: {
setProgramMemory: (arg: ProgramMemory) => void setProgramMemory: (arg: ProgramMemory) => void
setAst: (arg: Program) => void setAst: (arg: Node<Program>) => void
setLogs: (arg: string[]) => void setLogs: (arg: string[]) => void
setKclErrors: (arg: KCLError[]) => void setKclErrors: (arg: KCLError[]) => void
setIsExecuting: (arg: boolean) => void setIsExecuting: (arg: boolean) => void
@ -205,12 +206,12 @@ export class KclManager {
end: 0, end: 0,
nonCodeMeta: { nonCodeMeta: {
nonCodeNodes: {}, nonCodeNodes: {},
start: [], startNodes: [],
}, },
} }
} }
safeParse(code: string): Program | null { safeParse(code: string): Node<Program> | null {
const ast = parse(code) const ast = parse(code)
this.lints = [] this.lints = []
this.kclErrors = [] this.kclErrors = []
@ -377,7 +378,7 @@ export class KclManager {
Array.from(this.engineCommandManager.artifactGraph).forEach( Array.from(this.engineCommandManager.artifactGraph).forEach(
([commandId, artifact]) => { ([commandId, artifact]) => {
if (!('codeRef' in artifact)) return if (!('codeRef' in artifact)) return
const _node1 = getNodeFromPath<CallExpression>( const _node1 = getNodeFromPath<Node<CallExpression>>(
this.ast, this.ast,
artifact.codeRef.pathToNode, artifact.codeRef.pathToNode,
'CallExpression' 'CallExpression'
@ -441,7 +442,7 @@ export class KclManager {
// but should probably have think about which of the function to keep // but should probably have think about which of the function to keep
// This always updates the code state and editor and writes to the file system. // This always updates the code state and editor and writes to the file system.
async updateAst( async updateAst(
ast: Program, ast: Node<Program>,
execute: boolean, execute: boolean,
optionalParams?: { optionalParams?: {
focusPath?: Array<PathToNode> focusPath?: Array<PathToNode>
@ -452,7 +453,7 @@ export class KclManager {
} }
} }
): Promise<{ ): Promise<{
newAst: Program newAst: Node<Program>
selections?: Selections selections?: Selections
}> { }> {
const newCode = recast(ast) const newCode = recast(ast)
@ -588,7 +589,7 @@ export class KclManager {
} }
// Determines if there is no KCL code which means it is executing a blank KCL file // Determines if there is no KCL code which means it is executing a blank KCL file
_isAstEmpty(ast: Program) { _isAstEmpty(ast: Node<Program>) {
return ast.start === 0 && ast.end === 0 && ast.body.length === 0 return ast.start === 0 && ast.end === 0 && ast.body.length === 0
} }
} }

View File

@ -12,6 +12,7 @@ import { EngineCommandManager } from 'lang/std/engineConnection'
import { KCLError } from 'lang/errors' import { KCLError } from 'lang/errors'
import { Diagnostic } from '@codemirror/lint' import { Diagnostic } from '@codemirror/lint'
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator' import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
import { Node } from 'wasm-lib/kcl/bindings/Node'
export type ToolTip = export type ToolTip =
| 'lineTo' | 'lineTo'
@ -52,7 +53,7 @@ export async function executeAst({
programMemoryOverride, programMemoryOverride,
idGenerator, idGenerator,
}: { }: {
ast: Program ast: Node<Program>
engineCommandManager: EngineCommandManager engineCommandManager: EngineCommandManager
useFakeExecutor?: boolean useFakeExecutor?: boolean
programMemoryOverride?: ProgramMemory programMemoryOverride?: ProgramMemory

View File

@ -21,6 +21,7 @@ import { enginelessExecutor } from '../lib/testHelpers'
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst' import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { SimplifiedArgDetails } from './std/stdTypes' import { SimplifiedArgDetails } from './std/stdTypes'
import { Node } from 'wasm-lib/kcl/bindings/Node'
beforeAll(async () => { beforeAll(async () => {
await initPromise await initPromise
@ -109,7 +110,7 @@ describe('Testing findUniqueName', () => {
{ type: 'Identifier', name: 'yo07', start: 0, end: 0 }, { type: 'Identifier', name: 'yo07', start: 0, end: 0 },
{ type: 'Identifier', name: 'yo08', start: 0, end: 0 }, { type: 'Identifier', name: 'yo08', start: 0, end: 0 },
{ type: 'Identifier', name: 'yo09', start: 0, end: 0 }, { type: 'Identifier', name: 'yo09', start: 0, end: 0 },
] satisfies Identifier[]), ] satisfies Node<Identifier>[]),
'yo', 'yo',
2 2
) )
@ -123,7 +124,7 @@ describe('Testing addSketchTo', () => {
body: [], body: [],
start: 0, start: 0,
end: 0, end: 0,
nonCodeMeta: { nonCodeNodes: {}, start: [] }, nonCodeMeta: { nonCodeNodes: {}, startNodes: [] },
}, },
'yz' 'yz'
) )

View File

@ -42,12 +42,13 @@ import { SimplifiedArgDetails } from './std/stdTypes'
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator' import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
import { ExtrudeFacePlane } from 'machines/modelingMachine' import { ExtrudeFacePlane } from 'machines/modelingMachine'
import { Node } from 'wasm-lib/kcl/bindings/Node'
export function startSketchOnDefault( export function startSketchOnDefault(
node: Program, node: Node<Program>,
axis: DefaultPlaneStr, axis: DefaultPlaneStr,
name = '' name = ''
): { modifiedAst: Program; id: string; pathToNode: PathToNode } { ): { modifiedAst: Node<Program>; id: string; pathToNode: PathToNode } {
const _node = { ...node } const _node = { ...node }
const _name = const _name =
name || findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH) name || findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH)
@ -76,10 +77,10 @@ export function startSketchOnDefault(
} }
export function addStartProfileAt( export function addStartProfileAt(
node: Program, node: Node<Program>,
pathToNode: PathToNode, pathToNode: PathToNode,
at: [number, number] at: [number, number]
): { modifiedAst: Program; pathToNode: PathToNode } | Error { ): { modifiedAst: Node<Program>; pathToNode: PathToNode } | Error {
const _node1 = getNodeFromPath<VariableDeclaration>( const _node1 = getNodeFromPath<VariableDeclaration>(
node, node,
pathToNode, pathToNode,
@ -114,7 +115,7 @@ export function addStartProfileAt(
} }
export function addSketchTo( export function addSketchTo(
node: Program, node: Node<Program>,
axis: 'xy' | 'xz' | 'yz', axis: 'xy' | 'xz' | 'yz',
name = '' name = ''
): { modifiedAst: Program; id: string; pathToNode: PathToNode } { ): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
@ -210,7 +211,7 @@ export function mutateArrExp(node: Expr, updateWith: ArrayExpression): boolean {
export function mutateObjExpProp( export function mutateObjExpProp(
node: Expr, node: Expr,
updateWith: Literal | ArrayExpression, updateWith: Node<Literal> | Node<ArrayExpression>,
key: string key: string
): boolean { ): boolean {
if (node.type === 'ObjectExpression') { if (node.type === 'ObjectExpression') {
@ -248,13 +249,13 @@ export function mutateObjExpProp(
} }
export function extrudeSketch( export function extrudeSketch(
node: Program, node: Node<Program>,
pathToNode: PathToNode, pathToNode: PathToNode,
shouldPipe = false, shouldPipe = false,
distance: Expr = createLiteral(4) distance: Expr = createLiteral(4)
): ):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNode: PathToNode pathToNode: PathToNode
pathToExtrudeArg: PathToNode pathToExtrudeArg: PathToNode
} }
@ -343,13 +344,13 @@ export function extrudeSketch(
} }
export function revolveSketch( export function revolveSketch(
node: Program, node: Node<Program>,
pathToNode: PathToNode, pathToNode: PathToNode,
shouldPipe = false, shouldPipe = false,
angle: Expr = createLiteral(4) angle: Expr = createLiteral(4)
): ):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNode: PathToNode pathToNode: PathToNode
pathToRevolveArg: PathToNode pathToRevolveArg: PathToNode
} }
@ -439,7 +440,7 @@ export function revolveSketch(
} }
export function sketchOnExtrudedFace( export function sketchOnExtrudedFace(
node: Program, node: Node<Program>,
sketchPathToNode: PathToNode, sketchPathToNode: PathToNode,
extrudePathToNode: PathToNode, extrudePathToNode: PathToNode,
info: ExtrudeFacePlane['faceInfo'] = { type: 'wall' } info: ExtrudeFacePlane['faceInfo'] = { type: 'wall' }
@ -571,7 +572,7 @@ export function splitPathAtPipeExpression(pathToNode: PathToNode): {
return splitPathAtPipeExpression(pathToNode.slice(0, -1)) return splitPathAtPipeExpression(pathToNode.slice(0, -1))
} }
export function createLiteral(value: string | number): Literal { export function createLiteral(value: string | number): Node<Literal> {
return { return {
type: 'Literal', type: 'Literal',
start: 0, start: 0,
@ -581,7 +582,7 @@ export function createLiteral(value: string | number): Literal {
} }
} }
export function createTagDeclarator(value: string): TagDeclarator { export function createTagDeclarator(value: string): Node<TagDeclarator> {
return { return {
type: 'TagDeclarator', type: 'TagDeclarator',
start: 0, start: 0,
@ -591,7 +592,7 @@ export function createTagDeclarator(value: string): TagDeclarator {
} }
} }
export function createIdentifier(name: string): Identifier { export function createIdentifier(name: string): Node<Identifier> {
return { return {
type: 'Identifier', type: 'Identifier',
start: 0, start: 0,
@ -601,7 +602,7 @@ export function createIdentifier(name: string): Identifier {
} }
} }
export function createPipeSubstitution(): PipeSubstitution { export function createPipeSubstitution(): Node<PipeSubstitution> {
return { return {
type: 'PipeSubstitution', type: 'PipeSubstitution',
start: 0, start: 0,
@ -612,7 +613,7 @@ export function createPipeSubstitution(): PipeSubstitution {
export function createCallExpressionStdLib( export function createCallExpressionStdLib(
name: string, name: string,
args: CallExpression['arguments'] args: CallExpression['arguments']
): CallExpression { ): Node<CallExpression> {
return { return {
type: 'CallExpression', type: 'CallExpression',
start: 0, start: 0,
@ -632,7 +633,7 @@ export function createCallExpressionStdLib(
export function createCallExpression( export function createCallExpression(
name: string, name: string,
args: CallExpression['arguments'] args: CallExpression['arguments']
): CallExpression { ): Node<CallExpression> {
return { return {
type: 'CallExpression', type: 'CallExpression',
start: 0, start: 0,
@ -651,7 +652,7 @@ export function createCallExpression(
export function createArrayExpression( export function createArrayExpression(
elements: ArrayExpression['elements'] elements: ArrayExpression['elements']
): ArrayExpression { ): Node<ArrayExpression> {
return { return {
type: 'ArrayExpression', type: 'ArrayExpression',
start: 0, start: 0,
@ -664,7 +665,7 @@ export function createArrayExpression(
export function createPipeExpression( export function createPipeExpression(
body: PipeExpression['body'] body: PipeExpression['body']
): PipeExpression { ): Node<PipeExpression> {
return { return {
type: 'PipeExpression', type: 'PipeExpression',
start: 0, start: 0,
@ -680,7 +681,7 @@ export function createVariableDeclaration(
init: VariableDeclarator['init'], init: VariableDeclarator['init'],
visibility: VariableDeclaration['visibility'] = 'default', visibility: VariableDeclaration['visibility'] = 'default',
kind: VariableDeclaration['kind'] = 'const' kind: VariableDeclaration['kind'] = 'const'
): VariableDeclaration { ): Node<VariableDeclaration> {
return { return {
type: 'VariableDeclaration', type: 'VariableDeclaration',
start: 0, start: 0,
@ -703,7 +704,7 @@ export function createVariableDeclaration(
export function createObjectExpression(properties: { export function createObjectExpression(properties: {
[key: string]: Expr [key: string]: Expr
}): ObjectExpression { }): Node<ObjectExpression> {
return { return {
type: 'ObjectExpression', type: 'ObjectExpression',
start: 0, start: 0,
@ -724,7 +725,7 @@ export function createObjectExpression(properties: {
export function createUnaryExpression( export function createUnaryExpression(
argument: UnaryExpression['argument'], argument: UnaryExpression['argument'],
operator: UnaryExpression['operator'] = '-' operator: UnaryExpression['operator'] = '-'
): UnaryExpression { ): Node<UnaryExpression> {
return { return {
type: 'UnaryExpression', type: 'UnaryExpression',
start: 0, start: 0,
@ -739,7 +740,7 @@ export function createBinaryExpression([left, operator, right]: [
BinaryExpression['left'], BinaryExpression['left'],
BinaryExpression['operator'], BinaryExpression['operator'],
BinaryExpression['right'] BinaryExpression['right']
]): BinaryExpression { ]): Node<BinaryExpression> {
return { return {
type: 'BinaryExpression', type: 'BinaryExpression',
start: 0, start: 0,
@ -754,19 +755,19 @@ export function createBinaryExpression([left, operator, right]: [
export function createBinaryExpressionWithUnary([left, right]: [ export function createBinaryExpressionWithUnary([left, right]: [
BinaryExpression['left'], BinaryExpression['left'],
BinaryExpression['right'] BinaryExpression['right']
]): BinaryExpression { ]): Node<BinaryExpression> {
if (right.type === 'UnaryExpression' && right.operator === '-') if (right.type === 'UnaryExpression' && right.operator === '-')
return createBinaryExpression([left, '-', right.argument]) return createBinaryExpression([left, '-', right.argument])
return createBinaryExpression([left, '+', right]) return createBinaryExpression([left, '+', right])
} }
export function giveSketchFnCallTag( export function giveSketchFnCallTag(
ast: Program, ast: Node<Program>,
range: Selection['range'], range: Selection['range'],
tag?: string tag?: string
): ):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
tag: string tag: string
isTagExisting: boolean isTagExisting: boolean
pathToNode: PathToNode pathToNode: PathToNode
@ -801,7 +802,7 @@ export function giveSketchFnCallTag(
} }
export function moveValueIntoNewVariablePath( export function moveValueIntoNewVariablePath(
ast: Program, ast: Node<Program>,
programMemory: ProgramMemory, programMemory: ProgramMemory,
pathToNode: PathToNode, pathToNode: PathToNode,
variableName: string variableName: string
@ -834,12 +835,12 @@ export function moveValueIntoNewVariablePath(
} }
export function moveValueIntoNewVariable( export function moveValueIntoNewVariable(
ast: Program, ast: Node<Program>,
programMemory: ProgramMemory, programMemory: ProgramMemory,
sourceRange: Selection['range'], sourceRange: Selection['range'],
variableName: string variableName: string
): { ): {
modifiedAst: Program modifiedAst: Node<Program>
pathToReplacedNode?: PathToNode pathToReplacedNode?: PathToNode
} { } {
const meta = isNodeSafeToReplace(ast, sourceRange) const meta = isNodeSafeToReplace(ast, sourceRange)
@ -872,17 +873,17 @@ export function moveValueIntoNewVariable(
*/ */
export function deleteSegmentFromPipeExpression( export function deleteSegmentFromPipeExpression(
dependentRanges: SourceRange[], dependentRanges: SourceRange[],
modifiedAst: Program, modifiedAst: Node<Program>,
programMemory: ProgramMemory, programMemory: ProgramMemory,
code: string, code: string,
pathToNode: PathToNode pathToNode: PathToNode
): Program | Error { ): Node<Program> | Error {
let _modifiedAst = structuredClone(modifiedAst) let _modifiedAst = structuredClone(modifiedAst)
dependentRanges.forEach((range) => { dependentRanges.forEach((range) => {
const path = getNodePathFromSourceRange(_modifiedAst, range) const path = getNodePathFromSourceRange(_modifiedAst, range)
const callExp = getNodeFromPath<CallExpression>( const callExp = getNodeFromPath<Node<CallExpression>>(
_modifiedAst, _modifiedAst,
path, path,
'CallExpression', 'CallExpression',
@ -928,11 +929,11 @@ export function deleteSegmentFromPipeExpression(
export function removeSingleConstraintInfo( export function removeSingleConstraintInfo(
pathToCallExp: PathToNode, pathToCallExp: PathToNode,
argDetails: SimplifiedArgDetails, argDetails: SimplifiedArgDetails,
ast: Program, ast: Node<Program>,
programMemory: ProgramMemory programMemory: ProgramMemory
): ):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNodeMap: PathToNodeMap pathToNodeMap: PathToNodeMap
} }
| false { | false {
@ -954,12 +955,12 @@ export function removeSingleConstraintInfo(
} }
export async function deleteFromSelection( export async function deleteFromSelection(
ast: Program, ast: Node<Program>,
selection: Selection, selection: Selection,
programMemory: ProgramMemory, programMemory: ProgramMemory,
getFaceDetails: (id: string) => Promise<Models['FaceIsPlanar_type']> = () => getFaceDetails: (id: string) => Promise<Models['FaceIsPlanar_type']> = () =>
({} as any) ({} as any)
): Promise<Program | Error> { ): Promise<Node<Program> | Error> {
const astClone = structuredClone(ast) const astClone = structuredClone(ast)
const range = selection.range const range = selection.range
const path = getNodePathFromSourceRange(ast, range) const path = getNodePathFromSourceRange(ast, range)
@ -1134,5 +1135,5 @@ export async function deleteFromSelection(
} }
const nonCodeMetaEmpty = () => { const nonCodeMetaEmpty = () => {
return { nonCodeNodes: {}, start: [] } return { nonCodeNodes: {}, startNodes: [], start: 0, end: 0 }
} }

View File

@ -36,11 +36,12 @@ import {
getSweepFromSuspectedPath, getSweepFromSuspectedPath,
} from 'lang/std/artifactGraph' } from 'lang/std/artifactGraph'
import { kclManager, engineCommandManager, editorManager } from 'lib/singletons' import { kclManager, engineCommandManager, editorManager } from 'lib/singletons'
import { Node } from 'wasm-lib/kcl/bindings/Node'
// Apply Fillet To Selection // Apply Fillet To Selection
export function applyFilletToSelection( export function applyFilletToSelection(
ast: Program, ast: Node<Program>,
selection: Selections, selection: Selections,
radius: KclCommandValue radius: KclCommandValue
): void | Error { ): void | Error {
@ -55,10 +56,10 @@ export function applyFilletToSelection(
} }
export function modifyAstCloneWithFilletAndTag( export function modifyAstCloneWithFilletAndTag(
ast: Program, ast: Node<Program>,
selection: Selections, selection: Selections,
radius: KclCommandValue radius: KclCommandValue
): { modifiedAst: Program; pathToFilletNode: Array<PathToNode> } | Error { ): { modifiedAst: Node<Program>; pathToFilletNode: Array<PathToNode> } | Error {
let clonedAst = structuredClone(ast) let clonedAst = structuredClone(ast)
const clonedAstForGetExtrude = structuredClone(ast) const clonedAstForGetExtrude = structuredClone(ast)
@ -246,7 +247,7 @@ export function getPathToExtrudeForSegmentSelection(
} }
async function updateAstAndFocus( async function updateAstAndFocus(
modifiedAst: Program, modifiedAst: Node<Program>,
pathToFilletNode: Array<PathToNode> pathToFilletNode: Array<PathToNode>
) { ) {
const updatedAst = await kclManager.updateAst(modifiedAst, true, { const updatedAst = await kclManager.updateAst(modifiedAst, true, {
@ -258,7 +259,7 @@ async function updateAstAndFocus(
} }
function mutateAstWithTagForSketchSegment( function mutateAstWithTagForSketchSegment(
astClone: Program, astClone: Node<Program>,
pathToSegmentNode: PathToNode pathToSegmentNode: PathToNode
): { modifiedAst: Program; tag: string } | Error { ): { modifiedAst: Program; tag: string } | Error {
const segmentNode = getNodeFromPath<CallExpression>( const segmentNode = getNodeFromPath<CallExpression>(
@ -292,7 +293,7 @@ function mutateAstWithTagForSketchSegment(
function getEdgeTagCall( function getEdgeTagCall(
tag: string, tag: string,
selectionType: string selectionType: string
): Identifier | CallExpression { ): Node<Identifier | CallExpression> {
let tagCall: Expr = createIdentifier(tag) let tagCall: Expr = createIdentifier(tag)
// Modify the tag based on selectionType // Modify the tag based on selectionType
@ -426,7 +427,7 @@ export const hasValidFilletSelection = ({
code, code,
}: { }: {
selectionRanges: Selections selectionRanges: Selections
ast: Program ast: Node<Program>
code: string code: string
}) => { }) => {
// check if there is anything filletable in the scene // check if there is anything filletable in the scene
@ -454,7 +455,7 @@ export const hasValidFilletSelection = ({
for (const selection of selectionRanges.codeBasedSelections) { for (const selection of selectionRanges.codeBasedSelections) {
// check if all selections are in sketchLineHelperMap // check if all selections are in sketchLineHelperMap
const path = getNodePathFromSourceRange(ast, selection.range) const path = getNodePathFromSourceRange(ast, selection.range)
const segmentNode = getNodeFromPath<CallExpression>( const segmentNode = getNodeFromPath<Node<CallExpression>>(
ast, ast,
path, path,
'CallExpression' 'CallExpression'
@ -534,7 +535,7 @@ export const isTagUsedInFillet = ({
ast, ast,
callExp, callExp,
}: { }: {
ast: Program ast: Node<Program>
callExp: CallExpression callExp: CallExpression
}): Array<EdgeTypes> => { }): Array<EdgeTypes> => {
const tag = getTagFromCallExpression(callExp) const tag = getTagFromCallExpression(callExp)

View File

@ -29,6 +29,7 @@ import {
} from './std/sketchcombos' } from './std/sketchcombos'
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement' import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement'
import { Node } from 'wasm-lib/kcl/bindings/Node'
/** /**
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type. * Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
@ -121,12 +122,13 @@ export function getNodeFromPathCurry(
} }
function moreNodePathFromSourceRange( function moreNodePathFromSourceRange(
node: node: Node<
| Expr | Expr
| ImportStatement | ImportStatement
| ExpressionStatement | ExpressionStatement
| VariableDeclaration | VariableDeclaration
| ReturnStatement, | ReturnStatement
>,
sourceRange: Selection['range'], sourceRange: Selection['range'],
previousPath: PathToNode = [['body', '']] previousPath: PathToNode = [['body', '']]
): PathToNode { ): PathToNode {
@ -344,15 +346,16 @@ export function getNodePathFromSourceRange(
return path return path
} }
type KCLNode = type KCLNode = Node<
| Expr | Expr
| ExpressionStatement | ExpressionStatement
| VariableDeclaration | VariableDeclaration
| VariableDeclarator | VariableDeclarator
| ReturnStatement | ReturnStatement
>
export function traverse( export function traverse(
node: KCLNode | Program, node: KCLNode | Node<Program>,
option: { option: {
enter?: (node: KCLNode, pathToNode: PathToNode) => void enter?: (node: KCLNode, pathToNode: PathToNode) => void
leave?: (node: KCLNode) => void leave?: (node: KCLNode) => void
@ -512,9 +515,9 @@ export function findAllPreviousVariables(
} }
type ReplacerFn = ( type ReplacerFn = (
_ast: Program, _ast: Node<Program>,
varName: string varName: string
) => { modifiedAst: Program; pathToReplaced: PathToNode } | Error ) => { modifiedAst: Node<Program>; pathToReplaced: PathToNode } | Error
export function isNodeSafeToReplacePath( export function isNodeSafeToReplacePath(
ast: Program, ast: Program,
@ -583,12 +586,12 @@ export function isNodeSafeToReplacePath(
} }
export function isNodeSafeToReplace( export function isNodeSafeToReplace(
ast: Program, ast: Node<Program>,
sourceRange: [number, number] sourceRange: [number, number]
): ):
| { | {
isSafe: boolean isSafe: boolean
value: Expr value: Node<Expr>
replacer: ReplacerFn replacer: ReplacerFn
} }
| Error { | Error {
@ -837,7 +840,7 @@ export function findUsesOfTagInPipe(
? String(thirdParam.value) ? String(thirdParam.value)
: thirdParam.name : thirdParam.name
const varDec = getNodeFromPath<VariableDeclaration>( const varDec = getNodeFromPath<Node<VariableDeclaration>>(
ast, ast,
pathToNode, pathToNode,
'VariableDeclaration' 'VariableDeclaration'
@ -898,7 +901,7 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
} }
/** File must contain at least one sketch that has not been extruded already */ /** File must contain at least one sketch that has not been extruded already */
export function doesSceneHaveSweepableSketch(ast: Program) { export function doesSceneHaveSweepableSketch(ast: Node<Program>) {
const theMap: any = {} const theMap: any = {}
traverse(ast as any, { traverse(ast as any, {
enter(node) { enter(node) {

View File

@ -17,6 +17,7 @@ import {
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst' import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
import { enginelessExecutor } from '../../lib/testHelpers' import { enginelessExecutor } from '../../lib/testHelpers'
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { Node } from 'wasm-lib/kcl/bindings/Node'
const eachQuad: [number, [number, number]][] = [ const eachQuad: [number, [number, number]][] = [
[-315, [1, 1]], [-315, [1, 1]],
@ -687,7 +688,7 @@ describe('testing getConstraintInfo', () => {
] ]
if (err(ast)) return ast if (err(ast)) return ast
const pathToNode = getNodePathFromSourceRange(ast, sourceRange) const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
const callExp = getNodeFromPath<CallExpression>( const callExp = getNodeFromPath<Node<CallExpression>>(
ast, ast,
pathToNode, pathToNode,
'CallExpression' 'CallExpression'
@ -841,7 +842,7 @@ describe('testing getConstraintInfo', () => {
] ]
if (err(ast)) return ast if (err(ast)) return ast
const pathToNode = getNodePathFromSourceRange(ast, sourceRange) const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
const callExp = getNodeFromPath<CallExpression>( const callExp = getNodeFromPath<Node<CallExpression>>(
ast, ast,
pathToNode, pathToNode,
'CallExpression' 'CallExpression'
@ -1197,7 +1198,7 @@ describe('testing getConstraintInfo', () => {
] ]
if (err(ast)) return ast if (err(ast)) return ast
const pathToNode = getNodePathFromSourceRange(ast, sourceRange) const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
const callExp = getNodeFromPath<CallExpression>( const callExp = getNodeFromPath<Node<CallExpression>>(
ast, ast,
pathToNode, pathToNode,
'CallExpression' 'CallExpression'

View File

@ -55,6 +55,7 @@ import { err } from 'lib/trap'
import { perpendicularDistance } from 'sketch-helpers' import { perpendicularDistance } from 'sketch-helpers'
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator' import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
import { EdgeCutInfo } from 'machines/modelingMachine' import { EdgeCutInfo } from 'machines/modelingMachine'
import { Node } from 'wasm-lib/kcl/bindings/Node'
const STRAIGHT_SEGMENT_ERR = new Error( const STRAIGHT_SEGMENT_ERR = new Error(
'Invalid input, expected "straight-segment"' 'Invalid input, expected "straight-segment"'
@ -1785,7 +1786,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
) )
} }
if (intersectTag !== -1) { if (intersectTag !== -1) {
const tag = firstArg.properties[intersectTag]?.value as Identifier const tag = firstArg.properties[intersectTag]?.value as Node<Identifier>
const pathToTagProp: PathToNode = [ const pathToTagProp: PathToNode = [
...pathToObjectExp, ...pathToObjectExp,
[intersectTag, 'index'], [intersectTag, 'index'],
@ -1825,7 +1826,9 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
body: [], body: [],
nonCodeMeta: { nonCodeMeta: {
start: [], start: 0,
end: 0,
startNodes: [],
nonCodeNodes: [], nonCodeNodes: [],
}, },
}, },
@ -1865,7 +1868,7 @@ export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
} as const } as const
export function changeSketchArguments( export function changeSketchArguments(
node: Program, node: Node<Program>,
programMemory: ProgramMemory, programMemory: ProgramMemory,
sourceRangeOrPath: sourceRangeOrPath:
| { | {
@ -1877,7 +1880,7 @@ export function changeSketchArguments(
pathToNode: PathToNode pathToNode: PathToNode
}, },
input: SegmentInputs input: SegmentInputs
): { modifiedAst: Program; pathToNode: PathToNode } | Error { ): { modifiedAst: Node<Program>; pathToNode: PathToNode } | Error {
const _node = { ...node } const _node = { ...node }
const thePath = const thePath =
sourceRangeOrPath.type === 'sourceRange' sourceRangeOrPath.type === 'sourceRange'
@ -1906,7 +1909,7 @@ export function changeSketchArguments(
} }
export function getConstraintInfo( export function getConstraintInfo(
callExpression: CallExpression, callExpression: Node<CallExpression>,
code: string, code: string,
pathToNode: PathToNode pathToNode: PathToNode
): ConstrainInfo[] { ): ConstrainInfo[] {
@ -1944,7 +1947,7 @@ export function compareVec2Epsilon2(
} }
interface CreateLineFnCallArgs { interface CreateLineFnCallArgs {
node: Program node: Node<Program>
programMemory: ProgramMemory programMemory: ProgramMemory
input: SegmentInputs input: SegmentInputs
fnName: ToolTip fnName: ToolTip
@ -1961,7 +1964,7 @@ export function addNewSketchLn({
spliceBetween = false, spliceBetween = false,
}: CreateLineFnCallArgs): }: CreateLineFnCallArgs):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNode: PathToNode pathToNode: PathToNode
} }
| Error { | Error {
@ -1971,8 +1974,12 @@ export function addNewSketchLn({
return new Error('not a sketch line helper') return new Error('not a sketch line helper')
} }
getNodeFromPath<VariableDeclarator>(node, pathToNode, 'VariableDeclarator') getNodeFromPath<Node<VariableDeclarator>>(
getNodeFromPath<PipeExpression | CallExpression>( node,
pathToNode,
'VariableDeclarator'
)
getNodeFromPath<Node<PipeExpression | CallExpression>>(
node, node,
pathToNode, pathToNode,
'PipeExpression' 'PipeExpression'
@ -1991,13 +1998,13 @@ export function addCallExpressionsToPipe({
pathToNode, pathToNode,
expressions, expressions,
}: { }: {
node: Program node: Node<Program>
programMemory: ProgramMemory programMemory: ProgramMemory
pathToNode: PathToNode pathToNode: PathToNode
expressions: CallExpression[] expressions: Node<CallExpression>[]
}) { }) {
const _node = { ...node } const _node = { ...node }
const pipeExpression = getNodeFromPath<PipeExpression>( const pipeExpression = getNodeFromPath<Node<PipeExpression>>(
_node, _node,
pathToNode, pathToNode,
'PipeExpression' 'PipeExpression'
@ -2046,7 +2053,7 @@ export function replaceSketchLine({
replaceExistingCallback, replaceExistingCallback,
referencedSegment, referencedSegment,
}: { }: {
node: Program node: Node<Program>
programMemory: ProgramMemory programMemory: ProgramMemory
pathToNode: PathToNode pathToNode: PathToNode
fnName: ToolTip fnName: ToolTip
@ -2055,7 +2062,7 @@ export function replaceSketchLine({
referencedSegment?: Path referencedSegment?: Path
}): }):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
valueUsedInTransform?: number valueUsedInTransform?: number
pathToNode: PathToNode pathToNode: PathToNode
} }
@ -2107,7 +2114,7 @@ function addTagToChamfer(
edgeCutMeta: EdgeCutInfo | null edgeCutMeta: EdgeCutInfo | null
): ):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
tag: string tag: string
} }
| Error { | Error {
@ -2234,7 +2241,7 @@ export function addTagForSketchOnFace(
edgeCutMeta: EdgeCutInfo | null edgeCutMeta: EdgeCutInfo | null
): ):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
tag: string tag: string
} }
| Error { | Error {
@ -2272,12 +2279,14 @@ function isAngleLiteral(lineArugement: Expr): boolean {
: false : false
} }
type addTagFn = (a: AddTagInfo) => { modifiedAst: Program; tag: string } | Error type addTagFn = (
a: AddTagInfo
) => { modifiedAst: Node<Program>; tag: string } | Error
function addTag(tagIndex = 2): addTagFn { function addTag(tagIndex = 2): addTagFn {
return ({ node, pathToNode }) => { return ({ node, pathToNode }) => {
const _node = { ...node } const _node = { ...node }
const callExpr = getNodeFromPath<CallExpression>( const callExpr = getNodeFromPath<Node<CallExpression>>(
_node, _node,
pathToNode, pathToNode,
'CallExpression' 'CallExpression'

View File

@ -49,6 +49,7 @@ import {
getSketchSegmentFromSourceRange, getSketchSegmentFromSourceRange,
} from './sketchConstraints' } from './sketchConstraints'
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils' import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
import { Node } from 'wasm-lib/kcl/bindings/Node'
export type LineInputsType = export type LineInputsType =
| 'xAbsolute' | 'xAbsolute'
@ -325,7 +326,7 @@ const setHorzVertDistanceCreateNode =
if (isUndef(refNum) || err(literalArg)) return REF_NUM_ERR if (isUndef(refNum) || err(literalArg)) return REF_NUM_ERR
const valueUsedInTransform = roundOff(literalArg - refNum, 2) const valueUsedInTransform = roundOff(literalArg - refNum, 2)
let finalValue: Expr = createBinaryExpressionWithUnary([ let finalValue: Node<Expr> = createBinaryExpressionWithUnary([
createSegEnd(referenceSegName, !index), createSegEnd(referenceSegName, !index),
forceValueUsedInTransform || createLiteral(valueUsedInTransform), forceValueUsedInTransform || createLiteral(valueUsedInTransform),
]) ])
@ -683,6 +684,14 @@ const transformMap: TransformMap = {
tag tag
), ),
}, },
xAbs: {
tooltip: 'lineTo',
createNode: setAbsDistanceCreateNode('x'),
},
yAbs: {
tooltip: 'lineTo',
createNode: setAbsDistanceCreateNode('y'),
},
}, },
xAbsolute: { xAbsolute: {
equalLength: { equalLength: {
@ -1541,7 +1550,7 @@ export function transformSecondarySketchLinesTagFirst({
forceSegName, forceSegName,
forceValueUsedInTransform, forceValueUsedInTransform,
}: { }: {
ast: Program ast: Node<Program>
selectionRanges: Selections selectionRanges: Selections
transformInfos: TransformInfo[] transformInfos: TransformInfo[]
programMemory: ProgramMemory programMemory: ProgramMemory
@ -1549,7 +1558,7 @@ export function transformSecondarySketchLinesTagFirst({
forceValueUsedInTransform?: BinaryPart forceValueUsedInTransform?: BinaryPart
}): }):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
valueUsedInTransform?: number valueUsedInTransform?: number
pathToNodeMap: PathToNodeMap pathToNodeMap: PathToNodeMap
tagInfo: { tagInfo: {
@ -1620,7 +1629,7 @@ export function transformAstSketchLines({
forceValueUsedInTransform, forceValueUsedInTransform,
referencedSegmentRange, referencedSegmentRange,
}: { }: {
ast: Program ast: Node<Program>
selectionRanges: Selections | PathToNode[] selectionRanges: Selections | PathToNode[]
transformInfos: TransformInfo[] transformInfos: TransformInfo[]
programMemory: ProgramMemory programMemory: ProgramMemory
@ -1629,7 +1638,7 @@ export function transformAstSketchLines({
referencedSegmentRange?: Selection['range'] referencedSegmentRange?: Selection['range']
}): }):
| { | {
modifiedAst: Program modifiedAst: Node<Program>
valueUsedInTransform?: number valueUsedInTransform?: number
pathToNodeMap: PathToNodeMap pathToNodeMap: PathToNodeMap
} }
@ -1647,7 +1656,7 @@ export function transformAstSketchLines({
const getNode = getNodeFromPathCurry(node, _pathToNode) const getNode = getNodeFromPathCurry(node, _pathToNode)
const callExp = getNode<CallExpression>('CallExpression') const callExp = getNode<Node<CallExpression>>('CallExpression')
if (err(callExp)) return callExp if (err(callExp)) return callExp
const varDec = getNode<VariableDeclarator>('VariableDeclarator') const varDec = getNode<VariableDeclarator>('VariableDeclarator')
if (err(varDec)) return varDec if (err(varDec)) return varDec
@ -1806,13 +1815,16 @@ function createSegAngle(referenceSegName: string): BinaryPart {
return createCallExpression('segAng', [createIdentifier(referenceSegName)]) return createCallExpression('segAng', [createIdentifier(referenceSegName)])
} }
function createSegEnd(referenceSegName: string, isX: boolean): CallExpression { function createSegEnd(
referenceSegName: string,
isX: boolean
): Node<CallExpression> {
return createCallExpression(isX ? 'segEndX' : 'segEndY', [ return createCallExpression(isX ? 'segEndX' : 'segEndY', [
createIdentifier(referenceSegName), createIdentifier(referenceSegName),
]) ])
} }
function createLastSeg(isX: boolean): CallExpression { function createLastSeg(isX: boolean): Node<CallExpression> {
return createCallExpression(isX ? 'lastSegX' : 'lastSegY', [ return createCallExpression(isX ? 'lastSegX' : 'lastSegY', [
createPipeSubstitution(), createPipeSubstitution(),
]) ])
@ -1830,7 +1842,7 @@ export function getConstraintLevelFromSourceRange(
ast: Program | Error ast: Program | Error
): Error | { range: [number, number]; level: ConstraintLevel } { ): Error | { range: [number, number]; level: ConstraintLevel } {
if (err(ast)) return ast if (err(ast)) return ast
const nodeMeta = getNodeFromPath<CallExpression>( const nodeMeta = getNodeFromPath<Node<CallExpression>>(
ast, ast,
getNodePathFromSourceRange(ast, cursorRange), getNodePathFromSourceRange(ast, cursorRange),
'CallExpression' 'CallExpression'

View File

@ -11,16 +11,17 @@ import {
BinaryPart, BinaryPart,
} from '../wasm' } from '../wasm'
import { LineInputsType } from './sketchcombos' import { LineInputsType } from './sketchcombos'
import { Node } from 'wasm-lib/kcl/bindings/Node'
export interface ModifyAstBase { export interface ModifyAstBase {
node: Program node: Node<Program>
// TODO #896: Remove ProgramMemory from this interface // TODO #896: Remove ProgramMemory from this interface
previousProgramMemory: ProgramMemory previousProgramMemory: ProgramMemory
pathToNode: PathToNode pathToNode: PathToNode
} }
export interface AddTagInfo { export interface AddTagInfo {
node: Program node: Node<Program>
pathToNode: PathToNode pathToNode: PathToNode
} }
@ -134,7 +135,7 @@ type _InputArg<T> =
* Which is why a union type is used that can be type narrowed using the {@link RawArg.type} property * Which is why a union type is used that can be type narrowed using the {@link RawArg.type} property
* {@link RawArg.expr} is common to all of these types * {@link RawArg.expr} is common to all of these types
*/ */
export type InputArg = _InputArg<Expr> export type InputArg = _InputArg<Node<Expr>>
/** /**
* {@link RawArg.expr} is the literal equivalent of whatever current expression is * {@link RawArg.expr} is the literal equivalent of whatever current expression is
@ -142,7 +143,7 @@ export type InputArg = _InputArg<Expr>
* but of course works for expressions like myVar + someFn() etc too * but of course works for expressions like myVar + someFn() etc too
* This is useful in cases where we want to "un-constrain" inputs to segments * This is useful in cases where we want to "un-constrain" inputs to segments
*/ */
type RawArg = _InputArg<Literal> type RawArg = _InputArg<Node<Literal>>
export type InputArgs = Array<InputArg> export type InputArgs = Array<InputArg>
@ -186,7 +187,7 @@ export type CreateStdLibSketchCallExpr = (args: {
inputs: InputArgs inputs: InputArgs
rawArgs: RawArgs rawArgs: RawArgs
referenceSegName: string referenceSegName: string
tag?: Expr tag?: Node<Expr>
forceValueUsedInTransform?: BinaryPart forceValueUsedInTransform?: BinaryPart
referencedSegment?: Path referencedSegment?: Path
}) => CreatedSketchExprResult | Error }) => CreatedSketchExprResult | Error
@ -215,26 +216,26 @@ export interface ConstrainInfo {
export interface SketchLineHelper { export interface SketchLineHelper {
add: (a: addCall) => add: (a: addCall) =>
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNode: PathToNode pathToNode: PathToNode
valueUsedInTransform?: number valueUsedInTransform?: number
} }
| Error | Error
updateArgs: (a: updateArgs) => updateArgs: (a: updateArgs) =>
| { | {
modifiedAst: Program modifiedAst: Node<Program>
pathToNode: PathToNode pathToNode: PathToNode
} }
| Error | Error
getTag: (a: CallExpression) => string | Error getTag: (a: CallExpression) => string | Error
addTag: (a: AddTagInfo) => addTag: (a: AddTagInfo) =>
| { | {
modifiedAst: Program modifiedAst: Node<Program>
tag: string tag: string
} }
| Error | Error
getConstraintInfo: ( getConstraintInfo: (
callExp: CallExpression, callExp: Node<CallExpression>,
code: string, code: string,
pathToNode: PathToNode pathToNode: PathToNode
) => ConstrainInfo[] ) => ConstrainInfo[]

13
src/lang/wasm.test.ts Normal file
View File

@ -0,0 +1,13 @@
import { err } from 'lib/trap'
import { parse } from './wasm'
import { enginelessExecutor } from 'lib/testHelpers'
it('can execute parsed AST', async () => {
const code = `x = 1
// A comment.`
const ast = parse(code)
expect(err(ast)).toEqual(false)
const execState = await enginelessExecutor(ast)
expect(err(ast)).toEqual(false)
expect(execState.memory.get('x')?.value).toEqual(1)
})

View File

@ -42,6 +42,7 @@ import { ExecState as RawExecState } from '../wasm-lib/kcl/bindings/ExecState'
import { ProgramMemory as RawProgramMemory } from '../wasm-lib/kcl/bindings/ProgramMemory' import { ProgramMemory as RawProgramMemory } from '../wasm-lib/kcl/bindings/ProgramMemory'
import { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef' import { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef'
import { Environment } from '../wasm-lib/kcl/bindings/Environment' import { Environment } from '../wasm-lib/kcl/bindings/Environment'
import { Node } from 'wasm-lib/kcl/bindings/Node'
export type { Program } from '../wasm-lib/kcl/bindings/Program' export type { Program } from '../wasm-lib/kcl/bindings/Program'
export type { Expr } from '../wasm-lib/kcl/bindings/Expr' export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
@ -122,11 +123,11 @@ export const initPromise = initialise()
export const rangeTypeFix = (ranges: number[][]): [number, number][] => export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
ranges.map(([start, end]) => [start, end]) ranges.map(([start, end]) => [start, end])
export const parse = (code: string | Error): Program | Error => { export const parse = (code: string | Error): Node<Program> | Error => {
if (err(code)) return code if (err(code)) return code
try { try {
const program: Program = parse_wasm(code) const program: Node<Program> = parse_wasm(code)
return program return program
} catch (e: any) { } catch (e: any) {
// throw e // throw e
@ -378,7 +379,7 @@ export function sketchFromKclValue(
} }
export const executor = async ( export const executor = async (
node: Program, node: Node<Program>,
programMemory: ProgramMemory | Error = ProgramMemory.empty(), programMemory: ProgramMemory | Error = ProgramMemory.empty(),
idGenerator: IdGenerator = defaultIdGenerator(), idGenerator: IdGenerator = defaultIdGenerator(),
engineCommandManager: EngineCommandManager, engineCommandManager: EngineCommandManager,
@ -402,7 +403,7 @@ export const executor = async (
} }
export const _executor = async ( export const _executor = async (
node: Program, node: Node<Program>,
programMemory: ProgramMemory | Error = ProgramMemory.empty(), programMemory: ProgramMemory | Error = ProgramMemory.empty(),
idGenerator: IdGenerator = defaultIdGenerator(), idGenerator: IdGenerator = defaultIdGenerator(),
engineCommandManager: EngineCommandManager, engineCommandManager: EngineCommandManager,
@ -493,13 +494,13 @@ export function lexer(str: string): Token[] | Error {
export const modifyAstForSketch = async ( export const modifyAstForSketch = async (
engineCommandManager: EngineCommandManager, engineCommandManager: EngineCommandManager,
ast: Program, ast: Node<Program>,
variableName: string, variableName: string,
currentPlane: string, currentPlane: string,
engineId: string engineId: string
): Promise<Program> => { ): Promise<Node<Program>> => {
try { try {
const updatedAst: Program = await modify_ast_for_sketch_wasm( const updatedAst: Node<Program> = await modify_ast_for_sketch_wasm(
engineCommandManager, engineCommandManager,
JSON.stringify(ast), JSON.stringify(ast),
variableName, variableName,

View File

@ -6,6 +6,7 @@ import { Identifier, Expr, VariableDeclaration } from 'lang/wasm'
import { commandBarMachine } from 'machines/commandBarMachine' import { commandBarMachine } from 'machines/commandBarMachine'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { MachineManager } from 'components/MachineManagerProvider' import { MachineManager } from 'components/MachineManagerProvider'
import { Node } from 'wasm-lib/kcl/bindings/Node'
type Icon = CustomIconName type Icon = CustomIconName
const PLATFORMS = ['both', 'web', 'desktop'] as const const PLATFORMS = ['both', 'web', 'desktop'] as const
@ -24,8 +25,8 @@ export interface KclExpression {
} }
export interface KclExpressionWithVariable extends KclExpression { export interface KclExpressionWithVariable extends KclExpression {
variableName: string variableName: string
variableDeclarationAst: VariableDeclaration variableDeclarationAst: Node<VariableDeclaration>
variableIdentifierAst: Identifier variableIdentifierAst: Node<Identifier>
insertIndex: number insertIndex: number
} }
export type KclCommandValue = KclExpression | KclExpressionWithVariable export type KclCommandValue = KclExpression | KclExpressionWithVariable

View File

@ -36,6 +36,7 @@ import {
getWallCodeRef, getWallCodeRef,
ArtifactId, ArtifactId,
} from 'lang/std/artifactGraph' } from 'lang/std/artifactGraph'
import { Node } from 'wasm-lib/kcl/bindings/Node'
export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b' export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b'
export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01' export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01'
@ -244,7 +245,7 @@ export function getEventForSegmentSelection(
const updatedAst = parse(codeManager.code) const updatedAst = parse(codeManager.code)
if (err(updatedAst)) return null if (err(updatedAst)) return null
const nodeMeta = getNodeFromPath<CallExpression>( const nodeMeta = getNodeFromPath<Node<CallExpression>>(
updatedAst, updatedAst,
pathToNode, pathToNode,
'CallExpression' 'CallExpression'
@ -362,7 +363,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => { Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return
const nodeMeta = getNodeFromPath<CallExpression>( const nodeMeta = getNodeFromPath<Node<CallExpression>>(
updated, updated,
segmentGroup.userData.pathToNode, segmentGroup.userData.pathToNode,
'CallExpression' 'CallExpression'

View File

@ -17,6 +17,7 @@ import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
import { err, reportRejection } from 'lib/trap' import { err, reportRejection } from 'lib/trap'
import { toSync } from './utils' import { toSync } from './utils'
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator' import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
import { Node } from 'wasm-lib/kcl/bindings/Node'
type WebSocketResponse = Models['WebSocketResponse_type'] type WebSocketResponse = Models['WebSocketResponse_type']
@ -84,7 +85,7 @@ class MockEngineCommandManager {
} }
export async function enginelessExecutor( export async function enginelessExecutor(
ast: Program | Error, ast: Node<Program> | Error,
pm: ProgramMemory | Error = ProgramMemory.empty(), pm: ProgramMemory | Error = ProgramMemory.empty(),
idGenerator: IdGenerator = defaultIdGenerator() idGenerator: IdGenerator = defaultIdGenerator()
): Promise<ExecState> { ): Promise<ExecState> {
@ -109,7 +110,7 @@ export async function enginelessExecutor(
} }
export async function executor( export async function executor(
ast: Program, ast: Node<Program>,
pm: ProgramMemory = ProgramMemory.empty(), pm: ProgramMemory = ProgramMemory.empty(),
idGenerator: IdGenerator = defaultIdGenerator() idGenerator: IdGenerator = defaultIdGenerator()
): Promise<ExecState> { ): Promise<ExecState> {

View File

@ -0,0 +1,2 @@
test:
runner: nextest

View File

@ -1130,11 +1130,12 @@ dependencies = [
[[package]] [[package]]
name = "handlebars" name = "handlebars"
version = "6.1.0" version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce25b617d1375ef96eeb920ae717e3da34a02fc979fe632c75128350f9e1f74a" checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315"
dependencies = [ dependencies = [
"log", "log",
"num-order",
"pest", "pest",
"pest_derive", "pest_derive",
"serde", "serde",
@ -1452,13 +1453,14 @@ checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a"
[[package]] [[package]]
name = "insta" name = "insta"
version = "1.40.0" version = "1.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" checksum = "a1f72d3e19488cf7d8ea52d2fc0f8754fc933398b337cd3cbdb28aaeb35159ef"
dependencies = [ dependencies = [
"console", "console",
"lazy_static", "lazy_static",
"linked-hash-map", "linked-hash-map",
"regex",
"serde", "serde",
"similar", "similar",
] ]
@ -1937,6 +1939,21 @@ dependencies = [
"num-traits 0.2.19", "num-traits 0.2.19",
] ]
[[package]]
name = "num-modular"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f"
[[package]]
name = "num-order"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6"
dependencies = [
"num-modular",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.1.43" version = "0.1.43"
@ -2965,9 +2982,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.213" version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
@ -2983,9 +3000,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.213" version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -57,6 +57,10 @@ debug = true
[profile.dev] [profile.dev]
debug = 0 debug = 0
[profile.dev.package]
insta.opt-level = 3
similar.opt-level = 3
[profile.test] [profile.test]
debug = "line-tables-only" debug = "line-tables-only"

View File

@ -18,7 +18,7 @@ once_cell = "1.20.2"
proc-macro2 = "1" proc-macro2 = "1"
quote = "1" quote = "1"
regex = "1.11" regex = "1.11"
serde = { version = "1.0.213", features = ["derive"] } serde = { version = "1.0.214", features = ["derive"] }
serde_tokenstream = "0.2" serde_tokenstream = "0.2"
syn = { version = "2.0.85", features = ["full"] } syn = { version = "2.0.85", features = ["full"] }

View File

@ -1,7 +1,29 @@
cnr := "cargo nextest run"
cita := "cargo insta test --accept"
# Create a new KCL snapshot test from `tests/inputs/my-test.kcl`. # Create a new KCL snapshot test from `tests/inputs/my-test.kcl`.
new-test name: new-test name:
echo "kcl_test!(\"{{name}}\", {{name}});" >> tests/executor/visuals.rs echo "kcl_test!(\"{{name}}\", {{name}});" >> tests/executor/visuals.rs
TWENTY_TWENTY=overwrite cargo nextest run --test executor -E 'test(=visuals::{{name}})' TWENTY_TWENTY=overwrite {{cnr}} --test executor -E 'test(=visuals::{{name}})'
lint: lint:
cargo clippy --all --tests --benches -- -D warnings cargo clippy --workspace --all-targets -- -D warnings
redo-kcl-stdlib-docs:
EXPECTORATE=overwrite {{cnr}} -p kcl-lib docs::gen_std_tests::test_generate_stdlib
# Create a new KCL deterministic simulation test case.
new-sim-test test_name kcl_program:
# Each test file gets its own directory. This will contain the KCL program, and its
# snapshotted artifacts (e.g. serialized tokens, serialized ASTs, program memory,
# PNG snapshots, etc).
mkdir kcl/tests/{{test_name}}
echo "{{kcl_program}}" > kcl/tests/{{test_name}}/input.kcl
# Add the various tests for this new test case.
cat kcl/tests/simtest.tmpl | sed "s/TEST_NAME_HERE/{{test_name}}/" >> kcl/src/tests.rs
# Run all the tests for the first time, in the right order.
{{cita}} -p kcl-lib -- tests::{{test_name}}::tokenize
{{cita}} -p kcl-lib -- tests::{{test_name}}::parse
{{cita}} -p kcl-lib -- tests::{{test_name}}::unparse
TWENTY_TWENTY=overwrite {{cita}} -p kcl-lib -- tests::{{test_name}}::kcl_test_execute

View File

@ -1,6 +1,6 @@
extern crate alloc; extern crate alloc;
use kcl_lib::ast::types::{ use kcl_lib::ast::types::{
BodyItem, Expr, Identifier, ItemVisibility, Literal, LiteralValue, NonCodeMeta, Program, VariableDeclaration, BodyItem, Expr, Identifier, ItemVisibility, Literal, LiteralValue, Node, Program, VariableDeclaration,
VariableDeclarator, VariableKind, VariableDeclarator, VariableKind,
}; };
use kcl_macros::parse; use kcl_macros::parse;
@ -9,36 +9,46 @@ use pretty_assertions::assert_eq;
#[test] #[test]
fn basic() { fn basic() {
let actual = parse!("const y = 4"); let actual = parse!("const y = 4");
let expected = Program { let expected = Node {
start: 0, inner: Program {
end: 11, body: vec![BodyItem::VariableDeclaration(Box::new(Node::new(
body: vec![BodyItem::VariableDeclaration(Box::new(VariableDeclaration { VariableDeclaration {
start: 0, declarations: vec![Node::new(
end: 11, VariableDeclarator {
declarations: vec![VariableDeclarator { id: Node::new(
start: 6, Identifier {
end: 11,
id: Identifier {
start: 6,
end: 7,
name: "y".to_owned(), name: "y".to_owned(),
digest: None, digest: None,
}, },
init: Expr::Literal(Box::new(Literal { 6,
start: 10, 7,
end: 11, ),
init: Expr::Literal(Box::new(Node::new(
Literal {
value: LiteralValue::IInteger(4), value: LiteralValue::IInteger(4),
raw: "4".to_owned(), raw: "4".to_owned(),
digest: None, digest: None,
})), },
10,
11,
))),
digest: None, digest: None,
}], },
6,
11,
)],
visibility: ItemVisibility::Default, visibility: ItemVisibility::Default,
kind: VariableKind::Const, kind: VariableKind::Const,
digest: None, digest: None,
}))], },
non_code_meta: NonCodeMeta::default(), 0,
11,
)))],
non_code_meta: Default::default(),
digest: None, digest: None,
},
start: 0,
end: 11,
}; };
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }

View File

@ -10,6 +10,6 @@ anyhow = "1.0.91"
hyper = { version = "0.14.29", features = ["http1", "server", "tcp"] } hyper = { version = "0.14.29", features = ["http1", "server", "tcp"] }
kcl-lib = { version = "0.2", path = "../kcl" } kcl-lib = { version = "0.2", path = "../kcl" }
pico-args = "0.5.0" pico-args = "0.5.0"
serde = { version = "1.0.213", features = ["derive"] } serde = { version = "1.0.214", features = ["derive"] }
serde_json = "1.0.128" serde_json = "1.0.128"
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }

View File

@ -38,7 +38,7 @@ pyo3 = { version = "0.22.5", optional = true }
reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] } reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] }
ropey = "1.6.1" ropey = "1.6.1"
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1", "preserve_order"] } schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1", "preserve_order"] }
serde = { version = "1.0.213", features = ["derive"] } serde = { version = "1.0.214", features = ["derive"] }
serde_json = "1.0.128" serde_json = "1.0.128"
sha2 = "0.10.8" sha2 = "0.10.8"
tabled = { version = "0.15.0", optional = true } tabled = { version = "0.15.0", optional = true }
@ -77,24 +77,16 @@ engine = []
pyo3 = ["dep:pyo3"] pyo3 = ["dep:pyo3"]
# Helper functions also used in benchmarks. # Helper functions also used in benchmarks.
lsp-test-util = [] lsp-test-util = []
tabled = ["dep:tabled"] tabled = ["dep:tabled"]
[profile.release]
panic = "abort"
debug = true
[profile.bench]
debug = true # Flamegraphs of benchmarks require accurate debug symbols
[dev-dependencies] [dev-dependencies]
base64 = "0.22.1" base64 = "0.22.1"
criterion = { version = "0.5.1", features = ["async_tokio"] } criterion = { version = "0.5.1", features = ["async_tokio"] }
expectorate = "1.1.0" expectorate = "1.1.0"
handlebars = "6.1.0" handlebars = "6.2.0"
iai = "0.1" iai = "0.1"
image = { version = "0.25.3", default-features = false, features = ["png"] } image = { version = "0.25.3", default-features = false, features = ["png"] }
insta = { version = "1.40.0", features = ["json"] } insta = { version = "1.41.0", features = ["json", "filters"] }
itertools = "0.13.0" itertools = "0.13.0"
pretty_assertions = "1.4.1" pretty_assertions = "1.4.1"
tokio = { version = "1.40.0", features = ["rt-multi-thread", "macros", "time"] } tokio = { version = "1.40.0", features = ["rt-multi-thread", "macros", "time"] }

View File

@ -16,7 +16,7 @@ We've built a lot of tooling to make contributing to KCL easier. If you are inte
8. Add your new standard library function to [the long list of CORE_FNS in mod.rs](https://github.com/KittyCAD/modeling-app/blob/main/src/wasm-lib/kcl/src/std/mod.rs#L42) 8. Add your new standard library function to [the long list of CORE_FNS in mod.rs](https://github.com/KittyCAD/modeling-app/blob/main/src/wasm-lib/kcl/src/std/mod.rs#L42)
9. Get a production Zoo dev token and run `export KITTYCAD_API_TOKEN=your-token-here` in a terminal 9. Get a production Zoo dev token and run `export KITTYCAD_API_TOKEN=your-token-here` in a terminal
10. Run `TWENTY_TWENTY=overwrite cargo nextest run --workspace --no-fail-fast` to take snapshot tests of your example code running in the engine 10. Run `TWENTY_TWENTY=overwrite cargo nextest run --workspace --no-fail-fast` to take snapshot tests of your example code running in the engine
11. Run `EXPECTORATE=overwrite cargo test --all generate_stdlib -- --nocapture` to generate new Markdown documentation for your function that will be used [to generate docs on our website](https://zoo.dev/docs/kcl). 11. Run `just redo-kcl-stdlib-docs` to generate new Markdown documentation for your function that will be used [to generate docs on our website](https://zoo.dev/docs/kcl).
12. Create a PR in GitHub. 12. Create a PR in GitHub.
## Bumping the version ## Bumping the version

View File

@ -13,6 +13,7 @@ pub fn bench_parse(c: &mut Criterion) {
("cube", CUBE_PROGRAM), ("cube", CUBE_PROGRAM),
("math", MATH_PROGRAM), ("math", MATH_PROGRAM),
("mike_stress_test", MIKE_STRESS_TEST_PROGRAM), ("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
("koch snowflake", LSYSTEM_KOCH_SNOWFLAKE_PROGRAM),
] { ] {
let tokens = kcl_lib::token::lexer(file).unwrap(); let tokens = kcl_lib::token::lexer(file).unwrap();
c.bench_function(&format!("parse_{name}"), move |b| { c.bench_function(&format!("parse_{name}"), move |b| {
@ -37,3 +38,4 @@ const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_p
const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl"); const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl"); const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl");
const MIKE_STRESS_TEST_PROGRAM: &str = include_str!("../../tests/executor/inputs/mike_stress_test.kcl"); const MIKE_STRESS_TEST_PROGRAM: &str = include_str!("../../tests/executor/inputs/mike_stress_test.kcl");
const LSYSTEM_KOCH_SNOWFLAKE_PROGRAM: &str = include_str!("../../tests/executor/inputs/lsystem.kcl");

View File

@ -19,6 +19,9 @@ fn lex_cube() {
fn lex_math() { fn lex_math() {
black_box(kcl_lib::token::lexer(MATH_PROGRAM).unwrap()); black_box(kcl_lib::token::lexer(MATH_PROGRAM).unwrap());
} }
fn lex_lsystem() {
black_box(kcl_lib::token::lexer(LSYSTEM_PROGRAM).unwrap());
}
fn parse_kitt() { fn parse_kitt() {
parse(KITT_PROGRAM) parse(KITT_PROGRAM)
@ -32,19 +35,25 @@ fn parse_cube() {
fn parse_math() { fn parse_math() {
parse(MATH_PROGRAM) parse(MATH_PROGRAM)
} }
fn parse_lsystem() {
parse(LSYSTEM_PROGRAM)
}
iai::main! { iai::main! {
lex_kitt, lex_kitt,
lex_pipes, lex_pipes,
lex_cube, lex_cube,
lex_math, lex_math,
lex_lsystem,
parse_kitt, parse_kitt,
parse_pipes, parse_pipes,
parse_cube, parse_cube,
parse_math, parse_math,
parse_lsystem,
} }
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl"); const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl"); const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl");
const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl"); const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl"); const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl");
const LSYSTEM_PROGRAM: &str = include_str!("../../tests/executor/inputs/lsystem.kcl");

View File

@ -7,6 +7,7 @@ pub fn bench_digest(c: &mut Criterion) {
("cube", CUBE_PROGRAM), ("cube", CUBE_PROGRAM),
("math", MATH_PROGRAM), ("math", MATH_PROGRAM),
("mike_stress_test", MIKE_STRESS_TEST_PROGRAM), ("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
("lsystem", LSYSTEM_PROGRAM),
] { ] {
let tokens = kcl_lib::token::lexer(file).unwrap(); let tokens = kcl_lib::token::lexer(file).unwrap();
let prog = kcl_lib::parser::Parser::new(tokens).ast().unwrap(); let prog = kcl_lib::parser::Parser::new(tokens).ast().unwrap();
@ -29,3 +30,4 @@ const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_p
const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl"); const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl"); const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl");
const MIKE_STRESS_TEST_PROGRAM: &str = include_str!("../../tests/executor/inputs/mike_stress_test.kcl"); const MIKE_STRESS_TEST_PROGRAM: &str = include_str!("../../tests/executor/inputs/mike_stress_test.kcl");
const LSYSTEM_PROGRAM: &str = include_str!("../../tests/executor/inputs/lsystem.kcl");

View File

@ -8,6 +8,7 @@ pub fn bench_execute(c: &mut Criterion) {
("cube", CUBE_PROGRAM), ("cube", CUBE_PROGRAM),
("server_rack_lite", SERVER_RACK_LITE_PROGRAM), ("server_rack_lite", SERVER_RACK_LITE_PROGRAM),
("server_rack_heavy", SERVER_RACK_HEAVY_PROGRAM), ("server_rack_heavy", SERVER_RACK_HEAVY_PROGRAM),
("lsystem", LSYSTEM_PROGRAM),
] { ] {
let mut group = c.benchmark_group("executor"); let mut group = c.benchmark_group("executor");
// Configure Criterion.rs to detect smaller differences and increase sample size to improve // Configure Criterion.rs to detect smaller differences and increase sample size to improve
@ -52,3 +53,4 @@ const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
const SERVER_RACK_HEAVY_PROGRAM: &str = include_str!("../../tests/executor/inputs/server-rack-heavy.kcl"); const SERVER_RACK_HEAVY_PROGRAM: &str = include_str!("../../tests/executor/inputs/server-rack-heavy.kcl");
const SERVER_RACK_LITE_PROGRAM: &str = include_str!("../../tests/executor/inputs/server-rack-lite.kcl"); const SERVER_RACK_LITE_PROGRAM: &str = include_str!("../../tests/executor/inputs/server-rack-lite.kcl");
const LEGO_PROGRAM: &str = include_str!("../../tests/executor/inputs/slow_lego.kcl.tmpl"); const LEGO_PROGRAM: &str = include_str!("../../tests/executor/inputs/slow_lego.kcl.tmpl");
const LSYSTEM_PROGRAM: &str = include_str!("../../tests/executor/inputs/lsystem.kcl");

View File

@ -42,6 +42,7 @@ fn bench_kcl_lsp_semantic_tokens(c: &mut Criterion) {
("math", MATH_PROGRAM), ("math", MATH_PROGRAM),
("mike_stress_test", MIKE_STRESS_TEST_PROGRAM), ("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
("global_tags", GLOBAL_TAGS_FILE), ("global_tags", GLOBAL_TAGS_FILE),
("lsystem", LSYSTEM_PROGRAM),
] { ] {
c.bench_with_input(BenchmarkId::new("semantic_tokens_", name), &code, |b, &s| { c.bench_with_input(BenchmarkId::new("semantic_tokens_", name), &code, |b, &s| {
let rt = Runtime::new().unwrap(); let rt = Runtime::new().unwrap();
@ -63,3 +64,4 @@ const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl"); const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl");
const MIKE_STRESS_TEST_PROGRAM: &str = include_str!("../../tests/executor/inputs/mike_stress_test.kcl"); const MIKE_STRESS_TEST_PROGRAM: &str = include_str!("../../tests/executor/inputs/mike_stress_test.kcl");
const GLOBAL_TAGS_FILE: &str = include_str!("../../tests/executor/inputs/global-tags.kcl"); const GLOBAL_TAGS_FILE: &str = include_str!("../../tests/executor/inputs/global-tags.kcl");
const LSYSTEM_PROGRAM: &str = include_str!("../../tests/executor/inputs/lsystem.kcl");

View File

@ -16,6 +16,8 @@ use crate::{
executor::{Point2d, SourceRange}, executor::{Point2d, SourceRange},
}; };
use super::types::Node;
type Point3d = kcmc::shared::Point3d<f64>; type Point3d = kcmc::shared::Point3d<f64>;
#[derive(Debug)] #[derive(Debug)]
@ -35,7 +37,7 @@ const EPSILON: f64 = 0.015625; // or 2^-6
/// a move or a new line. /// a move or a new line.
pub async fn modify_ast_for_sketch( pub async fn modify_ast_for_sketch(
engine: &Arc<Box<dyn EngineManager>>, engine: &Arc<Box<dyn EngineManager>>,
program: &mut Program, program: &mut Node<Program>,
// The name of the sketch. // The name of the sketch.
sketch_name: &str, sketch_name: &str,
// The type of plane the sketch is on. `XY` or `XZ`, etc // The type of plane the sketch is on. `XY` or `XZ`, etc
@ -195,7 +197,7 @@ fn create_start_sketch_on(
end: [f64; 2], end: [f64; 2],
plane: crate::executor::PlaneType, plane: crate::executor::PlaneType,
additional_lines: Vec<[f64; 2]>, additional_lines: Vec<[f64; 2]>,
) -> Result<VariableDeclarator, KclError> { ) -> Result<Node<VariableDeclarator>, KclError> {
let start_sketch_on = CallExpression::new("startSketchOn", vec![Literal::new(plane.to_string().into()).into()])?; let start_sketch_on = CallExpression::new("startSketchOn", vec![Literal::new(plane.to_string().into()).into()])?;
let start_profile_at = CallExpression::new( let start_profile_at = CallExpression::new(
"startProfileAt", "startProfileAt",

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,10 @@
use crate::executor::Metadata;
use crate::executor::SourceRange; use crate::executor::SourceRange;
use super::impl_value_meta; use super::BoxNode;
use super::ConstraintLevel; use super::ConstraintLevel;
use super::Hover; use super::Hover;
use super::Node;
use super::NodeList;
use super::{Digest, Expr}; use super::{Digest, Expr};
use databake::*; use databake::*;
use schemars::JsonSchema; use schemars::JsonSchema;
@ -19,12 +20,10 @@ type IfBlock = crate::ast::types::Program;
#[ts(export)] #[ts(export)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub struct IfExpression { pub struct IfExpression {
pub start: usize,
pub end: usize,
pub cond: Box<Expr>, pub cond: Box<Expr>,
pub then_val: Box<IfBlock>, pub then_val: BoxNode<IfBlock>,
pub else_ifs: Vec<ElseIf>, pub else_ifs: NodeList<ElseIf>,
pub final_else: Box<IfBlock>, pub final_else: BoxNode<IfBlock>,
pub digest: Option<Digest>, pub digest: Option<Digest>,
} }
@ -34,57 +33,21 @@ pub struct IfExpression {
#[ts(export)] #[ts(export)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub struct ElseIf { pub struct ElseIf {
pub start: usize,
pub end: usize,
pub cond: Expr, pub cond: Expr,
pub then_val: Box<IfBlock>, pub then_val: BoxNode<IfBlock>,
pub digest: Option<Digest>, pub digest: Option<Digest>,
} }
// Source code metadata // Source code metadata
impl_value_meta!(IfExpression); impl Node<IfExpression> {
impl_value_meta!(ElseIf);
impl IfExpression {
fn source_ranges(&self) -> Vec<SourceRange> { fn source_ranges(&self) -> Vec<SourceRange> {
vec![SourceRange::from(self)] vec![SourceRange::from(self)]
} }
} }
impl From<IfExpression> for Metadata { impl Node<ElseIf> {
fn from(value: IfExpression) -> Self {
Self {
source_range: value.into(),
}
}
}
impl From<ElseIf> for Metadata {
fn from(value: ElseIf) -> Self {
Self {
source_range: value.into(),
}
}
}
impl From<&IfExpression> for Metadata {
fn from(value: &IfExpression) -> Self {
Self {
source_range: value.into(),
}
}
}
impl From<&ElseIf> for Metadata {
fn from(value: &ElseIf) -> Self {
Self {
source_range: value.into(),
}
}
}
impl ElseIf {
#[allow(dead_code)] #[allow(dead_code)]
fn source_ranges(&self) -> Vec<SourceRange> { fn source_ranges(&self) -> Vec<SourceRange> {
vec![SourceRange([self.start, self.end])] vec![SourceRange([self.start, self.end])]
@ -93,6 +56,15 @@ impl ElseIf {
// IDE support and refactors // IDE support and refactors
impl Node<IfExpression> {
/// Get the constraint level.
pub fn get_constraint_level(&self) -> ConstraintLevel {
ConstraintLevel::Full {
source_ranges: self.source_ranges(),
}
}
}
impl IfExpression { impl IfExpression {
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> { pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
self.cond self.cond
@ -115,12 +87,7 @@ impl IfExpression {
} }
self.final_else.rename_identifiers(old_name, new_name); self.final_else.rename_identifiers(old_name, new_name);
} }
/// Get the constraint level.
pub fn get_constraint_level(&self) -> ConstraintLevel {
ConstraintLevel::Full {
source_ranges: self.source_ranges(),
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Expr) { pub fn replace_value(&mut self, source_range: SourceRange, new_value: Expr) {
self.cond.replace_value(source_range, new_value.clone()); self.cond.replace_value(source_range, new_value.clone());
for else_if in &mut self.else_ifs { for else_if in &mut self.else_ifs {

View File

@ -1,6 +1,6 @@
use super::{ use super::{
human_friendly_type, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, human_friendly_type, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart,
CallExpression, Expr, IfExpression, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, CallExpression, Expr, IfExpression, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node,
ObjectExpression, TagDeclarator, UnaryExpression, UnaryOperator, ObjectExpression, TagDeclarator, UnaryExpression, UnaryOperator,
}; };
use crate::{ use crate::{
@ -32,7 +32,7 @@ impl BinaryPart {
} }
} }
impl MemberExpression { impl Node<MemberExpression> {
pub fn get_result_array(&self, exec_state: &mut ExecState, index: usize) -> Result<KclValue, KclError> { pub fn get_result_array(&self, exec_state: &mut ExecState, index: usize) -> Result<KclValue, KclError> {
let array = match &self.object { let array = match &self.object {
MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?, MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
@ -137,7 +137,7 @@ impl MemberExpression {
} }
} }
impl BinaryExpression { impl Node<BinaryExpression> {
#[async_recursion] #[async_recursion]
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> { pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let left_json_value = self.left.get_result(exec_state, ctx).await?.get_json_value()?; let left_json_value = self.left.get_result(exec_state, ctx).await?.get_json_value()?;
@ -186,7 +186,7 @@ impl BinaryExpression {
} }
} }
impl UnaryExpression { impl Node<UnaryExpression> {
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> { pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
if self.operator == UnaryOperator::Not { if self.operator == UnaryOperator::Not {
let value = self.argument.get_result(exec_state, ctx).await?.get_json_value()?; let value = self.argument.get_result(exec_state, ctx).await?.get_json_value()?;
@ -297,7 +297,7 @@ async fn inner_execute_pipe_body(
Ok(final_output) Ok(final_output)
} }
impl CallExpression { impl Node<CallExpression> {
#[async_recursion] #[async_recursion]
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> { pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let fn_name = &self.callee.name; let fn_name = &self.callee.name;
@ -502,7 +502,7 @@ impl CallExpression {
} }
} }
impl TagDeclarator { impl Node<TagDeclarator> {
pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> { pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier { let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
value: self.name.clone(), value: self.name.clone(),
@ -518,7 +518,7 @@ impl TagDeclarator {
} }
} }
impl ArrayExpression { impl Node<ArrayExpression> {
#[async_recursion] #[async_recursion]
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> { pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let mut results = Vec::with_capacity(self.elements.len()); let mut results = Vec::with_capacity(self.elements.len());
@ -543,21 +543,21 @@ impl ArrayExpression {
} }
} }
impl ArrayRangeExpression { impl Node<ArrayRangeExpression> {
#[async_recursion] #[async_recursion]
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> { pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let metadata = Metadata::from(&*self.start_element); let metadata = Metadata::from(&self.start_element);
let start = ctx let start = ctx
.execute_expr(&self.start_element, exec_state, &metadata, StatementKind::Expression) .execute_expr(&self.start_element, exec_state, &metadata, StatementKind::Expression)
.await? .await?
.get_json_value()?; .get_json_value()?;
let start = parse_json_number_as_i64(&start, (&*self.start_element).into())?; let start = parse_json_number_as_i64(&start, (&self.start_element).into())?;
let metadata = Metadata::from(&*self.end_element); let metadata = Metadata::from(&self.end_element);
let end = ctx let end = ctx
.execute_expr(&self.end_element, exec_state, &metadata, StatementKind::Expression) .execute_expr(&self.end_element, exec_state, &metadata, StatementKind::Expression)
.await? .await?
.get_json_value()?; .get_json_value()?;
let end = parse_json_number_as_i64(&end, (&*self.end_element).into())?; let end = parse_json_number_as_i64(&end, (&self.end_element).into())?;
if end < start { if end < start {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
@ -581,7 +581,7 @@ impl ArrayRangeExpression {
} }
} }
impl ObjectExpression { impl Node<ObjectExpression> {
#[async_recursion] #[async_recursion]
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> { pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let mut object = serde_json::Map::new(); let mut object = serde_json::Map::new();
@ -655,7 +655,7 @@ pub fn json_as_bool(j: &serde_json::Value) -> Option<bool> {
} }
} }
impl IfExpression { impl Node<IfExpression> {
#[async_recursion] #[async_recursion]
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> { pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
// Check the `if` branch. // Check the `if` branch.
@ -717,13 +717,13 @@ impl Property {
let property_src: SourceRange = value.clone().into(); let property_src: SourceRange = value.clone().into();
match value { match value {
LiteralIdentifier::Identifier(identifier) => { LiteralIdentifier::Identifier(identifier) => {
let name = identifier.name; let name = &identifier.name;
if !computed { if !computed {
// Treat the property as a literal // Treat the property as a literal
Ok(Property::String(name.to_string())) Ok(Property::String(name.to_string()))
} else { } else {
// Actually evaluate memory to compute the property. // 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)?;
let KclValue::UserVal(prop) = prop else { let KclValue::UserVal(prop) = prop else {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
source_ranges: property_sr, source_ranges: property_sr,
@ -732,7 +732,7 @@ impl Property {
), ),
})); }));
}; };
jvalue_to_prop(&prop.value, property_sr, &name) jvalue_to_prop(&prop.value, property_sr, name)
} }
} }
LiteralIdentifier::Literal(literal) => { LiteralIdentifier::Literal(literal) => {

View File

@ -5,6 +5,8 @@ use serde_json::Value as JValue;
use crate::ast::types::{Expr, Literal}; use crate::ast::types::{Expr, Literal};
use super::Node;
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
#[databake(path = kcl_lib::ast::types)] #[databake(path = kcl_lib::ast::types)]
#[ts(export)] #[ts(export)]
@ -33,8 +35,8 @@ impl LiteralValue {
} }
} }
impl From<Literal> for Expr { impl From<Node<Literal>> for Expr {
fn from(literal: Literal) -> Self { fn from(literal: Node<Literal>) -> Self {
Expr::Literal(Box::new(literal)) Expr::Literal(Box::new(literal))
} }
} }

View File

@ -396,6 +396,9 @@ fn generate_function(internal_fn: Box<dyn StdLibFn>) -> Result<BTreeMap<String,
fn cleanup_static_links(output: &str) -> String { fn cleanup_static_links(output: &str) -> String {
let mut cleaned_output = output.to_string(); let mut cleaned_output = output.to_string();
// Fix the links to the types. // Fix the links to the types.
// Gross hack for the stupid alias types.
cleaned_output = cleaned_output.replace("TagNode", "TagDeclarator");
let link = format!("[`{}`](/docs/kcl/types#tag-declaration)", "TagDeclarator"); let link = format!("[`{}`](/docs/kcl/types#tag-declaration)", "TagDeclarator");
cleaned_output = cleaned_output.replace("`TagDeclarator`", &link); cleaned_output = cleaned_output.replace("`TagDeclarator`", &link);
let link = format!("[`{}`](/docs/kcl/types#tag-identifier)", "TagIdentifier"); let link = format!("[`{}`](/docs/kcl/types#tag-identifier)", "TagIdentifier");
@ -409,7 +412,7 @@ fn cleanup_type_links(output: &str, types: Vec<String>) -> String {
let mut cleaned_output = output.to_string(); let mut cleaned_output = output.to_string();
// Fix the links to the types. // Fix the links to the types.
for type_name in types { for type_name in types {
if type_name == "TagDeclarator" || type_name == "TagIdentifier" { if type_name == "TagDeclarator" || type_name == "TagIdentifier" || type_name == "TagNode" {
continue; continue;
} else { } else {
let link = format!("(/docs/kcl/types/{})", type_name); let link = format!("(/docs/kcl/types/{})", type_name);
@ -486,7 +489,7 @@ fn generate_type(
} }
// Skip over TagDeclarator and TagIdentifier since they have custom docs. // Skip over TagDeclarator and TagIdentifier since they have custom docs.
if name == "TagDeclarator" || name == "TagIdentifier" { if name == "TagDeclarator" || name == "TagIdentifier" || name == "TagNode" {
return Ok(()); return Ok(());
} }

View File

@ -76,7 +76,7 @@ impl StdLibFnArg {
|| self.type_ == "SketchOrSurface" || self.type_ == "SketchOrSurface"
{ {
return Ok(Some((index, format!("${{{}:{}}}", index, "%")))); return Ok(Some((index, format!("${{{}:{}}}", index, "%"))));
} else if self.type_ == "TagDeclarator" && self.required { } else if (self.type_ == "TagDeclarator" || self.type_ == "TagNode") && self.required {
return Ok(Some((index, format!("${{{}:{}}}", index, "$myTag")))); return Ok(Some((index, format!("${{{}:{}}}", index, "$myTag"))));
} else if self.type_ == "TagIdentifier" && self.required { } else if self.type_ == "TagIdentifier" && self.required {
// TODO: actually use the ast to populate this. // TODO: actually use the ast to populate this.

View File

@ -26,8 +26,8 @@ type Point3D = kcmc::shared::Point3d<f64>;
use crate::{ use crate::{
ast::types::{ ast::types::{
human_friendly_type, BodyItem, Expr, ExpressionStatement, FunctionExpression, ImportStatement, ItemVisibility, human_friendly_type, BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, Node, NodeRef, Program,
KclNone, Program, ReturnStatement, TagDeclarator, TagDeclarator, TagNode,
}, },
engine::{EngineManager, ExecutionKind}, engine::{EngineManager, ExecutionKind},
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
@ -155,6 +155,7 @@ impl Default for ProgramMemory {
/// An index pointing to an environment. /// An index pointing to an environment.
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[schemars(transparent)]
pub struct EnvironmentRef(usize); pub struct EnvironmentRef(usize);
impl EnvironmentRef { impl EnvironmentRef {
@ -339,7 +340,7 @@ impl IdGenerator {
pub enum KclValue { pub enum KclValue {
UserVal(UserVal), UserVal(UserVal),
TagIdentifier(Box<TagIdentifier>), TagIdentifier(Box<TagIdentifier>),
TagDeclarator(Box<TagDeclarator>), TagDeclarator(crate::ast::types::BoxNode<TagDeclarator>),
Plane(Box<Plane>), Plane(Box<Plane>),
Face(Box<Face>), Face(Box<Face>),
@ -352,7 +353,7 @@ pub enum KclValue {
Function { Function {
#[serde(skip)] #[serde(skip)]
func: Option<MemoryFunction>, func: Option<MemoryFunction>,
expression: Box<FunctionExpression>, expression: crate::ast::types::BoxNode<FunctionExpression>,
memory: Box<ProgramMemory>, memory: Box<ProgramMemory>,
#[serde(rename = "__meta")] #[serde(rename = "__meta")]
meta: Vec<Metadata>, meta: Vec<Metadata>,
@ -890,7 +891,7 @@ pub type MemoryFunction =
fn( fn(
s: Vec<KclValue>, s: Vec<KclValue>,
memory: ProgramMemory, memory: ProgramMemory,
expression: Box<FunctionExpression>, expression: crate::ast::types::BoxNode<FunctionExpression>,
metadata: Vec<Metadata>, metadata: Vec<Metadata>,
exec_state: &ExecState, exec_state: &ExecState,
ctx: ExecutorContext, ctx: ExecutorContext,
@ -900,7 +901,7 @@ impl From<KclValue> for Vec<SourceRange> {
fn from(item: KclValue) -> Self { fn from(item: KclValue) -> Self {
match item { match item {
KclValue::UserVal(u) => u.meta.iter().map(|m| m.source_range).collect(), KclValue::UserVal(u) => u.meta.iter().map(|m| m.source_range).collect(),
KclValue::TagDeclarator(t) => t.into(), KclValue::TagDeclarator(t) => vec![(&t).into()],
KclValue::TagIdentifier(t) => t.meta.iter().map(|m| m.source_range).collect(), KclValue::TagIdentifier(t) => t.meta.iter().map(|m| m.source_range).collect(),
KclValue::Solid(e) => e.meta.iter().map(|m| m.source_range).collect(), KclValue::Solid(e) => e.meta.iter().map(|m| m.source_range).collect(),
KclValue::Solids { value } => value KclValue::Solids { value } => value
@ -1043,9 +1044,9 @@ impl KclValue {
} }
/// Get a tag declarator from a memory item. /// Get a tag declarator from a memory item.
pub fn get_tag_declarator(&self) -> Result<TagDeclarator, KclError> { pub fn get_tag_declarator(&self) -> Result<TagNode, KclError> {
match self { match self {
KclValue::TagDeclarator(t) => Ok(*t.clone()), KclValue::TagDeclarator(t) => Ok((**t).clone()),
_ => Err(KclError::Semantic(KclErrorDetails { _ => Err(KclError::Semantic(KclErrorDetails {
message: format!("Not a tag declarator: {:?}", self), message: format!("Not a tag declarator: {:?}", self),
source_ranges: self.clone().into(), source_ranges: self.clone().into(),
@ -1054,9 +1055,9 @@ impl KclValue {
} }
/// Get an optional tag from a memory item. /// Get an optional tag from a memory item.
pub fn get_tag_declarator_opt(&self) -> Result<Option<TagDeclarator>, KclError> { pub fn get_tag_declarator_opt(&self) -> Result<Option<TagNode>, KclError> {
match self { match self {
KclValue::TagDeclarator(t) => Ok(Some(*t.clone())), KclValue::TagDeclarator(t) => Ok(Some((**t).clone())),
_ => Err(KclError::Semantic(KclErrorDetails { _ => Err(KclError::Semantic(KclErrorDetails {
message: format!("Not a tag declarator: {:?}", self), message: format!("Not a tag declarator: {:?}", self),
source_ranges: self.clone().into(), source_ranges: self.clone().into(),
@ -1200,7 +1201,7 @@ pub struct GetTangentialInfoFromPathsResult {
} }
impl Sketch { impl Sketch {
pub(crate) fn add_tag(&mut self, tag: &TagDeclarator, current_path: &Path) { pub(crate) fn add_tag(&mut self, tag: NodeRef<'_, TagDeclarator>, current_path: &Path) {
let mut tag_identifier: TagIdentifier = tag.into(); let mut tag_identifier: TagIdentifier = tag.into();
let base = current_path.get_base(); let base = current_path.get_base();
tag_identifier.info = Some(TagEngineInfo { tag_identifier.info = Some(TagEngineInfo {
@ -1326,7 +1327,7 @@ pub enum EdgeCut {
/// The engine id of the edge to fillet. /// The engine id of the edge to fillet.
#[serde(rename = "edgeId")] #[serde(rename = "edgeId")]
edge_id: uuid::Uuid, edge_id: uuid::Uuid,
tag: Box<Option<TagDeclarator>>, tag: Box<Option<TagNode>>,
}, },
/// A chamfer. /// A chamfer.
Chamfer { Chamfer {
@ -1336,7 +1337,7 @@ pub enum EdgeCut {
/// The engine id of the edge to chamfer. /// The engine id of the edge to chamfer.
#[serde(rename = "edgeId")] #[serde(rename = "edgeId")]
edge_id: uuid::Uuid, edge_id: uuid::Uuid,
tag: Box<Option<TagDeclarator>>, tag: Box<Option<TagNode>>,
}, },
} }
@ -1355,7 +1356,7 @@ impl EdgeCut {
} }
} }
pub fn tag(&self) -> Option<TagDeclarator> { pub fn tag(&self) -> Option<TagNode> {
match self { match self {
EdgeCut::Fillet { tag, .. } => *tag.clone(), EdgeCut::Fillet { tag, .. } => *tag.clone(),
EdgeCut::Chamfer { tag, .. } => *tag.clone(), EdgeCut::Chamfer { tag, .. } => *tag.clone(),
@ -1529,26 +1530,10 @@ impl From<SourceRange> for Metadata {
} }
} }
impl From<&ImportStatement> for Metadata { impl<T> From<NodeRef<'_, T>> for Metadata {
fn from(stmt: &ImportStatement) -> Self { fn from(node: NodeRef<'_, T>) -> Self {
Self { Self {
source_range: SourceRange::new(stmt.start, stmt.end), source_range: SourceRange::new(node.start, node.end),
}
}
}
impl From<&ExpressionStatement> for Metadata {
fn from(exp_statement: &ExpressionStatement) -> Self {
Self {
source_range: SourceRange::new(exp_statement.start, exp_statement.end),
}
}
}
impl From<&ReturnStatement> for Metadata {
fn from(return_statement: &ReturnStatement) -> Self {
Self {
source_range: SourceRange::new(return_statement.start, return_statement.end),
} }
} }
} }
@ -1573,7 +1558,7 @@ pub struct BasePath {
#[ts(type = "[number, number]")] #[ts(type = "[number, number]")]
pub to: [f64; 2], pub to: [f64; 2],
/// The tag of the path. /// The tag of the path.
pub tag: Option<TagDeclarator>, pub tag: Option<TagNode>,
/// Metadata. /// Metadata.
#[serde(rename = "__geoMeta")] #[serde(rename = "__geoMeta")]
pub geo_meta: GeoMeta, pub geo_meta: GeoMeta,
@ -1709,7 +1694,7 @@ impl Path {
} }
} }
pub fn get_tag(&self) -> Option<TagDeclarator> { pub fn get_tag(&self) -> Option<TagNode> {
match self { match self {
Path::ToPoint { base } => base.tag.clone(), Path::ToPoint { base } => base.tag.clone(),
Path::Horizontal { base, .. } => base.tag.clone(), Path::Horizontal { base, .. } => base.tag.clone(),
@ -1820,7 +1805,7 @@ pub struct ChamferSurface {
/// The id for the chamfer surface. /// The id for the chamfer surface.
pub face_id: uuid::Uuid, pub face_id: uuid::Uuid,
/// The tag. /// The tag.
pub tag: Option<TagDeclarator>, pub tag: Option<Node<TagDeclarator>>,
/// Metadata. /// Metadata.
#[serde(flatten)] #[serde(flatten)]
pub geo_meta: GeoMeta, pub geo_meta: GeoMeta,
@ -1834,7 +1819,7 @@ pub struct FilletSurface {
/// The id for the fillet surface. /// The id for the fillet surface.
pub face_id: uuid::Uuid, pub face_id: uuid::Uuid,
/// The tag. /// The tag.
pub tag: Option<TagDeclarator>, pub tag: Option<Node<TagDeclarator>>,
/// Metadata. /// Metadata.
#[serde(flatten)] #[serde(flatten)]
pub geo_meta: GeoMeta, pub geo_meta: GeoMeta,
@ -1848,7 +1833,7 @@ pub struct ExtrudePlane {
/// The face id for the extrude plane. /// The face id for the extrude plane.
pub face_id: uuid::Uuid, pub face_id: uuid::Uuid,
/// The tag. /// The tag.
pub tag: Option<TagDeclarator>, pub tag: Option<Node<TagDeclarator>>,
/// Metadata. /// Metadata.
#[serde(flatten)] #[serde(flatten)]
pub geo_meta: GeoMeta, pub geo_meta: GeoMeta,
@ -1862,7 +1847,7 @@ pub struct ExtrudeArc {
/// The face id for the extrude plane. /// The face id for the extrude plane.
pub face_id: uuid::Uuid, pub face_id: uuid::Uuid,
/// The tag. /// The tag.
pub tag: Option<TagDeclarator>, pub tag: Option<Node<TagDeclarator>>,
/// Metadata. /// Metadata.
#[serde(flatten)] #[serde(flatten)]
pub geo_meta: GeoMeta, pub geo_meta: GeoMeta,
@ -1878,7 +1863,7 @@ impl ExtrudeSurface {
} }
} }
pub fn get_tag(&self) -> Option<TagDeclarator> { pub fn get_tag(&self) -> Option<Node<TagDeclarator>> {
match self { match self {
ExtrudeSurface::ExtrudePlane(ep) => ep.tag.clone(), ExtrudeSurface::ExtrudePlane(ep) => ep.tag.clone(),
ExtrudeSurface::ExtrudeArc(ea) => ea.tag.clone(), ExtrudeSurface::ExtrudeArc(ea) => ea.tag.clone(),
@ -2156,7 +2141,7 @@ impl ExecutorContext {
/// Kurt uses this for partial execution. /// Kurt uses this for partial execution.
pub async fn run( pub async fn run(
&self, &self,
program: &crate::ast::types::Program, program: NodeRef<'_, crate::ast::types::Program>,
memory: Option<ProgramMemory>, memory: Option<ProgramMemory>,
id_generator: IdGenerator, id_generator: IdGenerator,
project_directory: Option<String>, project_directory: Option<String>,
@ -2170,7 +2155,7 @@ impl ExecutorContext {
/// Kurt uses this for partial execution. /// Kurt uses this for partial execution.
pub async fn run_with_session_data( pub async fn run_with_session_data(
&self, &self,
program: &crate::ast::types::Program, program: NodeRef<'_, crate::ast::types::Program>,
memory: Option<ProgramMemory>, memory: Option<ProgramMemory>,
id_generator: IdGenerator, id_generator: IdGenerator,
project_directory: Option<String>, project_directory: Option<String>,
@ -2212,9 +2197,9 @@ impl ExecutorContext {
/// Execute an AST's program. /// Execute an AST's program.
#[async_recursion] #[async_recursion]
pub(crate) async fn inner_execute( pub(crate) async fn inner_execute<'a>(
&self, &'a self,
program: &crate::ast::types::Program, program: NodeRef<'a, crate::ast::types::Program>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
body_type: BodyType, body_type: BodyType,
) -> Result<Option<KclValue>, KclError> { ) -> Result<Option<KclValue>, KclError> {
@ -2450,11 +2435,23 @@ impl ExecutorContext {
/// Execute the program, then get a PNG screenshot. /// Execute the program, then get a PNG screenshot.
pub async fn execute_and_prepare_snapshot( pub async fn execute_and_prepare_snapshot(
&self, &self,
program: &Program, program: NodeRef<'_, Program>,
id_generator: IdGenerator, id_generator: IdGenerator,
project_directory: Option<String>, project_directory: Option<String>,
) -> Result<TakeSnapshot> { ) -> Result<TakeSnapshot> {
let _ = self.run(program, None, id_generator, project_directory).await?; self.execute_and_prepare(program, id_generator, project_directory)
.await
.map(|(_state, snap)| snap)
}
/// Execute the program, return the interpreter and outputs.
pub async fn execute_and_prepare(
&self,
program: NodeRef<'_, Program>,
id_generator: IdGenerator,
project_directory: Option<String>,
) -> Result<(ExecState, TakeSnapshot)> {
let state = self.run(program, None, id_generator, project_directory).await?;
// Zoom to fit. // Zoom to fit.
self.engine self.engine
@ -2487,7 +2484,7 @@ impl ExecutorContext {
else { else {
anyhow::bail!("Unexpected response from engine: {:?}", resp); anyhow::bail!("Unexpected response from engine: {:?}", resp);
}; };
Ok(contents) Ok((state, contents))
} }
} }
@ -2495,7 +2492,7 @@ impl ExecutorContext {
/// assign it to a parameter of the function, in the given block of function memory. /// assign it to a parameter of the function, in the given block of function memory.
/// Returns Err if too few/too many arguments were given for the function. /// Returns Err if too few/too many arguments were given for the function.
fn assign_args_to_params( fn assign_args_to_params(
function_expression: &FunctionExpression, function_expression: NodeRef<'_, FunctionExpression>,
args: Vec<KclValue>, args: Vec<KclValue>,
mut fn_memory: ProgramMemory, mut fn_memory: ProgramMemory,
) -> Result<ProgramMemory, KclError> { ) -> Result<ProgramMemory, KclError> {
@ -2547,7 +2544,7 @@ fn assign_args_to_params(
pub(crate) async fn call_user_defined_function( pub(crate) async fn call_user_defined_function(
args: Vec<KclValue>, args: Vec<KclValue>,
memory: &ProgramMemory, memory: &ProgramMemory,
function_expression: &FunctionExpression, function_expression: NodeRef<'_, FunctionExpression>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
ctx: &ExecutorContext, ctx: &ExecutorContext,
) -> Result<Option<KclValue>, KclError> { ) -> Result<Option<KclValue>, KclError> {
@ -2586,7 +2583,7 @@ mod tests {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use super::*; use super::*;
use crate::ast::types::{Identifier, Parameter}; use crate::ast::types::{Identifier, Node, Parameter};
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> { pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
let tokens = crate::token::lexer(code)?; let tokens = crate::token::lexer(code)?;
@ -3558,13 +3555,11 @@ let w = f() + f()
meta: Default::default(), meta: Default::default(),
}) })
} }
fn ident(s: &'static str) -> Identifier { fn ident(s: &'static str) -> Node<Identifier> {
Identifier { Node::no_src(Identifier {
start: 0,
end: 0,
name: s.to_owned(), name: s.to_owned(),
digest: None, digest: None,
} })
} }
fn opt_param(s: &'static str) -> Parameter { fn opt_param(s: &'static str) -> Parameter {
Parameter { Parameter {
@ -3656,20 +3651,20 @@ let w = f() + f()
), ),
] { ] {
// Run each test. // Run each test.
let func_expr = &FunctionExpression { let func_expr = &Node::no_src(FunctionExpression {
start: 0,
end: 0,
params, params,
body: crate::ast::types::Program { body: Node {
start: 0, inner: crate::ast::types::Program {
end: 0,
body: Vec::new(), body: Vec::new(),
non_code_meta: Default::default(), non_code_meta: Default::default(),
digest: None, digest: None,
}, },
start: 0,
end: 0,
},
return_type: None, return_type: None,
digest: None, digest: None,
}; });
let actual = assign_args_to_params(func_expr, args, ProgramMemory::new()); let actual = assign_args_to_params(func_expr, args, ProgramMemory::new());
assert_eq!( assert_eq!(
actual, expected, actual, expected,

View File

@ -13,7 +13,7 @@ use crate::{
pub struct FunctionParam<'a> { pub struct FunctionParam<'a> {
pub inner: Option<&'a MemoryFunction>, pub inner: Option<&'a MemoryFunction>,
pub memory: ProgramMemory, pub memory: ProgramMemory,
pub fn_expr: Box<FunctionExpression>, pub fn_expr: crate::ast::types::BoxNode<FunctionExpression>,
pub meta: Vec<Metadata>, pub meta: Vec<Metadata>,
pub ctx: ExecutorContext, pub ctx: ExecutorContext,
} }

View File

@ -25,6 +25,8 @@ pub mod lint;
pub mod lsp; pub mod lsp;
pub mod parser; pub mod parser;
pub mod settings; pub mod settings;
#[cfg(test)]
mod simulation_tests;
pub mod std; pub mod std;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub mod test_server; pub mod test_server;

View File

@ -3,7 +3,7 @@ use std::sync::Arc;
use anyhow::Result; use anyhow::Result;
use crate::{ use crate::{
ast::types::CallExpression, ast::types::{CallExpression, NodeRef},
docs::StdLibFn, docs::StdLibFn,
executor::SourceRange, executor::SourceRange,
lint::rule::{def_finding, Discovered, Finding}, lint::rule::{def_finding, Discovered, Finding},
@ -18,7 +18,10 @@ def_finding!(
Previously, we have not been failing when too many arguments are passed to a stdlib function. This is a problem because it can lead to unexpected behavior. We will in the future fail when too many arguments are passed to a function. So fix your code now." Previously, we have not been failing when too many arguments are passed to a stdlib function. This is a problem because it can lead to unexpected behavior. We will in the future fail when too many arguments are passed to a function. So fix your code now."
); );
fn lint_too_many_args_std_lib_function(f: Box<dyn StdLibFn>, exp: &CallExpression) -> Result<Vec<Discovered>> { fn lint_too_many_args_std_lib_function(
f: Box<dyn StdLibFn>,
exp: NodeRef<'_, CallExpression>,
) -> Result<Vec<Discovered>> {
let mut findings = vec![]; let mut findings = vec![];
if f.name() == "pow" { if f.name() == "pow" {

View File

@ -3,14 +3,14 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tower_lsp::lsp_types::notification::Notification; use tower_lsp::lsp_types::notification::Notification;
use crate::settings::types::UnitLength; use crate::{ast::types::Node, settings::types::UnitLength};
/// A notification that the AST has changed. /// A notification that the AST has changed.
#[derive(Debug)] #[derive(Debug)]
pub enum AstUpdated {} pub enum AstUpdated {}
impl Notification for AstUpdated { impl Notification for AstUpdated {
type Params = crate::ast::types::Program; type Params = Node<crate::ast::types::Program>;
const METHOD: &'static str = "kcl/astUpdated"; const METHOD: &'static str = "kcl/astUpdated";
} }

View File

@ -40,7 +40,7 @@ use tower_lsp::{
}; };
use crate::{ use crate::{
ast::types::{Expr, VariableKind}, ast::types::{Expr, Node, NodeRef, VariableKind},
executor::{IdGenerator, SourceRange}, executor::{IdGenerator, SourceRange},
lsp::{backend::Backend as _, util::IntoDiagnostic}, lsp::{backend::Backend as _, util::IntoDiagnostic},
parser::PIPE_OPERATOR, parser::PIPE_OPERATOR,
@ -99,7 +99,7 @@ pub struct Backend {
/// Token maps. /// Token maps.
pub token_map: DashMap<String, Vec<crate::token::Token>>, pub token_map: DashMap<String, Vec<crate::token::Token>>,
/// AST maps. /// AST maps.
pub ast_map: DashMap<String, crate::ast::types::Program>, pub ast_map: DashMap<String, Node<crate::ast::types::Program>>,
/// Memory maps. /// Memory maps.
pub memory_map: DashMap<String, crate::executor::ProgramMemory>, pub memory_map: DashMap<String, crate::executor::ProgramMemory>,
/// Current code. /// Current code.
@ -571,7 +571,7 @@ impl Backend {
self.client.publish_diagnostics(params.uri.clone(), items, None).await; self.client.publish_diagnostics(params.uri.clone(), items, None).await;
} }
async fn execute(&self, params: &TextDocumentItem, ast: &crate::ast::types::Program) -> Result<()> { async fn execute(&self, params: &TextDocumentItem, ast: NodeRef<'_, crate::ast::types::Program>) -> Result<()> {
// Check if we can execute. // Check if we can execute.
if !self.can_execute().await { if !self.can_execute().await {
return Ok(()); return Ok(());

View File

@ -7,6 +7,7 @@ use tower_lsp::{
}; };
use crate::{ use crate::{
ast::types::{Node, Program},
executor::ProgramMemory, executor::ProgramMemory,
lsp::test_util::{copilot_lsp_server, kcl_lsp_server}, lsp::test_util::{copilot_lsp_server, kcl_lsp_server},
}; };
@ -1070,7 +1071,7 @@ fn myFn = (param1) => {
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Send semantic tokens request. // Send semantic tokens request.
let semantic_tokens = server let semantic_tokens = server
@ -2396,7 +2397,7 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2416,7 +2417,7 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
}) })
.await; .await;
let mut default_hashed = crate::ast::types::Program::default(); let mut default_hashed = Node::<Program>::default();
default_hashed.compute_digest(); default_hashed.compute_digest();
// Get the ast. // Get the ast.
@ -2453,7 +2454,7 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2487,9 +2488,9 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
// Clear the ast and memory. // Clear the ast and memory.
server server
.ast_map .ast_map
.insert("file:///test.kcl".to_string(), crate::ast::types::Program::default()); .insert("file:///test.kcl".to_string(), Node::<Program>::default());
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert_eq!(ast, crate::ast::types::Program::default()); assert_eq!(ast, Node::<Program>::default());
server server
.memory_map .memory_map
.insert("file:///test.kcl".to_string(), ProgramMemory::default()); .insert("file:///test.kcl".to_string(), ProgramMemory::default());
@ -2513,7 +2514,7 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2549,7 +2550,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2604,7 +2605,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2640,7 +2641,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2698,7 +2699,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2734,7 +2735,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2770,7 +2771,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2806,7 +2807,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2841,7 +2842,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
assert!(memory != ProgramMemory::default()); assert!(memory != ProgramMemory::default());
@ -2883,7 +2884,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
let units = server.executor_ctx().await.clone().unwrap().settings.units; let units = server.executor_ctx().await.clone().unwrap().settings.units;
assert_eq!(units, crate::settings::types::UnitLength::Mm); assert_eq!(units, crate::settings::types::UnitLength::Mm);
let mut default_hashed = crate::ast::types::Program::default(); let mut default_hashed = Node::<Program>::default();
default_hashed.compute_digest(); default_hashed.compute_digest();
// Get the ast. // Get the ast.
@ -2924,7 +2925,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone(); let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
// Now it should NOT be the default memory. // Now it should NOT be the default memory.
@ -3064,7 +3065,7 @@ const part001 = startSketchOn('XY')
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Assure we have one diagnostics. // Assure we have one diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").unwrap().clone(); let diagnostics = server.diagnostics_map.get("file:///test.kcl").unwrap().clone();
@ -3087,7 +3088,7 @@ const part001 = startSketchOn('XY')
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Assure we have one diagnostics. // Assure we have one diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").unwrap().clone(); let diagnostics = server.diagnostics_map.get("file:///test.kcl").unwrap().clone();
@ -3183,7 +3184,7 @@ const part001 = startSketchOn('XY')
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl"); let memory = server.memory_map.get("file:///test.kcl");
assert!(memory.is_none()); assert!(memory.is_none());
@ -3205,7 +3206,7 @@ const part001 = startSketchOn('XY')
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl"); let memory = server.memory_map.get("file:///test.kcl");
assert!(memory.is_none()); assert!(memory.is_none());
@ -3248,7 +3249,7 @@ const part001 = startSketchOn('XY')
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl"); let memory = server.memory_map.get("file:///test.kcl");
assert!(memory.is_none()); assert!(memory.is_none());
@ -3278,7 +3279,7 @@ const NEW_LINT = 1"#
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the memory. // Get the memory.
let memory = server.memory_map.get("file:///test.kcl"); let memory = server.memory_map.get("file:///test.kcl");
assert!(memory.is_none()); assert!(memory.is_none());
@ -3394,7 +3395,7 @@ const part001 = startSketchOn('XY')
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the symbols map. // Get the symbols map.
let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone(); let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
@ -3489,7 +3490,7 @@ const part001 = startSketchOn('XY')
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the symbols map. // Get the symbols map.
let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone(); let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
@ -3532,7 +3533,7 @@ const part001 = startSketchOn('XY')
// Get the ast. // Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone(); let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
assert!(ast != crate::ast::types::Program::default()); assert!(ast != Node::<Program>::default());
// Get the symbols map. // Get the symbols map.
let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone(); let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
ast::types::Program, ast::types::{Node, Program},
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
executor::SourceRange, executor::SourceRange,
token::{Token, TokenType}, token::{Token, TokenType},
@ -13,7 +13,7 @@ pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
pub const PIPE_OPERATOR: &str = "|>"; pub const PIPE_OPERATOR: &str = "|>";
/// Parse the given KCL code into an AST. /// Parse the given KCL code into an AST.
pub fn parse(code: &str) -> Result<Program, KclError> { pub fn parse(code: &str) -> Result<Node<Program>, KclError> {
let tokens = crate::token::lexer(code)?; let tokens = crate::token::lexer(code)?;
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
parser.ast() parser.ast()
@ -33,7 +33,7 @@ impl Parser {
} }
/// Run the parser /// Run the parser
pub fn ast(&self) -> Result<Program, KclError> { pub fn ast(&self) -> Result<Node<Program>, KclError> {
if !self.unknown_tokens.is_empty() { if !self.unknown_tokens.is_empty() {
let source_ranges = self.unknown_tokens.iter().map(SourceRange::from).collect(); let source_ranges = self.unknown_tokens.iter().map(SourceRange::from).collect();
let token_list = self.unknown_tokens.iter().map(|t| t.value.as_str()).collect::<Vec<_>>(); let token_list = self.unknown_tokens.iter().map(|t| t.value.as_str()).collect::<Vec<_>>();
@ -48,7 +48,7 @@ impl Parser {
// Important, to not call this before the unknown tokens check. // Important, to not call this before the unknown tokens check.
if self.tokens.is_empty() { if self.tokens.is_empty() {
// Empty file should just do nothing. // Empty file should just do nothing.
return Ok(Program::default()); return Ok(Node::<Program>::default());
} }
// Check all the tokens are whitespace or comments. // Check all the tokens are whitespace or comments.
@ -57,7 +57,7 @@ impl Parser {
.iter() .iter()
.all(|t| t.token_type.is_whitespace() || t.token_type.is_comment()) .all(|t| t.token_type.is_whitespace() || t.token_type.is_comment())
{ {
return Ok(Program::default()); return Ok(Node::<Program>::default());
} }
parser_impl::run_parser(&mut self.tokens.as_slice()) parser_impl::run_parser(&mut self.tokens.as_slice())

View File

@ -1,18 +1,18 @@
use crate::{ use crate::{
ast::types::{BinaryExpression, BinaryOperator, BinaryPart}, ast::types::{BinaryExpression, BinaryOperator, BinaryPart, Node},
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
executor::SourceRange, executor::SourceRange,
}; };
/// Parses a list of tokens (in infix order, i.e. as the user typed them) /// Parses a list of tokens (in infix order, i.e. as the user typed them)
/// into a binary expression tree. /// into a binary expression tree.
pub fn parse(infix_tokens: Vec<BinaryExpressionToken>) -> Result<BinaryExpression, KclError> { pub fn parse(infix_tokens: Vec<BinaryExpressionToken>) -> Result<Node<BinaryExpression>, KclError> {
let rpn = postfix(infix_tokens); let rpn = postfix(infix_tokens);
evaluate(rpn) evaluate(rpn)
} }
/// Parses a list of tokens (in postfix order) into a binary expression tree. /// Parses a list of tokens (in postfix order) into a binary expression tree.
fn evaluate(rpn: Vec<BinaryExpressionToken>) -> Result<BinaryExpression, KclError> { fn evaluate(rpn: Vec<BinaryExpressionToken>) -> Result<Node<BinaryExpression>, KclError> {
let source_ranges = source_range(&rpn); let source_ranges = source_range(&rpn);
let mut operand_stack: Vec<BinaryPart> = Vec::new(); let mut operand_stack: Vec<BinaryPart> = Vec::new();
let e = KclError::Internal(KclErrorDetails { let e = KclError::Internal(KclErrorDetails {
@ -28,14 +28,19 @@ fn evaluate(rpn: Vec<BinaryExpressionToken>) -> Result<BinaryExpression, KclErro
let Some(left) = operand_stack.pop() else { let Some(left) = operand_stack.pop() else {
return Err(e); return Err(e);
}; };
BinaryPart::BinaryExpression(Box::new(BinaryExpression { let start = left.start();
start: left.start(), let end = right.end();
end: right.end(),
BinaryPart::BinaryExpression(Node::boxed(
BinaryExpression {
operator, operator,
left, left,
right, right,
digest: None, digest: None,
})) },
start,
end,
))
} }
BinaryExpressionToken::Operand(o) => o, BinaryExpressionToken::Operand(o) => o,
}; };
@ -125,13 +130,15 @@ mod tests {
fn parse_and_evaluate() { fn parse_and_evaluate() {
/// Make a literal /// Make a literal
fn lit(n: u8) -> BinaryPart { fn lit(n: u8) -> BinaryPart {
BinaryPart::Literal(Box::new(Literal { BinaryPart::Literal(Box::new(Node::new(
start: 0, Literal {
end: 0,
value: n.into(), value: n.into(),
raw: n.to_string(), raw: n.to_string(),
digest: None, digest: None,
})) },
0,
0,
)))
} }
let tests: Vec<Vec<BinaryExpressionToken>> = vec![ let tests: Vec<Vec<BinaryExpressionToken>> = vec![
// 3 + 4 × 2 ÷ ( 1 5 ) ^ 2 ^ 3 // 3 + 4 × 2 ÷ ( 1 5 ) ^ 2 ^ 3
@ -142,14 +149,16 @@ mod tests {
BinaryOperator::Mul.into(), BinaryOperator::Mul.into(),
lit(2).into(), lit(2).into(),
BinaryOperator::Div.into(), BinaryOperator::Div.into(),
BinaryPart::BinaryExpression(Box::new(BinaryExpression { BinaryPart::BinaryExpression(Node::boxed(
start: 0, BinaryExpression {
end: 0,
operator: BinaryOperator::Sub, operator: BinaryOperator::Sub,
left: lit(1), left: lit(1),
right: lit(5), right: lit(5),
digest: None, digest: None,
})) },
0,
0,
))
.into(), .into(),
BinaryOperator::Pow.into(), BinaryOperator::Pow.into(),
lit(2).into(), lit(2).into(),

File diff suppressed because it is too large Load Diff

View File

@ -4,23 +4,23 @@ expression: actual
--- ---
{ {
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 0,
"end": 5,
"operator": "+", "operator": "+",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 0,
"end": 1,
"value": 1, "value": 1,
"raw": "1" "raw": "1",
"start": 0,
"end": 1
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 4,
"end": 5,
"value": 2, "value": 2,
"raw": "2" "raw": "2",
} "start": 4,
"end": 5
},
"start": 0,
"end": 5
} }

View File

@ -4,23 +4,23 @@ expression: actual
--- ---
{ {
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 0,
"end": 3,
"operator": "+", "operator": "+",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 0,
"end": 1,
"value": 1, "value": 1,
"raw": "1" "raw": "1",
"start": 0,
"end": 1
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 2,
"end": 3,
"value": 2, "value": 2,
"raw": "2" "raw": "2",
} "start": 2,
"end": 3
},
"start": 0,
"end": 3
} }

View File

@ -4,23 +4,23 @@ expression: actual
--- ---
{ {
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 0,
"end": 4,
"operator": "-", "operator": "-",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 0,
"end": 1,
"value": 1, "value": 1,
"raw": "1" "raw": "1",
"start": 0,
"end": 1
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 3,
"end": 4,
"value": 2, "value": 2,
"raw": "2" "raw": "2",
} "start": 3,
"end": 4
},
"start": 0,
"end": 4
} }

View File

@ -4,38 +4,38 @@ expression: actual
--- ---
{ {
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 0,
"end": 9,
"operator": "+", "operator": "+",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 0,
"end": 1,
"value": 1, "value": 1,
"raw": "1" "raw": "1",
"start": 0,
"end": 1
}, },
"right": { "right": {
"type": "BinaryExpression", "type": "BinaryExpression",
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 4,
"end": 9,
"operator": "*", "operator": "*",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 4,
"end": 5,
"value": 2, "value": 2,
"raw": "2" "raw": "2",
"start": 4,
"end": 5
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 8,
"end": 9,
"value": 3, "value": 3,
"raw": "3" "raw": "3",
} "start": 8,
} "end": 9
},
"start": 4,
"end": 9
},
"start": 0,
"end": 9
} }

View File

@ -4,38 +4,38 @@ expression: actual
--- ---
{ {
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 0,
"end": 11,
"operator": "*", "operator": "*",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 0,
"end": 1,
"value": 1, "value": 1,
"raw": "1" "raw": "1",
"start": 0,
"end": 1
}, },
"right": { "right": {
"type": "BinaryExpression", "type": "BinaryExpression",
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 6,
"end": 11,
"operator": "+", "operator": "+",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 6,
"end": 7,
"value": 2, "value": 2,
"raw": "2" "raw": "2",
"start": 6,
"end": 7
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 10,
"end": 11,
"value": 3, "value": 3,
"raw": "3" "raw": "3",
} "start": 10,
} "end": 11
},
"start": 6,
"end": 11
},
"start": 0,
"end": 11
} }

View File

@ -4,53 +4,53 @@ expression: actual
--- ---
{ {
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 0,
"end": 17,
"operator": "/", "operator": "/",
"left": { "left": {
"type": "BinaryExpression", "type": "BinaryExpression",
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 0,
"end": 11,
"operator": "*", "operator": "*",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 0,
"end": 1,
"value": 1, "value": 1,
"raw": "1" "raw": "1",
"start": 0,
"end": 1
}, },
"right": { "right": {
"type": "BinaryExpression", "type": "BinaryExpression",
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 6,
"end": 11,
"operator": "+", "operator": "+",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 6,
"end": 7,
"value": 2, "value": 2,
"raw": "2" "raw": "2",
"start": 6,
"end": 7
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 10,
"end": 11,
"value": 3, "value": 3,
"raw": "3" "raw": "3",
} "start": 10,
} "end": 11
},
"start": 6,
"end": 11
},
"start": 0,
"end": 11
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 16,
"end": 17,
"value": 4, "value": 4,
"raw": "4" "raw": "4",
} "start": 16,
"end": 17
},
"start": 0,
"end": 17
} }

View File

@ -4,53 +4,53 @@ expression: actual
--- ---
{ {
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 0,
"end": 17,
"operator": "+", "operator": "+",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 0,
"end": 1,
"value": 1, "value": 1,
"raw": "1" "raw": "1",
"start": 0,
"end": 1
}, },
"right": { "right": {
"type": "BinaryExpression", "type": "BinaryExpression",
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 6,
"end": 17,
"operator": "/", "operator": "/",
"left": { "left": {
"type": "BinaryExpression", "type": "BinaryExpression",
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 6,
"end": 11,
"operator": "+", "operator": "+",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 6,
"end": 7,
"value": 2, "value": 2,
"raw": "2" "raw": "2",
"start": 6,
"end": 7
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 10,
"end": 11,
"value": 3, "value": 3,
"raw": "3" "raw": "3",
} "start": 10,
"end": 11
},
"start": 6,
"end": 11
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 16,
"end": 17,
"value": 4, "value": 4,
"raw": "4" "raw": "4",
} "start": 16,
} "end": 17
},
"start": 6,
"end": 17
},
"start": 0,
"end": 17
} }

View File

@ -4,68 +4,68 @@ expression: actual
--- ---
{ {
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 0,
"end": 22,
"operator": "*", "operator": "*",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 0,
"end": 1,
"value": 1, "value": 1,
"raw": "1" "raw": "1",
"start": 0,
"end": 1
}, },
"right": { "right": {
"type": "BinaryExpression", "type": "BinaryExpression",
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 7,
"end": 22,
"operator": "+", "operator": "+",
"left": { "left": {
"type": "BinaryExpression", "type": "BinaryExpression",
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 7,
"end": 18,
"operator": "/", "operator": "/",
"left": { "left": {
"type": "BinaryExpression", "type": "BinaryExpression",
"type": "BinaryExpression", "type": "BinaryExpression",
"start": 7,
"end": 12,
"operator": "+", "operator": "+",
"left": { "left": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 7,
"end": 8,
"value": 2, "value": 2,
"raw": "2" "raw": "2",
"start": 7,
"end": 8
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 11,
"end": 12,
"value": 3, "value": 3,
"raw": "3" "raw": "3",
} "start": 11,
"end": 12
},
"start": 7,
"end": 12
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 17,
"end": 18,
"value": 4, "value": 4,
"raw": "4" "raw": "4",
} "start": 17,
"end": 18
},
"start": 7,
"end": 18
}, },
"right": { "right": {
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"start": 21,
"end": 22,
"value": 5, "value": 5,
"raw": "5" "raw": "5",
} "start": 21,
} "end": 22
},
"start": 7,
"end": 22
},
"start": 0,
"end": 22
} }

Some files were not shown because too many files have changed in this diff Show More