Compare commits

...

14 Commits

Author SHA1 Message Date
5599a75dbd make sure sketch can be exited (#6862) 2025-05-12 11:30:31 +00:00
3a06ae6e34 fix sketch on offset plane animations (#6858)
* fix sketch on offset plane animations

* trigger CI
2025-05-12 07:28:09 -04:00
22857d77e9 Fix bug with undo startSketchOn removing existing sketch (#6834)
* Fix bug with `undo startSketchOn` removing existing sketch

Fixes #6822, I believe. in this case the `variableName` was not being
marked as in-use, so I just logged out the AST and made sure this case
was covered. @Irev-Dev this is probably worth a check from you.

* Add a regression test

It's an E2E test because I'm being lazy, but it should probably be an
XState unit test at some point.

* check what's checked

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2025-05-12 16:55:39 +10:00
1a325d0b29 Fix to use correct units for array range expressions (#6837)
* Fix to use correct units for array range expressions

* Update output
2025-05-12 01:30:33 -04:00
1240b23080 Don't truncate extrude arg docs (#6854)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-12 16:48:30 +12:00
8445080d7a Some improvements to math::sqrt (#6853)
* Treat number as any rather than default

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

* Don't square root negative numbers

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

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-12 16:47:01 +12:00
bbe89f56a7 Fix the KCL any type and array coercion incorrectly nesting (#6816)
* Add sim test for any type

* Fix doc comments to match code

* Add array ascription tests

* Commit new test output

* Fix to not panic when type is undefined

* Fix to not panic on use of the any type

* Update test and generated output

* Fix error message after rebase

* Fix subtype of any

* Fix KCL to use new keyword args

* Fix to not nest MixedArray in HomArray

* Update output

* Remove all creation of MixedArray and use HomArray instead

* Rename MixedArray to Tuple

* Fix to coerce arrays the way tuples are done

* Restructure to appease the type signature extraction

* Fix TS unit test

* Update output after switch to HomArray

* Update docs

* Fix to remove edge case when creating points

* Update docs with broken point signature

* Fix display of tuples to not collide with arrays

* Change push to an array with type mismatch to be an error

* Add sim test for push type error

* Fix acription to more general array element type

* Fix to coerce point types

* Change array push to not error when item type differs

* Fix coercion tests

* Change to only flatten as a last resort and remove flattening tuples

* Contort code to appease doc generation

* Update docs

* Fix coerce axes

* Fix flattening test to test arrays instead of tuples

* Remove special subtype case for singleton coercion
2025-05-12 03:57:31 +00:00
86e8bcfe0b fix the null app explosion (#6851)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-12 03:51:13 +00:00
21ccf129d6 Accept type ascription within binary expressions (#6849)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-12 14:07:57 +12:00
dfc4b7d0c5 Non-fatal error on using && or || (#6845)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-12 13:46:17 +12:00
17b1120a27 Include metadata environment variables with results (#6838)
* Include metadata environment variables with results

* Share updated report after uploading each shard

* Hide raw curl progress
2025-05-11 21:40:11 -04:00
2b509a515b More docs for Plane, Pi, etc. (#6850)
* Document the units of PI

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

* Add links between lang and std references

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

* Change signature of conversion functions

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

* Split foreign imports out of modules docs

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

* More docs for Plane

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

* Update docs/kcl-std/consts/std-math-PI.md

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

* Update rust/kcl-lib/std/math.kcl

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

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-05-12 12:59:45 +12:00
97594b9a9e Proper type-checking where a function requires a tag identifier not just a tag (#6848)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-12 12:57:50 +12:00
c65190a158 Extend warning message to selection step in prompt-to-edit (#6840)
* Extend warning messages to all args in ML commands

* Only when it works actually

* Add warningMessage to selection mixed input
2025-05-11 17:03:58 -04:00
114 changed files with 12240 additions and 4427 deletions

View File

@ -1,13 +1,41 @@
#!/bin/bash
set -euo pipefail
BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}"
COMMIT="${CI_COMMIT_SHA:-${GITHUB_SHA:-}}"
if [ -z "${TAB_API_URL:-}" ] || [ -z "${TAB_API_KEY:-}" ]; then
exit 0
fi
curl --request POST \
project="https://github.com/KittyCAD/modeling-app"
branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}"
commit="${CI_COMMIT_SHA:-${GITHUB_SHA:-}}"
echo "Uploading batch results"
curl --silent --request POST \
--header "X-API-Key: ${TAB_API_KEY}" \
--form "project=https://github.com/KittyCAD/modeling-app" \
--form "branch=${BRANCH}" \
--form "commit=${COMMIT}" \
--form "project=${project}" \
--form "branch=${branch}" \
--form "commit=${commit}" \
--form "tests=@test-results/junit.xml" \
--form "CI_COMMIT_SHA=${CI_COMMIT_SHA:-}" \
--form "CI_PR_NUMBER=${CI_PR_NUMBER:-}" \
--form "GITHUB_BASE_REF=${GITHUB_BASE_REF:-}" \
--form "GITHUB_EVENT_NAME=${GITHUB_EVENT_NAME:-}" \
--form "GITHUB_HEAD_REF=${GITHUB_HEAD_REF:-}" \
--form "GITHUB_REF_NAME=${GITHUB_REF_NAME:-}" \
--form "GITHUB_REF=${GITHUB_REF:-}" \
--form "GITHUB_SHA=${GITHUB_SHA:-}" \
--form "GITHUB_WORKFLOW=${GITHUB_WORKFLOW:-}" \
--form "RUNNER_ARCH=${RUNNER_ARCH:-}" \
${TAB_API_URL}/api/results/bulk
echo
echo "Sharing updated report"
curl --silent --request POST \
--header "Content-Type: application/json" \
--header "X-API-Key: ${TAB_API_KEY}" \
--data "{
\"project\": \"${project}\",
\"branch\": \"${branch}\",
\"commit\": \"${commit}\"
}" \
${TAB_API_URL}/api/share

View File

@ -188,6 +188,8 @@ jobs:
env:
TAB_API_URL: ${{ secrets.TAB_API_URL }}
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
run-wasm-tests:
name: Run wasm tests
strategy:

View File

@ -0,0 +1,99 @@
---
title: "Importing geometry from other CAD systems"
excerpt: "Documentation of the KCL language for the Zoo Design Studio."
layout: manual
---
`import` can also be used to import files from other CAD systems. The format of the statement is the
same as for KCL files. You can only import the whole file, not items from it. E.g.,
```norun
import "tests/inputs/cube.obj"
// Use `cube` just like a KCL object.
```
```kcl
import "tests/inputs/cube.sldprt" as cube
// Use `cube` just like a KCL object.
```
For formats lacking unit data (such as STL, OBJ, or PLY files), the default
unit of measurement is millimeters. Alternatively you may specify the unit
by using an attribute. Likewise, you can also specify a coordinate system. E.g.,
```kcl
@(lengthUnit = ft, coords = opengl)
import "tests/inputs/cube.obj"
```
When importing a GLTF file, the bin file will be imported as well.
Import paths are relative to the current project directory. Imports currently only work when
using the native Design Studio, not in the browser.
### Supported values
File formats: `fbx`, `gltf`/`glb`, `obj`+, `ply`+, `sldprt`, `step`/`stp`, `stl`+. (Those marked with a
'+' support customising the length unit and coordinate system).
Length units: `mm` (the default), `cm`, `m`, `inch`, `ft`, `yd`.
Coordinate systems:
- `zoo` (the default), forward: -Y, up: +Z, handedness: right
- `opengl`, forward: +Z, up: +Y, handedness: right
- `vulkan`, forward: +Z, up: -Y, handedness: left
---
## Performance deepdive for foreignfile imports
Parallelized foreignfile imports now let you overlap file reads, initialization,
and rendering. To maximize throughput, you need to understand the three distinct
stages—reading, initializing (background render start), and invocation (blocking)
—and structure your code to defer blocking operations until the end.
### Foreign import execution stages
1. **Import (Read / Initialization) Stage**
```kcl
import "tests/inputs/cube.step" as cube
```
- Reads the file from disk and makes its API available.
- Starts engine rendering but **does not block** your script.
- This kickstarts the render pipeline while you keep executing other code.
2. **Invocation (Blocking) Stage**
```kcl
import "tests/inputs/cube.step" as cube
cube
|> translate(z=10) // ← blocks here only
```
- Any method call (e.g., `translate`, `scale`, `rotate`) waits for the background render to finish before applying transformations.
### Best practices
#### 1. Defer blocking calls
```kcl
import "tests/inputs/cube.step" as cube // 1) Read / Background render starts
// --- perform other operations and calculations here ---
cube
|> translate(z=10) // 2) Blocks only here
```
#### 2. Split heavy work into separate modules
Place computationally expensive or IOheavy work into its own module so it can render in parallel while `main.kcl` continues.
#### Future improvements
Upcoming releases will autoanalyse dependencies and only block when truly necessary. Until then, explicit deferral will give you the best performance.

View File

@ -5,7 +5,7 @@ layout: manual
---
This is a reference for KCL. If you are learning KCL, you may prefer the [guide]() which explains
things in a more tutorial fashion.
things in a more tutorial fashion. See also our documentation of the [standard library](/docs/kcl-std).
## Topics
@ -14,7 +14,8 @@ things in a more tutorial fashion.
* [Values and types](/docs/kcl-lang/types)
* [Numeric types and units](/docs/kcl-lang/numeric)
* [Functions](/docs/kcl-lang/functions)
* [Projects, modules, and imports](/docs/kcl-lang/modules)
* [Projects and modules](/docs/kcl-lang/modules)
* [Attributes](/docs/kcl-lang/attributes)
* [Importing geometry from other CAD systems](/docs/kcl-lang/foreign-imports)
* [Settings](/docs/kcl-lang/settings)
* [Known Issues](/docs/kcl-lang/known-issues)

View File

@ -1,5 +1,5 @@
---
title: "Modules"
title: "Projects and modules"
excerpt: "Documentation of the KCL language for the Zoo Design Studio."
layout: manual
---
@ -264,102 +264,3 @@ cube
clone(cube)
|> translate(x=20)
```
---
## Importing files from other CAD systems
`import` can also be used to import files from other CAD systems. The format of the statement is the
same as for KCL files. You can only import the whole file, not items from it. E.g.,
```norun
import "tests/inputs/cube.obj"
// Use `cube` just like a KCL object.
```
```kcl
import "tests/inputs/cube.sldprt" as cube
// Use `cube` just like a KCL object.
```
For formats lacking unit data (such as STL, OBJ, or PLY files), the default
unit of measurement is millimeters. Alternatively you may specify the unit
by using an attribute. Likewise, you can also specify a coordinate system. E.g.,
```kcl
@(lengthUnit = ft, coords = opengl)
import "tests/inputs/cube.obj"
```
When importing a GLTF file, the bin file will be imported as well.
Import paths are relative to the current project directory. Imports currently only work when
using the native Design Studio, not in the browser.
### Supported values
File formats: `fbx`, `gltf`/`glb`, `obj`+, `ply`+, `sldprt`, `step`/`stp`, `stl`+. (Those marked with a
'+' support customising the length unit and coordinate system).
Length units: `mm` (the default), `cm`, `m`, `inch`, `ft`, `yd`.
Coordinate systems:
- `zoo` (the default), forward: -Y, up: +Z, handedness: right
- `opengl`, forward: +Z, up: +Y, handedness: right
- `vulkan`, forward: +Z, up: -Y, handedness: left
---
## Performance deepdive for foreignfile imports
Parallelized foreignfile imports now let you overlap file reads, initialization,
and rendering. To maximize throughput, you need to understand the three distinct
stages—reading, initializing (background render start), and invocation (blocking)
—and structure your code to defer blocking operations until the end.
### Foreign import execution stages
1. **Import (Read / Initialization) Stage**
```kcl
import "tests/inputs/cube.step" as cube
```
- Reads the file from disk and makes its API available.
- Starts engine rendering but **does not block** your script.
- This kickstarts the render pipeline while you keep executing other code.
2. **Invocation (Blocking) Stage**
```kcl
import "tests/inputs/cube.step" as cube
cube
|> translate(z=10) // ← blocks here only
```
- Any method call (e.g., `translate`, `scale`, `rotate`) waits for the background render to finish before applying transformations.
### Best practices
#### 1. Defer blocking calls
```kcl
import "tests/inputs/cube.step" as cube // 1) Read / Background render starts
// --- perform other operations and calculations here ---
cube
|> translate(z=10) // 2) Blocks only here
```
#### 2. Split heavy work into separate modules
Place computationally expensive or IOheavy work into its own module so it can render in parallel while `main.kcl` continues.
#### Future improvements
Upcoming releases will autoanalyse dependencies and only block when truly necessary. Until then, explicit deferral will give you the best performance.

View File

@ -28,6 +28,8 @@ Any of the suffixes described above can be used meaning that values with that ty
You can also use `number(Length)`, `number(Angle)`, or `number(Count)`. These types mean a number with any length, angle, or unitless (count) units, respectively (note that `number(_)` and `number(Count)` are equivalent since there is only one kind of unitless-ness).
Using just `number` means accepting any kind of number, even where the units are unknown by KCL.
## Function calls

View File

@ -11,7 +11,13 @@ The value of `pi`, Archimedes constant (π).
PI: number(_?) = 3.14159265358979323846264338327950288_?
```
`PI` is a number and is technically a ratio, so you might expect it to have type `number(_)`.
However, `PI` is nearly always used for converting between different units - usually degrees to or
from radians. Therefore, `PI` is treated a bit specially by KCL and always has unknown units. This
means that if you use `PI`, you will need to give KCL some extra information about the units of numbers.
Usually you should use type ascription on the result of calculations, e.g., `(2 * PI): number(rad)`.
You might prefer to use `units::toRadians` or `units::toDegrees` to convert between angles with
different units.
### Examples

View File

@ -26,7 +26,7 @@ You can provide more than one sketch to extrude, and they will all be extruded i
|----------|------|-------------|----------|
| `sketches` | [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) | Which sketch or sketches should be extruded | Yes |
| `length` | [`number`](/docs/kcl-std/types/std-types-number) | How far to extrude the given sketches | Yes |
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the | No |
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No |
| `bidirectionalLength` | [`number`](/docs/kcl-std/types/std-types-number) | If specified, will also extrude in the opposite direction to 'distance' to the specified distance. If 'symmetric' is true, this value is ignored. | No |
| `tagStart` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the start of the extrusion, i.e. the original sketch | No |
| `tagEnd` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the end of the extrusion, i.e. the new face created by extruding the original sketch | No |

View File

@ -8,7 +8,7 @@ layout: manual
Convert a number to centimeters from its current units.
```kcl
units::toCentimeters(@num: number(cm)): number(cm)
units::toCentimeters(@num: number(Length)): number(cm)
```
@ -17,7 +17,7 @@ units::toCentimeters(@num: number(cm)): number(cm)
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | [`number(cm)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Converts a number to degrees from its current units.
```kcl
units::toDegrees(@num: number(deg)): number(deg)
units::toDegrees(@num: number(Angle)): number(deg)
```
@ -17,7 +17,7 @@ units::toDegrees(@num: number(deg)): number(deg)
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | [`number(deg)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
| `num` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Convert a number to feet from its current units.
```kcl
units::toFeet(@num: number(ft)): number(ft)
units::toFeet(@num: number(Length)): number(ft)
```
@ -17,7 +17,7 @@ units::toFeet(@num: number(ft)): number(ft)
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | [`number(ft)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Convert a number to inches from its current units.
```kcl
units::toInches(@num: number(in)): number(in)
units::toInches(@num: number(Length)): number(in)
```
@ -17,7 +17,7 @@ units::toInches(@num: number(in)): number(in)
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | [`number(in)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Convert a number to meters from its current units.
```kcl
units::toMeters(@num: number(m)): number(m)
units::toMeters(@num: number(Length)): number(m)
```
@ -17,7 +17,7 @@ units::toMeters(@num: number(m)): number(m)
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | [`number(m)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Convert a number to millimeters from its current units.
```kcl
units::toMillimeters(@num: number(mm)): number(mm)
units::toMillimeters(@num: number(Length)): number(mm)
```
@ -17,7 +17,7 @@ units::toMillimeters(@num: number(mm)): number(mm)
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | [`number(mm)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Converts a number to radians from its current units.
```kcl
units::toRadians(@num: number(rad)): number(rad)
units::toRadians(@num: number(Angle)): number(rad)
```
@ -17,7 +17,7 @@ units::toRadians(@num: number(rad)): number(rad)
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | [`number(rad)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
| `num` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Converts a number to yards from its current units.
```kcl
units::toYards(@num: number(yd)): number(yd)
units::toYards(@num: number(Length)): number(yd)
```
@ -17,7 +17,7 @@ units::toYards(@num: number(yd)): number(yd)
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | [`number(yd)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns

View File

@ -11,6 +11,8 @@ Contains frequently used constants, functions for interacting with the KittyCAD
The standard library is organised into modules (listed below), but most things are always available in KCL programs.
You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL guide]().
## Modules
* [`array`](/docs/kcl-std/modules/std-array)

View File

@ -8,7 +8,7 @@ layout: manual
Remove the last element from an array.
```kcl
pop(@array: [any]): any
pop(@array: [any]): [any]
```
Returns a new array with the last element removed.
@ -21,7 +21,7 @@ Returns a new array with the last element removed.
### Returns
[`any`](/docs/kcl-std/types/std-types-any) - Any KCL value.
[`[any]`](/docs/kcl-std/types/std-types-any)
### Examples

View File

@ -11,7 +11,7 @@ Append an element to the end of an array.
push(
@array: [any],
item: any,
): any
): [any]
```
Returns a new array with the element appended.
@ -25,7 +25,7 @@ Returns a new array with the element appended.
### Returns
[`any`](/docs/kcl-std/types/std-types-any) - Any KCL value.
[`[any]`](/docs/kcl-std/types/std-types-any)
### Examples

File diff suppressed because it is too large Load Diff

View File

@ -7,8 +7,25 @@ layout: manual
An abstract plane.
A plane has a position and orientation in space defined by its origin and axes. A plane can be used
to sketch on.
A plane has a position and orientation in space defined by its origin and axes. A plane is abstract
in the sense that it is not part of the objects being drawn. A plane can be used to sketch on.
A plane can be created in several ways:
- you can use one of the default planes, e.g., `XY`.
- you can use `offsetPlane` to create a new plane offset from an existing one, e.g., `offsetPlane(XY, offset = 150)`.
- you can use negation to create a plane from an existing one which is identical but has an opposite normal
e.g., `-XY`.
- you can define an entirely custom plane, e.g.,
```js
myXY = {
origin = { x = 0, y = 0, z = 0 },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
}
```
Any object with appropriate `origin`, `xAxis`, and `yAxis` fields can be used as a plane.

View File

@ -841,6 +841,40 @@ washer = extrude(washerSketch, length = thicknessMax)`
await editor.expectEditor.toContain('@settings(defaultLengthUnit = yd)')
})
})
test('Exiting existing sketch without editing should not delete it', async ({
page,
editor,
homePage,
context,
toolbar,
scene,
cmdBar,
}) => {
await context.folderSetupFn(async (dir) => {
const testDir = path.join(dir, 'test')
await fsp.mkdir(testDir, { recursive: true })
await fsp.writeFile(
path.join(testDir, 'main.kcl'),
`s1 = startSketchOn(XY)
|> startProfile(at = [0, 25])
|> xLine(endAbsolute = -15 + 1.5)
s2 = startSketchOn(XY)
|> startProfile(at = [25, 0])
|> yLine(endAbsolute = -15 + 1.5)`,
'utf-8'
)
})
await homePage.openProject('test')
await scene.settled(cmdBar)
await toolbar.waitForFeatureTreeToBeBuilt()
await toolbar.editSketch(1)
await page.waitForTimeout(1000) // Just hang out for a second
await toolbar.exitSketch()
await editor.expectEditor.toContain('s2 = startSketchOn(XY)')
})
})
async function clickExportButton(page: Page) {

View File

@ -380,6 +380,21 @@ fn generate_function_from_kcl(
.enumerate()
.filter_map(|(index, example)| generate_example(index, &example.0, &example.1, &example_name))
.collect();
let args = function.args.iter().map(|arg| {
let docs = arg.docs.clone();
if let Some(docs) = &docs {
// We deliberately truncate to one line in the template so that if we are using the docs
// from the type, then we only take the summary. However, if there's a newline in the
// arg docs, then they would get truncated unintentionally.
assert!(!docs.contains('\n'), "Arg docs will get truncated");
};
json!({
"name": arg.name,
"type_": arg.ty,
"description": docs.or_else(|| arg.ty.as_ref().and_then(|t| super::docs_for_type(t, kcl_std))).unwrap_or_default(),
"required": arg.kind.required(),
})
}).collect::<Vec<_>>();
let data = json!({
"name": function.preferred_name,
@ -389,14 +404,7 @@ fn generate_function_from_kcl(
"deprecated": function.properties.deprecated,
"fn_signature": function.preferred_name.clone() + &function.fn_signature(),
"examples": examples,
"args": function.args.iter().map(|arg| {
json!({
"name": arg.name,
"type_": arg.ty,
"description": arg.docs.clone().or_else(|| arg.ty.as_ref().and_then(|t| super::docs_for_type(t, kcl_std))).unwrap_or_default(),
"required": arg.kind.required(),
})
}).collect::<Vec<_>>(),
"args": args,
"return_value": function.return_type.as_ref().map(|t| {
json!({
"type_": t,

View File

@ -167,6 +167,7 @@ impl StdLibFnArg {
pub fn description(&self, kcl_std: Option<&ModData>) -> Option<String> {
// Check if we explicitly gave this stdlib arg a description.
if !self.description.is_empty() {
assert!(!self.description.contains('\n'), "Arg docs will get truncated");
return Some(self.description.clone());
}

View File

@ -278,7 +278,7 @@ impl From<&KclValue> for OpKclValue {
ty: ty.clone(),
},
KclValue::String { value, .. } => Self::String { value: value.clone() },
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
let value = value.iter().map(Self::from).collect();
Self::Array { value }
}

View File

@ -16,12 +16,13 @@ use crate::{
BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, PlaneType, TagEngineInfo,
TagIdentifier,
},
fmt,
modules::{ModuleId, ModulePath, ModuleRepr},
parsing::ast::types::{
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector, ItemVisibility,
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, Node, NodeRef, ObjectExpression,
PipeExpression, Program, TagDeclarator, Type, UnaryExpression, UnaryOperator,
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
BinaryPart, BodyItem, CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector,
ItemVisibility, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, Node, NodeRef,
ObjectExpression, PipeExpression, Program, TagDeclarator, Type, UnaryExpression, UnaryOperator,
},
source_range::SourceRange,
std::{
@ -707,17 +708,25 @@ impl ExecutorContext {
// TODO this lets us use the label as a variable name, but not as a tag in most cases
result
}
Expr::AscribedExpression(expr) => {
let result = self
.execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
.await?;
apply_ascription(&result, &expr.ty, exec_state, expr.into())?
}
Expr::AscribedExpression(expr) => expr.get_result(exec_state, self).await?,
};
Ok(item)
}
}
impl Node<AscribedExpression> {
#[async_recursion]
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let metadata = Metadata {
source_range: SourceRange::from(self),
};
let result = ctx
.execute_expr(&self.expr, exec_state, &metadata, &[], StatementKind::Expression)
.await?;
apply_ascription(&result, &self.ty, exec_state, self.into())
}
}
fn apply_ascription(
value: &KclValue,
ty: &Node<Type>,
@ -758,6 +767,7 @@ impl BinaryPart {
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
}
}
}
@ -867,11 +877,7 @@ impl Node<MemberExpression> {
source_ranges: vec![self.clone().into()],
}))
}
(
KclValue::MixedArray { value: arr, .. } | KclValue::HomArray { value: arr, .. },
Property::UInt(index),
_,
) => {
(KclValue::HomArray { value: arr, .. }, Property::UInt(index), _) => {
let value_of_arr = arr.get(index);
if let Some(value) = value_of_arr {
Ok(value.to_owned())
@ -882,7 +888,7 @@ impl Node<MemberExpression> {
}))
}
}
(KclValue::MixedArray { .. } | KclValue::HomArray { .. }, p, _) => {
(KclValue::HomArray { .. }, p, _) => {
let t = p.type_name();
let article = article_for(t);
Err(KclError::Semantic(KclErrorDetails {
@ -1170,7 +1176,7 @@ impl Node<UnaryExpression> {
};
let direction = match direction {
KclValue::MixedArray { value: values, meta } => {
KclValue::Tuple { value: values, meta } => {
let values = values
.iter()
.map(|v| match v {
@ -1183,7 +1189,7 @@ impl Node<UnaryExpression> {
})
.collect::<Result<Vec<_>, _>>()?;
KclValue::MixedArray {
KclValue::Tuple {
value: values,
meta: meta.clone(),
}
@ -1551,7 +1557,7 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
}
}
}
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
for v in value {
update_memory_for_tags_of_geometry(v, exec_state)?;
}
@ -1595,9 +1601,9 @@ impl Node<ArrayExpression> {
results.push(value);
}
Ok(KclValue::MixedArray {
Ok(KclValue::HomArray {
value: results,
meta: vec![self.into()],
ty: RuntimeType::Primitive(PrimitiveType::Any),
})
}
}
@ -1606,7 +1612,7 @@ 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 start = ctx
let start_val = ctx
.execute_expr(
&self.start_element,
exec_state,
@ -1615,19 +1621,30 @@ impl Node<ArrayRangeExpression> {
StatementKind::Expression,
)
.await?;
let start = start.as_int().ok_or(KclError::Semantic(KclErrorDetails {
let (start, start_ty) = start_val.as_int_with_ty().ok_or(KclError::Semantic(KclErrorDetails {
source_ranges: vec![self.into()],
message: format!("Expected int but found {}", start.human_friendly_type()),
message: format!("Expected int but found {}", start_val.human_friendly_type()),
}))?;
let metadata = Metadata::from(&self.end_element);
let end = ctx
let end_val = ctx
.execute_expr(&self.end_element, exec_state, &metadata, &[], StatementKind::Expression)
.await?;
let end = end.as_int().ok_or(KclError::Semantic(KclErrorDetails {
let (end, end_ty) = end_val.as_int_with_ty().ok_or(KclError::Semantic(KclErrorDetails {
source_ranges: vec![self.into()],
message: format!("Expected int but found {}", end.human_friendly_type()),
message: format!("Expected int but found {}", end_val.human_friendly_type()),
}))?;
if start_ty != end_ty {
let start = start_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: start_ty });
let start = fmt::human_display_number(start.n, start.ty);
let end = end_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: end_ty });
let end = fmt::human_display_number(end.n, end.ty);
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![self.into()],
message: format!("Range start and end must be of the same type, but found {start} and {end}"),
}));
}
if end < start {
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![self.into()],
@ -1644,16 +1661,17 @@ impl Node<ArrayRangeExpression> {
let meta = vec![Metadata {
source_range: self.into(),
}];
Ok(KclValue::MixedArray {
Ok(KclValue::HomArray {
value: range
.into_iter()
.map(|num| KclValue::Number {
value: num as f64,
ty: NumericType::count(),
ty: start_ty.clone(),
meta: meta.clone(),
})
.collect(),
meta,
ty: RuntimeType::Primitive(PrimitiveType::Number(start_ty)),
})
}
}
@ -1868,7 +1886,7 @@ fn type_check_params_kw(
arg.value = arg
.value
.coerce(
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).map_err(|e| KclError::Semantic(e.into()))?,
exec_state,
)
.map_err(|e| {
@ -1946,7 +1964,8 @@ fn type_check_params_kw(
arg.value = arg
.value
.coerce(
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range)
.map_err(|e| KclError::Semantic(e.into()))?,
exec_state,
)
.map_err(|_| {
@ -2125,7 +2144,8 @@ impl FunctionSource {
arg.value = arg
.value
.coerce(
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range)
.map_err(|e| KclError::Semantic(e.into()))?,
exec_state,
)
.map_err(|_| {
@ -2235,9 +2255,10 @@ mod test {
use super::*;
use crate::{
exec::UnitType,
execution::{memory::Stack, parse_execute, ContextType},
parsing::ast::types::{DefaultParamVal, Identifier, Parameter},
ExecutorSettings,
ExecutorSettings, UnitLen,
};
#[tokio::test(flavor = "multi_thread")]
@ -2391,6 +2412,7 @@ p = {
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}: Plane
arr1 = [42]: [number(cm)]
"#;
let result = parse_execute(program).await.unwrap();
@ -2401,6 +2423,24 @@ p = {
.unwrap(),
KclValue::Plane { .. }
));
let arr1 = mem
.memory
.get_from("arr1", result.mem_env, SourceRange::default(), 0)
.unwrap();
if let KclValue::HomArray { value, ty } = arr1 {
assert_eq!(value.len(), 1, "Expected Vec with specific length: found {:?}", value);
assert_eq!(*ty, RuntimeType::known_length(UnitLen::Cm));
// Compare, ignoring meta.
if let KclValue::Number { value, ty, .. } = &value[0] {
// Converted from mm to cm.
assert_eq!(*value, 4.2);
assert_eq!(*ty, NumericType::Known(UnitType::Length(UnitLen::Cm)));
} else {
panic!("Expected a number; found {:?}", value[0]);
}
} else {
panic!("Expected HomArray; found {arr1:?}");
}
let program = r#"
a = 42: string
@ -2419,6 +2459,28 @@ a = 42: Plane
.unwrap_err()
.to_string()
.contains("could not coerce number value to type Plane"));
let program = r#"
arr = [0]: [string]
"#;
let result = parse_execute(program).await;
let err = result.unwrap_err();
assert!(
err.to_string()
.contains("could not coerce array (list) value to type [string]"),
"Expected error but found {err:?}"
);
let program = r#"
mixedArr = [0, "a"]: [number(mm)]
"#;
let result = parse_execute(program).await;
let err = result.unwrap_err();
assert!(
err.to_string()
.contains("could not coerce array (list) value to type [number(mm)]"),
"Expected error but found {err:?}"
);
}
#[tokio::test(flavor = "multi_thread")]
@ -2638,4 +2700,19 @@ sketch001 = startSketchOn(XY)
parse_execute(ast).await.unwrap();
}
#[tokio::test(flavor = "multi_thread")]
async fn ascription_in_binop() {
let ast = r#"foo = tan(0): number(rad) - 4deg"#;
parse_execute(ast).await.unwrap();
}
#[tokio::test(flavor = "multi_thread")]
async fn neg_sqrt() {
let ast = r#"bad = sqrt(-2)"#;
let e = parse_execute(ast).await.unwrap_err();
// Make sure we get a useful error message and not an engine error.
assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
}
}

View File

@ -1239,6 +1239,20 @@ impl Path {
[TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)]
}
/// The path segment start point and its type.
pub fn start_point_components(&self) -> ([f64; 2], NumericType) {
let p = &self.get_base().from;
let ty: NumericType = self.get_base().units.into();
(*p, ty)
}
/// The path segment end point and its type.
pub fn end_point_components(&self) -> ([f64; 2], NumericType) {
let p = &self.get_base().to;
let ty: NumericType = self.get_base().units.into();
(*p, ty)
}
/// Length of this path segment, in cartesian plane.
pub fn length(&self) -> TyF64 {
let n = match self {

View File

@ -48,7 +48,7 @@ pub enum KclValue {
#[serde(skip)]
meta: Vec<Metadata>,
},
MixedArray {
Tuple {
value: Vec<KclValue>,
#[serde(skip)]
meta: Vec<Metadata>,
@ -197,7 +197,7 @@ impl From<KclValue> for Vec<SourceRange> {
KclValue::Bool { meta, .. } => to_vec_sr(&meta),
KclValue::Number { meta, .. } => to_vec_sr(&meta),
KclValue::String { meta, .. } => to_vec_sr(&meta),
KclValue::MixedArray { meta, .. } => to_vec_sr(&meta),
KclValue::Tuple { meta, .. } => to_vec_sr(&meta),
KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
KclValue::Object { meta, .. } => to_vec_sr(&meta),
KclValue::Module { meta, .. } => to_vec_sr(&meta),
@ -228,7 +228,7 @@ impl From<&KclValue> for Vec<SourceRange> {
KclValue::Number { meta, .. } => to_vec_sr(meta),
KclValue::String { meta, .. } => to_vec_sr(meta),
KclValue::Uuid { meta, .. } => to_vec_sr(meta),
KclValue::MixedArray { meta, .. } => to_vec_sr(meta),
KclValue::Tuple { meta, .. } => to_vec_sr(meta),
KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
KclValue::Object { meta, .. } => to_vec_sr(meta),
KclValue::Module { meta, .. } => to_vec_sr(meta),
@ -252,7 +252,7 @@ impl KclValue {
KclValue::Bool { value: _, meta } => meta.clone(),
KclValue::Number { meta, .. } => meta.clone(),
KclValue::String { value: _, meta } => meta.clone(),
KclValue::MixedArray { value: _, meta } => meta.clone(),
KclValue::Tuple { value: _, meta } => meta.clone(),
KclValue::HomArray { value, .. } => value.iter().flat_map(|v| v.metadata()).collect(),
KclValue::Object { value: _, meta } => meta.clone(),
KclValue::TagIdentifier(x) => x.meta.clone(),
@ -307,7 +307,7 @@ impl KclValue {
} => "number(Angle)",
KclValue::Number { .. } => "number",
KclValue::String { .. } => "string (text)",
KclValue::MixedArray { .. } => "mixed array (list)",
KclValue::Tuple { .. } => "tuple (list)",
KclValue::HomArray { .. } => "array (list)",
KclValue::Object { .. } => "object",
KclValue::Module { .. } => "module",
@ -373,7 +373,7 @@ impl KclValue {
/// Put the point into a KCL value.
pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
Self::MixedArray {
Self::Tuple {
value: vec![
Self::Number {
value: p[0],
@ -404,6 +404,13 @@ impl KclValue {
}
}
pub fn as_int_with_ty(&self) -> Option<(i64, NumericType)> {
match self {
KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, ty.clone())),
_ => None,
}
}
pub fn as_object(&self) -> Option<&KclObjectFields> {
if let KclValue::Object { value, meta: _ } = &self {
Some(value)
@ -430,7 +437,7 @@ impl KclValue {
pub fn as_array(&self) -> Option<&[KclValue]> {
match self {
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => Some(value),
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => Some(value),
_ => None,
}
}
@ -602,7 +609,7 @@ impl KclValue {
KclValue::TagDeclarator(tag) => Some(format!("${}", tag.name)),
KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
// TODO better Array and Object stringification
KclValue::MixedArray { .. } => Some("[...]".to_owned()),
KclValue::Tuple { .. } => Some("[...]".to_owned()),
KclValue::HomArray { .. } => Some("[...]".to_owned()),
KclValue::Object { .. } => Some("{ ... }".to_owned()),
KclValue::Module { .. }

View File

@ -1932,7 +1932,7 @@ a = []
notArray = !a";
assert_eq!(
parse_execute(code5).await.unwrap_err().message(),
"Cannot apply unary operator ! to non-boolean value: mixed array (list)",
"Cannot apply unary operator ! to non-boolean value: array (list)",
);
let code6 = "

View File

@ -28,6 +28,10 @@ pub enum RuntimeType {
}
impl RuntimeType {
pub fn any() -> Self {
RuntimeType::Primitive(PrimitiveType::Any)
}
pub fn edge() -> Self {
RuntimeType::Primitive(PrimitiveType::Edge)
}
@ -72,6 +76,10 @@ impl RuntimeType {
RuntimeType::Primitive(PrimitiveType::Tag)
}
pub fn tag_identifier() -> Self {
RuntimeType::Primitive(PrimitiveType::TagId)
}
pub fn bool() -> Self {
RuntimeType::Primitive(PrimitiveType::Boolean)
}
@ -162,11 +170,16 @@ impl RuntimeType {
source_range: SourceRange,
) -> Result<Self, CompilationError> {
Ok(match value {
AstPrimitiveType::Any => RuntimeType::Primitive(PrimitiveType::Any),
AstPrimitiveType::String => RuntimeType::Primitive(PrimitiveType::String),
AstPrimitiveType::Boolean => RuntimeType::Primitive(PrimitiveType::Boolean),
AstPrimitiveType::Number(suffix) => RuntimeType::Primitive(PrimitiveType::Number(
NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
)),
AstPrimitiveType::Number(suffix) => {
let ty = match suffix {
NumericSuffix::None => NumericType::Any,
_ => NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
};
RuntimeType::Primitive(PrimitiveType::Number(ty))
}
AstPrimitiveType::Named(name) => Self::from_alias(&name.name, exec_state, source_range)?,
AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
})
@ -203,7 +216,7 @@ impl RuntimeType {
.collect::<Vec<_>>()
.join(" or "),
RuntimeType::Tuple(tys) => format!(
"an array with values of types ({})",
"a tuple with values of types ({})",
tys.iter().map(Self::human_friendly_type).collect::<Vec<_>>().join(", ")
),
RuntimeType::Object(_) => format!("an object with fields {}", self),
@ -215,6 +228,7 @@ impl RuntimeType {
use RuntimeType::*;
match (self, sup) {
(_, Primitive(PrimitiveType::Any)) => true,
(Primitive(t1), Primitive(t2)) => t1.subtype(t2),
(Array(t1, l1), Array(t2, l2)) => t1.subtype(t2) && l1.subtype(*l2),
(Tuple(t1), Tuple(t2)) => t1.len() == t2.len() && t1.iter().zip(t2).all(|(t1, t2)| t1.subtype(t2)),
@ -265,7 +279,7 @@ impl RuntimeType {
.map(|t| t.display_multiple())
.collect::<Vec<_>>()
.join(" or "),
RuntimeType::Tuple(_) => "arrays".to_owned(),
RuntimeType::Tuple(_) => "tuples".to_owned(),
RuntimeType::Object(_) => format!("objects with fields {self}"),
}
}
@ -282,7 +296,7 @@ impl fmt::Display for RuntimeType {
},
RuntimeType::Tuple(ts) => write!(
f,
"[{}]",
"({})",
ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
),
RuntimeType::Union(ts) => write!(
@ -333,10 +347,13 @@ impl ArrayLen {
#[derive(Debug, Clone, PartialEq)]
pub enum PrimitiveType {
Any,
Number(NumericType),
String,
Boolean,
Tag,
// Annoyingly some functions only want a TagIdentifier, not any kind of tag.
TagId,
Sketch,
Solid,
Plane,
@ -351,6 +368,7 @@ pub enum PrimitiveType {
impl PrimitiveType {
fn display_multiple(&self) -> String {
match self {
PrimitiveType::Any => "any values".to_owned(),
PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
PrimitiveType::Number(_) => "numbers".to_owned(),
PrimitiveType::String => "strings".to_owned(),
@ -365,12 +383,15 @@ impl PrimitiveType {
PrimitiveType::Axis3d => "3d axes".to_owned(),
PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
PrimitiveType::Tag => "tags".to_owned(),
PrimitiveType::TagId => "tag identifiers".to_owned(),
}
}
fn subtype(&self, other: &PrimitiveType) -> bool {
match (self, other) {
(_, PrimitiveType::Any) => true,
(PrimitiveType::Number(n1), PrimitiveType::Number(n2)) => n1.subtype(n2),
(PrimitiveType::TagId, PrimitiveType::Tag) => true,
(t1, t2) => t1 == t2,
}
}
@ -379,6 +400,7 @@ impl PrimitiveType {
impl fmt::Display for PrimitiveType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PrimitiveType::Any => write!(f, "any"),
PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
PrimitiveType::Number(NumericType::Unknown) => write!(f, "number(unknown units)"),
PrimitiveType::Number(NumericType::Default { .. }) => write!(f, "number(default units)"),
@ -386,6 +408,7 @@ impl fmt::Display for PrimitiveType {
PrimitiveType::String => write!(f, "string"),
PrimitiveType::Boolean => write!(f, "bool"),
PrimitiveType::Tag => write!(f, "tag"),
PrimitiveType::TagId => write!(f, "tag identifier"),
PrimitiveType::Sketch => write!(f, "Sketch"),
PrimitiveType::Solid => write!(f, "Solid"),
PrimitiveType::Plane => write!(f, "Plane"),
@ -663,6 +686,7 @@ impl NumericType {
// We don't have enough information to coerce.
(Unknown, _) => Err(CoercionError::from(val).with_explicit(self.example_ty().unwrap_or("mm".to_owned()))),
(_, Unknown) => Err(val.into()),
(Any, _) => Ok(KclValue::Number {
value: *value,
ty: self.clone(),
@ -994,9 +1018,9 @@ impl KclValue {
self_ty.subtype(ty)
}
/// Coerce `self` to a new value which has `ty` as it's closest supertype.
/// Coerce `self` to a new value which has `ty` as its closest supertype.
///
/// If the result is Some, then:
/// If the result is Ok, then:
/// - result.principal_type().unwrap().subtype(ty)
///
/// If self.principal_type() == ty then result == self
@ -1016,10 +1040,11 @@ impl KclValue {
exec_state: &mut ExecState,
) -> Result<KclValue, CoercionError> {
let value = match self {
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } if value.len() == 1 => &value[0],
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } if value.len() == 1 => &value[0],
_ => self,
};
match ty {
PrimitiveType::Any => Ok(value.clone()),
PrimitiveType::Number(ty) => ty.coerce(value),
PrimitiveType::String => match value {
KclValue::String { .. } => Ok(value.clone()),
@ -1159,6 +1184,10 @@ impl KclValue {
KclValue::ImportedGeometry { .. } => Ok(value.clone()),
_ => Err(self.into()),
},
PrimitiveType::TagId => match value {
KclValue::TagIdentifier { .. } => Ok(value.clone()),
_ => Err(self.into()),
},
PrimitiveType::Tag => match value {
KclValue::TagDeclarator { .. } | KclValue::TagIdentifier { .. } | KclValue::Uuid { .. } => {
Ok(value.clone())
@ -1178,41 +1207,49 @@ impl KclValue {
exec_state: &mut ExecState,
allow_shrink: bool,
) -> Result<KclValue, CoercionError> {
if len.satisfied(1, false).is_some() && self.has_type(ty) {
return Ok(KclValue::HomArray {
value: vec![self.clone()],
ty: ty.clone(),
});
}
match self {
KclValue::HomArray { value, ty: aty } => {
KclValue::HomArray { value, ty: aty, .. } => {
let satisfied_len = len.satisfied(value.len(), allow_shrink);
if aty.subtype(ty) {
len.satisfied(value.len(), allow_shrink)
// If the element type is a subtype of the target type and
// the length constraint is satisfied, we can just return
// the values unchanged, only adjusting the length. The new
// array element type should preserve its type because the
// target type oftentimes includes an unknown type as a way
// to say that the caller doesn't care.
return satisfied_len
.map(|len| KclValue::HomArray {
value: value[..len].to_vec(),
ty: aty.clone(),
})
.ok_or(self.into())
} else {
Err(self.into())
.ok_or(self.into());
}
}
KclValue::MixedArray { value, .. } => {
// Check if we have a nested homogeneous array that we can flatten.
// Ignore the array type, and coerce the elements of the array.
if let Some(satisfied_len) = satisfied_len {
let value_result = value
.iter()
.take(satisfied_len)
.map(|v| v.coerce(ty, exec_state))
.collect::<Result<Vec<_>, _>>();
if let Ok(value) = value_result {
// We were able to coerce all the elements.
return Ok(KclValue::HomArray { value, ty: ty.clone() });
}
}
// As a last resort, try to flatten the array.
let mut values = Vec::new();
for item in value {
if let KclValue::HomArray {
ty: inner_ty,
value: inner_value,
} = item
{
if inner_ty.subtype(ty) {
values.extend(inner_value.iter().cloned());
} else {
values.push(item.clone());
if let KclValue::HomArray { value: inner_value, .. } = item {
// Flatten elements.
for item in inner_value {
values.push(item.coerce(ty, exec_state)?);
}
} else {
values.push(item.clone());
values.push(item.coerce(ty, exec_state)?);
}
}
@ -1220,9 +1257,22 @@ impl KclValue {
.satisfied(values.len(), allow_shrink)
.ok_or(CoercionError::from(self))?;
let value = values[..len]
assert!(len <= values.len());
values.truncate(len);
Ok(KclValue::HomArray {
value: values,
ty: ty.clone(),
})
}
KclValue::Tuple { value, .. } => {
let len = len
.satisfied(value.len(), allow_shrink)
.ok_or(CoercionError::from(self))?;
let value = value
.iter()
.map(|v| v.coerce(ty, exec_state))
.map(|item| item.coerce(ty, exec_state))
.take(len)
.collect::<Result<Vec<_>, _>>()?;
Ok(KclValue::HomArray { value, ty: ty.clone() })
@ -1231,28 +1281,32 @@ impl KclValue {
value: Vec::new(),
ty: ty.clone(),
}),
_ if len.satisfied(1, false).is_some() => Ok(KclValue::HomArray {
value: vec![self.coerce(ty, exec_state)?],
ty: ty.clone(),
}),
_ => Err(self.into()),
}
}
fn coerce_to_tuple_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
match self {
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } if value.len() == tys.len() => {
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } if value.len() == tys.len() => {
let mut result = Vec::new();
for (i, t) in tys.iter().enumerate() {
result.push(value[i].coerce(t, exec_state)?);
}
Ok(KclValue::MixedArray {
Ok(KclValue::Tuple {
value: result,
meta: Vec::new(),
})
}
KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::MixedArray {
KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::Tuple {
value: Vec::new(),
meta: meta.clone(),
}),
value if tys.len() == 1 && value.has_type(&tys[0]) => Ok(KclValue::MixedArray {
value if tys.len() == 1 && value.has_type(&tys[0]) => Ok(KclValue::Tuple {
value: vec![value.clone()],
meta: Vec::new(),
}),
@ -1312,7 +1366,7 @@ impl KclValue {
KclValue::Face { .. } => Some(RuntimeType::Primitive(PrimitiveType::Face)),
KclValue::Helix { .. } => Some(RuntimeType::Primitive(PrimitiveType::Helix)),
KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
KclValue::MixedArray { value, .. } => Some(RuntimeType::Tuple(
KclValue::Tuple { value, .. } => Some(RuntimeType::Tuple(
value.iter().map(|v| v.principal_type()).collect::<Option<Vec<_>>>()?,
)),
KclValue::HomArray { ty, value, .. } => {
@ -1348,7 +1402,7 @@ mod test {
value: "hello".to_owned(),
meta: Vec::new(),
},
KclValue::MixedArray {
KclValue::Tuple {
value: Vec::new(),
meta: Vec::new(),
},
@ -1417,45 +1471,67 @@ mod test {
let aty1 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(1));
let aty0 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::NonEmpty);
assert_coerce_results(
v,
&aty,
&KclValue::HomArray {
value: vec![v.clone()],
ty: ty.clone(),
},
&mut exec_state,
);
assert_coerce_results(
v,
&aty1,
&KclValue::HomArray {
value: vec![v.clone()],
ty: ty.clone(),
},
&mut exec_state,
);
assert_coerce_results(
v,
&aty0,
&KclValue::HomArray {
value: vec![v.clone()],
ty: ty.clone(),
},
&mut exec_state,
);
match v {
KclValue::Tuple { .. } | KclValue::HomArray { .. } => {
// These will not get wrapped if possible.
assert_coerce_results(
v,
&aty,
&KclValue::HomArray {
value: vec![],
ty: ty.clone(),
},
&mut exec_state,
);
// Coercing an empty tuple or array to an array of length 1
// should fail.
v.coerce(&aty1, &mut exec_state).unwrap_err();
// Coercing an empty tuple or array to an array that's
// non-empty should fail.
v.coerce(&aty0, &mut exec_state).unwrap_err();
}
_ => {
assert_coerce_results(
v,
&aty,
&KclValue::HomArray {
value: vec![v.clone()],
ty: ty.clone(),
},
&mut exec_state,
);
assert_coerce_results(
v,
&aty1,
&KclValue::HomArray {
value: vec![v.clone()],
ty: ty.clone(),
},
&mut exec_state,
);
assert_coerce_results(
v,
&aty0,
&KclValue::HomArray {
value: vec![v.clone()],
ty: ty.clone(),
},
&mut exec_state,
);
// Tuple subtype
let tty = RuntimeType::Tuple(vec![ty.clone()]);
assert_coerce_results(
v,
&tty,
&KclValue::MixedArray {
value: vec![v.clone()],
meta: Vec::new(),
},
&mut exec_state,
);
// Tuple subtype
let tty = RuntimeType::Tuple(vec![ty.clone()]);
assert_coerce_results(
v,
&tty,
&KclValue::Tuple {
value: vec![v.clone()],
meta: Vec::new(),
},
&mut exec_state,
);
}
}
}
for v in &values[1..] {
@ -1503,7 +1579,7 @@ mod test {
assert_coerce_results(
&none,
&tty,
&KclValue::MixedArray {
&KclValue::Tuple {
value: Vec::new(),
meta: Vec::new(),
},
@ -1634,7 +1710,7 @@ mod test {
],
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
};
let mixed1 = KclValue::MixedArray {
let mixed1 = KclValue::Tuple {
value: vec![
KclValue::Number {
value: 0.0,
@ -1649,7 +1725,7 @@ mod test {
],
meta: Vec::new(),
};
let mixed2 = KclValue::MixedArray {
let mixed2 = KclValue::Tuple {
value: vec![
KclValue::Number {
value: 0.0,
@ -1739,7 +1815,7 @@ mod test {
],
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
};
let mixed0 = KclValue::MixedArray {
let mixed0 = KclValue::Tuple {
value: vec![],
meta: Vec::new(),
};
@ -2156,7 +2232,7 @@ d = cos(30)
async fn coerce_nested_array() {
let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock().await);
let mixed1 = KclValue::MixedArray {
let mixed1 = KclValue::HomArray {
value: vec![
KclValue::Number {
value: 0.0,
@ -2184,7 +2260,7 @@ d = cos(30)
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
},
],
meta: Vec::new(),
ty: RuntimeType::any(),
};
// Principal types

View File

@ -150,6 +150,7 @@ impl BinaryPart {
unary_expression.get_hover_value_for_position(pos, code, opts)
}
BinaryPart::IfExpression(e) => e.get_hover_value_for_position(pos, code, opts),
BinaryPart::AscribedExpression(e) => e.expr.get_hover_value_for_position(pos, code, opts),
BinaryPart::MemberExpression(member_expression) => {
member_expression.get_hover_value_for_position(pos, code, opts)
}

View File

@ -162,6 +162,7 @@ impl BinaryPart {
BinaryPart::UnaryExpression(ue) => ue.compute_digest(),
BinaryPart::MemberExpression(me) => me.compute_digest(),
BinaryPart::IfExpression(e) => e.compute_digest(),
BinaryPart::AscribedExpression(e) => e.compute_digest(),
}
}
}
@ -225,6 +226,7 @@ impl PrimitiveType {
pub fn compute_digest(&mut self) -> Digest {
let mut hasher = Sha256::new();
match self {
PrimitiveType::Any => hasher.update(b"any"),
PrimitiveType::Named(id) => hasher.update(id.compute_digest()),
PrimitiveType::String => hasher.update(b"string"),
PrimitiveType::Number(suffix) => hasher.update(suffix.digestable_id()),

View File

@ -52,6 +52,7 @@ impl BinaryPart {
BinaryPart::UnaryExpression(unary_expression) => unary_expression.module_id,
BinaryPart::MemberExpression(member_expression) => member_expression.module_id,
BinaryPart::IfExpression(e) => e.module_id,
BinaryPart::AscribedExpression(e) => e.module_id,
}
}
}

View File

@ -1215,6 +1215,7 @@ impl From<&BinaryPart> for Expr {
BinaryPart::UnaryExpression(unary_expression) => Expr::UnaryExpression(unary_expression.clone()),
BinaryPart::MemberExpression(member_expression) => Expr::MemberExpression(member_expression.clone()),
BinaryPart::IfExpression(e) => Expr::IfExpression(e.clone()),
BinaryPart::AscribedExpression(e) => Expr::AscribedExpression(e.clone()),
}
}
}
@ -1281,6 +1282,7 @@ pub enum BinaryPart {
UnaryExpression(BoxNode<UnaryExpression>),
MemberExpression(BoxNode<MemberExpression>),
IfExpression(BoxNode<IfExpression>),
AscribedExpression(BoxNode<AscribedExpression>),
}
impl From<BinaryPart> for SourceRange {
@ -1306,6 +1308,7 @@ impl BinaryPart {
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(),
BinaryPart::MemberExpression(member_expression) => member_expression.get_constraint_level(),
BinaryPart::IfExpression(e) => e.get_constraint_level(),
BinaryPart::AscribedExpression(e) => e.expr.get_constraint_level(),
}
}
@ -1324,6 +1327,7 @@ impl BinaryPart {
}
BinaryPart::MemberExpression(_) => {}
BinaryPart::IfExpression(e) => e.replace_value(source_range, new_value),
BinaryPart::AscribedExpression(e) => e.expr.replace_value(source_range, new_value),
}
}
@ -1336,6 +1340,7 @@ impl BinaryPart {
BinaryPart::UnaryExpression(unary_expression) => unary_expression.start,
BinaryPart::MemberExpression(member_expression) => member_expression.start,
BinaryPart::IfExpression(e) => e.start,
BinaryPart::AscribedExpression(e) => e.start,
}
}
@ -1348,6 +1353,7 @@ impl BinaryPart {
BinaryPart::UnaryExpression(unary_expression) => unary_expression.end,
BinaryPart::MemberExpression(member_expression) => member_expression.end,
BinaryPart::IfExpression(e) => e.end,
BinaryPart::AscribedExpression(e) => e.end,
}
}
@ -1369,6 +1375,7 @@ impl BinaryPart {
member_expression.rename_identifiers(old_name, new_name)
}
BinaryPart::IfExpression(ref mut if_expression) => if_expression.rename_identifiers(old_name, new_name),
BinaryPart::AscribedExpression(ref mut e) => e.expr.rename_identifiers(old_name, new_name),
}
}
}
@ -3179,6 +3186,8 @@ impl PipeExpression {
#[ts(export)]
#[serde(tag = "p_type")]
pub enum PrimitiveType {
/// The super type of all other types.
Any,
/// A string type.
String,
/// A number type.
@ -3195,6 +3204,7 @@ pub enum PrimitiveType {
impl PrimitiveType {
pub fn primitive_from_str(s: &str, suffix: Option<NumericSuffix>) -> Option<Self> {
match (s, suffix) {
("any", None) => Some(PrimitiveType::Any),
("string", None) => Some(PrimitiveType::String),
("bool", None) => Some(PrimitiveType::Boolean),
("tag", None) => Some(PrimitiveType::Tag),
@ -3208,6 +3218,7 @@ impl PrimitiveType {
impl fmt::Display for PrimitiveType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PrimitiveType::Any => write!(f, "any"),
PrimitiveType::Number(suffix) => {
write!(f, "number")?;
if *suffix != NumericSuffix::None {

View File

@ -582,6 +582,26 @@ fn binary_operator(i: &mut TokenSlice) -> PResult<BinaryOperator> {
"<=" => BinaryOperator::Lte,
"|" => BinaryOperator::Or,
"&" => BinaryOperator::And,
"||" => {
ParseContext::err(
CompilationError::err(
token.as_source_range(),
"`||` is not an operator, did you mean to use `|`?",
)
.with_suggestion("Replace `||` with `|`", "|", None, Tag::None),
);
BinaryOperator::Or
}
"&&" => {
ParseContext::err(
CompilationError::err(
token.as_source_range(),
"`&&` is not an operator, did you mean to use `&`?",
)
.with_suggestion("Replace `&&` with `&`", "&", None, Tag::None),
);
BinaryOperator::And
}
_ => {
return Err(CompilationError::fatal(
token.as_source_range(),
@ -611,8 +631,7 @@ fn operand(i: &mut TokenSlice) -> PResult<BinaryPart> {
| Expr::ArrayExpression(_)
| Expr::ArrayRangeExpression(_)
| Expr::ObjectExpression(_)
| Expr::LabelledExpression(..)
| Expr::AscribedExpression(..) => return Err(CompilationError::fatal(source_range, TODO_783)),
| Expr::LabelledExpression(..) => return Err(CompilationError::fatal(source_range, TODO_783)),
Expr::None(_) => {
return Err(CompilationError::fatal(
source_range,
@ -638,6 +657,7 @@ fn operand(i: &mut TokenSlice) -> PResult<BinaryPart> {
Expr::CallExpressionKw(x) => BinaryPart::CallExpressionKw(x),
Expr::MemberExpression(x) => BinaryPart::MemberExpression(x),
Expr::IfExpression(x) => BinaryPart::IfExpression(x),
Expr::AscribedExpression(x) => BinaryPart::AscribedExpression(x),
};
Ok(expr)
})
@ -2048,7 +2068,7 @@ fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
}
fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
alt((
let mut expr = alt((
unary_expression.map(Box::new).map(Expr::UnaryExpression),
bool_value.map(Expr::Literal),
member_expression.map(Box::new).map(Expr::MemberExpression),
@ -2061,7 +2081,14 @@ fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
.context(expected(
"a KCL value which can be used as an argument/operand to an operator",
))
.parse_next(i)
.parse_next(i)?;
let ty = opt((colon, opt(whitespace), argument_type)).parse_next(i)?;
if let Some((_, _, ty)) = ty {
expr = Expr::AscribedExpression(Box::new(AscribedExpression::new(expr, ty)))
}
Ok(expr)
}
/// Parse an item visibility specifier, e.g. export.
@ -2730,9 +2757,9 @@ fn labeled_argument(i: &mut TokenSlice) -> PResult<LabeledArg> {
/// A type of a function argument.
/// This can be:
/// - a primitive type, e.g. 'number' or 'string' or 'bool'
/// - an array type, e.g. 'number[]' or 'string[]' or 'bool[]'
/// - an object type, e.g. '{x: number, y: number}' or '{name: string, age: number}'
/// - a primitive type, e.g. `number` or `string` or `bool`
/// - an array type, e.g. `[number]` or `[string]` or `[bool]`
/// - an object type, e.g. `{x: number, y: number}` or `{name: string, age: number}`
fn argument_type(i: &mut TokenSlice) -> PResult<Node<Type>> {
let type_ = alt((
// Object types
@ -4511,6 +4538,13 @@ export fn cos(num: number(rad)): number(_) {}"#;
assert_eq!(errs.len(), 3, "found: {errs:#?}");
}
#[test]
fn error_double_and() {
let (_, errs) = assert_no_fatal("foo = true && false");
assert_eq!(errs.len(), 1, "found: {errs:#?}");
assert!(errs[0].message.contains("`&&`") && errs[0].message.contains("`&`") && errs[0].suggestion.is_some());
}
#[test]
fn error_type_ascription() {
let (_, errs) = assert_no_fatal("a + b: number");

View File

@ -181,7 +181,7 @@ fn word(i: &mut Input<'_>) -> PResult<Token> {
fn operator(i: &mut Input<'_>) -> PResult<Token> {
let (value, range) = alt((
">=", "<=", "==", "=>", "!=", "|>", "*", "+", "-", "/", "%", "=", "<", ">", r"\", "^", "|", "&",
">=", "<=", "==", "=>", "!=", "|>", "*", "+", "-", "/", "%", "=", "<", ">", r"\", "^", "||", "&&", "|", "&",
))
.with_span()
.parse_next(i)?;

View File

@ -363,6 +363,27 @@ mod cube_with_error {
super::execute(TEST_NAME, true).await
}
}
mod any_type {
const TEST_NAME: &str = "any_type";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME)
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[tokio::test(flavor = "multi_thread")]
async fn unparse() {
super::unparse(TEST_NAME).await
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, false).await
}
}
mod artifact_graph_example_code1 {
const TEST_NAME: &str = "artifact_graph_example_code1";
@ -573,6 +594,48 @@ mod array_range_negative_expr {
super::execute(TEST_NAME, false).await
}
}
mod array_range_with_units {
const TEST_NAME: &str = "array_range_with_units";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME)
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[tokio::test(flavor = "multi_thread")]
async fn unparse() {
super::unparse(TEST_NAME).await
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, false).await
}
}
mod array_range_mismatch_units {
const TEST_NAME: &str = "array_range_mismatch_units";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME)
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[tokio::test(flavor = "multi_thread")]
async fn unparse() {
super::unparse(TEST_NAME).await
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, false).await
}
}
mod sketch_in_object {
const TEST_NAME: &str = "sketch_in_object";
@ -1144,6 +1207,27 @@ mod array_elem_push_fail {
super::execute(TEST_NAME, false).await
}
}
mod array_push_item_wrong_type {
const TEST_NAME: &str = "array_push_item_wrong_type";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME)
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[tokio::test(flavor = "multi_thread")]
async fn unparse() {
super::unparse(TEST_NAME).await
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, false).await
}
}
mod sketch_on_face {
const TEST_NAME: &str = "sketch_on_face";

View File

@ -408,13 +408,10 @@ impl Args {
})?;
T::from_kcl_val(&arg).ok_or_else(|| {
KclError::Semantic(KclErrorDetails {
KclError::Internal(KclErrorDetails {
source_ranges: vec![self.source_range],
message: format!(
"This function expected the input argument to be {}",
ty.human_friendly_type(),
),
})
message: "Mismatch between type coercion and value extraction (this isn't your fault).\nTo assist in bug-reporting, expected type: {ty:?}; actual value: {arg:?}".to_owned(),
})
})
}
@ -560,24 +557,23 @@ impl Args {
Ok(())
}
pub(crate) fn make_user_val_from_point(&self, p: [TyF64; 2]) -> Result<KclValue, KclError> {
pub(crate) fn make_kcl_val_from_point(&self, p: [f64; 2], ty: NumericType) -> Result<KclValue, KclError> {
let meta = Metadata {
source_range: self.source_range,
};
let x = KclValue::Number {
value: p[0].n,
value: p[0],
meta: vec![meta],
ty: p[0].ty.clone(),
ty: ty.clone(),
};
let y = KclValue::Number {
value: p[1].n,
value: p[1],
meta: vec![meta],
ty: p[1].ty.clone(),
ty: ty.clone(),
};
Ok(KclValue::MixedArray {
value: vec![x, y],
meta: vec![meta],
})
let ty = RuntimeType::Primitive(PrimitiveType::Number(ty));
Ok(KclValue::HomArray { value: vec![x, y], ty })
}
pub(super) fn make_user_val_from_f64_with_type(&self, f: TyF64) -> KclValue {
@ -799,7 +795,7 @@ impl<'a> FromKclValue<'a> for Vec<TagIdentifier> {
let tags = value.iter().map(|v| v.get_tag_identifier().unwrap()).collect();
Some(tags)
}
KclValue::MixedArray { value, .. } => {
KclValue::Tuple { value, .. } => {
let tags = value.iter().map(|v| v.get_tag_identifier().unwrap()).collect();
Some(tags)
}
@ -1139,8 +1135,11 @@ impl_from_kcl_for_vec!(TyF64);
impl<'a> FromKclValue<'a> for SourceRange {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
let KclValue::MixedArray { value, meta: _ } = arg else {
return None;
let value = match arg {
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => value,
_ => {
return None;
}
};
if value.len() != 3 {
return None;
@ -1337,7 +1336,7 @@ impl<'a> FromKclValue<'a> for TyF64 {
impl<'a> FromKclValue<'a> for [TyF64; 2] {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
match arg {
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
if value.len() != 2 {
return None;
}
@ -1354,7 +1353,7 @@ impl<'a> FromKclValue<'a> for [TyF64; 2] {
impl<'a> FromKclValue<'a> for [TyF64; 3] {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
match arg {
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
if value.len() != 3 {
return None;
}

View File

@ -9,6 +9,7 @@ use crate::{
errors::{KclError, KclErrorDetails},
execution::{
kcl_value::{FunctionSource, KclValue},
types::RuntimeType,
ExecState,
},
source_range::SourceRange,
@ -19,9 +20,11 @@ use crate::{
pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let array: Vec<KclValue> = args.get_unlabeled_kw_arg("array")?;
let f: &FunctionSource = args.get_kw_arg("f")?;
let meta = vec![args.source_range.into()];
let new_array = inner_map(array, f, exec_state, &args).await?;
Ok(KclValue::MixedArray { value: new_array, meta })
Ok(KclValue::HomArray {
value: new_array,
ty: RuntimeType::any(),
})
}
/// Apply a function to every element of a list.
@ -257,6 +260,31 @@ async fn call_reduce_closure(
Ok(out)
}
pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let array = args.get_unlabeled_kw_arg("array")?;
let item: KclValue = args.get_kw_arg("item")?;
let KclValue::HomArray { value: values, ty } = array else {
let meta = vec![args.source_range];
let actual_type = array.human_friendly_type();
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: meta,
message: format!("You can't push to a value of type {actual_type}, only an array"),
}));
};
let ty = if item.has_type(&ty) {
ty
} else {
// The user pushed an item with a type that differs from the array's
// element type.
RuntimeType::any()
};
let new_array = inner_push(values, item);
Ok(KclValue::HomArray { value: new_array, ty })
}
/// Append an element to the end of an array.
///
/// Returns a new array with the element appended.
@ -276,28 +304,26 @@ async fn call_reduce_closure(
},
tags = ["array"]
}]
async fn inner_push(mut array: Vec<KclValue>, item: KclValue, args: &Args) -> Result<KclValue, KclError> {
fn inner_push(mut array: Vec<KclValue>, item: KclValue) -> Vec<KclValue> {
array.push(item);
Ok(KclValue::MixedArray {
value: array,
meta: vec![args.source_range.into()],
})
array
}
pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
// Extract the array and the element from the arguments
let val: KclValue = args.get_unlabeled_kw_arg("array")?;
let item = args.get_kw_arg("item")?;
let meta = vec![args.source_range];
let KclValue::MixedArray { value: array, meta: _ } = val else {
let actual_type = val.human_friendly_type();
pub async fn pop(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let array = args.get_unlabeled_kw_arg("array")?;
let KclValue::HomArray { value: values, ty } = array else {
let meta = vec![args.source_range];
let actual_type = array.human_friendly_type();
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: meta,
message: format!("You can't push to a value of type {actual_type}, only an array"),
message: format!("You can't pop from a value of type {actual_type}, only an array"),
}));
};
inner_push(array, item, &args).await
let new_array = inner_pop(values, &args)?;
Ok(KclValue::HomArray { value: new_array, ty })
}
/// Remove the last element from an array.
@ -320,7 +346,7 @@ pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
},
tags = ["array"]
}]
async fn inner_pop(array: Vec<KclValue>, args: &Args) -> Result<KclValue, KclError> {
fn inner_pop(array: Vec<KclValue>, args: &Args) -> Result<Vec<KclValue>, KclError> {
if array.is_empty() {
return Err(KclError::Semantic(KclErrorDetails {
message: "Cannot pop from an empty array".to_string(),
@ -331,24 +357,5 @@ async fn inner_pop(array: Vec<KclValue>, args: &Args) -> Result<KclValue, KclErr
// Create a new array with all elements except the last one
let new_array = array[..array.len() - 1].to_vec();
Ok(KclValue::MixedArray {
value: new_array,
meta: vec![args.source_range.into()],
})
}
pub async fn pop(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
// Extract the array from the arguments
let val = args.get_unlabeled_kw_arg("array")?;
let meta = vec![args.source_range];
let KclValue::MixedArray { value: array, meta: _ } = val else {
let actual_type = val.human_friendly_type();
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: meta,
message: format!("You can't pop from a value of type {actual_type}, only an array"),
}));
};
inner_pop(array, &args).await
Ok(new_array)
}

View File

@ -14,7 +14,7 @@ use crate::{
/// Get the opposite edge to the edge given.
pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::edge(), exec_state)?;
let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::tag_identifier(), exec_state)?;
let edge = inner_get_opposite_edge(input_edge, exec_state, args.clone()).await?;
Ok(KclValue::Uuid {
@ -98,7 +98,7 @@ async fn inner_get_opposite_edge(
/// Get the next adjacent edge to the edge given.
pub async fn get_next_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::edge(), exec_state)?;
let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::tag_identifier(), exec_state)?;
let edge = inner_get_next_adjacent_edge(input_edge, exec_state, args.clone()).await?;
Ok(KclValue::Uuid {
@ -191,7 +191,7 @@ async fn inner_get_next_adjacent_edge(
/// Get the previous adjacent edge to the edge given.
pub async fn get_previous_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::edge(), exec_state)?;
let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::tag_identifier(), exec_state)?;
let edge = inner_get_previous_adjacent_edge(input_edge, exec_state, args.clone()).await?;
Ok(KclValue::Uuid {

View File

@ -152,8 +152,7 @@ pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
args = {
sketches = { docs = "Which sketch or sketches should be extruded"},
length = { docs = "How far to extrude the given sketches"},
symmetric = { docs = "If true, the extrusion will happen symmetrically around the sketch. Otherwise, the
extrusion will happen on only one side of the sketch." },
symmetric = { docs = "If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch." },
bidirectional_length = { docs = "If specified, will also extrude in the opposite direction to 'distance' to the specified distance. If 'symmetric' is true, this value is ignored."},
tag_start = { docs = "A named tag for the face at the start of the extrusion, i.e. the original sketch" },
tag_end = { docs = "A named tag for the face at the end of the extrusion, i.e. the new face created by extruding the original sketch" },

View File

@ -3,7 +3,7 @@
use anyhow::Result;
use crate::{
errors::KclError,
errors::{KclError, KclErrorDetails},
execution::{
types::{ArrayLen, NumericType, RuntimeType},
ExecState, KclValue,
@ -54,6 +54,17 @@ pub async fn tan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
/// Compute the square root of a number.
pub async fn sqrt(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
if input.n < 0.0 {
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![args.source_range],
message: format!(
"Attempt to take square root (`sqrt`) of a number less than zero ({})",
input.n
),
}));
}
let result = input.n.sqrt();
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))

View File

@ -452,7 +452,7 @@ async fn make_transform<T: GeometryTrait>(
})?;
let transforms = match transform_fn_return {
KclValue::Object { value, meta: _ } => vec![value],
KclValue::MixedArray { value, meta: _ } => {
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
let transforms: Vec<_> = value
.into_iter()
.map(|val| {
@ -671,12 +671,44 @@ impl GeometryTrait for Solid {
#[cfg(test)]
mod tests {
use super::*;
use crate::execution::types::NumericType;
use crate::execution::types::{NumericType, PrimitiveType};
#[tokio::test(flavor = "multi_thread")]
async fn test_array_to_point3d() {
let mut exec_state = ExecState::new(&ExecutorContext::new_mock().await);
let input = KclValue::MixedArray {
let input = KclValue::HomArray {
value: vec![
KclValue::Number {
value: 1.1,
meta: Default::default(),
ty: NumericType::mm(),
},
KclValue::Number {
value: 2.2,
meta: Default::default(),
ty: NumericType::mm(),
},
KclValue::Number {
value: 3.3,
meta: Default::default(),
ty: NumericType::mm(),
},
],
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
};
let expected = [
TyF64::new(1.1, NumericType::mm()),
TyF64::new(2.2, NumericType::mm()),
TyF64::new(3.3, NumericType::mm()),
];
let actual = array_to_point3d(&input, Vec::new(), &mut exec_state);
assert_eq!(actual.unwrap(), expected);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_tuple_to_point3d() {
let mut exec_state = ExecState::new(&ExecutorContext::new_mock().await);
let input = KclValue::Tuple {
value: vec![
KclValue::Number {
value: 1.1,

View File

@ -17,9 +17,9 @@ use crate::{
/// Returns the point at the end of the given segment.
pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
let result = inner_segment_end(&tag, exec_state, args.clone())?;
let pt = inner_segment_end(&tag, exec_state, args.clone())?;
args.make_user_val_from_point(result)
args.make_kcl_val_from_point([pt[0].n, pt[1].n], pt[0].ty.clone())
}
/// Compute the ending point of the provided line segment.
@ -64,8 +64,11 @@ fn inner_segment_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args
source_ranges: vec![args.source_range],
})
})?;
let (p, ty) = path.end_point_components();
// Docs generation isn't smart enough to handle ([f64; 2], NumericType).
let point = [TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)];
Ok(path.get_to().clone())
Ok(point)
}
/// Returns the segment end of x.
@ -156,9 +159,9 @@ fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
/// Returns the point at the start of the given segment.
pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
let result = inner_segment_start(&tag, exec_state, args.clone())?;
let pt = inner_segment_start(&tag, exec_state, args.clone())?;
args.make_user_val_from_point(result)
args.make_kcl_val_from_point([pt[0].n, pt[1].n], pt[0].ty.clone())
}
/// Compute the starting point of the provided line segment.
@ -203,8 +206,11 @@ fn inner_segment_start(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
source_ranges: vec![args.source_range],
})
})?;
let (p, ty) = path.start_point_components();
// Docs generation isn't smart enough to handle ([f64; 2], NumericType).
let point = [TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)];
Ok(path.get_from().to_owned())
Ok(point)
}
/// Returns the segment start of x.

View File

@ -2,11 +2,11 @@ use std::fmt::Write;
use crate::parsing::{
ast::types::{
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions, FunctionExpression, IfExpression,
ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue,
MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter, PipeExpression,
Program, TagDeclarator, TypeDeclaration, UnaryExpression, VariableDeclaration, VariableKind,
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
BinaryPart, BodyItem, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions, FunctionExpression,
IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier,
LiteralValue, MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter,
PipeExpression, Program, TagDeclarator, TypeDeclaration, UnaryExpression, VariableDeclaration, VariableKind,
},
deprecation, DeprecationKind, PIPE_OPERATOR,
};
@ -308,18 +308,7 @@ impl Expr {
result += &e.label.name;
result
}
Expr::AscribedExpression(e) => {
let mut result = e.expr.recast(options, indentation_level, ctxt);
if matches!(
e.expr,
Expr::BinaryExpression(..) | Expr::PipeExpression(..) | Expr::UnaryExpression(..)
) {
result = format!("({result})");
}
result += ": ";
result += &e.ty.to_string();
result
}
Expr::AscribedExpression(e) => e.recast(options, indentation_level, ctxt),
Expr::None(_) => {
unimplemented!("there is no literal None, see https://github.com/KittyCAD/modeling-app/issues/1115")
}
@ -327,6 +316,21 @@ impl Expr {
}
}
impl AscribedExpression {
fn recast(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
let mut result = self.expr.recast(options, indentation_level, ctxt);
if matches!(
self.expr,
Expr::BinaryExpression(..) | Expr::PipeExpression(..) | Expr::UnaryExpression(..)
) {
result = format!("({result})");
}
result += ": ";
result += &self.ty.to_string();
result
}
}
impl BinaryPart {
fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
match &self {
@ -345,6 +349,7 @@ impl BinaryPart {
BinaryPart::UnaryExpression(unary_expression) => unary_expression.recast(options),
BinaryPart::MemberExpression(member_expression) => member_expression.recast(),
BinaryPart::IfExpression(e) => e.recast(options, indentation_level, ExprContext::Other),
BinaryPart::AscribedExpression(e) => e.recast(options, indentation_level, ExprContext::Other),
}
}
}
@ -722,6 +727,7 @@ impl UnaryExpression {
| BinaryPart::Name(_)
| BinaryPart::MemberExpression(_)
| BinaryPart::IfExpression(_)
| BinaryPart::AscribedExpression(_)
| BinaryPart::CallExpressionKw(_) => {
format!("{}{}", &self.operator, self.argument.recast(options, 0))
}

View File

@ -221,6 +221,7 @@ impl<'tree> From<&'tree types::BinaryPart> for Node<'tree> {
types::BinaryPart::UnaryExpression(ue) => ue.as_ref().into(),
types::BinaryPart::MemberExpression(me) => me.as_ref().into(),
types::BinaryPart::IfExpression(e) => e.as_ref().into(),
types::BinaryPart::AscribedExpression(e) => e.as_ref().into(),
}
}
}

View File

@ -7,6 +7,14 @@ import Point2d from "std::types"
/// The value of `pi`, Archimedes constant (π).
///
/// `PI` is a number and is technically a ratio, so you might expect it to have type `number(_)`.
/// However, `PI` is nearly always used for converting between different units - usually degrees to or
/// from radians. Therefore, `PI` is treated a bit specially by KCL and always has unknown units. This
/// means that if you use `PI`, you will need to give KCL some extra information about the units of numbers.
/// Usually you should use type ascription on the result of calculations, e.g., `(2 * PI): number(rad)`.
/// You might prefer to use `units::toRadians` or `units::toDegrees` to convert between angles with
/// different units.
///
/// ```
/// circumference = 70
///

View File

@ -5,6 +5,8 @@
///
/// The standard library is organised into modules (listed below), but most things are always available
/// in KCL programs.
///
/// You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL guide]().
@no_std
@settings(defaultLengthUnit = mm, kclVersion = 1.0)

View File

@ -165,8 +165,25 @@ export type tag
/// An abstract plane.
///
/// A plane has a position and orientation in space defined by its origin and axes. A plane can be used
/// to sketch on.
/// A plane has a position and orientation in space defined by its origin and axes. A plane is abstract
/// in the sense that it is not part of the objects being drawn. A plane can be used to sketch on.
///
/// A plane can be created in several ways:
/// - you can use one of the default planes, e.g., `XY`.
/// - you can use `offsetPlane` to create a new plane offset from an existing one, e.g., `offsetPlane(XY, offset = 150)`.
/// - you can use negation to create a plane from an existing one which is identical but has an opposite normal
/// e.g., `-XY`.
/// - you can define an entirely custom plane, e.g.,
///
/// ```kcl,inline,norun
/// myXY = {
/// origin = { x = 0, y = 0, z = 0 },
/// xAxis = { x = 1, y = 0, z = 0 },
/// yAxis = { x = 0, y = 1, z = 0 },
/// }
/// ```
///
/// Any object with appropriate `origin`, `xAxis`, and `yAxis` fields can be used as a plane.
@(impl = std_rust)
export type Plane

View File

@ -11,32 +11,32 @@
@settings(defaultLengthUnit = mm, kclVersion = 1.0)
/// Convert a number to millimeters from its current units.
export fn toMillimeters(@num: number(mm)): number(mm) {
export fn toMillimeters(@num: number(Length)): number(mm) {
return num
}
/// Convert a number to centimeters from its current units.
export fn toCentimeters(@num: number(cm)): number(cm) {
export fn toCentimeters(@num: number(Length)): number(cm) {
return num
}
/// Convert a number to meters from its current units.
export fn toMeters(@num: number(m)): number(m) {
export fn toMeters(@num: number(Length)): number(m) {
return num
}
/// Convert a number to inches from its current units.
export fn toInches(@num: number(in)): number(in) {
export fn toInches(@num: number(Length)): number(in) {
return num
}
/// Convert a number to feet from its current units.
export fn toFeet(@num: number(ft)): number(ft) {
export fn toFeet(@num: number(Length)): number(ft) {
return num
}
/// Converts a number to yards from its current units.
export fn toYards(@num: number(yd)): number(yd) {
export fn toYards(@num: number(Length)): number(yd) {
return num
}
@ -54,7 +54,7 @@ export fn toYards(@num: number(yd)): number(yd) {
///
/// example = extrude(exampleSketch, length = 5)
/// ```
export fn toRadians(@num: number(rad)): number(rad) {
export fn toRadians(@num: number(Angle)): number(rad) {
return num
}
@ -72,6 +72,6 @@ export fn toRadians(@num: number(rad)): number(rad) {
///
/// example = extrude(exampleSketch, length = 5)
/// ```
export fn toDegrees(@num: number(deg)): number(deg) {
export fn toDegrees(@num: number(Angle)): number(deg) {
return num
}

View File

@ -0,0 +1,32 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands any_type.kcl
---
[
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
}
]

View File

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

View File

@ -0,0 +1,3 @@
```mermaid
flowchart LR
```

View File

@ -0,0 +1,921 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing any_type.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "id",
"start": 0,
"type": "Identifier"
},
"init": {
"body": {
"body": [
{
"argument": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "x",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"commentStart": 0,
"end": 0,
"start": 0
},
"commentStart": 0,
"end": 0,
"params": [
{
"type": "Parameter",
"identifier": {
"commentStart": 0,
"end": 0,
"name": "x",
"start": 0,
"type": "Identifier"
},
"labeled": false
}
],
"start": 0,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "singleton",
"start": 0,
"type": "Identifier"
},
"init": {
"body": {
"body": [
{
"argument": {
"commentStart": 0,
"elements": [
{
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "x",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"commentStart": 0,
"end": 0,
"start": 0
},
"commentStart": 0,
"end": 0,
"params": [
{
"type": "Parameter",
"identifier": {
"commentStart": 0,
"end": 0,
"name": "x",
"start": 0,
"type": "Identifier"
},
"labeled": false
}
],
"start": 0,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "len",
"start": 0,
"type": "Identifier"
},
"init": {
"body": {
"body": [
{
"argument": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "initial",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "f",
"start": 0,
"type": "Identifier"
},
"arg": {
"body": {
"body": [
{
"argument": {
"commentStart": 0,
"end": 0,
"left": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "accum",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"operator": "+",
"right": {
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"commentStart": 0,
"end": 0,
"start": 0
},
"commentStart": 0,
"end": 0,
"params": [
{
"type": "Parameter",
"identifier": {
"commentStart": 0,
"end": 0,
"name": "_",
"start": 0,
"type": "Identifier"
},
"labeled": false
},
{
"type": "Parameter",
"identifier": {
"commentStart": 0,
"end": 0,
"name": "accum",
"start": 0,
"type": "Identifier"
}
}
],
"start": 0,
"type": "FunctionExpression",
"type": "FunctionExpression"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "reduce",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "a",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"commentStart": 0,
"end": 0,
"start": 0
},
"commentStart": 0,
"end": 0,
"params": [
{
"type": "Parameter",
"identifier": {
"commentStart": 0,
"end": 0,
"name": "a",
"start": 0,
"type": "Identifier"
},
"labeled": false
}
],
"start": 0,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "one",
"start": 0,
"type": "Identifier"
},
"init": {
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "id",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
}
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "a",
"start": 0,
"type": "Identifier"
},
"init": {
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "id",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"end": 0,
"raw": "\"a\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "a"
}
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "arr1",
"start": 0,
"type": "Identifier"
},
"init": {
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "singleton",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
}
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "len0",
"start": 0,
"type": "Identifier"
},
"init": {
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "len",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"elements": [],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "len1",
"start": 0,
"type": "Identifier"
},
"init": {
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "len",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "one",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "len0",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
"preComments": [
"// TODO: we cannot currently assert on strings.",
"// assert(a, isEqualTo = \"a\")",
"// TODO: we cannot currently assert on arrays.",
"// assert(arr1, isEqualTo = [1])"
],
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "len1",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"commentStart": 0,
"end": 0,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
],
"1": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
],
"2": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
],
"7": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
],
"10": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,32 @@
fn id(@x: any): any {
return x
}
fn singleton(@x: any): [any; 1] {
return [x]
}
fn len(@a: [any]): number(_) {
return reduce(
a,
initial = 0,
f = fn(@_, accum) {
return accum + 1
},
)
}
one = id(1)
a = id("a")
arr1 = singleton(1)
len0 = len([])
len1 = len([1])
assert(one, isEqualTo = 1)
// TODO: we cannot currently assert on strings.
// assert(a, isEqualTo = "a")
// TODO: we cannot currently assert on arrays.
// assert(arr1, isEqualTo = [1])
assert(len0, isEqualTo = 0)
assert(len1, isEqualTo = 1)

View File

@ -0,0 +1,184 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed any_type.kcl
---
[
{
"type": "GroupBegin",
"group": {
"type": "FunctionCall",
"name": null,
"functionSourceRange": [],
"unlabeledArg": {
"value": {
"type": "Number",
"value": 1.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
},
"labeledArgs": {
"accum": {
"value": {
"type": "Number",
"value": 0.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
}
}
},
"sourceRange": []
},
{
"type": "GroupBegin",
"group": {
"type": "FunctionCall",
"name": "id",
"functionSourceRange": [],
"unlabeledArg": {
"value": {
"type": "Number",
"value": 1.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
},
"labeledArgs": {}
},
"sourceRange": []
},
{
"type": "GroupBegin",
"group": {
"type": "FunctionCall",
"name": "id",
"functionSourceRange": [],
"unlabeledArg": {
"value": {
"type": "String",
"value": "a"
},
"sourceRange": []
},
"labeledArgs": {}
},
"sourceRange": []
},
{
"type": "GroupBegin",
"group": {
"type": "FunctionCall",
"name": "singleton",
"functionSourceRange": [],
"unlabeledArg": {
"value": {
"type": "Number",
"value": 1.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
},
"labeledArgs": {}
},
"sourceRange": []
},
{
"type": "GroupBegin",
"group": {
"type": "FunctionCall",
"name": "len",
"functionSourceRange": [],
"unlabeledArg": {
"value": {
"type": "Array",
"value": []
},
"sourceRange": []
},
"labeledArgs": {}
},
"sourceRange": []
},
{
"type": "GroupBegin",
"group": {
"type": "FunctionCall",
"name": "len",
"functionSourceRange": [],
"unlabeledArg": {
"value": {
"type": "Array",
"value": [
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]
},
"sourceRange": []
},
"labeledArgs": {}
},
"sourceRange": []
},
{
"type": "GroupEnd"
},
{
"type": "GroupEnd"
},
{
"type": "GroupEnd"
},
{
"type": "GroupEnd"
},
{
"type": "GroupEnd"
},
{
"type": "GroupEnd"
}
]

View File

@ -0,0 +1,69 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing any_type.kcl
---
{
"a": {
"type": "String",
"value": "a"
},
"arr1": {
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]
},
"id": {
"type": "Function",
"value": null
},
"len": {
"type": "Function",
"value": null
},
"len0": {
"type": "Number",
"value": 0.0,
"ty": {
"type": "Known",
"type": "Count"
}
},
"len1": {
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
}
},
"one": {
"type": "Number",
"value": 1.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"singleton": {
"type": "Function",
"value": null
}
}

View File

@ -0,0 +1,35 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing any_type.kcl
---
fn id(@x: any): any {
return x
}
fn singleton(@x: any): [any; 1] {
return [x]
}
fn len(@a: [any]): number(_) {
return reduce(
a,
initial = 0,
f = fn(@_, accum) {
return accum + 1
},
)
}
one = id(1)
a = id("a")
arr1 = singleton(1)
len0 = len([])
len1 = len([1])
assert(one, isEqualTo = 1)
// TODO: we cannot currently assert on strings.
// assert(a, isEqualTo = "a")
// TODO: we cannot currently assert on arrays.
// assert(arr1, isEqualTo = [1])
assert(len0, isEqualTo = 0)
assert(len1, isEqualTo = 1)

View File

@ -4,7 +4,7 @@ description: Variables in memory after executing array_elem_pop.kcl
---
{
"arr": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -48,7 +48,7 @@ description: Variables in memory after executing array_elem_pop.kcl
]
},
"new_arr1": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -79,7 +79,7 @@ description: Variables in memory after executing array_elem_pop.kcl
]
},
"new_arr2": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -97,7 +97,7 @@ description: Variables in memory after executing array_elem_pop.kcl
]
},
"new_arr3": {
"type": "MixedArray",
"type": "HomArray",
"value": []
}
}

View File

@ -4,7 +4,7 @@ description: Variables in memory after executing array_elem_push.kcl
---
{
"arr": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -48,7 +48,7 @@ description: Variables in memory after executing array_elem_push.kcl
]
},
"new_arr1": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -105,7 +105,7 @@ description: Variables in memory after executing array_elem_push.kcl
]
},
"new_arr2": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

View File

@ -0,0 +1,32 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands array_push_item_wrong_type.kcl
---
[
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
}
]

View File

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

View File

@ -0,0 +1,3 @@
```mermaid
flowchart LR
```

View File

@ -0,0 +1,269 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing array_push_item_wrong_type.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "arr",
"start": 0,
"type": "Identifier"
},
"init": {
"commentStart": 0,
"end": 0,
"expr": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 0,
"ty": {
"commentStart": 0,
"end": 0,
"len": "None",
"start": 0,
"ty": {
"Count": null,
"p_type": "Number",
"type": "Primitive"
},
"type": "Array"
},
"type": "AscribedExpression",
"type": "AscribedExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "arrPrime",
"start": 0,
"type": "Identifier"
},
"init": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "item",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "4mm",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 4.0,
"suffix": "Mm"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "push",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "arr",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "4mm",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 4.0,
"suffix": "Mm"
}
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"should have been added to the end of the array\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "should have been added to the end of the array"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"commentStart": 0,
"end": 0,
"name": "arrPrime",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"commentStart": 0,
"end": 0,
"start": 0
}
}

View File

@ -0,0 +1,3 @@
arr = [1, 2]: [number(_)]
arrPrime = push(arr, item = 4mm)
assert(arrPrime[2], isEqualTo = 4mm, error = "should have been added to the end of the array")

View File

@ -0,0 +1,5 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed array_push_item_wrong_type.kcl
---
[]

View File

@ -0,0 +1,57 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing array_push_item_wrong_type.kcl
---
{
"arr": {
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
}
}
]
},
"arrPrime": {
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
}
},
{
"type": "Number",
"value": 4.0,
"ty": {
"type": "Known",
"type": "Length",
"type": "Mm"
}
}
]
}
}

View File

@ -0,0 +1,7 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing array_push_item_wrong_type.kcl
---
arr = [1, 2]: [number(_)]
arrPrime = push(arr, item = 4mm)
assert(arrPrime[2], isEqualTo = 4mm, error = "should have been added to the end of the array")

View File

@ -30,181 +30,281 @@ description: Variables in memory after executing array_range_expr.kcl
}
},
"r1": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 0.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 3.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 4.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]
},
"r2": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 0.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 3.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 4.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]
},
"r3": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 0.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 3.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 4.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 5.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]
},
"r4": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 3.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 4.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]

View File

@ -0,0 +1,32 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands array_range_mismatch_units.kcl
---
[
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
}
]

View File

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

View File

@ -0,0 +1,3 @@
```mermaid
flowchart LR
```

View File

@ -0,0 +1,125 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing array_range_mismatch_units.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "a",
"start": 0,
"type": "Identifier"
},
"init": {
"commentStart": 0,
"end": 0,
"endElement": {
"commentStart": 0,
"end": 0,
"raw": "3cm",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.0,
"suffix": "Cm"
}
},
"endInclusive": true,
"start": 0,
"startElement": {
"commentStart": 0,
"end": 0,
"raw": "1mm",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "Mm"
}
},
"type": "ArrayRangeExpression",
"type": "ArrayRangeExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"shouldn't make it here\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "shouldn't make it here"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assertIs",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"end": 0,
"raw": "false",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": false
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"commentStart": 0,
"end": 0,
"start": 0
}
}

View File

@ -0,0 +1,14 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Error from executing array_range_mismatch_units.kcl
---
KCL Semantic error
× semantic: Range start and end must be of the same type, but found 1:
│ number(mm) and 3: number(cm)
╭─[1:5]
1 │ a = [1mm..3cm]
· ─────┬────
· ╰── tests/array_range_mismatch_units/input.kcl
2 │ assertIs(false, error = "shouldn't make it here")
╰────

View File

@ -0,0 +1,2 @@
a = [1mm..3cm]
assertIs(false, error = "shouldn't make it here")

View File

@ -0,0 +1,5 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed array_range_mismatch_units.kcl
---
[]

View File

@ -0,0 +1,6 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing array_range_mismatch_units.kcl
---
a = [1mm..3cm]
assertIs(false, error = "shouldn't make it here")

View File

@ -4,94 +4,149 @@ description: Variables in memory after executing array_range_negative_expr.kcl
---
{
"xs": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
"value": -5.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": -4.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": -3.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": -2.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": -1.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 0.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 3.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 4.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 5.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]

View File

@ -0,0 +1,32 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands array_range_with_units.kcl
---
[
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
}
]

View File

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

View File

@ -0,0 +1,3 @@
```mermaid
flowchart LR
```

View File

@ -0,0 +1,309 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing array_range_with_units.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "a",
"start": 0,
"type": "Identifier"
},
"init": {
"commentStart": 0,
"end": 0,
"endElement": {
"commentStart": 0,
"end": 0,
"raw": "3cm",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.0,
"suffix": "Cm"
}
},
"endInclusive": true,
"start": 0,
"startElement": {
"commentStart": 0,
"end": 0,
"raw": "1cm",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "Cm"
}
},
"type": "ArrayRangeExpression",
"type": "ArrayRangeExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "1cm",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "Cm"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"commentStart": 0,
"end": 0,
"name": "a",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "2cm",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "Cm"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"commentStart": 0,
"end": 0,
"name": "a",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
},
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "3cm",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.0,
"suffix": "Cm"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"commentStart": 0,
"end": 0,
"name": "a",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"commentStart": 0,
"end": 0,
"start": 0
}
}

View File

@ -0,0 +1,4 @@
a = [1cm..3cm]
assert(a[0], isEqualTo = 1cm)
assert(a[1], isEqualTo = 2cm)
assert(a[2], isEqualTo = 3cm)

View File

@ -0,0 +1,5 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed array_range_with_units.kcl
---
[]

View File

@ -0,0 +1,38 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing array_range_with_units.kcl
---
{
"a": {
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Length",
"type": "Cm"
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Length",
"type": "Cm"
}
},
{
"type": "Number",
"value": 3.0,
"ty": {
"type": "Known",
"type": "Length",
"type": "Cm"
}
}
]
}
}

View File

@ -0,0 +1,8 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing array_range_with_units.kcl
---
a = [1cm..3cm]
assert(a[0], isEqualTo = 1cm)
assert(a[1], isEqualTo = 2cm)
assert(a[2], isEqualTo = 3cm)

View File

@ -4,7 +4,7 @@ description: Variables in memory after executing computed_var.kcl
---
{
"arr": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

View File

@ -14,8 +14,13 @@ description: Operations executed double_map_fn.kcl
"type": "Number",
"value": 0.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
@ -35,8 +40,13 @@ description: Operations executed double_map_fn.kcl
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
@ -56,8 +66,13 @@ description: Operations executed double_map_fn.kcl
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
@ -77,8 +92,13 @@ description: Operations executed double_map_fn.kcl
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
@ -98,8 +118,13 @@ description: Operations executed double_map_fn.kcl
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
@ -119,8 +144,13 @@ description: Operations executed double_map_fn.kcl
"type": "Number",
"value": 3.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []

View File

@ -8,59 +8,89 @@ description: Variables in memory after executing double_map_fn.kcl
"value": null
},
"xs": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 0.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]
},
"ys": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 3.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 4.0,
"ty": {
"type": "Known",
"type": "Count"
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]

View File

@ -614,7 +614,7 @@ description: Variables in memory after executing i_shape.kcl
}
},
"d_wrist_circumference": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ description: Variables in memory after executing import_async.kcl
}
},
"angles": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -1126,7 +1126,7 @@ description: Variables in memory after executing import_async.kcl
}
},
"invas": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2268,7 +2268,7 @@ description: Variables in memory after executing import_async.kcl
"value": null
},
"rs": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -86833,7 +86833,7 @@ description: Variables in memory after executing import_async.kcl
}
},
"xs": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -88164,7 +88164,7 @@ description: Variables in memory after executing import_async.kcl
]
},
"ys": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

View File

@ -4,7 +4,7 @@ description: Variables in memory after executing index_of_array.kcl
---
{
"arr": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

View File

@ -29,7 +29,7 @@ description: Variables in memory after executing dodecahedron.kcl
}
},
"dodecFaces": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Solid",
@ -2194,10 +2194,10 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
"faceRotations": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2254,7 +2254,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2311,7 +2311,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2368,7 +2368,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2425,7 +2425,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2482,7 +2482,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2539,7 +2539,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2596,7 +2596,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2653,7 +2653,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2710,7 +2710,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2767,7 +2767,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2824,7 +2824,7 @@ description: Variables in memory after executing dodecahedron.kcl
]
},
{
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

View File

@ -2018,7 +2018,7 @@ description: Variables in memory after executing food-service-spatula.kcl
"type": "Object",
"value": {
"origin": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2062,7 +2062,7 @@ description: Variables in memory after executing food-service-spatula.kcl
]
},
"xAxis": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2106,7 +2106,7 @@ description: Variables in memory after executing food-service-spatula.kcl
]
},
"yAxis": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2150,7 +2150,7 @@ description: Variables in memory after executing food-service-spatula.kcl
]
},
"zAxis": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

View File

@ -4190,7 +4190,7 @@ description: Variables in memory after executing french-press.kcl
"type": "Object",
"value": {
"origin": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -4234,7 +4234,7 @@ description: Variables in memory after executing french-press.kcl
]
},
"xAxis": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -4278,7 +4278,7 @@ description: Variables in memory after executing french-press.kcl
]
},
"yAxis": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -4322,7 +4322,7 @@ description: Variables in memory after executing french-press.kcl
]
},
"zAxis": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ description: Variables in memory after executing gear.kcl
}
},
"angles": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -1126,7 +1126,7 @@ description: Variables in memory after executing gear.kcl
}
},
"invas": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -2261,7 +2261,7 @@ description: Variables in memory after executing gear.kcl
"value": null
},
"rs": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -86790,7 +86790,7 @@ description: Variables in memory after executing gear.kcl
}
},
"xs": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -88121,7 +88121,7 @@ description: Variables in memory after executing gear.kcl
]
},
"ys": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

View File

@ -7,7 +7,7 @@ description: Variables in memory after executing gridfinity-baseplate-magnets.kc
"type": "Object",
"value": {
"direction": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -38,7 +38,7 @@ description: Variables in memory after executing gridfinity-baseplate-magnets.kc
]
},
"origin": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

View File

@ -7,7 +7,7 @@ description: Variables in memory after executing gridfinity-baseplate.kcl
"type": "Object",
"value": {
"direction": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",
@ -38,7 +38,7 @@ description: Variables in memory after executing gridfinity-baseplate.kcl
]
},
"origin": {
"type": "MixedArray",
"type": "HomArray",
"value": [
{
"type": "Number",

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