Compare commits
17 Commits
stream-pau
...
franknoiro
Author | SHA1 | Date | |
---|---|---|---|
57e030a0ad | |||
0adecf4f58 | |||
26e995dc3f | |||
08e411e1a3 | |||
a8b816a3e2 | |||
43bec115c0 | |||
0c6c646fe7 | |||
0d52851da2 | |||
6b105897f7 | |||
9ff51de301 | |||
c161f578fd | |||
4804eedf3e | |||
99db31a6a4 | |||
90b57ec202 | |||
3f86f99f5e | |||
83e2b093a6 | |||
58f7e0086d |
2
.github/workflows/cargo-check.yml
vendored
2
.github/workflows/cargo-check.yml
vendored
@ -37,4 +37,4 @@ jobs:
|
||||
# We specifically want to test the disable-println feature
|
||||
# Since it is not enabled by default, we need to specify it
|
||||
# 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
|
||||
|
42014
docs/kcl/std.json
42014
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
@ -23,11 +23,11 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -43,10 +43,10 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `left` |[`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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -83,12 +83,12 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | 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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -104,11 +104,11 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -124,12 +124,12 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | 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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -145,13 +145,13 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | 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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
@ -23,12 +23,12 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -44,10 +44,10 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | 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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -84,10 +84,10 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
||||
|
||||
----
|
||||
|
@ -15,10 +15,10 @@ layout: manual
|
||||
|
||||
| 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 |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
|
@ -16,6 +16,6 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `bindings` |`object`| | No |
|
||||
| `parent` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||
| `parent` |`integer`| | No |
|
||||
|
||||
|
||||
|
@ -24,11 +24,11 @@ An expression can be evaluated to yield a single KCL value.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | 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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| An expression can be evaluated to yield a single KCL value. | 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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `None`| | No |
|
||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
@ -15,10 +15,10 @@ layout: manual
|
||||
|
||||
| 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 |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
|
@ -15,9 +15,9 @@ layout: manual
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | 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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
|
@ -17,8 +17,8 @@ layout: manual
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
|
@ -59,10 +59,10 @@ Any KCL value.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
||||
|
||||
----
|
||||
|
@ -23,10 +23,10 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
@ -23,12 +23,12 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | 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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -44,10 +44,10 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
||||
|
||||
----
|
||||
|
@ -16,7 +16,7 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
|
||||
|
||||
|
@ -15,9 +15,9 @@ layout: manual
|
||||
|
||||
| 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 |
|
||||
| `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 |
|
||||
|
||||
|
||||
|
@ -15,10 +15,10 @@ layout: manual
|
||||
|
||||
| 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 |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
|
@ -16,10 +16,10 @@ A KCL program top level, or function body.
|
||||
|
||||
| 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 |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@ layout: manual
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `environments` |`[` [`Environment`](/docs/kcl/types/Environment) `]`| | No |
|
||||
| `currentEnv` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||
| `currentEnv` |`integer`| | No |
|
||||
| `return` |[`KclValue`](/docs/kcl/types/KclValue)| | No |
|
||||
|
||||
|
||||
|
@ -15,10 +15,10 @@ layout: manual
|
||||
|
||||
| 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 |
|
||||
| `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 |
|
||||
| `start` |`integer`| | No |
|
||||
| `end` |`integer`| | No |
|
||||
|
||||
|
||||
|
@ -86,7 +86,7 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> line([${commonPoints.num1}, 0], %)
|
||||
|> line([0, ${commonPoints.num1 + 0.01}], %)
|
||||
|> line([-${commonPoints.num2}, 0], %)`)
|
||||
|> lineTo([0, ${commonPoints.num3}], %)`)
|
||||
}
|
||||
|
||||
// deselect line tool
|
||||
|
@ -193,61 +193,74 @@ test.describe('when using the file tree to', () => {
|
||||
test(
|
||||
'create a new file with the same name as an existing file cancels the operation',
|
||||
{ tag: '@electron' },
|
||||
async ({ browser: _, tronApp }, testInfo) => {
|
||||
await tronApp.initialise()
|
||||
async (
|
||||
{ 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 {
|
||||
openKclCodePanel,
|
||||
openFilePanel,
|
||||
pasteCodeInEditor,
|
||||
createNewFileAndSelect,
|
||||
renameFile,
|
||||
selectFile,
|
||||
editorTextMatches,
|
||||
waitForPageLoad,
|
||||
} = await getUtils(tronApp.page, _test)
|
||||
|
||||
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
||||
tronApp.page.on('console', console.log)
|
||||
await test.step(`Setup: Open project and navigate to ${secondFile}`, async () => {
|
||||
await homePage.expectState({
|
||||
projectCards: [
|
||||
{
|
||||
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 selectFile(secondFile)
|
||||
})
|
||||
|
||||
await createProject({ name: 'project-000', page: tronApp.page })
|
||||
await openKclCodePanel()
|
||||
await openFilePanel()
|
||||
// File the main.kcl with contents
|
||||
const kclCube = await fsp.readFile(
|
||||
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
||||
'utf-8'
|
||||
)
|
||||
await pasteCodeInEditor(kclCube)
|
||||
await test.step(`Attempt to rename ${secondFile} to ${mainFile}`, async () => {
|
||||
await renameFile(secondFile, mainFile)
|
||||
})
|
||||
|
||||
// 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)
|
||||
|
||||
const kcl1 = 'main.kcl'
|
||||
const kcl2 = '2.kcl'
|
||||
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 test.step(`Postcondition: ${mainFile} still has the original content`, async () => {
|
||||
await selectFile(mainFile)
|
||||
await editorTextMatches(kclCube)
|
||||
})
|
||||
await tronApp.page.waitForTimeout(500)
|
||||
|
||||
await test.step(`Postcondition: ${kcl2} still exists with the original content`, async () => {
|
||||
await selectFile(kcl2)
|
||||
await test.step(`Postcondition: ${secondFile} still exists with the original content`, async () => {
|
||||
await selectFile(secondFile)
|
||||
await editorTextMatches(kclCylinder)
|
||||
})
|
||||
|
||||
await tronApp?.close?.()
|
||||
await tronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
@ -273,11 +286,11 @@ test.describe('when using the file tree to', () => {
|
||||
)
|
||||
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('')
|
||||
})
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
import type { Page, Locator } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
import { sansWhitespace } from '../test-utils'
|
||||
import {
|
||||
closePane,
|
||||
checkIfPaneIsOpen,
|
||||
openPane,
|
||||
sansWhitespace,
|
||||
} from '../test-utils'
|
||||
|
||||
interface EditorState {
|
||||
activeLines: Array<string>
|
||||
@ -11,6 +16,7 @@ interface EditorState {
|
||||
export class EditorFixture {
|
||||
public page: Page
|
||||
|
||||
private paneButtonTestId = 'code-pane-button'
|
||||
private diagnosticsTooltip!: Locator
|
||||
private diagnosticsGutterIcon!: Locator
|
||||
private codeContent!: Locator
|
||||
@ -31,19 +37,32 @@ export class EditorFixture {
|
||||
|
||||
private _expectEditorToContain =
|
||||
(not = false) =>
|
||||
(
|
||||
async (
|
||||
code: string,
|
||||
{
|
||||
shouldNormalise = false,
|
||||
timeout = 5_000,
|
||||
}: { shouldNormalise?: boolean; timeout?: number } = {}
|
||||
) => {
|
||||
const wasPaneOpen = await this.checkIfPaneIsOpen()
|
||||
if (!wasPaneOpen) {
|
||||
await this.openPane()
|
||||
}
|
||||
const resetPane = async () => {
|
||||
if (!wasPaneOpen) {
|
||||
await this.closePane()
|
||||
}
|
||||
}
|
||||
if (!shouldNormalise) {
|
||||
const expectStart = expect(this.codeContent)
|
||||
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 expectStart = expect.poll(
|
||||
@ -56,9 +75,13 @@ export class EditorFixture {
|
||||
}
|
||||
)
|
||||
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 = {
|
||||
toContain: this._expectEditorToContain(),
|
||||
@ -115,4 +138,13 @@ export class EditorFixture {
|
||||
code = code.replace(findCode, replaceCode)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ export class AuthenticatedApp {
|
||||
public readonly page: Page
|
||||
public readonly context: BrowserContext
|
||||
public readonly testInfo: TestInfo
|
||||
public readonly viewPortSize = { width: 1000, height: 500 }
|
||||
|
||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||
this.page = page
|
||||
@ -36,7 +37,7 @@ export class AuthenticatedApp {
|
||||
;(window as any).playwrightSkipFilePicker = true
|
||||
}, code)
|
||||
|
||||
await this.page.setViewportSize({ width: 1000, height: 500 })
|
||||
await this.page.setViewportSize(this.viewPortSize)
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
}
|
||||
|
@ -10,7 +10,13 @@ import {
|
||||
} from '../test-utils'
|
||||
|
||||
type mouseParams = {
|
||||
pixelDiff: number
|
||||
pixelDiff?: number
|
||||
}
|
||||
type mouseDragToParams = mouseParams & {
|
||||
fromPoint: { x: number; y: number }
|
||||
}
|
||||
type mouseDragFromParams = mouseParams & {
|
||||
toPoint: { x: number; y: number }
|
||||
}
|
||||
|
||||
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 {
|
||||
public page: Page
|
||||
|
||||
@ -55,7 +68,7 @@ export class SceneFixture {
|
||||
x: number,
|
||||
y: number,
|
||||
{ steps }: { steps: number } = { steps: 20 }
|
||||
) =>
|
||||
): [ClickHandler, MoveHandler] =>
|
||||
[
|
||||
(clickParams?: mouseParams) => {
|
||||
if (clickParams?.pixelDiff) {
|
||||
@ -78,6 +91,47 @@ export class SceneFixture {
|
||||
return this.page.mouse.move(x, y, { steps })
|
||||
},
|
||||
] 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.
|
||||
*
|
||||
|
@ -7,6 +7,7 @@ export class ToolbarFixture {
|
||||
|
||||
extrudeButton!: Locator
|
||||
startSketchBtn!: Locator
|
||||
lineBtn!: Locator
|
||||
rectangleBtn!: Locator
|
||||
exitSketchBtn!: Locator
|
||||
editSketchBtn!: Locator
|
||||
@ -24,6 +25,7 @@ export class ToolbarFixture {
|
||||
this.page = page
|
||||
this.extrudeButton = page.getByTestId('extrude')
|
||||
this.startSketchBtn = page.getByTestId('sketch')
|
||||
this.lineBtn = page.getByTestId('line')
|
||||
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
||||
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
||||
this.editSketchBtn = page.getByText('Edit Sketch')
|
||||
|
@ -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
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -1497,6 +1497,7 @@ test(
|
||||
'i_shape.kcl',
|
||||
'kittycad_svg.kcl',
|
||||
'lego.kcl',
|
||||
'lsystem.kcl',
|
||||
'math.kcl',
|
||||
'member_expression_sketch.kcl',
|
||||
'mike_stress_test.kcl',
|
||||
|
@ -637,7 +637,6 @@ test.describe('Sketch tests', () => {
|
||||
|> revolve({ axis: "X" }, %)`)
|
||||
})
|
||||
test('Can add multiple sketches', async ({ page }) => {
|
||||
test.skip(process.platform === 'darwin', 'Can add multiple sketches')
|
||||
const u = await getUtils(page)
|
||||
const viewportSize = { width: 1200, height: 500 }
|
||||
await page.setViewportSize(viewportSize)
|
||||
@ -675,15 +674,16 @@ test.describe('Sketch tests', () => {
|
||||
|
||||
await click00r(50, 0)
|
||||
await page.waitForTimeout(100)
|
||||
codeStr += ` |> line(${toSU([50, 0])}, %)`
|
||||
codeStr += ` |> lineTo(${toSU([50, 0])}, %)`
|
||||
await expect(u.codeLocator).toHaveText(codeStr)
|
||||
|
||||
await click00r(0, 50)
|
||||
codeStr += ` |> line(${toSU([0, 50])}, %)`
|
||||
await expect(u.codeLocator).toHaveText(codeStr)
|
||||
|
||||
await click00r(-50, 0)
|
||||
codeStr += ` |> line(${toSU([-50, 0])}, %)`
|
||||
let clickCoords = await click00r(-50, 0)
|
||||
expect(clickCoords).not.toBeUndefined()
|
||||
codeStr += ` |> lineTo(${toSU(clickCoords!)}, %)`
|
||||
await expect(u.codeLocator).toHaveText(codeStr)
|
||||
|
||||
// exit the sketch, reset relative clicker
|
||||
@ -709,8 +709,10 @@ test.describe('Sketch tests', () => {
|
||||
codeStr += ` |> startProfileAt([2.03, 0], %)`
|
||||
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)
|
||||
codeStr += ` |> line([2.04, 0], %)`
|
||||
codeStr += ` |> lineTo([4.07, 0], %)`
|
||||
await expect(u.codeLocator).toHaveText(codeStr)
|
||||
|
||||
await click00r(0, 30)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@ -219,7 +219,7 @@ test.describe('Test network and connection issues', () => {
|
||||
|> startProfileAt([12.34, -12.34], %)
|
||||
|> line([12.34, 0], %)
|
||||
|> line([-12.34, 12.34], %)
|
||||
|> line([-12.34, 0], %)
|
||||
|> lineTo([0, -12.34], %)
|
||||
|
||||
`)
|
||||
|
||||
|
@ -45,7 +45,9 @@ export const commonPoints = {
|
||||
startAt: '[7.19, -9.7]',
|
||||
num1: 7.25,
|
||||
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
|
||||
* in dark mode in the default camera position
|
||||
@ -118,15 +120,32 @@ async function waitForDefaultPlanesToBeVisible(page: Page) {
|
||||
)
|
||||
}
|
||||
|
||||
async function openPane(page: Page, testId: string) {
|
||||
const locator = page.getByTestId(testId)
|
||||
await expect(locator).toBeVisible()
|
||||
const isOpen = (await locator?.getAttribute('aria-pressed')) === 'true'
|
||||
export async function checkIfPaneIsOpen(page: Page, testId: string) {
|
||||
const paneButtonLocator = page.getByTestId(testId)
|
||||
await expect(paneButtonLocator).toBeVisible()
|
||||
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) {
|
||||
await locator.click()
|
||||
await expect(locator).toHaveAttribute('aria-pressed', 'true')
|
||||
await paneButtonLocator.click()
|
||||
}
|
||||
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) {
|
||||
@ -506,6 +525,9 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
||||
.locator('[data-testid="file-pane-scroll-container"] button')
|
||||
.filter({ hasText: name })
|
||||
.click()
|
||||
await expect(page.getByTestId('project-sidebar-toggle')).toContainText(
|
||||
name
|
||||
)
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -96,7 +96,7 @@ test.describe('Testing selections', () => {
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> line([${commonPoints.num1}, 0], %)
|
||||
|> line([0, ${commonPoints.num1 + 0.01}], %)
|
||||
|> line([-${commonPoints.num2}, 0], %)`)
|
||||
|> lineTo([0, ${commonPoints.num3}], %)`)
|
||||
|
||||
// deselect line tool
|
||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||
@ -157,7 +157,9 @@ test.describe('Testing selections', () => {
|
||||
await emptySpaceClick()
|
||||
|
||||
// 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 constrainButton.click()
|
||||
await expect(absYButton).toBeDisabled()
|
||||
@ -180,7 +182,9 @@ test.describe('Testing selections', () => {
|
||||
process.platform === 'linux' ? 'Control' : 'Meta'
|
||||
)
|
||||
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 page.waitForTimeout(500)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import * as fsp from 'fs/promises'
|
||||
import * as fs from 'fs'
|
||||
import { join } from 'path'
|
||||
import {
|
||||
getUtils,
|
||||
@ -266,10 +267,15 @@ test.describe('Testing settings', () => {
|
||||
process.platform === 'win32',
|
||||
'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,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
const bracketDir = join(dir, projectName)
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
@ -281,6 +287,12 @@ test.describe('Testing settings', () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
// Selectors and constants
|
||||
const tempProjectSettingsFilePath = join(
|
||||
projectDirName,
|
||||
projectName,
|
||||
PROJECT_SETTINGS_FILE_NAME
|
||||
)
|
||||
const tempUserSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
|
||||
const userThemeColor = '120'
|
||||
const projectThemeColor = '50'
|
||||
const settingsOpenButton = page.getByRole('link', {
|
||||
@ -293,6 +305,21 @@ test.describe('Testing settings', () => {
|
||||
const projectLink = page.getByText('bracket')
|
||||
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 expect(settingsOpenButton).toBeVisible()
|
||||
await settingsOpenButton.click()
|
||||
@ -300,6 +327,7 @@ test.describe('Testing settings', () => {
|
||||
await expect(userSettingsTab).toBeChecked()
|
||||
await themeColorSetting.fill(userThemeColor)
|
||||
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
|
||||
await confirmThemeWasWritten(tempUserSettingsFilePath, userThemeColor)
|
||||
await settingsCloseButton.click()
|
||||
})
|
||||
|
||||
@ -311,6 +339,11 @@ test.describe('Testing settings', () => {
|
||||
await expect(projectSettingsTab).toBeChecked()
|
||||
await themeColorSetting.fill(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()
|
||||
})
|
||||
|
||||
@ -324,6 +357,7 @@ test.describe('Testing settings', () => {
|
||||
|
||||
await test.step(`Navigate back to the home view and see user setting applied`, async () => {
|
||||
await logoLink.click()
|
||||
await page.screenshot({ path: 'out.png' })
|
||||
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
|
||||
})
|
||||
|
||||
|
@ -56,7 +56,7 @@
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-modal-promise": "^1.0.2",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"react-router-dom": "^6.27.0",
|
||||
"sketch-helpers": "^0.0.4",
|
||||
"three": "^0.166.1",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
@ -76,7 +76,7 @@
|
||||
"build:both": "vite build",
|
||||
"build:both:local": "yarn build:wasm && vite build",
|
||||
"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:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||
|
@ -34,6 +34,11 @@
|
||||
"title": "Car Wheel Assembly",
|
||||
"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",
|
||||
"title": "Enclosure",
|
||||
@ -54,6 +59,11 @@
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"file": "food-service-spatula.kcl",
|
||||
"title": "Food Service Spatula",
|
||||
"description": "Use these spatulas for mixing, flipping, and scraping."
|
||||
},
|
||||
{
|
||||
"file": "french-press.kcl",
|
||||
"title": "French Press",
|
||||
@ -61,7 +71,7 @@
|
||||
},
|
||||
{
|
||||
"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."
|
||||
},
|
||||
{
|
||||
|
@ -44,6 +44,7 @@ import {
|
||||
import { ActionButton } from 'components/ActionButton'
|
||||
import { err, reportRejection, trap } from 'lib/trap'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
||||
const [isCamMoving, setIsCamMoving] = useState(false)
|
||||
@ -201,7 +202,7 @@ const Overlay = ({
|
||||
let xAlignment = overlay.angle < 0 ? '0%' : '-100%'
|
||||
let yAlignment = overlay.angle < -90 || overlay.angle >= 90 ? '0%' : '-100%'
|
||||
|
||||
const _node1 = getNodeFromPath<CallExpression>(
|
||||
const _node1 = getNodeFromPath<Node<CallExpression>>(
|
||||
kclManager.ast,
|
||||
overlay.pathToNode,
|
||||
'CallExpression'
|
||||
@ -381,7 +382,7 @@ export async function deleteSegment({
|
||||
pathToNode: PathToNode
|
||||
sketchDetails: SketchDetails | null
|
||||
}) {
|
||||
let modifiedAst: Program | Error = kclManager.ast
|
||||
let modifiedAst: Node<Program> | Error = kclManager.ast
|
||||
const dependentRanges = findUsesOfTagInPipe(modifiedAst, pathToNode)
|
||||
|
||||
const shouldContinueSegDelete = dependentRanges.length
|
||||
|
@ -19,6 +19,8 @@ import {
|
||||
import {
|
||||
ARROWHEAD,
|
||||
AXIS_GROUP,
|
||||
DRAFT_POINT,
|
||||
DRAFT_POINT_GROUP,
|
||||
getSceneScale,
|
||||
INTERSECTION_PLANE_LAYER,
|
||||
OnClickCallbackArgs,
|
||||
@ -53,7 +55,7 @@ import {
|
||||
editorManager,
|
||||
} from 'lib/singletons'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import { executeAst, ToolTip } from 'lang/langHelpers'
|
||||
import {
|
||||
createProfileStartHandle,
|
||||
SegmentUtils,
|
||||
@ -92,6 +94,7 @@ import { err, reportRejection, trap } from 'lib/trap'
|
||||
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
||||
import { Point3d } from 'wasm-lib/kcl/bindings/Point3d'
|
||||
import { SegmentInputs } from 'lang/std/stdTypes'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
type DraftSegment = 'line' | 'tangentialArcTo'
|
||||
|
||||
@ -313,6 +316,27 @@ export class SceneEntities {
|
||||
const intersectionPlane = this.scene.getObjectByName(RAYCASTABLE_PLANE)
|
||||
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({
|
||||
sketchDetails,
|
||||
@ -321,22 +345,78 @@ export class SceneEntities {
|
||||
sketchDetails: SketchDetails
|
||||
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()
|
||||
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(
|
||||
new Vector3(...sketchDetails.yAxis),
|
||||
new Vector3(...sketchDetails.zAxis)
|
||||
)
|
||||
|
||||
// Position the click raycast plane
|
||||
if (this.intersectionPlane) {
|
||||
this.intersectionPlane.setRotationFromQuaternion(quaternion)
|
||||
this.intersectionPlane.position.copy(
|
||||
new Vector3(...(sketchDetails?.origin || [0, 0, 0]))
|
||||
)
|
||||
}
|
||||
this.intersectionPlane!.setRotationFromQuaternion(quaternion)
|
||||
this.intersectionPlane!.position.copy(
|
||||
new Vector3(...(sketchDetails?.origin || [0, 0, 0]))
|
||||
)
|
||||
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) => {
|
||||
this.removeDraftPoint()
|
||||
if (!args) return
|
||||
// If there is a valid camera interaction that matches, do that instead
|
||||
const interaction = sceneInfra.camControls.getInteractionType(
|
||||
@ -346,10 +426,25 @@ export class SceneEntities {
|
||||
if (args.mouseEvent.which !== 1) return
|
||||
const { intersectionPoint } = args
|
||||
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(
|
||||
kclManager.ast,
|
||||
sketchDetails.sketchPathToNode,
|
||||
[intersectionPoint.twoD.x, intersectionPoint.twoD.y]
|
||||
[snappedClickPoint.x, snappedClickPoint.y]
|
||||
)
|
||||
|
||||
if (trap(addStartProfileAtRes)) return
|
||||
@ -357,6 +452,7 @@ export class SceneEntities {
|
||||
|
||||
await kclManager.updateAst(modifiedAst, false)
|
||||
this.removeIntersectionPlane()
|
||||
this.scene.remove(draftPointGroup)
|
||||
|
||||
// Now perform the caller-specified action
|
||||
afterClick(args)
|
||||
@ -374,14 +470,14 @@ export class SceneEntities {
|
||||
selectionRanges,
|
||||
}: {
|
||||
sketchPathToNode: PathToNode
|
||||
maybeModdedAst: Program
|
||||
maybeModdedAst: Node<Program>
|
||||
draftExpressionsIndices?: { start: number; end: number }
|
||||
forward: [number, number, number]
|
||||
up: [number, number, number]
|
||||
position?: [number, number, number]
|
||||
selectionRanges?: Selections
|
||||
}): Promise<{
|
||||
truncatedAst: Program
|
||||
truncatedAst: Node<Program>
|
||||
programMemoryOverride: ProgramMemory
|
||||
sketch: Sketch
|
||||
variableDeclarationName: string
|
||||
@ -429,12 +525,7 @@ export class SceneEntities {
|
||||
const dummy = new Mesh()
|
||||
// TODO: When we actually have sketch positions and rotations we can use them here.
|
||||
dummy.position.set(0, 0, 0)
|
||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||
const factor =
|
||||
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
||||
? orthoFactor
|
||||
: perspScale(sceneInfra.camControls.camera, dummy)) /
|
||||
sceneInfra._baseUnitMultiplier
|
||||
const scale = sceneInfra.getClientSceneScaleFactor(dummy)
|
||||
|
||||
const segPathToNode = getNodePathFromSourceRange(
|
||||
maybeModdedAst,
|
||||
@ -445,8 +536,9 @@ export class SceneEntities {
|
||||
from: sketch.start.from,
|
||||
id: sketch.start.__geoMeta.id,
|
||||
pathToNode: segPathToNode,
|
||||
scale: factor,
|
||||
scale,
|
||||
theme: sceneInfra._theme,
|
||||
isDraft: false,
|
||||
})
|
||||
_profileStart.layers.set(SKETCH_LAYER)
|
||||
_profileStart.traverse((child) => {
|
||||
@ -522,7 +614,7 @@ export class SceneEntities {
|
||||
id: segment.__geoMeta.id,
|
||||
pathToNode: segPathToNode,
|
||||
isDraftSegment,
|
||||
scale: factor,
|
||||
scale,
|
||||
texture: sceneInfra.extraSegmentTexture,
|
||||
theme: sceneInfra._theme,
|
||||
isSelected,
|
||||
@ -566,7 +658,7 @@ export class SceneEntities {
|
||||
}
|
||||
updateAstAndRejigSketch = async (
|
||||
sketchPathToNode: PathToNode,
|
||||
modifiedAst: Program | Error,
|
||||
modifiedAst: Node<Program> | Error,
|
||||
forward: [number, number, number],
|
||||
up: [number, number, number],
|
||||
origin: [number, number, number]
|
||||
@ -659,12 +751,14 @@ export class SceneEntities {
|
||||
|
||||
const { intersectionPoint } = args
|
||||
let intersection2d = intersectionPoint?.twoD
|
||||
const profileStart = args.intersects
|
||||
const intersectsProfileStart = args.intersects
|
||||
.map(({ object }) => getParentGroup(object, [PROFILE_START]))
|
||||
.find((a) => a?.name === PROFILE_START)
|
||||
|
||||
let modifiedAst
|
||||
if (profileStart) {
|
||||
|
||||
// Snapping logic for the profile start handle
|
||||
if (intersectsProfileStart) {
|
||||
const lastSegment = sketch.paths.slice(-1)[0]
|
||||
modifiedAst = addCallExpressionsToPipe({
|
||||
node: kclManager.ast,
|
||||
@ -697,19 +791,39 @@ export class SceneEntities {
|
||||
})
|
||||
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
||||
} 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 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({
|
||||
node: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
from: [lastSegment.to[0], lastSegment.to[1]],
|
||||
to: [intersection2d.x, intersection2d.y],
|
||||
to: [snappedPoint.x, snappedPoint.y],
|
||||
},
|
||||
fnName:
|
||||
lastSegment.type === 'TangentialArcTo'
|
||||
? 'tangentialArcTo'
|
||||
: 'line',
|
||||
fnName: resolvedFunctionName,
|
||||
pathToNode: sketchPathToNode,
|
||||
})
|
||||
if (trap(tmp)) return Promise.reject(tmp)
|
||||
@ -721,7 +835,7 @@ export class SceneEntities {
|
||||
}
|
||||
|
||||
await kclManager.executeAstMock(modifiedAst)
|
||||
if (profileStart) {
|
||||
if (intersectsProfileStart) {
|
||||
sceneInfra.modelingSend({ type: 'CancelSketch' })
|
||||
} else {
|
||||
await this.setUpDraftSegment(
|
||||
@ -1202,7 +1316,7 @@ export class SceneEntities {
|
||||
}
|
||||
prepareTruncatedMemoryAndAst = (
|
||||
sketchPathToNode: PathToNode,
|
||||
ast?: Program,
|
||||
ast?: Node<Program>,
|
||||
draftSegment?: DraftSegment
|
||||
) =>
|
||||
prepareTruncatedMemoryAndAst(
|
||||
@ -1223,20 +1337,35 @@ export class SceneEntities {
|
||||
sketchPathToNode: PathToNode
|
||||
intersects: Intersection<Object3D<Object3DEventMap>>[]
|
||||
draftInfo?: {
|
||||
truncatedAst: Program
|
||||
truncatedAst: Node<Program>
|
||||
programMemoryOverride: ProgramMemory
|
||||
variableDeclarationName: string
|
||||
}
|
||||
}) {
|
||||
const profileStart =
|
||||
const intersectsProfileStart =
|
||||
draftInfo &&
|
||||
intersects
|
||||
.map(({ object }) => getParentGroup(object, [PROFILE_START]))
|
||||
.find((a) => a?.name === PROFILE_START)
|
||||
const intersection2d = profileStart
|
||||
? new Vector2(profileStart.position.x, profileStart.position.y)
|
||||
const intersection2d = intersectsProfileStart
|
||||
? new Vector2(
|
||||
intersectsProfileStart.position.x,
|
||||
intersectsProfileStart.position.y
|
||||
)
|
||||
: _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 subGroup = getParentGroup(object, [ARROWHEAD, CIRCLE_CENTER_HANDLE])
|
||||
if (!group) return
|
||||
@ -1256,10 +1385,10 @@ export class SceneEntities {
|
||||
group.userData.from[0],
|
||||
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 }
|
||||
|
||||
const _node = getNodeFromPath<CallExpression>(
|
||||
const _node = getNodeFromPath<Node<CallExpression>>(
|
||||
modifiedAst,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
@ -1271,7 +1400,7 @@ export class SceneEntities {
|
||||
|
||||
let modded:
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNode: PathToNode
|
||||
}
|
||||
| Error
|
||||
@ -1566,7 +1695,7 @@ export class SceneEntities {
|
||||
if (parent?.userData?.pathToNode) {
|
||||
const updatedAst = parse(recast(kclManager.ast))
|
||||
if (trap(updatedAst)) return
|
||||
const _node = getNodeFromPath<CallExpression>(
|
||||
const _node = getNodeFromPath<Node<CallExpression>>(
|
||||
updatedAst,
|
||||
parent.userData.pathToNode,
|
||||
'CallExpression'
|
||||
@ -1701,12 +1830,12 @@ export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ'
|
||||
|
||||
function prepareTruncatedMemoryAndAst(
|
||||
sketchPathToNode: PathToNode,
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
programMemory: ProgramMemory,
|
||||
draftSegment?: DraftSegment
|
||||
):
|
||||
| {
|
||||
truncatedAst: Program
|
||||
truncatedAst: Node<Program>
|
||||
programMemoryOverride: ProgramMemory
|
||||
variableDeclarationName: string
|
||||
}
|
||||
@ -1714,7 +1843,7 @@ function prepareTruncatedMemoryAndAst(
|
||||
const bodyIndex = Number(sketchPathToNode?.[1]?.[0]) || 0
|
||||
const _ast = structuredClone(ast)
|
||||
|
||||
const _node = getNodeFromPath<VariableDeclaration>(
|
||||
const _node = getNodeFromPath<Node<VariableDeclaration>>(
|
||||
_ast,
|
||||
sketchPathToNode || [],
|
||||
'VariableDeclaration'
|
||||
@ -1764,15 +1893,15 @@ function prepareTruncatedMemoryAndAst(
|
||||
).body.slice(-1)[0].start = lastPipeItem.start
|
||||
|
||||
_ast.end = lastPipeItem.end
|
||||
const varDec = _ast.body[bodyIndex] as VariableDeclaration
|
||||
const varDec = _ast.body[bodyIndex] as Node<VariableDeclaration>
|
||||
varDec.end = lastPipeItem.end
|
||||
const declarator = varDec.declarations[0]
|
||||
declarator.end = lastPipeItem.end
|
||||
const init = declarator.init as PipeExpression
|
||||
const init = declarator.init as Node<PipeExpression>
|
||||
init.end = lastPipeItem.end
|
||||
init.body.slice(-1)[0].end = lastPipeItem.end
|
||||
}
|
||||
const truncatedAst: Program = {
|
||||
const truncatedAst: Node<Program> = {
|
||||
..._ast,
|
||||
body: [structuredClone(_ast.body[bodyIndex])],
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import { MouseState, SegmentOverlayPayload } from 'machines/modelingMachine'
|
||||
import { getAngle, throttle } from 'lib/utils'
|
||||
import { Themes } from 'lib/theme'
|
||||
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
||||
import { orthoScale, perspScale } from './helpers'
|
||||
|
||||
type SendType = ReturnType<typeof useModelingContext>['send']
|
||||
|
||||
@ -49,6 +50,10 @@ export const RAYCASTABLE_PLANE = 'raycastable-plane'
|
||||
|
||||
export const X_AXIS = 'xAxis'
|
||||
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 SKETCH_GROUP_SEGMENTS = 'sketch-group-segments'
|
||||
export const ARROWHEAD = 'arrowhead'
|
||||
@ -60,6 +65,11 @@ export interface OnMouseEnterLeaveArgs {
|
||||
selected: Object3D<Object3DEventMap>
|
||||
dragSelected?: Object3D<Object3DEventMap>
|
||||
mouseEvent: MouseEvent
|
||||
/** The intersection of the mouse with the THREEjs raycast plane */
|
||||
intersectionPoint?: {
|
||||
twoD?: Vector2
|
||||
threeD?: Vector3
|
||||
}
|
||||
}
|
||||
|
||||
interface OnDragCallbackArgs extends OnMouseEnterLeaveArgs {
|
||||
@ -348,29 +358,42 @@ export class SceneInfra {
|
||||
window.removeEventListener('resize', this.onWindowResize)
|
||||
// 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 = (): {
|
||||
twoD?: Vector2
|
||||
threeD?: Vector3
|
||||
intersection: Intersection<Object3D<Object3DEventMap>>
|
||||
} | null => {
|
||||
// Get the orientations from the camera and mouse position
|
||||
this.planeRaycaster.setFromCamera(
|
||||
this.currentMouseVector,
|
||||
this.camControls.camera
|
||||
)
|
||||
// Get the intersection of the ray with the default planes
|
||||
const planeIntersects = this.planeRaycaster.intersectObjects(
|
||||
this.scene.children,
|
||||
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
|
||||
)
|
||||
if (!planeIntersects.length) return null
|
||||
if (!recastablePlaneIntersect) return { intersection: planeIntersects[0] }
|
||||
const planePosition = planeIntersects[0].object.position
|
||||
const inversePlaneQuaternion = planeIntersects[0].object.quaternion
|
||||
.clone()
|
||||
.invert()
|
||||
const intersectPoint = planeIntersects[0].point
|
||||
if (!raycastablePlaneIntersection)
|
||||
return { intersection: planeIntersects[0] }
|
||||
const planePosition = raycastablePlaneIntersection.object.position
|
||||
const inversePlaneQuaternion =
|
||||
raycastablePlaneIntersection.object.quaternion.clone().invert()
|
||||
const intersectPoint = raycastablePlaneIntersection.point
|
||||
let transformedPoint = intersectPoint.clone()
|
||||
if (transformedPoint) {
|
||||
transformedPoint.applyQuaternion(inversePlaneQuaternion)
|
||||
@ -447,18 +470,26 @@ export class SceneInfra {
|
||||
|
||||
if (intersects[0]) {
|
||||
const firstIntersectObject = intersects[0].object
|
||||
const planeIntersectPoint = this.getPlaneIntersectPoint()
|
||||
const intersectionPoint = {
|
||||
twoD: planeIntersectPoint?.twoD,
|
||||
threeD: planeIntersectPoint?.threeD,
|
||||
}
|
||||
|
||||
if (this.hoveredObject !== firstIntersectObject) {
|
||||
const hoveredObj = this.hoveredObject
|
||||
this.hoveredObject = null
|
||||
await this.onMouseLeave({
|
||||
selected: hoveredObj,
|
||||
mouseEvent: mouseEvent,
|
||||
intersectionPoint,
|
||||
})
|
||||
this.hoveredObject = firstIntersectObject
|
||||
await this.onMouseEnter({
|
||||
selected: this.hoveredObject,
|
||||
dragSelected: this.selected?.object,
|
||||
mouseEvent: mouseEvent,
|
||||
intersectionPoint,
|
||||
})
|
||||
if (!this.selected)
|
||||
this.updateMouseState({
|
||||
|
@ -45,6 +45,7 @@ import {
|
||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||
import {
|
||||
ARROWHEAD,
|
||||
DRAFT_POINT,
|
||||
SceneInfra,
|
||||
SEGMENT_LENGTH_LABEL,
|
||||
SEGMENT_LENGTH_LABEL_OFFSET_PX,
|
||||
@ -686,19 +687,20 @@ class CircleSegment implements SegmentUtils {
|
||||
|
||||
export function createProfileStartHandle({
|
||||
from,
|
||||
id,
|
||||
pathToNode,
|
||||
isDraft = false,
|
||||
scale = 1,
|
||||
theme,
|
||||
isSelected,
|
||||
...rest
|
||||
}: {
|
||||
from: Coords2d
|
||||
id: string
|
||||
pathToNode: PathToNode
|
||||
scale?: number
|
||||
theme: Themes
|
||||
isSelected?: boolean
|
||||
}) {
|
||||
} & (
|
||||
| { isDraft: true }
|
||||
| { isDraft: false; id: string; pathToNode: PathToNode }
|
||||
)) {
|
||||
const group = new Group()
|
||||
|
||||
const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later
|
||||
@ -711,13 +713,12 @@ export function createProfileStartHandle({
|
||||
|
||||
group.userData = {
|
||||
type: PROFILE_START,
|
||||
id,
|
||||
from,
|
||||
pathToNode,
|
||||
isSelected,
|
||||
baseColor,
|
||||
...rest,
|
||||
}
|
||||
group.name = PROFILE_START
|
||||
group.name = isDraft ? DRAFT_POINT : PROFILE_START
|
||||
group.position.set(from[0], from[1], 0)
|
||||
group.scale.set(scale, scale, scale)
|
||||
return group
|
||||
|
@ -91,6 +91,7 @@ import { submitAndAwaitTextToKcl } from 'lib/textToCad'
|
||||
import { useFileContext } from 'hooks/useFileContext'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { IndexLoaderData } from 'lib/types'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
type MachineContext<T extends AnyStateMachine> = {
|
||||
state: StateFrom<T>
|
||||
@ -971,7 +972,7 @@ export const ModelingMachineProvider = ({
|
||||
})
|
||||
let parsed = parse(recast(kclManager.ast))
|
||||
if (trap(parsed)) return Promise.reject(parsed)
|
||||
parsed = parsed as Program
|
||||
parsed = parsed as Node<Program>
|
||||
|
||||
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
||||
moveValueIntoNewVariablePath(
|
||||
@ -982,7 +983,7 @@ export const ModelingMachineProvider = ({
|
||||
)
|
||||
parsed = parse(recast(_modifiedAst))
|
||||
if (trap(parsed)) return Promise.reject(parsed)
|
||||
parsed = parsed as Program
|
||||
parsed = parsed as Node<Program>
|
||||
if (!pathToReplacedNode)
|
||||
return Promise.reject(new Error('No path to replaced node'))
|
||||
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
export function setEqualLengthInfo({
|
||||
selectionRanges,
|
||||
@ -86,7 +87,7 @@ export function applyConstraintEqualLength({
|
||||
selectionRanges: Selections
|
||||
}):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNodeMap: PathToNodeMap
|
||||
}
|
||||
| Error {
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
export function horzVertInfo(
|
||||
selectionRanges: Selections,
|
||||
@ -55,11 +56,11 @@ export function horzVertInfo(
|
||||
export function applyConstraintHorzVert(
|
||||
selectionRanges: Selections,
|
||||
horOrVert: 'vertical' | 'horizontal',
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
programMemory: ProgramMemory
|
||||
):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNodeMap: PathToNodeMap
|
||||
}
|
||||
| Error {
|
||||
|
@ -19,6 +19,7 @@ import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
const getModalInfo = createInfoModal(GetInfoModal)
|
||||
|
||||
@ -136,7 +137,7 @@ export async function applyConstraintIntersect({
|
||||
}: {
|
||||
selectionRanges: Selections
|
||||
}): Promise<{
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNodeMap: PathToNodeMap
|
||||
}> {
|
||||
const info = intersectInfo({
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
export function removeConstrainingValuesInfo({
|
||||
selectionRanges,
|
||||
@ -77,7 +78,7 @@ export function applyRemoveConstrainingValues({
|
||||
pathToNodes?: Array<PathToNode>
|
||||
}):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNodeMap: PathToNodeMap
|
||||
}
|
||||
| Error {
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
||||
|
||||
@ -161,7 +162,7 @@ export function applyConstraintAxisAlign({
|
||||
constraint: 'snapToYAxis' | 'snapToXAxis'
|
||||
}):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNodeMap: PathToNodeMap
|
||||
}
|
||||
| Error {
|
||||
|
@ -18,6 +18,7 @@ import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { cleanErrs, err } from 'lib/trap'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
const getModalInfo = createInfoModal(GetInfoModal)
|
||||
|
||||
@ -185,7 +186,7 @@ export function applyConstraintHorzVertAlign({
|
||||
constraint: 'setHorzDistance' | 'setVertDistance'
|
||||
}):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNodeMap: PathToNodeMap
|
||||
}
|
||||
| Error {
|
||||
|
@ -21,9 +21,10 @@ import {
|
||||
import { getNodeFromPath } from './queryAst'
|
||||
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
||||
import { Diagnostic } from '@codemirror/lint'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
interface ExecuteArgs {
|
||||
ast?: Program
|
||||
ast?: Node<Program>
|
||||
zoomToFit?: boolean
|
||||
executionId?: number
|
||||
zoomOnRangeAndType?: {
|
||||
@ -33,13 +34,13 @@ interface ExecuteArgs {
|
||||
}
|
||||
|
||||
export class KclManager {
|
||||
private _ast: Program = {
|
||||
private _ast: Node<Program> = {
|
||||
body: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
nonCodeMeta: {
|
||||
nonCodeNodes: {},
|
||||
start: [],
|
||||
startNodes: [],
|
||||
},
|
||||
}
|
||||
private _execState: ExecState = emptyExecState()
|
||||
@ -55,7 +56,7 @@ export class KclManager {
|
||||
engineCommandManager: EngineCommandManager
|
||||
|
||||
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
||||
private _astCallBack: (arg: Program) => void = () => {}
|
||||
private _astCallBack: (arg: Node<Program>) => void = () => {}
|
||||
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
|
||||
private _logsCallBack: (arg: string[]) => void = () => {}
|
||||
private _kclErrorsCallBack: (arg: KCLError[]) => void = () => {}
|
||||
@ -181,7 +182,7 @@ export class KclManager {
|
||||
setWasmInitFailed,
|
||||
}: {
|
||||
setProgramMemory: (arg: ProgramMemory) => void
|
||||
setAst: (arg: Program) => void
|
||||
setAst: (arg: Node<Program>) => void
|
||||
setLogs: (arg: string[]) => void
|
||||
setKclErrors: (arg: KCLError[]) => void
|
||||
setIsExecuting: (arg: boolean) => void
|
||||
@ -205,12 +206,12 @@ export class KclManager {
|
||||
end: 0,
|
||||
nonCodeMeta: {
|
||||
nonCodeNodes: {},
|
||||
start: [],
|
||||
startNodes: [],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
safeParse(code: string): Program | null {
|
||||
safeParse(code: string): Node<Program> | null {
|
||||
const ast = parse(code)
|
||||
this.lints = []
|
||||
this.kclErrors = []
|
||||
@ -377,7 +378,7 @@ export class KclManager {
|
||||
Array.from(this.engineCommandManager.artifactGraph).forEach(
|
||||
([commandId, artifact]) => {
|
||||
if (!('codeRef' in artifact)) return
|
||||
const _node1 = getNodeFromPath<CallExpression>(
|
||||
const _node1 = getNodeFromPath<Node<CallExpression>>(
|
||||
this.ast,
|
||||
artifact.codeRef.pathToNode,
|
||||
'CallExpression'
|
||||
@ -441,7 +442,7 @@ export class KclManager {
|
||||
// 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.
|
||||
async updateAst(
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
execute: boolean,
|
||||
optionalParams?: {
|
||||
focusPath?: Array<PathToNode>
|
||||
@ -452,7 +453,7 @@ export class KclManager {
|
||||
}
|
||||
}
|
||||
): Promise<{
|
||||
newAst: Program
|
||||
newAst: Node<Program>
|
||||
selections?: Selections
|
||||
}> {
|
||||
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
|
||||
_isAstEmpty(ast: Program) {
|
||||
_isAstEmpty(ast: Node<Program>) {
|
||||
return ast.start === 0 && ast.end === 0 && ast.body.length === 0
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import { KCLError } from 'lang/errors'
|
||||
import { Diagnostic } from '@codemirror/lint'
|
||||
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
export type ToolTip =
|
||||
| 'lineTo'
|
||||
@ -52,7 +53,7 @@ export async function executeAst({
|
||||
programMemoryOverride,
|
||||
idGenerator,
|
||||
}: {
|
||||
ast: Program
|
||||
ast: Node<Program>
|
||||
engineCommandManager: EngineCommandManager
|
||||
useFakeExecutor?: boolean
|
||||
programMemoryOverride?: ProgramMemory
|
||||
|
@ -21,6 +21,7 @@ import { enginelessExecutor } from '../lib/testHelpers'
|
||||
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
|
||||
import { err } from 'lib/trap'
|
||||
import { SimplifiedArgDetails } from './std/stdTypes'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
@ -109,7 +110,7 @@ describe('Testing findUniqueName', () => {
|
||||
{ type: 'Identifier', name: 'yo07', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo08', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo09', start: 0, end: 0 },
|
||||
] satisfies Identifier[]),
|
||||
] satisfies Node<Identifier>[]),
|
||||
'yo',
|
||||
2
|
||||
)
|
||||
@ -123,7 +124,7 @@ describe('Testing addSketchTo', () => {
|
||||
body: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
nonCodeMeta: { nonCodeNodes: {}, start: [] },
|
||||
nonCodeMeta: { nonCodeNodes: {}, startNodes: [] },
|
||||
},
|
||||
'yz'
|
||||
)
|
||||
|
@ -42,12 +42,13 @@ import { SimplifiedArgDetails } from './std/stdTypes'
|
||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
export function startSketchOnDefault(
|
||||
node: Program,
|
||||
node: Node<Program>,
|
||||
axis: DefaultPlaneStr,
|
||||
name = ''
|
||||
): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
|
||||
): { modifiedAst: Node<Program>; id: string; pathToNode: PathToNode } {
|
||||
const _node = { ...node }
|
||||
const _name =
|
||||
name || findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH)
|
||||
@ -76,10 +77,10 @@ export function startSketchOnDefault(
|
||||
}
|
||||
|
||||
export function addStartProfileAt(
|
||||
node: Program,
|
||||
node: Node<Program>,
|
||||
pathToNode: PathToNode,
|
||||
at: [number, number]
|
||||
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
||||
): { modifiedAst: Node<Program>; pathToNode: PathToNode } | Error {
|
||||
const _node1 = getNodeFromPath<VariableDeclaration>(
|
||||
node,
|
||||
pathToNode,
|
||||
@ -114,7 +115,7 @@ export function addStartProfileAt(
|
||||
}
|
||||
|
||||
export function addSketchTo(
|
||||
node: Program,
|
||||
node: Node<Program>,
|
||||
axis: 'xy' | 'xz' | 'yz',
|
||||
name = ''
|
||||
): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
|
||||
@ -210,7 +211,7 @@ export function mutateArrExp(node: Expr, updateWith: ArrayExpression): boolean {
|
||||
|
||||
export function mutateObjExpProp(
|
||||
node: Expr,
|
||||
updateWith: Literal | ArrayExpression,
|
||||
updateWith: Node<Literal> | Node<ArrayExpression>,
|
||||
key: string
|
||||
): boolean {
|
||||
if (node.type === 'ObjectExpression') {
|
||||
@ -248,13 +249,13 @@ export function mutateObjExpProp(
|
||||
}
|
||||
|
||||
export function extrudeSketch(
|
||||
node: Program,
|
||||
node: Node<Program>,
|
||||
pathToNode: PathToNode,
|
||||
shouldPipe = false,
|
||||
distance: Expr = createLiteral(4)
|
||||
):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNode: PathToNode
|
||||
pathToExtrudeArg: PathToNode
|
||||
}
|
||||
@ -343,13 +344,13 @@ export function extrudeSketch(
|
||||
}
|
||||
|
||||
export function revolveSketch(
|
||||
node: Program,
|
||||
node: Node<Program>,
|
||||
pathToNode: PathToNode,
|
||||
shouldPipe = false,
|
||||
angle: Expr = createLiteral(4)
|
||||
):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNode: PathToNode
|
||||
pathToRevolveArg: PathToNode
|
||||
}
|
||||
@ -439,7 +440,7 @@ export function revolveSketch(
|
||||
}
|
||||
|
||||
export function sketchOnExtrudedFace(
|
||||
node: Program,
|
||||
node: Node<Program>,
|
||||
sketchPathToNode: PathToNode,
|
||||
extrudePathToNode: PathToNode,
|
||||
info: ExtrudeFacePlane['faceInfo'] = { type: 'wall' }
|
||||
@ -571,7 +572,7 @@ export function splitPathAtPipeExpression(pathToNode: PathToNode): {
|
||||
return splitPathAtPipeExpression(pathToNode.slice(0, -1))
|
||||
}
|
||||
|
||||
export function createLiteral(value: string | number): Literal {
|
||||
export function createLiteral(value: string | number): Node<Literal> {
|
||||
return {
|
||||
type: 'Literal',
|
||||
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 {
|
||||
type: 'TagDeclarator',
|
||||
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 {
|
||||
type: 'Identifier',
|
||||
start: 0,
|
||||
@ -601,7 +602,7 @@ export function createIdentifier(name: string): Identifier {
|
||||
}
|
||||
}
|
||||
|
||||
export function createPipeSubstitution(): PipeSubstitution {
|
||||
export function createPipeSubstitution(): Node<PipeSubstitution> {
|
||||
return {
|
||||
type: 'PipeSubstitution',
|
||||
start: 0,
|
||||
@ -612,7 +613,7 @@ export function createPipeSubstitution(): PipeSubstitution {
|
||||
export function createCallExpressionStdLib(
|
||||
name: string,
|
||||
args: CallExpression['arguments']
|
||||
): CallExpression {
|
||||
): Node<CallExpression> {
|
||||
return {
|
||||
type: 'CallExpression',
|
||||
start: 0,
|
||||
@ -632,7 +633,7 @@ export function createCallExpressionStdLib(
|
||||
export function createCallExpression(
|
||||
name: string,
|
||||
args: CallExpression['arguments']
|
||||
): CallExpression {
|
||||
): Node<CallExpression> {
|
||||
return {
|
||||
type: 'CallExpression',
|
||||
start: 0,
|
||||
@ -651,7 +652,7 @@ export function createCallExpression(
|
||||
|
||||
export function createArrayExpression(
|
||||
elements: ArrayExpression['elements']
|
||||
): ArrayExpression {
|
||||
): Node<ArrayExpression> {
|
||||
return {
|
||||
type: 'ArrayExpression',
|
||||
start: 0,
|
||||
@ -664,7 +665,7 @@ export function createArrayExpression(
|
||||
|
||||
export function createPipeExpression(
|
||||
body: PipeExpression['body']
|
||||
): PipeExpression {
|
||||
): Node<PipeExpression> {
|
||||
return {
|
||||
type: 'PipeExpression',
|
||||
start: 0,
|
||||
@ -680,7 +681,7 @@ export function createVariableDeclaration(
|
||||
init: VariableDeclarator['init'],
|
||||
visibility: VariableDeclaration['visibility'] = 'default',
|
||||
kind: VariableDeclaration['kind'] = 'const'
|
||||
): VariableDeclaration {
|
||||
): Node<VariableDeclaration> {
|
||||
return {
|
||||
type: 'VariableDeclaration',
|
||||
start: 0,
|
||||
@ -703,7 +704,7 @@ export function createVariableDeclaration(
|
||||
|
||||
export function createObjectExpression(properties: {
|
||||
[key: string]: Expr
|
||||
}): ObjectExpression {
|
||||
}): Node<ObjectExpression> {
|
||||
return {
|
||||
type: 'ObjectExpression',
|
||||
start: 0,
|
||||
@ -724,7 +725,7 @@ export function createObjectExpression(properties: {
|
||||
export function createUnaryExpression(
|
||||
argument: UnaryExpression['argument'],
|
||||
operator: UnaryExpression['operator'] = '-'
|
||||
): UnaryExpression {
|
||||
): Node<UnaryExpression> {
|
||||
return {
|
||||
type: 'UnaryExpression',
|
||||
start: 0,
|
||||
@ -739,7 +740,7 @@ export function createBinaryExpression([left, operator, right]: [
|
||||
BinaryExpression['left'],
|
||||
BinaryExpression['operator'],
|
||||
BinaryExpression['right']
|
||||
]): BinaryExpression {
|
||||
]): Node<BinaryExpression> {
|
||||
return {
|
||||
type: 'BinaryExpression',
|
||||
start: 0,
|
||||
@ -754,19 +755,19 @@ export function createBinaryExpression([left, operator, right]: [
|
||||
export function createBinaryExpressionWithUnary([left, right]: [
|
||||
BinaryExpression['left'],
|
||||
BinaryExpression['right']
|
||||
]): BinaryExpression {
|
||||
]): Node<BinaryExpression> {
|
||||
if (right.type === 'UnaryExpression' && right.operator === '-')
|
||||
return createBinaryExpression([left, '-', right.argument])
|
||||
return createBinaryExpression([left, '+', right])
|
||||
}
|
||||
|
||||
export function giveSketchFnCallTag(
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
range: Selection['range'],
|
||||
tag?: string
|
||||
):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
tag: string
|
||||
isTagExisting: boolean
|
||||
pathToNode: PathToNode
|
||||
@ -801,7 +802,7 @@ export function giveSketchFnCallTag(
|
||||
}
|
||||
|
||||
export function moveValueIntoNewVariablePath(
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
programMemory: ProgramMemory,
|
||||
pathToNode: PathToNode,
|
||||
variableName: string
|
||||
@ -834,12 +835,12 @@ export function moveValueIntoNewVariablePath(
|
||||
}
|
||||
|
||||
export function moveValueIntoNewVariable(
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
programMemory: ProgramMemory,
|
||||
sourceRange: Selection['range'],
|
||||
variableName: string
|
||||
): {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToReplacedNode?: PathToNode
|
||||
} {
|
||||
const meta = isNodeSafeToReplace(ast, sourceRange)
|
||||
@ -872,17 +873,17 @@ export function moveValueIntoNewVariable(
|
||||
*/
|
||||
export function deleteSegmentFromPipeExpression(
|
||||
dependentRanges: SourceRange[],
|
||||
modifiedAst: Program,
|
||||
modifiedAst: Node<Program>,
|
||||
programMemory: ProgramMemory,
|
||||
code: string,
|
||||
pathToNode: PathToNode
|
||||
): Program | Error {
|
||||
): Node<Program> | Error {
|
||||
let _modifiedAst = structuredClone(modifiedAst)
|
||||
|
||||
dependentRanges.forEach((range) => {
|
||||
const path = getNodePathFromSourceRange(_modifiedAst, range)
|
||||
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = getNodeFromPath<Node<CallExpression>>(
|
||||
_modifiedAst,
|
||||
path,
|
||||
'CallExpression',
|
||||
@ -928,11 +929,11 @@ export function deleteSegmentFromPipeExpression(
|
||||
export function removeSingleConstraintInfo(
|
||||
pathToCallExp: PathToNode,
|
||||
argDetails: SimplifiedArgDetails,
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
programMemory: ProgramMemory
|
||||
):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNodeMap: PathToNodeMap
|
||||
}
|
||||
| false {
|
||||
@ -954,12 +955,12 @@ export function removeSingleConstraintInfo(
|
||||
}
|
||||
|
||||
export async function deleteFromSelection(
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
selection: Selection,
|
||||
programMemory: ProgramMemory,
|
||||
getFaceDetails: (id: string) => Promise<Models['FaceIsPlanar_type']> = () =>
|
||||
({} as any)
|
||||
): Promise<Program | Error> {
|
||||
): Promise<Node<Program> | Error> {
|
||||
const astClone = structuredClone(ast)
|
||||
const range = selection.range
|
||||
const path = getNodePathFromSourceRange(ast, range)
|
||||
@ -1134,5 +1135,5 @@ export async function deleteFromSelection(
|
||||
}
|
||||
|
||||
const nonCodeMetaEmpty = () => {
|
||||
return { nonCodeNodes: {}, start: [] }
|
||||
return { nonCodeNodes: {}, startNodes: [], start: 0, end: 0 }
|
||||
}
|
||||
|
@ -36,11 +36,12 @@ import {
|
||||
getSweepFromSuspectedPath,
|
||||
} from 'lang/std/artifactGraph'
|
||||
import { kclManager, engineCommandManager, editorManager } from 'lib/singletons'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
// Apply Fillet To Selection
|
||||
|
||||
export function applyFilletToSelection(
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
selection: Selections,
|
||||
radius: KclCommandValue
|
||||
): void | Error {
|
||||
@ -55,10 +56,10 @@ export function applyFilletToSelection(
|
||||
}
|
||||
|
||||
export function modifyAstCloneWithFilletAndTag(
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
selection: Selections,
|
||||
radius: KclCommandValue
|
||||
): { modifiedAst: Program; pathToFilletNode: Array<PathToNode> } | Error {
|
||||
): { modifiedAst: Node<Program>; pathToFilletNode: Array<PathToNode> } | Error {
|
||||
let clonedAst = structuredClone(ast)
|
||||
const clonedAstForGetExtrude = structuredClone(ast)
|
||||
|
||||
@ -246,7 +247,7 @@ export function getPathToExtrudeForSegmentSelection(
|
||||
}
|
||||
|
||||
async function updateAstAndFocus(
|
||||
modifiedAst: Program,
|
||||
modifiedAst: Node<Program>,
|
||||
pathToFilletNode: Array<PathToNode>
|
||||
) {
|
||||
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
||||
@ -258,7 +259,7 @@ async function updateAstAndFocus(
|
||||
}
|
||||
|
||||
function mutateAstWithTagForSketchSegment(
|
||||
astClone: Program,
|
||||
astClone: Node<Program>,
|
||||
pathToSegmentNode: PathToNode
|
||||
): { modifiedAst: Program; tag: string } | Error {
|
||||
const segmentNode = getNodeFromPath<CallExpression>(
|
||||
@ -292,7 +293,7 @@ function mutateAstWithTagForSketchSegment(
|
||||
function getEdgeTagCall(
|
||||
tag: string,
|
||||
selectionType: string
|
||||
): Identifier | CallExpression {
|
||||
): Node<Identifier | CallExpression> {
|
||||
let tagCall: Expr = createIdentifier(tag)
|
||||
|
||||
// Modify the tag based on selectionType
|
||||
@ -426,7 +427,7 @@ export const hasValidFilletSelection = ({
|
||||
code,
|
||||
}: {
|
||||
selectionRanges: Selections
|
||||
ast: Program
|
||||
ast: Node<Program>
|
||||
code: string
|
||||
}) => {
|
||||
// check if there is anything filletable in the scene
|
||||
@ -454,7 +455,7 @@ export const hasValidFilletSelection = ({
|
||||
for (const selection of selectionRanges.codeBasedSelections) {
|
||||
// check if all selections are in sketchLineHelperMap
|
||||
const path = getNodePathFromSourceRange(ast, selection.range)
|
||||
const segmentNode = getNodeFromPath<CallExpression>(
|
||||
const segmentNode = getNodeFromPath<Node<CallExpression>>(
|
||||
ast,
|
||||
path,
|
||||
'CallExpression'
|
||||
@ -534,7 +535,7 @@ export const isTagUsedInFillet = ({
|
||||
ast,
|
||||
callExp,
|
||||
}: {
|
||||
ast: Program
|
||||
ast: Node<Program>
|
||||
callExp: CallExpression
|
||||
}): Array<EdgeTypes> => {
|
||||
const tag = getTagFromCallExpression(callExp)
|
||||
|
@ -29,6 +29,7 @@ import {
|
||||
} from './std/sketchcombos'
|
||||
import { err } from 'lib/trap'
|
||||
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.
|
||||
@ -121,12 +122,13 @@ export function getNodeFromPathCurry(
|
||||
}
|
||||
|
||||
function moreNodePathFromSourceRange(
|
||||
node:
|
||||
node: Node<
|
||||
| Expr
|
||||
| ImportStatement
|
||||
| ExpressionStatement
|
||||
| VariableDeclaration
|
||||
| ReturnStatement,
|
||||
| ReturnStatement
|
||||
>,
|
||||
sourceRange: Selection['range'],
|
||||
previousPath: PathToNode = [['body', '']]
|
||||
): PathToNode {
|
||||
@ -344,15 +346,16 @@ export function getNodePathFromSourceRange(
|
||||
return path
|
||||
}
|
||||
|
||||
type KCLNode =
|
||||
type KCLNode = Node<
|
||||
| Expr
|
||||
| ExpressionStatement
|
||||
| VariableDeclaration
|
||||
| VariableDeclarator
|
||||
| ReturnStatement
|
||||
>
|
||||
|
||||
export function traverse(
|
||||
node: KCLNode | Program,
|
||||
node: KCLNode | Node<Program>,
|
||||
option: {
|
||||
enter?: (node: KCLNode, pathToNode: PathToNode) => void
|
||||
leave?: (node: KCLNode) => void
|
||||
@ -512,9 +515,9 @@ export function findAllPreviousVariables(
|
||||
}
|
||||
|
||||
type ReplacerFn = (
|
||||
_ast: Program,
|
||||
_ast: Node<Program>,
|
||||
varName: string
|
||||
) => { modifiedAst: Program; pathToReplaced: PathToNode } | Error
|
||||
) => { modifiedAst: Node<Program>; pathToReplaced: PathToNode } | Error
|
||||
|
||||
export function isNodeSafeToReplacePath(
|
||||
ast: Program,
|
||||
@ -583,12 +586,12 @@ export function isNodeSafeToReplacePath(
|
||||
}
|
||||
|
||||
export function isNodeSafeToReplace(
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
sourceRange: [number, number]
|
||||
):
|
||||
| {
|
||||
isSafe: boolean
|
||||
value: Expr
|
||||
value: Node<Expr>
|
||||
replacer: ReplacerFn
|
||||
}
|
||||
| Error {
|
||||
@ -837,7 +840,7 @@ export function findUsesOfTagInPipe(
|
||||
? String(thirdParam.value)
|
||||
: thirdParam.name
|
||||
|
||||
const varDec = getNodeFromPath<VariableDeclaration>(
|
||||
const varDec = getNodeFromPath<Node<VariableDeclaration>>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'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 */
|
||||
export function doesSceneHaveSweepableSketch(ast: Program) {
|
||||
export function doesSceneHaveSweepableSketch(ast: Node<Program>) {
|
||||
const theMap: any = {}
|
||||
traverse(ast as any, {
|
||||
enter(node) {
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||
import { err } from 'lib/trap'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
const eachQuad: [number, [number, number]][] = [
|
||||
[-315, [1, 1]],
|
||||
@ -687,7 +688,7 @@ describe('testing getConstraintInfo', () => {
|
||||
]
|
||||
if (err(ast)) return ast
|
||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = getNodeFromPath<Node<CallExpression>>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
@ -841,7 +842,7 @@ describe('testing getConstraintInfo', () => {
|
||||
]
|
||||
if (err(ast)) return ast
|
||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = getNodeFromPath<Node<CallExpression>>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
@ -1197,7 +1198,7 @@ describe('testing getConstraintInfo', () => {
|
||||
]
|
||||
if (err(ast)) return ast
|
||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
const callExp = getNodeFromPath<Node<CallExpression>>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
|
@ -55,6 +55,7 @@ import { err } from 'lib/trap'
|
||||
import { perpendicularDistance } from 'sketch-helpers'
|
||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||
import { EdgeCutInfo } from 'machines/modelingMachine'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
const STRAIGHT_SEGMENT_ERR = new Error(
|
||||
'Invalid input, expected "straight-segment"'
|
||||
@ -1785,7 +1786,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
)
|
||||
}
|
||||
if (intersectTag !== -1) {
|
||||
const tag = firstArg.properties[intersectTag]?.value as Identifier
|
||||
const tag = firstArg.properties[intersectTag]?.value as Node<Identifier>
|
||||
const pathToTagProp: PathToNode = [
|
||||
...pathToObjectExp,
|
||||
[intersectTag, 'index'],
|
||||
@ -1825,7 +1826,9 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
||||
body: [],
|
||||
|
||||
nonCodeMeta: {
|
||||
start: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
startNodes: [],
|
||||
nonCodeNodes: [],
|
||||
},
|
||||
},
|
||||
@ -1865,7 +1868,7 @@ export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
|
||||
} as const
|
||||
|
||||
export function changeSketchArguments(
|
||||
node: Program,
|
||||
node: Node<Program>,
|
||||
programMemory: ProgramMemory,
|
||||
sourceRangeOrPath:
|
||||
| {
|
||||
@ -1877,7 +1880,7 @@ export function changeSketchArguments(
|
||||
pathToNode: PathToNode
|
||||
},
|
||||
input: SegmentInputs
|
||||
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
||||
): { modifiedAst: Node<Program>; pathToNode: PathToNode } | Error {
|
||||
const _node = { ...node }
|
||||
const thePath =
|
||||
sourceRangeOrPath.type === 'sourceRange'
|
||||
@ -1906,7 +1909,7 @@ export function changeSketchArguments(
|
||||
}
|
||||
|
||||
export function getConstraintInfo(
|
||||
callExpression: CallExpression,
|
||||
callExpression: Node<CallExpression>,
|
||||
code: string,
|
||||
pathToNode: PathToNode
|
||||
): ConstrainInfo[] {
|
||||
@ -1944,7 +1947,7 @@ export function compareVec2Epsilon2(
|
||||
}
|
||||
|
||||
interface CreateLineFnCallArgs {
|
||||
node: Program
|
||||
node: Node<Program>
|
||||
programMemory: ProgramMemory
|
||||
input: SegmentInputs
|
||||
fnName: ToolTip
|
||||
@ -1961,7 +1964,7 @@ export function addNewSketchLn({
|
||||
spliceBetween = false,
|
||||
}: CreateLineFnCallArgs):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNode: PathToNode
|
||||
}
|
||||
| Error {
|
||||
@ -1971,8 +1974,12 @@ export function addNewSketchLn({
|
||||
return new Error('not a sketch line helper')
|
||||
}
|
||||
|
||||
getNodeFromPath<VariableDeclarator>(node, pathToNode, 'VariableDeclarator')
|
||||
getNodeFromPath<PipeExpression | CallExpression>(
|
||||
getNodeFromPath<Node<VariableDeclarator>>(
|
||||
node,
|
||||
pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
getNodeFromPath<Node<PipeExpression | CallExpression>>(
|
||||
node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
@ -1991,13 +1998,13 @@ export function addCallExpressionsToPipe({
|
||||
pathToNode,
|
||||
expressions,
|
||||
}: {
|
||||
node: Program
|
||||
node: Node<Program>
|
||||
programMemory: ProgramMemory
|
||||
pathToNode: PathToNode
|
||||
expressions: CallExpression[]
|
||||
expressions: Node<CallExpression>[]
|
||||
}) {
|
||||
const _node = { ...node }
|
||||
const pipeExpression = getNodeFromPath<PipeExpression>(
|
||||
const pipeExpression = getNodeFromPath<Node<PipeExpression>>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
@ -2046,7 +2053,7 @@ export function replaceSketchLine({
|
||||
replaceExistingCallback,
|
||||
referencedSegment,
|
||||
}: {
|
||||
node: Program
|
||||
node: Node<Program>
|
||||
programMemory: ProgramMemory
|
||||
pathToNode: PathToNode
|
||||
fnName: ToolTip
|
||||
@ -2055,7 +2062,7 @@ export function replaceSketchLine({
|
||||
referencedSegment?: Path
|
||||
}):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
valueUsedInTransform?: number
|
||||
pathToNode: PathToNode
|
||||
}
|
||||
@ -2107,7 +2114,7 @@ function addTagToChamfer(
|
||||
edgeCutMeta: EdgeCutInfo | null
|
||||
):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
tag: string
|
||||
}
|
||||
| Error {
|
||||
@ -2234,7 +2241,7 @@ export function addTagForSketchOnFace(
|
||||
edgeCutMeta: EdgeCutInfo | null
|
||||
):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
tag: string
|
||||
}
|
||||
| Error {
|
||||
@ -2272,12 +2279,14 @@ function isAngleLiteral(lineArugement: Expr): boolean {
|
||||
: 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 {
|
||||
return ({ node, pathToNode }) => {
|
||||
const _node = { ...node }
|
||||
const callExpr = getNodeFromPath<CallExpression>(
|
||||
const callExpr = getNodeFromPath<Node<CallExpression>>(
|
||||
_node,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
|
@ -49,6 +49,7 @@ import {
|
||||
getSketchSegmentFromSourceRange,
|
||||
} from './sketchConstraints'
|
||||
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
export type LineInputsType =
|
||||
| 'xAbsolute'
|
||||
@ -325,7 +326,7 @@ const setHorzVertDistanceCreateNode =
|
||||
if (isUndef(refNum) || err(literalArg)) return REF_NUM_ERR
|
||||
|
||||
const valueUsedInTransform = roundOff(literalArg - refNum, 2)
|
||||
let finalValue: Expr = createBinaryExpressionWithUnary([
|
||||
let finalValue: Node<Expr> = createBinaryExpressionWithUnary([
|
||||
createSegEnd(referenceSegName, !index),
|
||||
forceValueUsedInTransform || createLiteral(valueUsedInTransform),
|
||||
])
|
||||
@ -683,6 +684,14 @@ const transformMap: TransformMap = {
|
||||
tag
|
||||
),
|
||||
},
|
||||
xAbs: {
|
||||
tooltip: 'lineTo',
|
||||
createNode: setAbsDistanceCreateNode('x'),
|
||||
},
|
||||
yAbs: {
|
||||
tooltip: 'lineTo',
|
||||
createNode: setAbsDistanceCreateNode('y'),
|
||||
},
|
||||
},
|
||||
xAbsolute: {
|
||||
equalLength: {
|
||||
@ -1541,7 +1550,7 @@ export function transformSecondarySketchLinesTagFirst({
|
||||
forceSegName,
|
||||
forceValueUsedInTransform,
|
||||
}: {
|
||||
ast: Program
|
||||
ast: Node<Program>
|
||||
selectionRanges: Selections
|
||||
transformInfos: TransformInfo[]
|
||||
programMemory: ProgramMemory
|
||||
@ -1549,7 +1558,7 @@ export function transformSecondarySketchLinesTagFirst({
|
||||
forceValueUsedInTransform?: BinaryPart
|
||||
}):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
valueUsedInTransform?: number
|
||||
pathToNodeMap: PathToNodeMap
|
||||
tagInfo: {
|
||||
@ -1620,7 +1629,7 @@ export function transformAstSketchLines({
|
||||
forceValueUsedInTransform,
|
||||
referencedSegmentRange,
|
||||
}: {
|
||||
ast: Program
|
||||
ast: Node<Program>
|
||||
selectionRanges: Selections | PathToNode[]
|
||||
transformInfos: TransformInfo[]
|
||||
programMemory: ProgramMemory
|
||||
@ -1629,7 +1638,7 @@ export function transformAstSketchLines({
|
||||
referencedSegmentRange?: Selection['range']
|
||||
}):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
valueUsedInTransform?: number
|
||||
pathToNodeMap: PathToNodeMap
|
||||
}
|
||||
@ -1647,7 +1656,7 @@ export function transformAstSketchLines({
|
||||
|
||||
const getNode = getNodeFromPathCurry(node, _pathToNode)
|
||||
|
||||
const callExp = getNode<CallExpression>('CallExpression')
|
||||
const callExp = getNode<Node<CallExpression>>('CallExpression')
|
||||
if (err(callExp)) return callExp
|
||||
const varDec = getNode<VariableDeclarator>('VariableDeclarator')
|
||||
if (err(varDec)) return varDec
|
||||
@ -1806,13 +1815,16 @@ function createSegAngle(referenceSegName: string): BinaryPart {
|
||||
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', [
|
||||
createIdentifier(referenceSegName),
|
||||
])
|
||||
}
|
||||
|
||||
function createLastSeg(isX: boolean): CallExpression {
|
||||
function createLastSeg(isX: boolean): Node<CallExpression> {
|
||||
return createCallExpression(isX ? 'lastSegX' : 'lastSegY', [
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
@ -1830,7 +1842,7 @@ export function getConstraintLevelFromSourceRange(
|
||||
ast: Program | Error
|
||||
): Error | { range: [number, number]; level: ConstraintLevel } {
|
||||
if (err(ast)) return ast
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
const nodeMeta = getNodeFromPath<Node<CallExpression>>(
|
||||
ast,
|
||||
getNodePathFromSourceRange(ast, cursorRange),
|
||||
'CallExpression'
|
||||
|
@ -11,16 +11,17 @@ import {
|
||||
BinaryPart,
|
||||
} from '../wasm'
|
||||
import { LineInputsType } from './sketchcombos'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
export interface ModifyAstBase {
|
||||
node: Program
|
||||
node: Node<Program>
|
||||
// TODO #896: Remove ProgramMemory from this interface
|
||||
previousProgramMemory: ProgramMemory
|
||||
pathToNode: PathToNode
|
||||
}
|
||||
|
||||
export interface AddTagInfo {
|
||||
node: Program
|
||||
node: Node<Program>
|
||||
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
|
||||
* {@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
|
||||
@ -142,7 +143,7 @@ export type InputArg = _InputArg<Expr>
|
||||
* 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
|
||||
*/
|
||||
type RawArg = _InputArg<Literal>
|
||||
type RawArg = _InputArg<Node<Literal>>
|
||||
|
||||
export type InputArgs = Array<InputArg>
|
||||
|
||||
@ -186,7 +187,7 @@ export type CreateStdLibSketchCallExpr = (args: {
|
||||
inputs: InputArgs
|
||||
rawArgs: RawArgs
|
||||
referenceSegName: string
|
||||
tag?: Expr
|
||||
tag?: Node<Expr>
|
||||
forceValueUsedInTransform?: BinaryPart
|
||||
referencedSegment?: Path
|
||||
}) => CreatedSketchExprResult | Error
|
||||
@ -215,26 +216,26 @@ export interface ConstrainInfo {
|
||||
export interface SketchLineHelper {
|
||||
add: (a: addCall) =>
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNode: PathToNode
|
||||
valueUsedInTransform?: number
|
||||
}
|
||||
| Error
|
||||
updateArgs: (a: updateArgs) =>
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
pathToNode: PathToNode
|
||||
}
|
||||
| Error
|
||||
getTag: (a: CallExpression) => string | Error
|
||||
addTag: (a: AddTagInfo) =>
|
||||
| {
|
||||
modifiedAst: Program
|
||||
modifiedAst: Node<Program>
|
||||
tag: string
|
||||
}
|
||||
| Error
|
||||
getConstraintInfo: (
|
||||
callExp: CallExpression,
|
||||
callExp: Node<CallExpression>,
|
||||
code: string,
|
||||
pathToNode: PathToNode
|
||||
) => ConstrainInfo[]
|
||||
|
13
src/lang/wasm.test.ts
Normal file
13
src/lang/wasm.test.ts
Normal 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)
|
||||
})
|
@ -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 { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef'
|
||||
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 { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
||||
@ -122,11 +123,11 @@ export const initPromise = initialise()
|
||||
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
|
||||
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
|
||||
|
||||
try {
|
||||
const program: Program = parse_wasm(code)
|
||||
const program: Node<Program> = parse_wasm(code)
|
||||
return program
|
||||
} catch (e: any) {
|
||||
// throw e
|
||||
@ -378,7 +379,7 @@ export function sketchFromKclValue(
|
||||
}
|
||||
|
||||
export const executor = async (
|
||||
node: Program,
|
||||
node: Node<Program>,
|
||||
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
||||
idGenerator: IdGenerator = defaultIdGenerator(),
|
||||
engineCommandManager: EngineCommandManager,
|
||||
@ -402,7 +403,7 @@ export const executor = async (
|
||||
}
|
||||
|
||||
export const _executor = async (
|
||||
node: Program,
|
||||
node: Node<Program>,
|
||||
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
||||
idGenerator: IdGenerator = defaultIdGenerator(),
|
||||
engineCommandManager: EngineCommandManager,
|
||||
@ -493,13 +494,13 @@ export function lexer(str: string): Token[] | Error {
|
||||
|
||||
export const modifyAstForSketch = async (
|
||||
engineCommandManager: EngineCommandManager,
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
variableName: string,
|
||||
currentPlane: string,
|
||||
engineId: string
|
||||
): Promise<Program> => {
|
||||
): Promise<Node<Program>> => {
|
||||
try {
|
||||
const updatedAst: Program = await modify_ast_for_sketch_wasm(
|
||||
const updatedAst: Node<Program> = await modify_ast_for_sketch_wasm(
|
||||
engineCommandManager,
|
||||
JSON.stringify(ast),
|
||||
variableName,
|
||||
|
@ -6,6 +6,7 @@ import { Identifier, Expr, VariableDeclaration } from 'lang/wasm'
|
||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||
import { ReactNode } from 'react'
|
||||
import { MachineManager } from 'components/MachineManagerProvider'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
type Icon = CustomIconName
|
||||
const PLATFORMS = ['both', 'web', 'desktop'] as const
|
||||
@ -24,8 +25,8 @@ export interface KclExpression {
|
||||
}
|
||||
export interface KclExpressionWithVariable extends KclExpression {
|
||||
variableName: string
|
||||
variableDeclarationAst: VariableDeclaration
|
||||
variableIdentifierAst: Identifier
|
||||
variableDeclarationAst: Node<VariableDeclaration>
|
||||
variableIdentifierAst: Node<Identifier>
|
||||
insertIndex: number
|
||||
}
|
||||
export type KclCommandValue = KclExpression | KclExpressionWithVariable
|
||||
|
@ -36,6 +36,7 @@ import {
|
||||
getWallCodeRef,
|
||||
ArtifactId,
|
||||
} from 'lang/std/artifactGraph'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b'
|
||||
export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01'
|
||||
@ -244,7 +245,7 @@ export function getEventForSegmentSelection(
|
||||
const updatedAst = parse(codeManager.code)
|
||||
if (err(updatedAst)) return null
|
||||
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
const nodeMeta = getNodeFromPath<Node<CallExpression>>(
|
||||
updatedAst,
|
||||
pathToNode,
|
||||
'CallExpression'
|
||||
@ -362,7 +363,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
||||
|
||||
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
||||
if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
const nodeMeta = getNodeFromPath<Node<CallExpression>>(
|
||||
updated,
|
||||
segmentGroup.userData.pathToNode,
|
||||
'CallExpression'
|
||||
|
@ -17,6 +17,7 @@ import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
||||
import { err, reportRejection } from 'lib/trap'
|
||||
import { toSync } from './utils'
|
||||
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
type WebSocketResponse = Models['WebSocketResponse_type']
|
||||
|
||||
@ -84,7 +85,7 @@ class MockEngineCommandManager {
|
||||
}
|
||||
|
||||
export async function enginelessExecutor(
|
||||
ast: Program | Error,
|
||||
ast: Node<Program> | Error,
|
||||
pm: ProgramMemory | Error = ProgramMemory.empty(),
|
||||
idGenerator: IdGenerator = defaultIdGenerator()
|
||||
): Promise<ExecState> {
|
||||
@ -109,7 +110,7 @@ export async function enginelessExecutor(
|
||||
}
|
||||
|
||||
export async function executor(
|
||||
ast: Program,
|
||||
ast: Node<Program>,
|
||||
pm: ProgramMemory = ProgramMemory.empty(),
|
||||
idGenerator: IdGenerator = defaultIdGenerator()
|
||||
): Promise<ExecState> {
|
||||
|
2
src/wasm-lib/.config/insta.yaml
Normal file
2
src/wasm-lib/.config/insta.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
test:
|
||||
runner: nextest
|
33
src/wasm-lib/Cargo.lock
generated
33
src/wasm-lib/Cargo.lock
generated
@ -1130,11 +1130,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "6.1.0"
|
||||
version = "6.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25b617d1375ef96eeb920ae717e3da34a02fc979fe632c75128350f9e1f74a"
|
||||
checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315"
|
||||
dependencies = [
|
||||
"log",
|
||||
"num-order",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"serde",
|
||||
@ -1452,13 +1453,14 @@ checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a"
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.40.0"
|
||||
version = "1.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60"
|
||||
checksum = "a1f72d3e19488cf7d8ea52d2fc0f8754fc933398b337cd3cbdb28aaeb35159ef"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
"linked-hash-map",
|
||||
"regex",
|
||||
"serde",
|
||||
"similar",
|
||||
]
|
||||
@ -1937,6 +1939,21 @@ dependencies = [
|
||||
"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]]
|
||||
name = "num-traits"
|
||||
version = "0.1.43"
|
||||
@ -2965,9 +2982,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.213"
|
||||
version = "1.0.214"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
||||
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -2983,9 +3000,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.213"
|
||||
version = "1.0.214"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
||||
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -57,6 +57,10 @@ debug = true
|
||||
[profile.dev]
|
||||
debug = 0
|
||||
|
||||
[profile.dev.package]
|
||||
insta.opt-level = 3
|
||||
similar.opt-level = 3
|
||||
|
||||
[profile.test]
|
||||
debug = "line-tables-only"
|
||||
|
||||
|
@ -18,7 +18,7 @@ once_cell = "1.20.2"
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
regex = "1.11"
|
||||
serde = { version = "1.0.213", features = ["derive"] }
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_tokenstream = "0.2"
|
||||
syn = { version = "2.0.85", features = ["full"] }
|
||||
|
||||
|
@ -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`.
|
||||
new-test name:
|
||||
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:
|
||||
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
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
extern crate alloc;
|
||||
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,
|
||||
};
|
||||
use kcl_macros::parse;
|
||||
@ -9,36 +9,46 @@ use pretty_assertions::assert_eq;
|
||||
#[test]
|
||||
fn basic() {
|
||||
let actual = parse!("const y = 4");
|
||||
let expected = Program {
|
||||
start: 0,
|
||||
end: 11,
|
||||
body: vec![BodyItem::VariableDeclaration(Box::new(VariableDeclaration {
|
||||
start: 0,
|
||||
end: 11,
|
||||
declarations: vec![VariableDeclarator {
|
||||
start: 6,
|
||||
end: 11,
|
||||
id: Identifier {
|
||||
start: 6,
|
||||
end: 7,
|
||||
name: "y".to_owned(),
|
||||
let expected = Node {
|
||||
inner: Program {
|
||||
body: vec![BodyItem::VariableDeclaration(Box::new(Node::new(
|
||||
VariableDeclaration {
|
||||
declarations: vec![Node::new(
|
||||
VariableDeclarator {
|
||||
id: Node::new(
|
||||
Identifier {
|
||||
name: "y".to_owned(),
|
||||
digest: None,
|
||||
},
|
||||
6,
|
||||
7,
|
||||
),
|
||||
init: Expr::Literal(Box::new(Node::new(
|
||||
Literal {
|
||||
value: LiteralValue::IInteger(4),
|
||||
raw: "4".to_owned(),
|
||||
digest: None,
|
||||
},
|
||||
10,
|
||||
11,
|
||||
))),
|
||||
digest: None,
|
||||
},
|
||||
6,
|
||||
11,
|
||||
)],
|
||||
visibility: ItemVisibility::Default,
|
||||
kind: VariableKind::Const,
|
||||
digest: None,
|
||||
},
|
||||
init: Expr::Literal(Box::new(Literal {
|
||||
start: 10,
|
||||
end: 11,
|
||||
value: LiteralValue::IInteger(4),
|
||||
raw: "4".to_owned(),
|
||||
digest: None,
|
||||
})),
|
||||
digest: None,
|
||||
}],
|
||||
visibility: ItemVisibility::Default,
|
||||
kind: VariableKind::Const,
|
||||
0,
|
||||
11,
|
||||
)))],
|
||||
non_code_meta: Default::default(),
|
||||
digest: None,
|
||||
}))],
|
||||
non_code_meta: NonCodeMeta::default(),
|
||||
digest: None,
|
||||
},
|
||||
start: 0,
|
||||
end: 11,
|
||||
};
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
@ -10,6 +10,6 @@ anyhow = "1.0.91"
|
||||
hyper = { version = "0.14.29", features = ["http1", "server", "tcp"] }
|
||||
kcl-lib = { version = "0.2", path = "../kcl" }
|
||||
pico-args = "0.5.0"
|
||||
serde = { version = "1.0.213", features = ["derive"] }
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_json = "1.0.128"
|
||||
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
|
||||
|
@ -38,7 +38,7 @@ pyo3 = { version = "0.22.5", optional = true }
|
||||
reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] }
|
||||
ropey = "1.6.1"
|
||||
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"
|
||||
sha2 = "0.10.8"
|
||||
tabled = { version = "0.15.0", optional = true }
|
||||
@ -77,24 +77,16 @@ engine = []
|
||||
pyo3 = ["dep:pyo3"]
|
||||
# Helper functions also used in benchmarks.
|
||||
lsp-test-util = []
|
||||
|
||||
tabled = ["dep:tabled"]
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
debug = true
|
||||
|
||||
[profile.bench]
|
||||
debug = true # Flamegraphs of benchmarks require accurate debug symbols
|
||||
|
||||
[dev-dependencies]
|
||||
base64 = "0.22.1"
|
||||
criterion = { version = "0.5.1", features = ["async_tokio"] }
|
||||
expectorate = "1.1.0"
|
||||
handlebars = "6.1.0"
|
||||
handlebars = "6.2.0"
|
||||
iai = "0.1"
|
||||
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"
|
||||
pretty_assertions = "1.4.1"
|
||||
tokio = { version = "1.40.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||
|
@ -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)
|
||||
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
|
||||
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.
|
||||
|
||||
## Bumping the version
|
||||
|
@ -13,6 +13,7 @@ pub fn bench_parse(c: &mut Criterion) {
|
||||
("cube", CUBE_PROGRAM),
|
||||
("math", MATH_PROGRAM),
|
||||
("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
|
||||
("koch snowflake", LSYSTEM_KOCH_SNOWFLAKE_PROGRAM),
|
||||
] {
|
||||
let tokens = kcl_lib::token::lexer(file).unwrap();
|
||||
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 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 LSYSTEM_KOCH_SNOWFLAKE_PROGRAM: &str = include_str!("../../tests/executor/inputs/lsystem.kcl");
|
||||
|
@ -19,6 +19,9 @@ fn lex_cube() {
|
||||
fn lex_math() {
|
||||
black_box(kcl_lib::token::lexer(MATH_PROGRAM).unwrap());
|
||||
}
|
||||
fn lex_lsystem() {
|
||||
black_box(kcl_lib::token::lexer(LSYSTEM_PROGRAM).unwrap());
|
||||
}
|
||||
|
||||
fn parse_kitt() {
|
||||
parse(KITT_PROGRAM)
|
||||
@ -32,19 +35,25 @@ fn parse_cube() {
|
||||
fn parse_math() {
|
||||
parse(MATH_PROGRAM)
|
||||
}
|
||||
fn parse_lsystem() {
|
||||
parse(LSYSTEM_PROGRAM)
|
||||
}
|
||||
|
||||
iai::main! {
|
||||
lex_kitt,
|
||||
lex_pipes,
|
||||
lex_cube,
|
||||
lex_math,
|
||||
lex_lsystem,
|
||||
parse_kitt,
|
||||
parse_pipes,
|
||||
parse_cube,
|
||||
parse_math,
|
||||
parse_lsystem,
|
||||
}
|
||||
|
||||
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 CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
|
||||
const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl");
|
||||
const LSYSTEM_PROGRAM: &str = include_str!("../../tests/executor/inputs/lsystem.kcl");
|
||||
|
@ -7,6 +7,7 @@ pub fn bench_digest(c: &mut Criterion) {
|
||||
("cube", CUBE_PROGRAM),
|
||||
("math", MATH_PROGRAM),
|
||||
("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
|
||||
("lsystem", LSYSTEM_PROGRAM),
|
||||
] {
|
||||
let tokens = kcl_lib::token::lexer(file).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 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 LSYSTEM_PROGRAM: &str = include_str!("../../tests/executor/inputs/lsystem.kcl");
|
||||
|
@ -8,6 +8,7 @@ pub fn bench_execute(c: &mut Criterion) {
|
||||
("cube", CUBE_PROGRAM),
|
||||
("server_rack_lite", SERVER_RACK_LITE_PROGRAM),
|
||||
("server_rack_heavy", SERVER_RACK_HEAVY_PROGRAM),
|
||||
("lsystem", LSYSTEM_PROGRAM),
|
||||
] {
|
||||
let mut group = c.benchmark_group("executor");
|
||||
// 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_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 LSYSTEM_PROGRAM: &str = include_str!("../../tests/executor/inputs/lsystem.kcl");
|
||||
|
@ -42,6 +42,7 @@ fn bench_kcl_lsp_semantic_tokens(c: &mut Criterion) {
|
||||
("math", MATH_PROGRAM),
|
||||
("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
|
||||
("global_tags", GLOBAL_TAGS_FILE),
|
||||
("lsystem", LSYSTEM_PROGRAM),
|
||||
] {
|
||||
c.bench_with_input(BenchmarkId::new("semantic_tokens_", name), &code, |b, &s| {
|
||||
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 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 LSYSTEM_PROGRAM: &str = include_str!("../../tests/executor/inputs/lsystem.kcl");
|
||||
|
@ -16,6 +16,8 @@ use crate::{
|
||||
executor::{Point2d, SourceRange},
|
||||
};
|
||||
|
||||
use super::types::Node;
|
||||
|
||||
type Point3d = kcmc::shared::Point3d<f64>;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -35,7 +37,7 @@ const EPSILON: f64 = 0.015625; // or 2^-6
|
||||
/// a move or a new line.
|
||||
pub async fn modify_ast_for_sketch(
|
||||
engine: &Arc<Box<dyn EngineManager>>,
|
||||
program: &mut Program,
|
||||
program: &mut Node<Program>,
|
||||
// The name of the sketch.
|
||||
sketch_name: &str,
|
||||
// The type of plane the sketch is on. `XY` or `XZ`, etc
|
||||
@ -195,7 +197,7 @@ fn create_start_sketch_on(
|
||||
end: [f64; 2],
|
||||
plane: crate::executor::PlaneType,
|
||||
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_profile_at = CallExpression::new(
|
||||
"startProfileAt",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,10 @@
|
||||
use crate::executor::Metadata;
|
||||
use crate::executor::SourceRange;
|
||||
|
||||
use super::impl_value_meta;
|
||||
use super::BoxNode;
|
||||
use super::ConstraintLevel;
|
||||
use super::Hover;
|
||||
use super::Node;
|
||||
use super::NodeList;
|
||||
use super::{Digest, Expr};
|
||||
use databake::*;
|
||||
use schemars::JsonSchema;
|
||||
@ -19,12 +20,10 @@ type IfBlock = crate::ast::types::Program;
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub struct IfExpression {
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
pub cond: Box<Expr>,
|
||||
pub then_val: Box<IfBlock>,
|
||||
pub else_ifs: Vec<ElseIf>,
|
||||
pub final_else: Box<IfBlock>,
|
||||
pub then_val: BoxNode<IfBlock>,
|
||||
pub else_ifs: NodeList<ElseIf>,
|
||||
pub final_else: BoxNode<IfBlock>,
|
||||
|
||||
pub digest: Option<Digest>,
|
||||
}
|
||||
@ -34,57 +33,21 @@ pub struct IfExpression {
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub struct ElseIf {
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
pub cond: Expr,
|
||||
pub then_val: Box<IfBlock>,
|
||||
pub then_val: BoxNode<IfBlock>,
|
||||
|
||||
pub digest: Option<Digest>,
|
||||
}
|
||||
|
||||
// Source code metadata
|
||||
|
||||
impl_value_meta!(IfExpression);
|
||||
impl_value_meta!(ElseIf);
|
||||
|
||||
impl IfExpression {
|
||||
impl Node<IfExpression> {
|
||||
fn source_ranges(&self) -> Vec<SourceRange> {
|
||||
vec![SourceRange::from(self)]
|
||||
}
|
||||
}
|
||||
|
||||
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 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 {
|
||||
impl Node<ElseIf> {
|
||||
#[allow(dead_code)]
|
||||
fn source_ranges(&self) -> Vec<SourceRange> {
|
||||
vec![SourceRange([self.start, self.end])]
|
||||
@ -93,6 +56,15 @@ impl ElseIf {
|
||||
|
||||
// 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 {
|
||||
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
||||
self.cond
|
||||
@ -115,12 +87,7 @@ impl IfExpression {
|
||||
}
|
||||
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) {
|
||||
self.cond.replace_value(source_range, new_value.clone());
|
||||
for else_if in &mut self.else_ifs {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
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,
|
||||
};
|
||||
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> {
|
||||
let array = match &self.object {
|
||||
MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
|
||||
@ -137,7 +137,7 @@ impl MemberExpression {
|
||||
}
|
||||
}
|
||||
|
||||
impl BinaryExpression {
|
||||
impl Node<BinaryExpression> {
|
||||
#[async_recursion]
|
||||
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()?;
|
||||
@ -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> {
|
||||
if self.operator == UnaryOperator::Not {
|
||||
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)
|
||||
}
|
||||
|
||||
impl CallExpression {
|
||||
impl Node<CallExpression> {
|
||||
#[async_recursion]
|
||||
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
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> {
|
||||
let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
|
||||
value: self.name.clone(),
|
||||
@ -518,7 +518,7 @@ impl TagDeclarator {
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayExpression {
|
||||
impl Node<ArrayExpression> {
|
||||
#[async_recursion]
|
||||
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
let mut results = Vec::with_capacity(self.elements.len());
|
||||
@ -543,21 +543,21 @@ impl ArrayExpression {
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayRangeExpression {
|
||||
impl Node<ArrayRangeExpression> {
|
||||
#[async_recursion]
|
||||
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
|
||||
.execute_expr(&self.start_element, exec_state, &metadata, StatementKind::Expression)
|
||||
.await?
|
||||
.get_json_value()?;
|
||||
let start = parse_json_number_as_i64(&start, (&*self.start_element).into())?;
|
||||
let metadata = Metadata::from(&*self.end_element);
|
||||
let start = parse_json_number_as_i64(&start, (&self.start_element).into())?;
|
||||
let metadata = Metadata::from(&self.end_element);
|
||||
let end = ctx
|
||||
.execute_expr(&self.end_element, exec_state, &metadata, StatementKind::Expression)
|
||||
.await?
|
||||
.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 {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
@ -581,7 +581,7 @@ impl ArrayRangeExpression {
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectExpression {
|
||||
impl Node<ObjectExpression> {
|
||||
#[async_recursion]
|
||||
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
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]
|
||||
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
// Check the `if` branch.
|
||||
@ -717,13 +717,13 @@ impl Property {
|
||||
let property_src: SourceRange = value.clone().into();
|
||||
match value {
|
||||
LiteralIdentifier::Identifier(identifier) => {
|
||||
let name = identifier.name;
|
||||
let name = &identifier.name;
|
||||
if !computed {
|
||||
// Treat the property as a literal
|
||||
Ok(Property::String(name.to_string()))
|
||||
} else {
|
||||
// Actually evaluate memory to compute the property.
|
||||
let prop = exec_state.memory.get(&name, property_src)?;
|
||||
let prop = exec_state.memory.get(name, property_src)?;
|
||||
let KclValue::UserVal(prop) = prop else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
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) => {
|
||||
|
@ -5,6 +5,8 @@ use serde_json::Value as JValue;
|
||||
|
||||
use crate::ast::types::{Expr, Literal};
|
||||
|
||||
use super::Node;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||
#[databake(path = kcl_lib::ast::types)]
|
||||
#[ts(export)]
|
||||
@ -33,8 +35,8 @@ impl LiteralValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Literal> for Expr {
|
||||
fn from(literal: Literal) -> Self {
|
||||
impl From<Node<Literal>> for Expr {
|
||||
fn from(literal: Node<Literal>) -> Self {
|
||||
Expr::Literal(Box::new(literal))
|
||||
}
|
||||
}
|
||||
|
@ -396,6 +396,9 @@ fn generate_function(internal_fn: Box<dyn StdLibFn>) -> Result<BTreeMap<String,
|
||||
fn cleanup_static_links(output: &str) -> String {
|
||||
let mut cleaned_output = output.to_string();
|
||||
// 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");
|
||||
cleaned_output = cleaned_output.replace("`TagDeclarator`", &link);
|
||||
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();
|
||||
// Fix the links to the types.
|
||||
for type_name in types {
|
||||
if type_name == "TagDeclarator" || type_name == "TagIdentifier" {
|
||||
if type_name == "TagDeclarator" || type_name == "TagIdentifier" || type_name == "TagNode" {
|
||||
continue;
|
||||
} else {
|
||||
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.
|
||||
if name == "TagDeclarator" || name == "TagIdentifier" {
|
||||
if name == "TagDeclarator" || name == "TagIdentifier" || name == "TagNode" {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ impl StdLibFnArg {
|
||||
|| self.type_ == "SketchOrSurface"
|
||||
{
|
||||
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"))));
|
||||
} else if self.type_ == "TagIdentifier" && self.required {
|
||||
// TODO: actually use the ast to populate this.
|
||||
|
@ -26,8 +26,8 @@ type Point3D = kcmc::shared::Point3d<f64>;
|
||||
|
||||
use crate::{
|
||||
ast::types::{
|
||||
human_friendly_type, BodyItem, Expr, ExpressionStatement, FunctionExpression, ImportStatement, ItemVisibility,
|
||||
KclNone, Program, ReturnStatement, TagDeclarator,
|
||||
human_friendly_type, BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, Node, NodeRef, Program,
|
||||
TagDeclarator, TagNode,
|
||||
},
|
||||
engine::{EngineManager, ExecutionKind},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
@ -155,6 +155,7 @@ impl Default for ProgramMemory {
|
||||
|
||||
/// An index pointing to an environment.
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[schemars(transparent)]
|
||||
pub struct EnvironmentRef(usize);
|
||||
|
||||
impl EnvironmentRef {
|
||||
@ -339,7 +340,7 @@ impl IdGenerator {
|
||||
pub enum KclValue {
|
||||
UserVal(UserVal),
|
||||
TagIdentifier(Box<TagIdentifier>),
|
||||
TagDeclarator(Box<TagDeclarator>),
|
||||
TagDeclarator(crate::ast::types::BoxNode<TagDeclarator>),
|
||||
Plane(Box<Plane>),
|
||||
Face(Box<Face>),
|
||||
|
||||
@ -352,7 +353,7 @@ pub enum KclValue {
|
||||
Function {
|
||||
#[serde(skip)]
|
||||
func: Option<MemoryFunction>,
|
||||
expression: Box<FunctionExpression>,
|
||||
expression: crate::ast::types::BoxNode<FunctionExpression>,
|
||||
memory: Box<ProgramMemory>,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
@ -890,7 +891,7 @@ pub type MemoryFunction =
|
||||
fn(
|
||||
s: Vec<KclValue>,
|
||||
memory: ProgramMemory,
|
||||
expression: Box<FunctionExpression>,
|
||||
expression: crate::ast::types::BoxNode<FunctionExpression>,
|
||||
metadata: Vec<Metadata>,
|
||||
exec_state: &ExecState,
|
||||
ctx: ExecutorContext,
|
||||
@ -900,7 +901,7 @@ impl From<KclValue> for Vec<SourceRange> {
|
||||
fn from(item: KclValue) -> Self {
|
||||
match item {
|
||||
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::Solid(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Solids { value } => value
|
||||
@ -1043,9 +1044,9 @@ impl KclValue {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
KclValue::TagDeclarator(t) => Ok(*t.clone()),
|
||||
KclValue::TagDeclarator(t) => Ok((**t).clone()),
|
||||
_ => Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Not a tag declarator: {:?}", self),
|
||||
source_ranges: self.clone().into(),
|
||||
@ -1054,9 +1055,9 @@ impl KclValue {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
KclValue::TagDeclarator(t) => Ok(Some(*t.clone())),
|
||||
KclValue::TagDeclarator(t) => Ok(Some((**t).clone())),
|
||||
_ => Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Not a tag declarator: {:?}", self),
|
||||
source_ranges: self.clone().into(),
|
||||
@ -1200,7 +1201,7 @@ pub struct GetTangentialInfoFromPathsResult {
|
||||
}
|
||||
|
||||
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 base = current_path.get_base();
|
||||
tag_identifier.info = Some(TagEngineInfo {
|
||||
@ -1326,7 +1327,7 @@ pub enum EdgeCut {
|
||||
/// The engine id of the edge to fillet.
|
||||
#[serde(rename = "edgeId")]
|
||||
edge_id: uuid::Uuid,
|
||||
tag: Box<Option<TagDeclarator>>,
|
||||
tag: Box<Option<TagNode>>,
|
||||
},
|
||||
/// A chamfer.
|
||||
Chamfer {
|
||||
@ -1336,7 +1337,7 @@ pub enum EdgeCut {
|
||||
/// The engine id of the edge to chamfer.
|
||||
#[serde(rename = "edgeId")]
|
||||
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 {
|
||||
EdgeCut::Fillet { tag, .. } => *tag.clone(),
|
||||
EdgeCut::Chamfer { tag, .. } => *tag.clone(),
|
||||
@ -1529,26 +1530,10 @@ impl From<SourceRange> for Metadata {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ImportStatement> for Metadata {
|
||||
fn from(stmt: &ImportStatement) -> Self {
|
||||
impl<T> From<NodeRef<'_, T>> for Metadata {
|
||||
fn from(node: NodeRef<'_, T>) -> Self {
|
||||
Self {
|
||||
source_range: SourceRange::new(stmt.start, stmt.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),
|
||||
source_range: SourceRange::new(node.start, node.end),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1573,7 +1558,7 @@ pub struct BasePath {
|
||||
#[ts(type = "[number, number]")]
|
||||
pub to: [f64; 2],
|
||||
/// The tag of the path.
|
||||
pub tag: Option<TagDeclarator>,
|
||||
pub tag: Option<TagNode>,
|
||||
/// Metadata.
|
||||
#[serde(rename = "__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 {
|
||||
Path::ToPoint { base } => base.tag.clone(),
|
||||
Path::Horizontal { base, .. } => base.tag.clone(),
|
||||
@ -1820,7 +1805,7 @@ pub struct ChamferSurface {
|
||||
/// The id for the chamfer surface.
|
||||
pub face_id: uuid::Uuid,
|
||||
/// The tag.
|
||||
pub tag: Option<TagDeclarator>,
|
||||
pub tag: Option<Node<TagDeclarator>>,
|
||||
/// Metadata.
|
||||
#[serde(flatten)]
|
||||
pub geo_meta: GeoMeta,
|
||||
@ -1834,7 +1819,7 @@ pub struct FilletSurface {
|
||||
/// The id for the fillet surface.
|
||||
pub face_id: uuid::Uuid,
|
||||
/// The tag.
|
||||
pub tag: Option<TagDeclarator>,
|
||||
pub tag: Option<Node<TagDeclarator>>,
|
||||
/// Metadata.
|
||||
#[serde(flatten)]
|
||||
pub geo_meta: GeoMeta,
|
||||
@ -1848,7 +1833,7 @@ pub struct ExtrudePlane {
|
||||
/// The face id for the extrude plane.
|
||||
pub face_id: uuid::Uuid,
|
||||
/// The tag.
|
||||
pub tag: Option<TagDeclarator>,
|
||||
pub tag: Option<Node<TagDeclarator>>,
|
||||
/// Metadata.
|
||||
#[serde(flatten)]
|
||||
pub geo_meta: GeoMeta,
|
||||
@ -1862,7 +1847,7 @@ pub struct ExtrudeArc {
|
||||
/// The face id for the extrude plane.
|
||||
pub face_id: uuid::Uuid,
|
||||
/// The tag.
|
||||
pub tag: Option<TagDeclarator>,
|
||||
pub tag: Option<Node<TagDeclarator>>,
|
||||
/// Metadata.
|
||||
#[serde(flatten)]
|
||||
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 {
|
||||
ExtrudeSurface::ExtrudePlane(ep) => ep.tag.clone(),
|
||||
ExtrudeSurface::ExtrudeArc(ea) => ea.tag.clone(),
|
||||
@ -2156,7 +2141,7 @@ impl ExecutorContext {
|
||||
/// Kurt uses this for partial execution.
|
||||
pub async fn run(
|
||||
&self,
|
||||
program: &crate::ast::types::Program,
|
||||
program: NodeRef<'_, crate::ast::types::Program>,
|
||||
memory: Option<ProgramMemory>,
|
||||
id_generator: IdGenerator,
|
||||
project_directory: Option<String>,
|
||||
@ -2170,7 +2155,7 @@ impl ExecutorContext {
|
||||
/// Kurt uses this for partial execution.
|
||||
pub async fn run_with_session_data(
|
||||
&self,
|
||||
program: &crate::ast::types::Program,
|
||||
program: NodeRef<'_, crate::ast::types::Program>,
|
||||
memory: Option<ProgramMemory>,
|
||||
id_generator: IdGenerator,
|
||||
project_directory: Option<String>,
|
||||
@ -2212,9 +2197,9 @@ impl ExecutorContext {
|
||||
|
||||
/// Execute an AST's program.
|
||||
#[async_recursion]
|
||||
pub(crate) async fn inner_execute(
|
||||
&self,
|
||||
program: &crate::ast::types::Program,
|
||||
pub(crate) async fn inner_execute<'a>(
|
||||
&'a self,
|
||||
program: NodeRef<'a, crate::ast::types::Program>,
|
||||
exec_state: &mut ExecState,
|
||||
body_type: BodyType,
|
||||
) -> Result<Option<KclValue>, KclError> {
|
||||
@ -2450,11 +2435,23 @@ impl ExecutorContext {
|
||||
/// Execute the program, then get a PNG screenshot.
|
||||
pub async fn execute_and_prepare_snapshot(
|
||||
&self,
|
||||
program: &Program,
|
||||
program: NodeRef<'_, Program>,
|
||||
id_generator: IdGenerator,
|
||||
project_directory: Option<String>,
|
||||
) -> 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.
|
||||
self.engine
|
||||
@ -2487,7 +2484,7 @@ impl ExecutorContext {
|
||||
else {
|
||||
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.
|
||||
/// Returns Err if too few/too many arguments were given for the function.
|
||||
fn assign_args_to_params(
|
||||
function_expression: &FunctionExpression,
|
||||
function_expression: NodeRef<'_, FunctionExpression>,
|
||||
args: Vec<KclValue>,
|
||||
mut fn_memory: ProgramMemory,
|
||||
) -> Result<ProgramMemory, KclError> {
|
||||
@ -2547,7 +2544,7 @@ fn assign_args_to_params(
|
||||
pub(crate) async fn call_user_defined_function(
|
||||
args: Vec<KclValue>,
|
||||
memory: &ProgramMemory,
|
||||
function_expression: &FunctionExpression,
|
||||
function_expression: NodeRef<'_, FunctionExpression>,
|
||||
exec_state: &mut ExecState,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<Option<KclValue>, KclError> {
|
||||
@ -2586,7 +2583,7 @@ mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
use crate::ast::types::{Identifier, Parameter};
|
||||
use crate::ast::types::{Identifier, Node, Parameter};
|
||||
|
||||
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
|
||||
let tokens = crate::token::lexer(code)?;
|
||||
@ -3558,13 +3555,11 @@ let w = f() + f()
|
||||
meta: Default::default(),
|
||||
})
|
||||
}
|
||||
fn ident(s: &'static str) -> Identifier {
|
||||
Identifier {
|
||||
start: 0,
|
||||
end: 0,
|
||||
fn ident(s: &'static str) -> Node<Identifier> {
|
||||
Node::no_src(Identifier {
|
||||
name: s.to_owned(),
|
||||
digest: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
fn opt_param(s: &'static str) -> Parameter {
|
||||
Parameter {
|
||||
@ -3656,20 +3651,20 @@ let w = f() + f()
|
||||
),
|
||||
] {
|
||||
// Run each test.
|
||||
let func_expr = &FunctionExpression {
|
||||
start: 0,
|
||||
end: 0,
|
||||
let func_expr = &Node::no_src(FunctionExpression {
|
||||
params,
|
||||
body: crate::ast::types::Program {
|
||||
body: Node {
|
||||
inner: crate::ast::types::Program {
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
digest: None,
|
||||
},
|
||||
start: 0,
|
||||
end: 0,
|
||||
body: Vec::new(),
|
||||
non_code_meta: Default::default(),
|
||||
digest: None,
|
||||
},
|
||||
return_type: None,
|
||||
digest: None,
|
||||
};
|
||||
});
|
||||
let actual = assign_args_to_params(func_expr, args, ProgramMemory::new());
|
||||
assert_eq!(
|
||||
actual, expected,
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
pub struct FunctionParam<'a> {
|
||||
pub inner: Option<&'a MemoryFunction>,
|
||||
pub memory: ProgramMemory,
|
||||
pub fn_expr: Box<FunctionExpression>,
|
||||
pub fn_expr: crate::ast::types::BoxNode<FunctionExpression>,
|
||||
pub meta: Vec<Metadata>,
|
||||
pub ctx: ExecutorContext,
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ pub mod lint;
|
||||
pub mod lsp;
|
||||
pub mod parser;
|
||||
pub mod settings;
|
||||
#[cfg(test)]
|
||||
mod simulation_tests;
|
||||
pub mod std;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod test_server;
|
||||
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{
|
||||
ast::types::CallExpression,
|
||||
ast::types::{CallExpression, NodeRef},
|
||||
docs::StdLibFn,
|
||||
executor::SourceRange,
|
||||
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."
|
||||
);
|
||||
|
||||
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![];
|
||||
|
||||
if f.name() == "pow" {
|
||||
|
@ -3,14 +3,14 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
pub enum AstUpdated {}
|
||||
|
||||
impl Notification for AstUpdated {
|
||||
type Params = crate::ast::types::Program;
|
||||
type Params = Node<crate::ast::types::Program>;
|
||||
const METHOD: &'static str = "kcl/astUpdated";
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ use tower_lsp::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ast::types::{Expr, VariableKind},
|
||||
ast::types::{Expr, Node, NodeRef, VariableKind},
|
||||
executor::{IdGenerator, SourceRange},
|
||||
lsp::{backend::Backend as _, util::IntoDiagnostic},
|
||||
parser::PIPE_OPERATOR,
|
||||
@ -99,7 +99,7 @@ pub struct Backend {
|
||||
/// Token maps.
|
||||
pub token_map: DashMap<String, Vec<crate::token::Token>>,
|
||||
/// AST maps.
|
||||
pub ast_map: DashMap<String, crate::ast::types::Program>,
|
||||
pub ast_map: DashMap<String, Node<crate::ast::types::Program>>,
|
||||
/// Memory maps.
|
||||
pub memory_map: DashMap<String, crate::executor::ProgramMemory>,
|
||||
/// Current code.
|
||||
@ -571,7 +571,7 @@ impl Backend {
|
||||
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.
|
||||
if !self.can_execute().await {
|
||||
return Ok(());
|
||||
|
@ -7,6 +7,7 @@ use tower_lsp::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ast::types::{Node, Program},
|
||||
executor::ProgramMemory,
|
||||
lsp::test_util::{copilot_lsp_server, kcl_lsp_server},
|
||||
};
|
||||
@ -1070,7 +1071,7 @@ fn myFn = (param1) => {
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
@ -2416,7 +2417,7 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut default_hashed = crate::ast::types::Program::default();
|
||||
let mut default_hashed = Node::<Program>::default();
|
||||
default_hashed.compute_digest();
|
||||
|
||||
// Get the ast.
|
||||
@ -2453,7 +2454,7 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
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.
|
||||
server
|
||||
.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();
|
||||
assert_eq!(ast, crate::ast::types::Program::default());
|
||||
assert_eq!(ast, Node::<Program>::default());
|
||||
server
|
||||
.memory_map
|
||||
.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.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
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.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
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.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
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.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
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.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
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.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
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.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
@ -2806,7 +2807,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
@ -2841,7 +2842,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
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;
|
||||
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();
|
||||
|
||||
// Get the ast.
|
||||
@ -2924,7 +2925,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
// Now it should NOT be the default memory.
|
||||
@ -3064,7 +3065,7 @@ const part001 = startSketchOn('XY')
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let diagnostics = server.diagnostics_map.get("file:///test.kcl").unwrap().clone();
|
||||
@ -3087,7 +3088,7 @@ const part001 = startSketchOn('XY')
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let diagnostics = server.diagnostics_map.get("file:///test.kcl").unwrap().clone();
|
||||
@ -3183,7 +3184,7 @@ const part001 = startSketchOn('XY')
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
@ -3205,7 +3206,7 @@ const part001 = startSketchOn('XY')
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
@ -3248,7 +3249,7 @@ const part001 = startSketchOn('XY')
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
@ -3278,7 +3279,7 @@ const NEW_LINT = 1"#
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
@ -3394,7 +3395,7 @@ const part001 = startSketchOn('XY')
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
|
||||
@ -3489,7 +3490,7 @@ const part001 = startSketchOn('XY')
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
|
||||
@ -3532,7 +3533,7 @@ const part001 = startSketchOn('XY')
|
||||
|
||||
// Get the ast.
|
||||
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.
|
||||
let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
ast::types::Program,
|
||||
ast::types::{Node, Program},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::SourceRange,
|
||||
token::{Token, TokenType},
|
||||
@ -13,7 +13,7 @@ pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
|
||||
pub const PIPE_OPERATOR: &str = "|>";
|
||||
|
||||
/// 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 parser = Parser::new(tokens);
|
||||
parser.ast()
|
||||
@ -33,7 +33,7 @@ impl 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() {
|
||||
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<_>>();
|
||||
@ -48,7 +48,7 @@ impl Parser {
|
||||
// Important, to not call this before the unknown tokens check.
|
||||
if self.tokens.is_empty() {
|
||||
// Empty file should just do nothing.
|
||||
return Ok(Program::default());
|
||||
return Ok(Node::<Program>::default());
|
||||
}
|
||||
|
||||
// Check all the tokens are whitespace or comments.
|
||||
@ -57,7 +57,7 @@ impl Parser {
|
||||
.iter()
|
||||
.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())
|
||||
|
@ -1,18 +1,18 @@
|
||||
use crate::{
|
||||
ast::types::{BinaryExpression, BinaryOperator, BinaryPart},
|
||||
ast::types::{BinaryExpression, BinaryOperator, BinaryPart, Node},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::SourceRange,
|
||||
};
|
||||
|
||||
/// Parses a list of tokens (in infix order, i.e. as the user typed them)
|
||||
/// 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);
|
||||
evaluate(rpn)
|
||||
}
|
||||
|
||||
/// 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 mut operand_stack: Vec<BinaryPart> = Vec::new();
|
||||
let e = KclError::Internal(KclErrorDetails {
|
||||
@ -28,14 +28,19 @@ fn evaluate(rpn: Vec<BinaryExpressionToken>) -> Result<BinaryExpression, KclErro
|
||||
let Some(left) = operand_stack.pop() else {
|
||||
return Err(e);
|
||||
};
|
||||
BinaryPart::BinaryExpression(Box::new(BinaryExpression {
|
||||
start: left.start(),
|
||||
end: right.end(),
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
digest: None,
|
||||
}))
|
||||
let start = left.start();
|
||||
let end = right.end();
|
||||
|
||||
BinaryPart::BinaryExpression(Node::boxed(
|
||||
BinaryExpression {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
digest: None,
|
||||
},
|
||||
start,
|
||||
end,
|
||||
))
|
||||
}
|
||||
BinaryExpressionToken::Operand(o) => o,
|
||||
};
|
||||
@ -125,13 +130,15 @@ mod tests {
|
||||
fn parse_and_evaluate() {
|
||||
/// Make a literal
|
||||
fn lit(n: u8) -> BinaryPart {
|
||||
BinaryPart::Literal(Box::new(Literal {
|
||||
start: 0,
|
||||
end: 0,
|
||||
value: n.into(),
|
||||
raw: n.to_string(),
|
||||
digest: None,
|
||||
}))
|
||||
BinaryPart::Literal(Box::new(Node::new(
|
||||
Literal {
|
||||
value: n.into(),
|
||||
raw: n.to_string(),
|
||||
digest: None,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
)))
|
||||
}
|
||||
let tests: Vec<Vec<BinaryExpressionToken>> = vec![
|
||||
// 3 + 4 × 2 ÷ ( 1 − 5 ) ^ 2 ^ 3
|
||||
@ -142,14 +149,16 @@ mod tests {
|
||||
BinaryOperator::Mul.into(),
|
||||
lit(2).into(),
|
||||
BinaryOperator::Div.into(),
|
||||
BinaryPart::BinaryExpression(Box::new(BinaryExpression {
|
||||
start: 0,
|
||||
end: 0,
|
||||
operator: BinaryOperator::Sub,
|
||||
left: lit(1),
|
||||
right: lit(5),
|
||||
digest: None,
|
||||
}))
|
||||
BinaryPart::BinaryExpression(Node::boxed(
|
||||
BinaryExpression {
|
||||
operator: BinaryOperator::Sub,
|
||||
left: lit(1),
|
||||
right: lit(5),
|
||||
digest: None,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
))
|
||||
.into(),
|
||||
BinaryOperator::Pow.into(),
|
||||
lit(2).into(),
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,23 +4,23 @@ expression: actual
|
||||
---
|
||||
{
|
||||
"type": "BinaryExpression",
|
||||
"start": 0,
|
||||
"end": 5,
|
||||
"operator": "+",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 0,
|
||||
"end": 1,
|
||||
"value": 1,
|
||||
"raw": "1"
|
||||
"raw": "1",
|
||||
"start": 0,
|
||||
"end": 1
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 4,
|
||||
"end": 5,
|
||||
"value": 2,
|
||||
"raw": "2"
|
||||
}
|
||||
"raw": "2",
|
||||
"start": 4,
|
||||
"end": 5
|
||||
},
|
||||
"start": 0,
|
||||
"end": 5
|
||||
}
|
||||
|
@ -4,23 +4,23 @@ expression: actual
|
||||
---
|
||||
{
|
||||
"type": "BinaryExpression",
|
||||
"start": 0,
|
||||
"end": 3,
|
||||
"operator": "+",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 0,
|
||||
"end": 1,
|
||||
"value": 1,
|
||||
"raw": "1"
|
||||
"raw": "1",
|
||||
"start": 0,
|
||||
"end": 1
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 2,
|
||||
"end": 3,
|
||||
"value": 2,
|
||||
"raw": "2"
|
||||
}
|
||||
"raw": "2",
|
||||
"start": 2,
|
||||
"end": 3
|
||||
},
|
||||
"start": 0,
|
||||
"end": 3
|
||||
}
|
||||
|
@ -4,23 +4,23 @@ expression: actual
|
||||
---
|
||||
{
|
||||
"type": "BinaryExpression",
|
||||
"start": 0,
|
||||
"end": 4,
|
||||
"operator": "-",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 0,
|
||||
"end": 1,
|
||||
"value": 1,
|
||||
"raw": "1"
|
||||
"raw": "1",
|
||||
"start": 0,
|
||||
"end": 1
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 3,
|
||||
"end": 4,
|
||||
"value": 2,
|
||||
"raw": "2"
|
||||
}
|
||||
"raw": "2",
|
||||
"start": 3,
|
||||
"end": 4
|
||||
},
|
||||
"start": 0,
|
||||
"end": 4
|
||||
}
|
||||
|
@ -4,38 +4,38 @@ expression: actual
|
||||
---
|
||||
{
|
||||
"type": "BinaryExpression",
|
||||
"start": 0,
|
||||
"end": 9,
|
||||
"operator": "+",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 0,
|
||||
"end": 1,
|
||||
"value": 1,
|
||||
"raw": "1"
|
||||
"raw": "1",
|
||||
"start": 0,
|
||||
"end": 1
|
||||
},
|
||||
"right": {
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression",
|
||||
"start": 4,
|
||||
"end": 9,
|
||||
"operator": "*",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 4,
|
||||
"end": 5,
|
||||
"value": 2,
|
||||
"raw": "2"
|
||||
"raw": "2",
|
||||
"start": 4,
|
||||
"end": 5
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 8,
|
||||
"end": 9,
|
||||
"value": 3,
|
||||
"raw": "3"
|
||||
}
|
||||
}
|
||||
"raw": "3",
|
||||
"start": 8,
|
||||
"end": 9
|
||||
},
|
||||
"start": 4,
|
||||
"end": 9
|
||||
},
|
||||
"start": 0,
|
||||
"end": 9
|
||||
}
|
||||
|
@ -4,38 +4,38 @@ expression: actual
|
||||
---
|
||||
{
|
||||
"type": "BinaryExpression",
|
||||
"start": 0,
|
||||
"end": 11,
|
||||
"operator": "*",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 0,
|
||||
"end": 1,
|
||||
"value": 1,
|
||||
"raw": "1"
|
||||
"raw": "1",
|
||||
"start": 0,
|
||||
"end": 1
|
||||
},
|
||||
"right": {
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression",
|
||||
"start": 6,
|
||||
"end": 11,
|
||||
"operator": "+",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"value": 2,
|
||||
"raw": "2"
|
||||
"raw": "2",
|
||||
"start": 6,
|
||||
"end": 7
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 10,
|
||||
"end": 11,
|
||||
"value": 3,
|
||||
"raw": "3"
|
||||
}
|
||||
}
|
||||
"raw": "3",
|
||||
"start": 10,
|
||||
"end": 11
|
||||
},
|
||||
"start": 6,
|
||||
"end": 11
|
||||
},
|
||||
"start": 0,
|
||||
"end": 11
|
||||
}
|
||||
|
@ -4,53 +4,53 @@ expression: actual
|
||||
---
|
||||
{
|
||||
"type": "BinaryExpression",
|
||||
"start": 0,
|
||||
"end": 17,
|
||||
"operator": "/",
|
||||
"left": {
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression",
|
||||
"start": 0,
|
||||
"end": 11,
|
||||
"operator": "*",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 0,
|
||||
"end": 1,
|
||||
"value": 1,
|
||||
"raw": "1"
|
||||
"raw": "1",
|
||||
"start": 0,
|
||||
"end": 1
|
||||
},
|
||||
"right": {
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression",
|
||||
"start": 6,
|
||||
"end": 11,
|
||||
"operator": "+",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"value": 2,
|
||||
"raw": "2"
|
||||
"raw": "2",
|
||||
"start": 6,
|
||||
"end": 7
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 10,
|
||||
"end": 11,
|
||||
"value": 3,
|
||||
"raw": "3"
|
||||
}
|
||||
}
|
||||
"raw": "3",
|
||||
"start": 10,
|
||||
"end": 11
|
||||
},
|
||||
"start": 6,
|
||||
"end": 11
|
||||
},
|
||||
"start": 0,
|
||||
"end": 11
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 16,
|
||||
"end": 17,
|
||||
"value": 4,
|
||||
"raw": "4"
|
||||
}
|
||||
"raw": "4",
|
||||
"start": 16,
|
||||
"end": 17
|
||||
},
|
||||
"start": 0,
|
||||
"end": 17
|
||||
}
|
||||
|
@ -4,53 +4,53 @@ expression: actual
|
||||
---
|
||||
{
|
||||
"type": "BinaryExpression",
|
||||
"start": 0,
|
||||
"end": 17,
|
||||
"operator": "+",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 0,
|
||||
"end": 1,
|
||||
"value": 1,
|
||||
"raw": "1"
|
||||
"raw": "1",
|
||||
"start": 0,
|
||||
"end": 1
|
||||
},
|
||||
"right": {
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression",
|
||||
"start": 6,
|
||||
"end": 17,
|
||||
"operator": "/",
|
||||
"left": {
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression",
|
||||
"start": 6,
|
||||
"end": 11,
|
||||
"operator": "+",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"value": 2,
|
||||
"raw": "2"
|
||||
"raw": "2",
|
||||
"start": 6,
|
||||
"end": 7
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 10,
|
||||
"end": 11,
|
||||
"value": 3,
|
||||
"raw": "3"
|
||||
}
|
||||
"raw": "3",
|
||||
"start": 10,
|
||||
"end": 11
|
||||
},
|
||||
"start": 6,
|
||||
"end": 11
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 16,
|
||||
"end": 17,
|
||||
"value": 4,
|
||||
"raw": "4"
|
||||
}
|
||||
}
|
||||
"raw": "4",
|
||||
"start": 16,
|
||||
"end": 17
|
||||
},
|
||||
"start": 6,
|
||||
"end": 17
|
||||
},
|
||||
"start": 0,
|
||||
"end": 17
|
||||
}
|
||||
|
@ -4,68 +4,68 @@ expression: actual
|
||||
---
|
||||
{
|
||||
"type": "BinaryExpression",
|
||||
"start": 0,
|
||||
"end": 22,
|
||||
"operator": "*",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 0,
|
||||
"end": 1,
|
||||
"value": 1,
|
||||
"raw": "1"
|
||||
"raw": "1",
|
||||
"start": 0,
|
||||
"end": 1
|
||||
},
|
||||
"right": {
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression",
|
||||
"start": 7,
|
||||
"end": 22,
|
||||
"operator": "+",
|
||||
"left": {
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression",
|
||||
"start": 7,
|
||||
"end": 18,
|
||||
"operator": "/",
|
||||
"left": {
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression",
|
||||
"start": 7,
|
||||
"end": 12,
|
||||
"operator": "+",
|
||||
"left": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 7,
|
||||
"end": 8,
|
||||
"value": 2,
|
||||
"raw": "2"
|
||||
"raw": "2",
|
||||
"start": 7,
|
||||
"end": 8
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 11,
|
||||
"end": 12,
|
||||
"value": 3,
|
||||
"raw": "3"
|
||||
}
|
||||
"raw": "3",
|
||||
"start": 11,
|
||||
"end": 12
|
||||
},
|
||||
"start": 7,
|
||||
"end": 12
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 17,
|
||||
"end": 18,
|
||||
"value": 4,
|
||||
"raw": "4"
|
||||
}
|
||||
"raw": "4",
|
||||
"start": 17,
|
||||
"end": 18
|
||||
},
|
||||
"start": 7,
|
||||
"end": 18
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"start": 21,
|
||||
"end": 22,
|
||||
"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
Reference in New Issue
Block a user