Compare commits

...

16 Commits

Author SHA1 Message Date
481332202d Merge branch 'main' into pierremtb/codeowners 2024-12-02 10:31:38 -05:00
ec49b0752e Remove auto creation of the draft release (#4588)
Fixes #4586
2024-12-02 10:30:24 -05:00
3b171fb881 Update nightly and release icons from Figma (#4597)
* Update nightly and windows icons

* Remove icon.ico, keep only icon.png, see https://www.electron.build/icons.html#windows-nsis

* Revert "Remove icon.ico, keep only icon.png, see https://www.electron.build/icons.html#windows-nsis"

This reverts commit b97f81b07d.

* Update windows icons

* Reset windows ico

* Test ico no margin

* Converted with freeconvert

* Use convertico.com for conversion
2024-12-02 10:24:14 -05:00
302ff1f09a Add @kittycad/frontend as codeowners for all files in modeling-app 2024-12-02 10:11:07 -05:00
7d8b6ae0a3 Merge branch 'main' into pierremtb/codeowners 2024-12-02 10:10:26 -05:00
0548409da0 Bump to Rust 1.83 (#4604) 2024-11-28 18:31:11 +00:00
dd052b35fd KCL: Remove unnecessary 'optional: bool' field on CallExpression (#4584)
It was put there in the original KCL JS-to-Rust rewrite, and I don't think it's used at all.
2024-11-27 18:01:42 -06:00
46be4e7eef Log simple performance metrics (#4596)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2024-11-28 11:27:17 +13:00
412d1b7a99 Update KCL Types doc (#4591)
* use `=` instead of `:`, fix formatting

* remove `%` from `segEnd` and `segLen` calls
2024-11-27 11:14:59 -05:00
cfdd22af74 Add ability to immediately enter sketch mode by double-clicking an existing sketch (#4573)
* Implement the functionality

* Another fmt

* Fix handler to not rely on modelingMachine's context,
because that creates an implicit race

* Write an E2E test

* Fix tsc and fmt

* Use artifactGraph helpers for more concise code

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

* Fix up imports and whatnot from commit 2bfc5f5c

* Make early return more clear with curly braces

* Whoops should have linted

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-11-27 10:08:23 -05:00
68a11e7aa5 Remove the lexer from KCL's API (#4589)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2024-11-27 03:30:28 +00:00
3139e18dc7 Make = and => optional in function declarations (#4577)
* Make `=` and `=>` optional in function declarations

And requires `:` for return types

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

* Tests

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

* Format types in function decls

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

* Require  in anon function decls

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

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
2024-11-27 15:46:58 +13:00
d461b09a4d KCL refactor: break typechecking into its own fn (#4587) 2024-11-26 16:02:31 -06:00
9c42c39ba9 Fix publish path in release bucket (#4585) 2024-11-26 15:11:26 -05:00
aa3f40e22c KCL: Two tiny refactors (#4580)
* Refactor: Combine two impl blocks

* Refactor: Constant for NO_META(data)
2024-11-26 12:27:09 -06:00
1f6d67d5fc Add CODEOWNERS 2024-11-22 13:40:42 -05:00
159 changed files with 7227 additions and 9398 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
* @KittyCAD/frontend

View File

@ -382,13 +382,3 @@ jobs:
glob: '*'
parent: false
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
- name: Create draft release
uses: softprops/action-gh-release@v2
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: ${{ env.VERSION }}
tag_name: ${{ env.VERSION }}
draft: true
generate_release_notes: true
files: 'out/Zoo*'

View File

@ -123,7 +123,7 @@ jobs:
path: out
glob: '*'
parent: false
destination: 'dl.kittycad.io/releases/modeling-app/test/new-workflow'
destination: 'dl.kittycad.io/releases/modeling-app'
- name: Invalidate bucket cache on latest*.yml and last_download.json files
run: |
@ -132,6 +132,12 @@ jobs:
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-mac.yml" --async
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest.yml" --async
- name: Upload release files to Github
if: ${{ github.event_name == 'release' }}
uses: softprops/action-gh-release@v2
with:
files: 'out/Zoo*'
announce_release:
needs: [publish-apps-release]

View File

@ -136,7 +136,7 @@ https://github.com/KittyCAD/modeling-app/issues/new
#### 2. Push a new tag
Create a new tag and push it to the repo (eg. `v0.28.0` for `$VERSION`)
Create a new tag and push it to the repo. The `semantic-release.sh` script will automatically bump the minor part, which we use the most. For instance going from `v0.27.0` to `v0.28.0`.
```
VERSION=$(./scripts/semantic-release.sh)
@ -146,16 +146,14 @@ git push origin --tags
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
Once the workflow succeeds, a draft release will be created at https://github.com/KittyCAD/modeling-app/releases.
The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)).
#### 3. Manually test artifacts from the Cut Release PR
#### 3. Manually test artifacts
##### Release builds
The release builds can be found under the `out-{arch}-{platform}` zip files, at the very bottom of the `build-apps` summary page for the workflow (triggered by the tag in 2.).
Alternatively, the draft release will also include these builds.
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
##### Updater-test builds
@ -178,9 +176,11 @@ If the prompt doesn't show up, start the app in command line to grab the electro
#### 4. Publish the release
Head over to https://github.com/KittyCAD/modeling-app/releases, paste in the changelog discussed in the issue, and publish the draft release created by the `build-apps` workflow from step 2.
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the _Release title_ field as well.
A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions, which can be found under `release` event filter. On success, the files will be uploaded to the public bucket and the announcement on Discord will be sent.
Hit _Generate release notes_ as a starting point to discuss the changelog in the issue. Once done, make sure _Set as the latest release_ is checked, and hit _Publish release_.
A new `publish-apps-release` will kick in and you should be able to find it [here](https://github.com/KittyCAD/modeling-app/actions?query=event%3Arelease). On success, the files will be uploaded to the public bucket as well as to the GitHub release, and the announcement on Discord will be sent.
#### 5. Close the issue
@ -450,3 +450,9 @@ PS: for the debug panel, the following JSON is useful for snapping the camera
## KCL
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).
### Logging
To display logging (to the terminal or console) set `ZOO_LOG=1`. This will log some warnings and simple performance metrics. To view these in test runs, use `-- --nocapture`.
To enable memory metrics, build with `--features dhat-heap`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -58,7 +58,7 @@ mountingPlate = extrude(thickness, mountingPlateSketch)
```js
// Sketch on the face of a chamfer.
fn cube = (pos, scale) => {
fn cube(pos, scale) {
sg = startSketchOn('XY')
|> startProfileAt(pos, %)
|> line([0, scale], %)

File diff suppressed because one or more lines are too long

View File

@ -37,7 +37,7 @@ assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 2 }, %)
|> extrude(5, %)
|> patternTransform(n, (id) => {
|> patternTransform(n, fn(id) {
return { translate = [4 * id, 0, 0] }
}, %)
```

View File

@ -29,7 +29,7 @@ map(array: [KclValue], map_fn: FunctionParam) -> [KclValue]
```js
r = 10 // radius
fn drawCircle = (id) => {
fn drawCircle(id) {
return startSketchOn("XY")
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
}
@ -45,7 +45,7 @@ circles = map([1..3], drawCircle)
```js
r = 10 // radius
// Call `map`, using an anonymous function instead of a named one.
circles = map([1..3], (id) => {
circles = map([1..3], (id) {
return startSketchOn("XY")
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
})

File diff suppressed because one or more lines are too long

View File

@ -30,7 +30,7 @@ patternTransform2d(total_instances: u32, transform_function: FunctionParam, soli
```js
// Each instance will be shifted along the X axis.
fn transform = (id) => {
fn transform(id) {
return { translate = [4 * id, 0] }
}

View File

@ -30,14 +30,14 @@ reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue
```js
// This function adds two numbers.
fn add = (a, b) => {
fn add(a, b) {
return a + b
}
// This function adds an array of numbers.
// It uses the `reduce` function, to call the `add` function on every
// element of the `arr` parameter. The starting value is 0.
fn sum = (arr) => {
fn sum(arr) {
return reduce(arr, 0, add)
}
@ -61,7 +61,7 @@ assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")
// an anonymous `add` function as its parameter, instead of declaring a
// named function outside.
arr = [1, 2, 3]
sum = reduce(arr, 0, (i, result_so_far) => {
sum = reduce(arr, 0, (i, result_so_far) {
return i + result_so_far
})
@ -74,7 +74,7 @@ assertEqual(sum, 6, 0.00001, "1 + 2 + 3 summed is 6")
```js
// Declare a function that sketches a decagon.
fn decagon = (radius) => {
fn decagon(radius) {
// Each side of the decagon is turned this many degrees from the previous angle.
stepAngle = 1 / 10 * tau()
@ -84,7 +84,7 @@ fn decagon = (radius) => {
// Use a `reduce` to draw the remaining decagon sides.
// For each number in the array 1..10, run the given function,
// which takes a partially-sketched decagon and adds one more edge to it.
fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) => {
fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) {
// Draw one edge of the decagon.
x = cos(stepAngle * i) * radius
y = sin(stepAngle * i) * radius

View File

@ -36,7 +36,7 @@ cube = startSketchAt([0, 0])
|> close(%)
|> extrude(5, %)
fn cylinder = (radius, tag) => {
fn cylinder(radius, tag) {
return startSketchAt([0, 0])
|> circle({
radius = radius,

View File

@ -36,7 +36,7 @@ cube = startSketchAt([0, 0])
|> close(%)
|> extrude(5, %)
fn cylinder = (radius, tag) => {
fn cylinder(radius, tag) {
return startSketchAt([0, 0])
|> circle({
radius = radius,

View File

@ -49755,7 +49755,7 @@
"deprecated": false,
"examples": [
"// Chamfer a mounting plate.\nwidth = 20\nlength = 10\nthickness = 1\nchamferLength = 2\n\nmountingPlateSketch = startSketchOn(\"XY\")\n |> startProfileAt([-width / 2, -length / 2], %)\n |> lineTo([width / 2, -length / 2], %, $edge1)\n |> lineTo([width / 2, length / 2], %, $edge2)\n |> lineTo([-width / 2, length / 2], %, $edge3)\n |> close(%, $edge4)\n\nmountingPlate = extrude(thickness, mountingPlateSketch)\n |> chamfer({\n length = chamferLength,\n tags = [\n getNextAdjacentEdge(edge1),\n getNextAdjacentEdge(edge2),\n getNextAdjacentEdge(edge3),\n getNextAdjacentEdge(edge4)\n ]\n }, %)",
"// Sketch on the face of a chamfer.\nfn cube = (pos, scale) => {\n sg = startSketchOn('XY')\n |> startProfileAt(pos, %)\n |> line([0, scale], %)\n |> line([scale, 0], %)\n |> line([0, -scale], %)\n\n return sg\n}\n\npart001 = cube([0, 0], 20)\n |> close(%, $line1)\n |> extrude(20, %)\n |> chamfer({\n length = 10,\n tags = [getOppositeEdge(line1)]\n }, %, $chamfer1) // We tag the chamfer to reference it later.\n\nsketch001 = startSketchOn(part001, chamfer1)\n |> startProfileAt([10, 10], %)\n |> line([2, 0], %)\n |> line([0, 2], %)\n |> line([-2, 0], %)\n |> lineTo([profileStartX(%), profileStartY(%)], %)\n |> close(%)\n |> extrude(10, %)"
"// Sketch on the face of a chamfer.\nfn cube(pos, scale) {\n sg = startSketchOn('XY')\n |> startProfileAt(pos, %)\n |> line([0, scale], %)\n |> line([scale, 0], %)\n |> line([0, -scale], %)\n\n return sg\n}\n\npart001 = cube([0, 0], 20)\n |> close(%, $line1)\n |> extrude(20, %)\n |> chamfer({\n length = 10,\n tags = [getOppositeEdge(line1)]\n }, %, $chamfer1) // We tag the chamfer to reference it later.\n\nsketch001 = startSketchOn(part001, chamfer1)\n |> startProfileAt([10, 10], %)\n |> line([2, 0], %)\n |> line([0, 2], %)\n |> line([-2, 0], %)\n |> lineTo([profileStartX(%), profileStartY(%)], %)\n |> close(%)\n |> extrude(10, %)"
]
},
{
@ -75615,7 +75615,7 @@
"deprecated": false,
"examples": [
"exampleSketch = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 5], %)\n |> line([5, 0], %)\n |> line([0, -5], %)\n |> close(%)\n |> hole(circle({ center = [1, 1], radius = .25 }, %), %)\n |> hole(circle({ center = [1, 4], radius = .25 }, %), %)\n\nexample = extrude(1, exampleSketch)",
"fn squareHoleSketch = () => {\n squareSketch = startSketchOn('-XZ')\n |> startProfileAt([-1, -1], %)\n |> line([2, 0], %)\n |> line([0, 2], %)\n |> line([-2, 0], %)\n |> close(%)\n return squareSketch\n}\n\nexampleSketch = startSketchOn('-XZ')\n |> circle({ center = [0, 0], radius = 3 }, %)\n |> hole(squareHoleSketch(), %)\nexample = extrude(1, exampleSketch)"
"fn squareHoleSketch() {\n squareSketch = startSketchOn('-XZ')\n |> startProfileAt([-1, -1], %)\n |> line([2, 0], %)\n |> line([0, 2], %)\n |> line([-2, 0], %)\n |> close(%)\n return squareSketch\n}\n\nexampleSketch = startSketchOn('-XZ')\n |> circle({ center = [0, 0], radius = 3 }, %)\n |> hole(squareHoleSketch(), %)\nexample = extrude(1, exampleSketch)"
]
},
{
@ -78872,7 +78872,7 @@
"unpublished": false,
"deprecated": false,
"examples": [
"n = int(ceil(5 / 2))\nassertEqual(n, 3, 0.0001, \"5/2 = 2.5, rounded up makes 3\")\n// Draw n cylinders.\nstartSketchOn('XZ')\n |> circle({ center = [0, 0], radius = 2 }, %)\n |> extrude(5, %)\n |> patternTransform(n, (id) => {\n return { translate = [4 * id, 0, 0] }\n }, %)"
"n = int(ceil(5 / 2))\nassertEqual(n, 3, 0.0001, \"5/2 = 2.5, rounded up makes 3\")\n// Draw n cylinders.\nstartSketchOn('XZ')\n |> circle({ center = [0, 0], radius = 2 }, %)\n |> extrude(5, %)\n |> patternTransform(n, fn(id) {\n return { translate = [4 * id, 0, 0] }\n }, %)"
]
},
{
@ -96790,7 +96790,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -96809,9 +96808,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -97462,7 +97458,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -97481,9 +97476,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -100773,7 +100765,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -100792,9 +100783,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -101445,7 +101433,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -101464,9 +101451,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -104760,7 +104744,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -104779,9 +104762,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -105432,7 +105412,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -105451,9 +105430,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -106213,8 +106189,8 @@
"unpublished": false,
"deprecated": false,
"examples": [
"r = 10 // radius\nfn drawCircle = (id) => {\n return startSketchOn(\"XY\")\n |> circle({ center = [id * 2 * r, 0], radius = r }, %)\n}\n\n// Call `drawCircle`, passing in each element of the array.\n// The outputs from each `drawCircle` form a new array,\n// which is the return value from `map`.\ncircles = map([1..3], drawCircle)",
"r = 10 // radius\n// Call `map`, using an anonymous function instead of a named one.\ncircles = map([1..3], (id) => {\n return startSketchOn(\"XY\")\n |> circle({ center = [id * 2 * r, 0], radius = r }, %)\n})"
"r = 10 // radius\nfn drawCircle(id) {\n return startSketchOn(\"XY\")\n |> circle({ center = [id * 2 * r, 0], radius = r }, %)\n}\n\n// Call `drawCircle`, passing in each element of the array.\n// The outputs from each `drawCircle` form a new array,\n// which is the return value from `map`.\ncircles = map([1..3], drawCircle)",
"r = 10 // radius\n// Call `map`, using an anonymous function instead of a named one.\ncircles = map([1..3], (id) {\n return startSketchOn(\"XY\")\n |> circle({ center = [id * 2 * r, 0], radius = r }, %)\n})"
]
},
{
@ -124301,12 +124277,12 @@
"unpublished": false,
"deprecated": false,
"examples": [
"// Each instance will be shifted along the X axis.\nfn transform = (id) => {\n return { translate = [4 * id, 0, 0] }\n}\n\n// Sketch 4 cylinders.\nsketch001 = startSketchOn('XZ')\n |> circle({ center = [0, 0], radius = 2 }, %)\n |> extrude(5, %)\n |> patternTransform(4, transform, %)",
"// Each instance will be shifted along the X axis,\n// with a gap between the original (at x = 0) and the first replica\n// (at x = 8). This is because `id` starts at 1.\nfn transform = (id) => {\n return { translate = [4 * (1 + id), 0, 0] }\n}\n\nsketch001 = startSketchOn('XZ')\n |> circle({ center = [0, 0], radius = 2 }, %)\n |> extrude(5, %)\n |> patternTransform(4, transform, %)",
"fn cube = (length, center) => {\n l = length / 2\n x = center[0]\n y = center[1]\n p0 = [-l + x, -l + y]\n p1 = [-l + x, l + y]\n p2 = [l + x, l + y]\n p3 = [l + x, -l + y]\n\n return startSketchAt(p0)\n |> lineTo(p1, %)\n |> lineTo(p2, %)\n |> lineTo(p3, %)\n |> lineTo(p0, %)\n |> close(%)\n |> extrude(length, %)\n}\n\nwidth = 20\nfn transform = (i) => {\n return {\n // Move down each time.\n translate = [0, 0, -i * width],\n // Make the cube longer, wider and flatter each time.\n scale = [pow(1.1, i), pow(1.1, i), pow(0.9, i)],\n // Turn by 15 degrees each time.\n rotation = { angle = 15 * i, origin = \"local\" }\n }\n}\n\nmyCubes = cube(width, [100, 0])\n |> patternTransform(25, transform, %)",
"fn cube = (length, center) => {\n l = length / 2\n x = center[0]\n y = center[1]\n p0 = [-l + x, -l + y]\n p1 = [-l + x, l + y]\n p2 = [l + x, l + y]\n p3 = [l + x, -l + y]\n\n return startSketchAt(p0)\n |> lineTo(p1, %)\n |> lineTo(p2, %)\n |> lineTo(p3, %)\n |> lineTo(p0, %)\n |> close(%)\n |> extrude(length, %)\n}\n\nwidth = 20\nfn transform = (i) => {\n return {\n translate = [0, 0, -i * width],\n rotation = {\n angle = 90 * i,\n // Rotate around the overall scene's origin.\n origin = \"global\"\n }\n }\n}\nmyCubes = cube(width, [100, 100])\n |> patternTransform(4, transform, %)",
"// Parameters\nr = 50 // base radius\nh = 10 // layer height\nt = 0.005 // taper factor [0-1)\n// Defines how to modify each layer of the vase.\n// Each replica is shifted up the Z axis, and has a smoothly-varying radius\nfn transform = (replicaId) => {\n scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8))\n return {\n translate = [0, 0, replicaId * 10],\n scale = [scale, scale, 0]\n }\n}\n// Each layer is just a pretty thin cylinder.\nfn layer = () => {\n return startSketchOn(\"XY\")\n // or some other plane idk\n |> circle({ center = [0, 0], radius = 1 }, %, $tag1)\n |> extrude(h, %)\n}\n// The vase is 100 layers tall.\n// The 100 layers are replica of each other, with a slight transformation applied to each.\nvase = layer()\n |> patternTransform(100, transform, %)",
"fn transform = (i) => {\n // Transform functions can return multiple transforms. They'll be applied in order.\n return [\n { translate = [30 * i, 0, 0] },\n { rotation = { angle = 45 * i } }\n ]\n}\nstartSketchAt([0, 0])\n |> polygon({\n radius = 10,\n numSides = 4,\n center = [0, 0],\n inscribed = false\n }, %)\n |> extrude(4, %)\n |> patternTransform(3, transform, %)"
"// Each instance will be shifted along the X axis.\nfn transform(id) {\n return { translate = [4 * id, 0, 0] }\n}\n\n// Sketch 4 cylinders.\nsketch001 = startSketchOn('XZ')\n |> circle({ center = [0, 0], radius = 2 }, %)\n |> extrude(5, %)\n |> patternTransform(4, transform, %)",
"// Each instance will be shifted along the X axis,\n// with a gap between the original (at x = 0) and the first replica\n// (at x = 8). This is because `id` starts at 1.\nfn transform(id) {\n return { translate = [4 * (1 + id), 0, 0] }\n}\n\nsketch001 = startSketchOn('XZ')\n |> circle({ center = [0, 0], radius = 2 }, %)\n |> extrude(5, %)\n |> patternTransform(4, transform, %)",
"fn cube(length, center) {\n l = length / 2\n x = center[0]\n y = center[1]\n p0 = [-l + x, -l + y]\n p1 = [-l + x, l + y]\n p2 = [l + x, l + y]\n p3 = [l + x, -l + y]\n\n return startSketchAt(p0)\n |> lineTo(p1, %)\n |> lineTo(p2, %)\n |> lineTo(p3, %)\n |> lineTo(p0, %)\n |> close(%)\n |> extrude(length, %)\n}\n\nwidth = 20\nfn transform(i) {\n return {\n // Move down each time.\n translate = [0, 0, -i * width],\n // Make the cube longer, wider and flatter each time.\n scale = [pow(1.1, i), pow(1.1, i), pow(0.9, i)],\n // Turn by 15 degrees each time.\n rotation = { angle = 15 * i, origin = \"local\" }\n }\n}\n\nmyCubes = cube(width, [100, 0])\n |> patternTransform(25, transform, %)",
"fn cube(length, center) {\n l = length / 2\n x = center[0]\n y = center[1]\n p0 = [-l + x, -l + y]\n p1 = [-l + x, l + y]\n p2 = [l + x, l + y]\n p3 = [l + x, -l + y]\n\n return startSketchAt(p0)\n |> lineTo(p1, %)\n |> lineTo(p2, %)\n |> lineTo(p3, %)\n |> lineTo(p0, %)\n |> close(%)\n |> extrude(length, %)\n}\n\nwidth = 20\nfn transform(i) {\n return {\n translate = [0, 0, -i * width],\n rotation = {\n angle = 90 * i,\n // Rotate around the overall scene's origin.\n origin = \"global\"\n }\n }\n}\nmyCubes = cube(width, [100, 100])\n |> patternTransform(4, transform, %)",
"// Parameters\nr = 50 // base radius\nh = 10 // layer height\nt = 0.005 // taper factor [0-1)\n// Defines how to modify each layer of the vase.\n// Each replica is shifted up the Z axis, and has a smoothly-varying radius\nfn transform(replicaId) {\n scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8))\n return {\n translate = [0, 0, replicaId * 10],\n scale = [scale, scale, 0]\n }\n}\n// Each layer is just a pretty thin cylinder.\nfn layer() {\n return startSketchOn(\"XY\")\n // or some other plane idk\n |> circle({ center = [0, 0], radius = 1 }, %, $tag1)\n |> extrude(h, %)\n}\n// The vase is 100 layers tall.\n// The 100 layers are replica of each other, with a slight transformation applied to each.\nvase = layer()\n |> patternTransform(100, transform, %)",
"fn transform(i) {\n // Transform functions can return multiple transforms. They'll be applied in order.\n return [\n { translate = [30 * i, 0, 0] },\n { rotation = { angle = 45 * i } }\n ]\n}\nstartSketchAt([0, 0])\n |> polygon({\n radius = 10,\n numSides = 4,\n center = [0, 0],\n inscribed = false\n }, %)\n |> extrude(4, %)\n |> patternTransform(3, transform, %)"
]
},
{
@ -127064,7 +127040,7 @@
"unpublished": false,
"deprecated": false,
"examples": [
"// Each instance will be shifted along the X axis.\nfn transform = (id) => {\n return { translate = [4 * id, 0] }\n}\n\n// Sketch 4 circles.\nsketch001 = startSketchOn('XZ')\n |> circle({ center = [0, 0], radius = 2 }, %)\n |> patternTransform2d(4, transform, %)"
"// Each instance will be shifted along the X axis.\nfn transform(id) {\n return { translate = [4 * id, 0] }\n}\n\n// Sketch 4 circles.\nsketch001 = startSketchOn('XZ')\n |> circle({ center = [0, 0], radius = 2 }, %)\n |> patternTransform2d(4, transform, %)"
]
},
{
@ -138002,7 +137978,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -138021,9 +137996,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -138674,7 +138646,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -138693,9 +138664,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -142601,7 +142569,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -142620,9 +142587,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -143273,7 +143237,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -143292,9 +143255,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -147201,7 +147161,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -147220,9 +147179,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -147873,7 +147829,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -147892,9 +147847,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -151199,7 +151151,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -151218,9 +151169,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -151871,7 +151819,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -151890,9 +151837,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -155798,7 +155742,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -155817,9 +155760,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -156470,7 +156410,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -156489,9 +156428,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -159781,7 +159717,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -159800,9 +159735,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -160453,7 +160385,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -160472,9 +160403,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -164381,7 +164309,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -164400,9 +164327,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -165053,7 +164977,6 @@
"required": [
"arguments",
"callee",
"optional",
"type"
],
"properties": {
@ -165072,9 +164995,6 @@
"$ref": "#/components/schemas/Expr"
}
},
"optional": {
"type": "boolean"
},
"digest": {
"type": "array",
"items": {
@ -165834,9 +165754,9 @@
"unpublished": false,
"deprecated": false,
"examples": [
"// This function adds two numbers.\nfn add = (a, b) => {\n return a + b\n}\n\n// This function adds an array of numbers.\n// It uses the `reduce` function, to call the `add` function on every\n// element of the `arr` parameter. The starting value is 0.\nfn sum = (arr) => {\n return reduce(arr, 0, add)\n}\n\n/* The above is basically like this pseudo-code:\nfn sum(arr):\n let sumSoFar = 0\n for i in arr:\n sumSoFar = add(sumSoFar, i)\n return sumSoFar */\n\n\n// We use `assertEqual` to check that our `sum` function gives the\n// expected result. It's good to check your work!\nassertEqual(sum([1, 2, 3]), 6, 0.00001, \"1 + 2 + 3 summed is 6\")",
"// This example works just like the previous example above, but it uses\n// an anonymous `add` function as its parameter, instead of declaring a\n// named function outside.\narr = [1, 2, 3]\nsum = reduce(arr, 0, (i, result_so_far) => {\n return i + result_so_far\n})\n\n// We use `assertEqual` to check that our `sum` function gives the\n// expected result. It's good to check your work!\nassertEqual(sum, 6, 0.00001, \"1 + 2 + 3 summed is 6\")",
"// Declare a function that sketches a decagon.\nfn decagon = (radius) => {\n // Each side of the decagon is turned this many degrees from the previous angle.\n stepAngle = 1 / 10 * tau()\n\n // Start the decagon sketch at this point.\n startOfDecagonSketch = startSketchAt([cos(0) * radius, sin(0) * radius])\n\n // Use a `reduce` to draw the remaining decagon sides.\n // For each number in the array 1..10, run the given function,\n // which takes a partially-sketched decagon and adds one more edge to it.\n fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) => {\n // Draw one edge of the decagon.\n x = cos(stepAngle * i) * radius\n y = sin(stepAngle * i) * radius\n return lineTo([x, y], partialDecagon)\n })\n\n return fullDecagon\n}\n\n/* The `decagon` above is basically like this pseudo-code:\nfn decagon(radius):\n let stepAngle = (1/10) * tau()\n let startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])\n\n // Here's the reduce part.\n let partialDecagon = startOfDecagonSketch\n for i in [1..10]:\n let x = cos(stepAngle * i) * radius\n let y = sin(stepAngle * i) * radius\n partialDecagon = lineTo([x, y], partialDecagon)\n fullDecagon = partialDecagon // it's now full\n return fullDecagon */\n\n\n// Use the `decagon` function declared above, to sketch a decagon with radius 5.\ndecagon(5.0)\n |> close(%)"
"// This function adds two numbers.\nfn add(a, b) {\n return a + b\n}\n\n// This function adds an array of numbers.\n// It uses the `reduce` function, to call the `add` function on every\n// element of the `arr` parameter. The starting value is 0.\nfn sum(arr) {\n return reduce(arr, 0, add)\n}\n\n/* The above is basically like this pseudo-code:\nfn sum(arr):\n let sumSoFar = 0\n for i in arr:\n sumSoFar = add(sumSoFar, i)\n return sumSoFar */\n\n\n// We use `assertEqual` to check that our `sum` function gives the\n// expected result. It's good to check your work!\nassertEqual(sum([1, 2, 3]), 6, 0.00001, \"1 + 2 + 3 summed is 6\")",
"// This example works just like the previous example above, but it uses\n// an anonymous `add` function as its parameter, instead of declaring a\n// named function outside.\narr = [1, 2, 3]\nsum = reduce(arr, 0, (i, result_so_far) {\n return i + result_so_far\n})\n\n// We use `assertEqual` to check that our `sum` function gives the\n// expected result. It's good to check your work!\nassertEqual(sum, 6, 0.00001, \"1 + 2 + 3 summed is 6\")",
"// Declare a function that sketches a decagon.\nfn decagon(radius) {\n // Each side of the decagon is turned this many degrees from the previous angle.\n stepAngle = 1 / 10 * tau()\n\n // Start the decagon sketch at this point.\n startOfDecagonSketch = startSketchAt([cos(0) * radius, sin(0) * radius])\n\n // Use a `reduce` to draw the remaining decagon sides.\n // For each number in the array 1..10, run the given function,\n // which takes a partially-sketched decagon and adds one more edge to it.\n fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) {\n // Draw one edge of the decagon.\n x = cos(stepAngle * i) * radius\n y = sin(stepAngle * i) * radius\n return lineTo([x, y], partialDecagon)\n })\n\n return fullDecagon\n}\n\n/* The `decagon` above is basically like this pseudo-code:\nfn decagon(radius):\n let stepAngle = (1/10) * tau()\n let startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])\n\n // Here's the reduce part.\n let partialDecagon = startOfDecagonSketch\n for i in [1..10]:\n let x = cos(stepAngle * i) * radius\n let y = sin(stepAngle * i) * radius\n partialDecagon = lineTo([x, y], partialDecagon)\n fullDecagon = partialDecagon // it's now full\n return fullDecagon */\n\n\n// Use the `decagon` function declared above, to sketch a decagon with radius 5.\ndecagon(5.0)\n |> close(%)"
]
},
{
@ -171633,7 +171553,7 @@
"unpublished": false,
"deprecated": false,
"examples": [
"w = 15\ncube = startSketchAt([0, 0])\n |> line([w, 0], %, $line1)\n |> line([0, w], %, $line2)\n |> line([-w, 0], %, $line3)\n |> line([0, -w], %, $line4)\n |> close(%)\n |> extrude(5, %)\n\nfn cylinder = (radius, tag) => {\n return startSketchAt([0, 0])\n |> circle({\n radius = radius,\n center = segEnd(tag)\n }, %)\n |> extrude(radius, %)\n}\n\ncylinder(1, line1)\ncylinder(2, line2)\ncylinder(3, line3)\ncylinder(4, line4)"
"w = 15\ncube = startSketchAt([0, 0])\n |> line([w, 0], %, $line1)\n |> line([0, w], %, $line2)\n |> line([-w, 0], %, $line3)\n |> line([0, -w], %, $line4)\n |> close(%)\n |> extrude(5, %)\n\nfn cylinder(radius, tag) {\n return startSketchAt([0, 0])\n |> circle({\n radius = radius,\n center = segEnd(tag)\n }, %)\n |> extrude(radius, %)\n}\n\ncylinder(1, line1)\ncylinder(2, line2)\ncylinder(3, line3)\ncylinder(4, line4)"
]
},
{
@ -175250,7 +175170,7 @@
"unpublished": false,
"deprecated": false,
"examples": [
"w = 15\ncube = startSketchAt([0, 0])\n |> line([w, 0], %, $line1)\n |> line([0, w], %, $line2)\n |> line([-w, 0], %, $line3)\n |> line([0, -w], %, $line4)\n |> close(%)\n |> extrude(5, %)\n\nfn cylinder = (radius, tag) => {\n return startSketchAt([0, 0])\n |> circle({\n radius = radius,\n center = segStart(tag)\n }, %)\n |> extrude(radius, %)\n}\n\ncylinder(1, line1)\ncylinder(2, line2)\ncylinder(3, line3)\ncylinder(4, line4)"
"w = 15\ncube = startSketchAt([0, 0])\n |> line([w, 0], %, $line1)\n |> line([0, w], %, $line2)\n |> line([-w, 0], %, $line3)\n |> line([0, -w], %, $line4)\n |> close(%)\n |> extrude(5, %)\n\nfn cylinder(radius, tag) {\n return startSketchAt([0, 0])\n |> circle({\n radius = radius,\n center = segStart(tag)\n }, %)\n |> extrude(radius, %)\n}\n\ncylinder(1, line1)\ncylinder(2, line2)\ncylinder(3, line3)\ncylinder(4, line4)"
]
},
{

View File

@ -41,7 +41,7 @@ If you want to get a value from an array you can use the index like so:
An object is defined with `{}` braces. Here is an example object:
```
myObj = {a: 0, b: "thing"}
myObj = { a = 0, b = "thing" }
```
We support two different ways of getting properties from objects, you can call
@ -90,12 +90,12 @@ startSketchOn('XZ')
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001, %) - 90,
segAng(rectangleSegmentA001) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001, %),
-segLen(rectangleSegmentA001, %)
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
@ -120,18 +120,18 @@ However if the code was written like this:
```
fn rect = (origin) => {
return startSketchOn('XZ')
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001, %) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001, %),
-segLen(rectangleSegmentA001, %)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
}
rect([0, 0])
@ -148,26 +148,29 @@ For example the following code works.
```
fn rect = (origin) => {
return startSketchOn('XZ')
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001, %) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001, %),
-segLen(rectangleSegmentA001, %)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
}
rect([0, 0])
myRect = rect([20, 0])
myRect
myRect
|> extrude(10, %)
|> fillet({radius: 0.5, tags: [myRect.tags.rectangleSegmentA001]}, %)
|> fillet({
radius = 0.5,
tags = [myRect.tags.rectangleSegmentA001]
}, %)
```
See how we use the tag `rectangleSegmentA001` in the `fillet` function outside

View File

@ -85,7 +85,6 @@ layout: manual
| `type` |enum: `CallExpression`| | No |
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
| `optional` |`boolean`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

View File

@ -125,7 +125,6 @@ An expression can be evaluated to yield a single KCL value.
| `type` |enum: `CallExpression`| | No |
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No |
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
| `optional` |`boolean`| | No |
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
| `start` |`integer`| | No |
| `end` |`integer`| | No |

View File

@ -28,6 +28,7 @@ type SceneSerialised = {
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
type DblClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
type DragFromHandler = (
dragParams: mouseDragFromParams
@ -68,7 +69,7 @@ export class SceneFixture {
x: number,
y: number,
{ steps }: { steps: number } = { steps: 20 }
): [ClickHandler, MoveHandler] =>
): [ClickHandler, MoveHandler, DblClickHandler] =>
[
(clickParams?: mouseParams) => {
if (clickParams?.pixelDiff) {
@ -90,6 +91,16 @@ export class SceneFixture {
}
return this.page.mouse.move(x, y, { steps })
},
(clickParams?: mouseParams) => {
if (clickParams?.pixelDiff) {
return doAndWaitForImageDiff(
this.page,
() => this.page.mouse.dblclick(x, y),
clickParams.pixelDiff
)
}
return this.page.mouse.dblclick(x, y)
},
] as const
makeDragHelpers = (
x: number,

View File

@ -552,6 +552,82 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
})
})
test(`Verify user can double-click to edit a sketch`, async ({
app,
editor,
toolbar,
scene,
}) => {
const initialCode = `closedSketch = startSketchOn('XZ')
|> circle({ center = [8, 5], radius = 2 }, %)
openSketch = startSketchOn('XY')
|> startProfileAt([-5, 0], %)
|> lineTo([0, 5], %)
|> xLine(5, %)
|> tangentialArcTo([10, 0], %)
`
await app.initialise(initialCode)
const pointInsideCircle = {
x: app.viewPortSize.width * 0.63,
y: app.viewPortSize.height * 0.5,
}
const pointOnPathAfterSketching = {
x: app.viewPortSize.width * 0.58,
y: app.viewPortSize.height * 0.5,
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_clickOpenPath, moveToOpenPath, dblClickOpenPath] =
scene.makeMouseHelpers(
pointOnPathAfterSketching.x,
pointOnPathAfterSketching.y
)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_clickCircle, moveToCircle, dblClickCircle] = scene.makeMouseHelpers(
pointInsideCircle.x,
pointInsideCircle.y
)
const exitSketch = async () => {
await test.step(`Exit sketch mode`, async () => {
await toolbar.exitSketchBtn.click()
await expect(toolbar.exitSketchBtn).not.toBeVisible()
await expect(toolbar.startSketchBtn).toBeEnabled()
})
}
await test.step(`Double-click on the closed sketch`, async () => {
await moveToCircle()
await dblClickCircle()
await expect(toolbar.startSketchBtn).not.toBeVisible()
await expect(toolbar.exitSketchBtn).toBeVisible()
await editor.expectState({
activeLines: [`|>circle({center=[8,5],radius=2},%)`],
highlightedCode: 'circle({center=[8,5],radius=2},%)',
diagnostics: [],
})
})
await exitSketch()
await test.step(`Double-click on the open sketch`, async () => {
await moveToOpenPath()
await scene.expectPixelColor([250, 250, 250], pointOnPathAfterSketching, 15)
// There is a full execution after exiting sketch that clears the scene.
await app.page.waitForTimeout(500)
await dblClickOpenPath()
await expect(toolbar.startSketchBtn).not.toBeVisible()
await expect(toolbar.exitSketchBtn).toBeVisible()
// Wait for enter sketch mode to complete
await app.page.waitForTimeout(500)
await editor.expectState({
activeLines: [`|>xLine(5,%)`],
highlightedCode: 'xLine(5,%)',
diagnostics: [],
})
})
})
test(`Offset plane point-and-click`, async ({
app,
scene,

View File

@ -18,6 +18,8 @@ import { useRouteLoaderData } from 'react-router-dom'
import { PATHS } from 'lib/paths'
import { IndexLoaderData } from 'lib/types'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { err, reportRejection } from 'lib/trap'
import { getArtifactOfTypes } from 'lang/std/artifactGraph'
enum StreamState {
Playing = 'playing',
@ -280,12 +282,49 @@ export const Stream = () => {
}
}
/**
* On double-click of sketch entities we automatically enter sketch mode with the selected sketch,
* allowing for quick editing of sketches. TODO: This should be moved to a more central place.
*/
const enterSketchModeIfSelectingSketch: MouseEventHandler<HTMLDivElement> = (
e
) => {
if (
!isNetworkOkay ||
!videoRef.current ||
state.matches('Sketch') ||
state.matches({ idle: 'showPlanes' }) ||
sceneInfra.camControls.wasDragging === true ||
!btnName(e.nativeEvent).left
) {
return
}
sendSelectEventToEngine(e, videoRef.current)
.then(({ entity_id }) => {
if (!entity_id) {
// No entity selected. This is benign
return
}
const path = getArtifactOfTypes(
{ key: entity_id, types: ['path', 'solid2D', 'segment'] },
engineCommandManager.artifactGraph
)
if (err(path)) {
return path
}
sceneInfra.modelingSend({ type: 'Enter sketch' })
})
.catch(reportRejection)
}
return (
<div
className="absolute inset-0 z-0"
id="stream"
data-testid="stream"
onClick={handleMouseUp}
onDoubleClick={enterSketchModeIfSelectingSketch}
onContextMenu={(e) => e.preventDefault()}
onContextMenuCapture={(e) => e.preventDefault()}
>

View File

@ -384,7 +384,6 @@ const myVar = funcN(1, 2)`
raw: '2',
},
],
optional: false,
},
},
],
@ -465,7 +464,6 @@ describe('testing pipe operator special', () => {
],
},
],
optional: false,
},
{
type: 'CallExpression',
@ -508,7 +506,6 @@ describe('testing pipe operator special', () => {
end: 60,
},
],
optional: false,
},
{
type: 'CallExpression',
@ -556,7 +553,6 @@ describe('testing pipe operator special', () => {
value: 'myPath',
},
],
optional: false,
},
{
type: 'CallExpression',
@ -598,7 +594,6 @@ describe('testing pipe operator special', () => {
end: 115,
},
],
optional: false,
},
{
type: 'CallExpression',
@ -625,7 +620,6 @@ describe('testing pipe operator special', () => {
end: 130,
},
],
optional: false,
},
],
},
@ -711,7 +705,6 @@ describe('testing pipe operator special', () => {
end: 35,
},
],
optional: false,
},
],
},
@ -1765,7 +1758,6 @@ describe('test UnaryExpression', () => {
raw: '100',
},
],
optional: false,
},
})
})
@ -1837,11 +1829,9 @@ describe('testing nested call expressions', () => {
raw: '3',
},
],
optional: false,
},
},
],
optional: false,
})
})
})
@ -1879,7 +1869,6 @@ describe('should recognise callExpresions in binaryExpressions', () => {
name: 'seg02',
},
],
optional: false,
},
right: {
type: 'Literal',

View File

@ -727,7 +727,6 @@ export function createCallExpressionStdLib(
name,
},
optional: false,
arguments: args,
}
}
@ -749,7 +748,6 @@ export function createCallExpression(
name,
},
optional: false,
arguments: args,
}
}

View File

@ -63,7 +63,7 @@ log(5, myVar)
})
it('function declaration with call', () => {
const code = [
'fn funcN = (a, b) => {',
'fn funcN(a, b) {',
' return a + b',
'}',
'theVar = 60',
@ -101,7 +101,7 @@ log(5, myVar)
})
it('recast BinaryExpression piped into CallExpression', () => {
const code = [
'fn myFn = (a) => {',
'fn myFn(a) {',
' return a + 1',
'}',
'myVar = 5 + 1',
@ -245,7 +245,7 @@ key = 'c'
expect(recasted).toBe(code)
})
it('comments in a fn block', () => {
const code = `fn myFn = () => {
const code = `fn myFn() {
// this is a comment
yo = { a = { b = { c = '123' } } }

View File

@ -1,382 +0,0 @@
import { lexer, initPromise } from './wasm'
import { err } from 'lib/trap'
beforeAll(async () => {
await initPromise
})
describe('testing lexer', () => {
it('async lexer works too', async () => {
const code = '1 + 2'
const code2 = `const yo = {key: 'value'}`
const code3 = `const yo = 45 /* this is a comment
const ya = 6 */
const yi=45`
expect(lexer(code)).toEqual(lexer(code))
expect(lexer(code2)).toEqual(lexer(code2))
expect(lexer(code3)).toEqual(lexer(code3))
})
it('test lexer', () => {
expect(stringSummaryLexer('1 + 2')).toEqual([
"number '1' from 0 to 1",
"whitespace ' ' from 1 to 3",
"operator '+' from 3 to 4",
"whitespace ' ' from 4 to 5",
"number '2' from 5 to 6",
])
expect(stringSummaryLexer('54 + 22500 + 6')).toEqual([
"number '54' from 0 to 2",
"whitespace ' ' from 2 to 3",
"operator '+' from 3 to 4",
"whitespace ' ' from 4 to 5",
"number '22500' from 5 to 10",
"whitespace ' ' from 10 to 11",
"operator '+' from 11 to 12",
"whitespace ' ' from 12 to 13",
"number '6' from 13 to 14",
])
expect(stringSummaryLexer('a + bo + t5 - 6')).toEqual([
"word 'a' from 0 to 1",
"whitespace ' ' from 1 to 2",
"operator '+' from 2 to 3",
"whitespace ' ' from 3 to 4",
"word 'bo' from 4 to 6",
"whitespace ' ' from 6 to 7",
"operator '+' from 7 to 8",
"whitespace ' ' from 8 to 9",
"word 't5' from 9 to 11",
"whitespace ' ' from 11 to 12",
"operator '-' from 12 to 13",
"whitespace ' ' from 13 to 14",
"number '6' from 14 to 15",
])
expect(stringSummaryLexer('a + "a str" - 6')).toEqual([
"word 'a' from 0 to 1",
"whitespace ' ' from 1 to 2",
"operator '+' from 2 to 3",
"whitespace ' ' from 3 to 4",
'string \'"a str"\' from 4 to 11',
"whitespace ' ' from 11 to 12",
"operator '-' from 12 to 13",
"whitespace ' ' from 13 to 14",
"number '6' from 14 to 15",
])
expect(stringSummaryLexer("a + 'str'")).toEqual([
"word 'a' from 0 to 1",
"whitespace ' ' from 1 to 2",
"operator '+' from 2 to 3",
"whitespace ' ' from 3 to 4",
"string ''str'' from 4 to 9",
])
expect(stringSummaryLexer("a +'str'")).toEqual([
"word 'a' from 0 to 1",
"whitespace ' ' from 1 to 2",
"operator '+' from 2 to 3",
"string ''str'' from 3 to 8",
])
expect(stringSummaryLexer('a + (sick)')).toEqual([
"word 'a' from 0 to 1",
"whitespace ' ' from 1 to 2",
"operator '+' from 2 to 3",
"whitespace ' ' from 3 to 4",
"brace '(' from 4 to 5",
"word 'sick' from 5 to 9",
"brace ')' from 9 to 10",
])
expect(stringSummaryLexer('a + { sick}')).toEqual([
"word 'a' from 0 to 1",
"whitespace ' ' from 1 to 2",
"operator '+' from 2 to 3",
"whitespace ' ' from 3 to 4",
"brace '{' from 4 to 5",
"whitespace ' ' from 5 to 6",
"word 'sick' from 6 to 10",
"brace '}' from 10 to 11",
])
expect(stringSummaryLexer("log('hi')")).toEqual([
"word 'log' from 0 to 3",
"brace '(' from 3 to 4",
"string ''hi'' from 4 to 8",
"brace ')' from 8 to 9",
])
expect(stringSummaryLexer("log('hi', 'hello')")).toEqual([
"word 'log' from 0 to 3",
"brace '(' from 3 to 4",
"string ''hi'' from 4 to 8",
"comma ',' from 8 to 9",
"whitespace ' ' from 9 to 10",
"string ''hello'' from 10 to 17",
"brace ')' from 17 to 18",
])
expect(stringSummaryLexer('fn funcName = (param1, param2) => {}')).toEqual([
"keyword 'fn' from 0 to 2",
"whitespace ' ' from 2 to 3",
"word 'funcName' from 3 to 11",
"whitespace ' ' from 11 to 12",
"operator '=' from 12 to 13",
"whitespace ' ' from 13 to 14",
"brace '(' from 14 to 15",
"word 'param1' from 15 to 21",
"comma ',' from 21 to 22",
"whitespace ' ' from 22 to 23",
"word 'param2' from 23 to 29",
"brace ')' from 29 to 30",
"whitespace ' ' from 30 to 31",
"operator '=>' from 31 to 33",
"whitespace ' ' from 33 to 34",
"brace '{' from 34 to 35",
"brace '}' from 35 to 36",
])
})
it('test negative and decimal numbers', () => {
expect(stringSummaryLexer('-1')).toEqual([
"operator '-' from 0 to 1",
"number '1' from 1 to 2",
])
expect(stringSummaryLexer('-1.5')).toEqual([
"operator '-' from 0 to 1",
"number '1.5' from 1 to 4",
])
expect(stringSummaryLexer('1.5')).toEqual([
"number '1.5' from 0 to 3",
])
expect(stringSummaryLexer('1.5 + 2.5')).toEqual([
"number '1.5' from 0 to 3",
"whitespace ' ' from 3 to 4",
"operator '+' from 4 to 5",
"whitespace ' ' from 5 to 6",
"number '2.5' from 6 to 9",
])
expect(stringSummaryLexer('1.5 - 2.5')).toEqual([
"number '1.5' from 0 to 3",
"whitespace ' ' from 3 to 4",
"operator '-' from 4 to 5",
"whitespace ' ' from 5 to 6",
"number '2.5' from 6 to 9",
])
expect(stringSummaryLexer('1.5 + -2.5')).toEqual([
"number '1.5' from 0 to 3",
"whitespace ' ' from 3 to 4",
"operator '+' from 4 to 5",
"whitespace ' ' from 5 to 6",
"operator '-' from 6 to 7",
"number '2.5' from 7 to 10",
])
expect(stringSummaryLexer('-1.5 + 2.5')).toEqual([
"operator '-' from 0 to 1",
"number '1.5' from 1 to 4",
"whitespace ' ' from 4 to 5",
"operator '+' from 5 to 6",
"whitespace ' ' from 6 to 7",
"number '2.5' from 7 to 10",
])
})
it('testing piping operator', () => {
const result = stringSummaryLexer(`sketch mySketch {
lineTo(2, 3)
} |> rx(45, %)`)
expect(result).toEqual([
"type 'sketch' from 0 to 6",
"whitespace ' ' from 6 to 7",
"word 'mySketch' from 7 to 15",
"whitespace ' ' from 15 to 16",
"brace '{' from 16 to 17",
"whitespace '\n ' from 17 to 24",
"word 'lineTo' from 24 to 30",
"brace '(' from 30 to 31",
"number '2' from 31 to 32",
"comma ',' from 32 to 33",
"whitespace ' ' from 33 to 34",
"number '3' from 34 to 35",
"brace ')' from 35 to 36",
"whitespace '\n ' from 36 to 41",
"brace '}' from 41 to 42",
"whitespace ' ' from 42 to 43",
"operator '|>' from 43 to 45",
"whitespace ' ' from 45 to 46",
"word 'rx' from 46 to 48",
"brace '(' from 48 to 49",
"number '45' from 49 to 51",
"comma ',' from 51 to 52",
"whitespace ' ' from 52 to 53",
"operator '%' from 53 to 54",
"brace ')' from 54 to 55",
])
})
it('testing array declaration', () => {
const result = stringSummaryLexer(`const yo = [1, 2]`)
expect(result).toEqual([
"keyword 'const' from 0 to 5",
"whitespace ' ' from 5 to 6",
"word 'yo' from 6 to 8",
"whitespace ' ' from 8 to 9",
"operator '=' from 9 to 10",
"whitespace ' ' from 10 to 11",
"brace '[' from 11 to 12",
"number '1' from 12 to 13",
"comma ',' from 13 to 14",
"whitespace ' ' from 14 to 15",
"number '2' from 15 to 16",
"brace ']' from 16 to 17",
])
})
it('testing object declaration', () => {
const result = stringSummaryLexer(`const yo = {key: 'value'}`)
expect(result).toEqual([
"keyword 'const' from 0 to 5",
"whitespace ' ' from 5 to 6",
"word 'yo' from 6 to 8",
"whitespace ' ' from 8 to 9",
"operator '=' from 9 to 10",
"whitespace ' ' from 10 to 11",
"brace '{' from 11 to 12",
"word 'key' from 12 to 15",
"colon ':' from 15 to 16",
"whitespace ' ' from 16 to 17",
"string ''value'' from 17 to 24",
"brace '}' from 24 to 25",
])
})
it('testing object property access', () => {
const result = stringSummaryLexer(`const yo = {key: 'value'}
const prop = yo.key
const prop2 = yo['key']
const key = 'key'
const prop3 = yo[key]`)
expect(result).toEqual([
"keyword 'const' from 0 to 5",
"whitespace ' ' from 5 to 6",
"word 'yo' from 6 to 8",
"whitespace ' ' from 8 to 9",
"operator '=' from 9 to 10",
"whitespace ' ' from 10 to 11",
"brace '{' from 11 to 12",
"word 'key' from 12 to 15",
"colon ':' from 15 to 16",
"whitespace ' ' from 16 to 17",
"string ''value'' from 17 to 24",
"brace '}' from 24 to 25",
"whitespace '\n' from 25 to 26",
"keyword 'const' from 26 to 31",
"whitespace ' ' from 31 to 32",
"word 'prop' from 32 to 36",
"whitespace ' ' from 36 to 37",
"operator '=' from 37 to 38",
"whitespace ' ' from 38 to 39",
"word 'yo' from 39 to 41",
"period '.' from 41 to 42",
"word 'key' from 42 to 45",
"whitespace '\n' from 45 to 46",
"keyword 'const' from 46 to 51",
"whitespace ' ' from 51 to 52",
"word 'prop2' from 52 to 57",
"whitespace ' ' from 57 to 58",
"operator '=' from 58 to 59",
"whitespace ' ' from 59 to 60",
"word 'yo' from 60 to 62",
"brace '[' from 62 to 63",
"string ''key'' from 63 to 68",
"brace ']' from 68 to 69",
"whitespace '\n' from 69 to 70",
"keyword 'const' from 70 to 75",
"whitespace ' ' from 75 to 76",
"word 'key' from 76 to 79",
"whitespace ' ' from 79 to 80",
"operator '=' from 80 to 81",
"whitespace ' ' from 81 to 82",
"string ''key'' from 82 to 87",
"whitespace '\n' from 87 to 88",
"keyword 'const' from 88 to 93",
"whitespace ' ' from 93 to 94",
"word 'prop3' from 94 to 99",
"whitespace ' ' from 99 to 100",
"operator '=' from 100 to 101",
"whitespace ' ' from 101 to 102",
"word 'yo' from 102 to 104",
"brace '[' from 104 to 105",
"word 'key' from 105 to 108",
"brace ']' from 108 to 109",
])
})
it('testing tokenising line comments', () => {
const result = stringSummaryLexer(`const yo = 45 // this is a comment
const yo = 6`)
expect(result).toEqual([
"keyword 'const' from 0 to 5",
"whitespace ' ' from 5 to 6",
"word 'yo' from 6 to 8",
"whitespace ' ' from 8 to 9",
"operator '=' from 9 to 10",
"whitespace ' ' from 10 to 11",
"number '45' from 11 to 13",
"whitespace ' ' from 13 to 14",
"lineComment '// this is a comment' from 14 to 34",
"whitespace '\n' from 34 to 35",
"keyword 'const' from 35 to 40",
"whitespace ' ' from 40 to 41",
"word 'yo' from 41 to 43",
"whitespace ' ' from 43 to 44",
"operator '=' from 44 to 45",
"whitespace ' ' from 45 to 46",
"number '6' from 46 to 47",
])
})
it('testing tokenising line comments by itself', () => {
const result = stringSummaryLexer(`log('hi')
// comment on a line by itself
const yo=45`)
expect(result).toEqual([
"word 'log' from 0 to 3",
"brace '(' from 3 to 4",
"string ''hi'' from 4 to 8",
"brace ')' from 8 to 9",
"whitespace '\n' from 9 to 10",
"lineComment '// comment on a line by itself' from 10 to 40",
"whitespace '\n' from 40 to 41",
"keyword 'const' from 41 to 46",
"whitespace ' ' from 46 to 47",
"word 'yo' from 47 to 49",
"operator '=' from 49 to 50",
"number '45' from 50 to 52",
])
})
it('testing tokenising block comments', () => {
const result = stringSummaryLexer(`const yo = 45 /* this is a comment
const ya = 6 */
const yi=45`)
expect(result).toEqual([
"keyword 'const' from 0 to 5",
"whitespace ' ' from 5 to 6",
"word 'yo' from 6 to 8",
"whitespace ' ' from 8 to 9",
"operator '=' from 9 to 10",
"whitespace ' ' from 10 to 11",
"number '45' from 11 to 13",
"whitespace ' ' from 13 to 14",
`blockComment '/* this is a comment
const ya = 6 */' from 14 to 50`,
"whitespace '\n' from 50 to 51",
"keyword 'const' from 51 to 56",
"whitespace ' ' from 56 to 57",
"word 'yi' from 57 to 59",
"operator '=' from 59 to 60",
"number '45' from 60 to 62",
])
})
})
// helpers
const stringSummaryLexer = (input: string) => {
const tokens = lexer(input)
if (err(tokens)) return []
return tokens.map(
({ type, value, start, end }) =>
`${type.padEnd(12, ' ')} ${`'${value}'`.padEnd(10, ' ')} from ${String(
start
).padEnd(3, ' ')} to ${end}`
)
}

View File

@ -3,7 +3,6 @@ import init, {
recast_wasm,
execute_wasm,
kcl_lint,
lexer_wasm,
modify_ast_for_sketch_wasm,
is_points_ccw,
get_tangential_arc_to_info,
@ -24,7 +23,6 @@ import { EngineCommandManager } from './std/engineConnection'
import { Discovered } from '../wasm-lib/kcl/bindings/Discovered'
import { KclValue } from '../wasm-lib/kcl/bindings/KclValue'
import type { Program } from '../wasm-lib/kcl/bindings/Program'
import type { Token } from '../wasm-lib/kcl/bindings/Token'
import { Coords2d } from './std/sketch'
import { fileSystemManager } from 'lang/std/fileSystemManager'
import { CoreDumpInfo } from 'wasm-lib/kcl/bindings/CoreDumpInfo'
@ -507,10 +505,6 @@ export const modifyGrid = async (
}
}
export function lexer(str: string): Token[] | Error {
return lexer_wasm(str)
}
export const modifyAstForSketch = async (
engineCommandManager: EngineCommandManager,
ast: Node<Program>,

View File

@ -774,6 +774,22 @@ dependencies = [
"syn 2.0.87",
]
[[package]]
name = "dhat"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cd11d84628e233de0ce467de10b8633f4ddaecafadefc86e13b84b8739b827"
dependencies = [
"backtrace",
"lazy_static",
"mintex",
"parking_lot 0.12.3",
"rustc-hash 1.1.0",
"serde",
"serde_json",
"thousands",
]
[[package]]
name = "diff"
version = "0.1.13"
@ -1705,6 +1721,7 @@ dependencies = [
"dashmap 6.1.0",
"databake",
"derive-docs",
"dhat",
"expectorate",
"fnv",
"form_urlencoded",
@ -1749,6 +1766,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"web-time",
"winnow",
"zip",
]
@ -2056,6 +2074,12 @@ version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff"
[[package]]
name = "mintex"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07"
[[package]]
name = "mio"
version = "1.0.2"
@ -2644,7 +2668,7 @@ dependencies = [
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustc-hash 2.0.0",
"rustls",
"socket2",
"thiserror 1.0.68",
@ -2661,7 +2685,7 @@ dependencies = [
"bytes",
"rand 0.8.5",
"ring",
"rustc-hash",
"rustc-hash 2.0.0",
"rustls",
"slab",
"thiserror 1.0.68",
@ -3001,6 +3025,12 @@ version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hash"
version = "2.0.0"
@ -3637,6 +3667,12 @@ dependencies = [
"syn 2.0.87",
]
[[package]]
name = "thousands"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
[[package]]
name = "thread_local"
version = "1.1.8"

View File

@ -21,6 +21,7 @@ convert_case = "0.6.0"
dashmap = "6.1.0"
databake = { version = "0.1.8", features = ["derive"] }
derive-docs = { version = "0.1.29", path = "../derive-docs" }
dhat = { version = "0.3", optional = true }
fnv = "1.0.7"
form_urlencoded = "1.2.1"
futures = { version = "0.3.31" }
@ -51,6 +52,7 @@ url = { version = "2.5.3", features = ["serde"] }
urlencoding = "2.1.3"
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
validator = { version = "0.19.0", features = ["derive"] }
web-time = "1.1"
winnow = "0.6.18"
zip = { version = "2.0.0", default-features = false }
@ -72,6 +74,7 @@ tower-lsp = { version = "0.20.0", features = ["proposed"] }
[features]
default = ["engine"]
cli = ["dep:clap"]
dhat-heap = ["dep:dhat"]
# For the lsp server, when run with stdout for rpc we want to disable println.
# This is used for editor extensions that use the lsp server.
disable-println = []

View File

@ -644,7 +644,7 @@ pub enum Expr {
impl Expr {
pub fn get_lsp_folding_range(&self) -> Option<FoldingRange> {
let recasted = self.recast(&FormatOptions::default(), 0, false);
let recasted = self.recast(&FormatOptions::default(), 0, crate::unparser::ExprContext::Other);
// If the code only has one line then we don't need to fold it.
if recasted.lines().count() <= 1 {
return None;
@ -1181,7 +1181,7 @@ impl Node<ImportItem> {
self.alias = Some(Identifier::new(new_name));
}
// Return implicit name.
return Some(self.identifier().to_owned());
Some(self.identifier().to_owned())
}
}
}
@ -1264,7 +1264,6 @@ pub struct ExpressionStatement {
pub struct CallExpression {
pub callee: Node<Identifier>,
pub arguments: Vec<Expr>,
pub optional: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
@ -1301,7 +1300,6 @@ impl CallExpression {
Ok(Node::no_src(Self {
callee: Identifier::new(name),
arguments,
optional: false,
digest: None,
}))
}
@ -3069,9 +3067,9 @@ ghi("things")
folding_ranges[1].collapsed_text,
Some("startSketchOn('XY')".to_string())
);
assert_eq!(folding_ranges[2].start_line, 390);
assert_eq!(folding_ranges[2].start_line, 384);
assert_eq!(folding_ranges[2].end_line, 403);
assert_eq!(folding_ranges[2].collapsed_text, Some("fn ghi = (x) => {".to_string()));
assert_eq!(folding_ranges[2].collapsed_text, Some("fn ghi(x) {".to_string()));
}
#[test]
@ -3270,7 +3268,7 @@ const cylinder = startSketchOn('-XZ')
#[tokio::test(flavor = "multi_thread")]
async fn test_parse_return_type_on_functions() {
let some_program_string = r#"fn thing = () => {thing: number, things: string[], more?: string} {
let some_program_string = r#"fn thing(): {thing: number, things: string[], more?: string} {
return 1
}"#;
let module_id = ModuleId::default();
@ -3296,8 +3294,8 @@ const cylinder = startSketchOn('-XZ')
name: "thing".to_owned(),
digest: None
},
13,
18,
23,
module_id,
),
type_: Some(FnArgType::Primitive(FnArgPrimitive::Number)),
@ -3310,8 +3308,8 @@ const cylinder = startSketchOn('-XZ')
name: "things".to_owned(),
digest: None
},
33,
39,
28,
34,
module_id,
),
type_: Some(FnArgType::Array(FnArgPrimitive::String)),
@ -3324,8 +3322,8 @@ const cylinder = startSketchOn('-XZ')
name: "more".to_owned(),
digest: None
},
51,
55,
46,
50,
module_id,
),
type_: Some(FnArgType::Primitive(FnArgPrimitive::String)),

View File

@ -369,7 +369,6 @@ impl CallExpression {
for argument in slf.arguments.iter_mut() {
hasher.update(argument.compute_digest());
}
hasher.update(if slf.optional { [1] } else { [0] });
});
}

View File

@ -282,8 +282,8 @@ impl EngineConnection {
}
Err(e) => {
match &e {
WebSocketReadError::Read(e) => eprintln!("could not read from WS: {:?}", e),
WebSocketReadError::Deser(e) => eprintln!("could not deserialize msg from WS: {:?}", e),
WebSocketReadError::Read(e) => crate::logln!("could not read from WS: {:?}", e),
WebSocketReadError::Deser(e) => crate::logln!("could not deserialize msg from WS: {:?}", e),
}
*socket_health_tcp_read.lock().unwrap() = SocketHealth::Inactive;
return Err(e);

View File

@ -197,24 +197,17 @@ pub struct Environment {
parent: Option<EnvironmentRef>,
}
const NO_META: Vec<Metadata> = Vec::new();
impl Environment {
pub fn root() -> Self {
Self {
// Prelude
bindings: HashMap::from([
("ZERO".to_string(), KclValue::from_number(0.0, Default::default())),
(
"QUARTER_TURN".to_string(),
KclValue::from_number(90.0, Default::default()),
),
(
"HALF_TURN".to_string(),
KclValue::from_number(180.0, Default::default()),
),
(
"THREE_QUARTER_TURN".to_string(),
KclValue::from_number(270.0, Default::default()),
),
("ZERO".to_string(), KclValue::from_number(0.0, NO_META)),
("QUARTER_TURN".to_string(), KclValue::from_number(90.0, NO_META)),
("HALF_TURN".to_string(), KclValue::from_number(180.0, NO_META)),
("THREE_QUARTER_TURN".to_string(), KclValue::from_number(270.0, NO_META)),
]),
parent: None,
}
@ -1916,6 +1909,7 @@ impl ExecutorContext {
program: &Program,
exec_state: &mut ExecState,
) -> Result<Option<ModelingSessionData>, KclError> {
let _stats = crate::log::LogPerfStats::new("Interpretation");
// TODO: Use the top-level file's path.
exec_state.add_module(std::path::PathBuf::from(""));
// Before we even start executing the program, set the units.

View File

@ -37,7 +37,7 @@ impl<'a> FunctionParam<'a> {
}
}
impl<'a> JsonSchema for FunctionParam<'a> {
impl JsonSchema for FunctionParam<'_> {
fn schema_name() -> String {
"FunctionParam".to_owned()
}

View File

@ -86,83 +86,6 @@ pub enum KclValue {
},
}
impl KclValue {
pub(crate) fn metadata(&self) -> Vec<Metadata> {
match self {
KclValue::Uuid { value: _, meta } => meta.clone(),
KclValue::Bool { value: _, meta } => meta.clone(),
KclValue::Number { value: _, meta } => meta.clone(),
KclValue::Int { value: _, meta } => meta.clone(),
KclValue::String { value: _, meta } => meta.clone(),
KclValue::Array { value: _, meta } => meta.clone(),
KclValue::Object { value: _, meta } => meta.clone(),
KclValue::TagIdentifier(x) => x.meta.clone(),
KclValue::TagDeclarator(x) => vec![x.metadata()],
KclValue::Plane(x) => x.meta.clone(),
KclValue::Face(x) => x.meta.clone(),
KclValue::Sketch { value } => value.meta.clone(),
KclValue::Sketches { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
KclValue::Solid(x) => x.meta.clone(),
KclValue::Solids { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
KclValue::ImportedGeometry(x) => x.meta.clone(),
KclValue::Function { meta, .. } => meta.clone(),
KclValue::KclNone { meta, .. } => meta.clone(),
}
}
pub(crate) fn get_solid_set(&self) -> Result<SolidSet> {
match self {
KclValue::Solid(e) => Ok(SolidSet::Solid(e.clone())),
KclValue::Solids { value } => Ok(SolidSet::Solids(value.clone())),
KclValue::Array { value, .. } => {
let solids: Vec<_> = value
.iter()
.enumerate()
.map(|(i, v)| {
v.as_solid().map(|v| v.to_owned()).map(Box::new).ok_or_else(|| {
anyhow::anyhow!(
"expected this array to only contain solids, but element {i} was actually {}",
v.human_friendly_type()
)
})
})
.collect::<Result<_, _>>()?;
Ok(SolidSet::Solids(solids))
}
_ => anyhow::bail!("Not a solid or solids: {:?}", self),
}
}
/// Human readable type name used in error messages. Should not be relied
/// on for program logic.
pub(crate) fn human_friendly_type(&self) -> &'static str {
match self {
KclValue::Uuid { .. } => "Unique ID (uuid)",
KclValue::TagDeclarator(_) => "TagDeclarator",
KclValue::TagIdentifier(_) => "TagIdentifier",
KclValue::Solid(_) => "Solid",
KclValue::Solids { .. } => "Solids",
KclValue::Sketch { .. } => "Sketch",
KclValue::Sketches { .. } => "Sketches",
KclValue::ImportedGeometry(_) => "ImportedGeometry",
KclValue::Function { .. } => "Function",
KclValue::Plane(_) => "Plane",
KclValue::Face(_) => "Face",
KclValue::Bool { .. } => "boolean (true/false value)",
KclValue::Number { .. } => "number",
KclValue::Int { .. } => "integer",
KclValue::String { .. } => "string (text)",
KclValue::Array { .. } => "array (list)",
KclValue::Object { .. } => "object",
KclValue::KclNone { .. } => "None",
}
}
pub(crate) fn is_function(&self) -> bool {
matches!(self, KclValue::Function { .. })
}
}
impl From<SketchSet> for KclValue {
fn from(sg: SketchSet) -> Self {
match sg {
@ -251,8 +174,82 @@ impl From<&KclValue> for Vec<SourceRange> {
}
impl KclValue {
pub(crate) fn metadata(&self) -> Vec<Metadata> {
match self {
KclValue::Uuid { value: _, meta } => meta.clone(),
KclValue::Bool { value: _, meta } => meta.clone(),
KclValue::Number { value: _, meta } => meta.clone(),
KclValue::Int { value: _, meta } => meta.clone(),
KclValue::String { value: _, meta } => meta.clone(),
KclValue::Array { value: _, meta } => meta.clone(),
KclValue::Object { value: _, meta } => meta.clone(),
KclValue::TagIdentifier(x) => x.meta.clone(),
KclValue::TagDeclarator(x) => vec![x.metadata()],
KclValue::Plane(x) => x.meta.clone(),
KclValue::Face(x) => x.meta.clone(),
KclValue::Sketch { value } => value.meta.clone(),
KclValue::Sketches { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
KclValue::Solid(x) => x.meta.clone(),
KclValue::Solids { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
KclValue::ImportedGeometry(x) => x.meta.clone(),
KclValue::Function { meta, .. } => meta.clone(),
KclValue::KclNone { meta, .. } => meta.clone(),
}
}
pub(crate) fn get_solid_set(&self) -> Result<SolidSet> {
match self {
KclValue::Solid(e) => Ok(SolidSet::Solid(e.clone())),
KclValue::Solids { value } => Ok(SolidSet::Solids(value.clone())),
KclValue::Array { value, .. } => {
let solids: Vec<_> = value
.iter()
.enumerate()
.map(|(i, v)| {
v.as_solid().map(|v| v.to_owned()).map(Box::new).ok_or_else(|| {
anyhow::anyhow!(
"expected this array to only contain solids, but element {i} was actually {}",
v.human_friendly_type()
)
})
})
.collect::<Result<_, _>>()?;
Ok(SolidSet::Solids(solids))
}
_ => anyhow::bail!("Not a solid or solids: {:?}", self),
}
}
/// Human readable type name used in error messages. Should not be relied
/// on for program logic.
pub(crate) fn human_friendly_type(&self) -> &'static str {
match self {
KclValue::Uuid { .. } => "Unique ID (uuid)",
KclValue::TagDeclarator(_) => "TagDeclarator",
KclValue::TagIdentifier(_) => "TagIdentifier",
KclValue::Solid(_) => "Solid",
KclValue::Solids { .. } => "Solids",
KclValue::Sketch { .. } => "Sketch",
KclValue::Sketches { .. } => "Sketches",
KclValue::ImportedGeometry(_) => "ImportedGeometry",
KclValue::Function { .. } => "Function",
KclValue::Plane(_) => "Plane",
KclValue::Face(_) => "Face",
KclValue::Bool { .. } => "boolean (true/false value)",
KclValue::Number { .. } => "number",
KclValue::Int { .. } => "integer",
KclValue::String { .. } => "string (text)",
KclValue::Array { .. } => "array (list)",
KclValue::Object { .. } => "object",
KclValue::KclNone { .. } => "None",
}
}
pub(crate) fn is_function(&self) -> bool {
matches!(self, KclValue::Function { .. })
}
/// Put the number into a KCL value.
pub fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
pub const fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
Self::Number { value: f, meta }
}

View File

@ -8,11 +8,54 @@
#[allow(unused_macros)]
macro_rules! println {
($($rest:tt)*) => {
#[cfg(feature = "disable-println")]
{
let _ = format!($($rest)*);
}
#[cfg(not(feature = "disable-println"))]
std::println!($($rest)*)
}
}
#[allow(unused_macros)]
macro_rules! eprintln {
($($rest:tt)*) => {
#[cfg(feature = "disable-println")]
{
let _ = format!($($rest)*);
}
#[cfg(not(feature = "disable-println"))]
std::eprintln!($($rest)*)
}
}
#[allow(unused_macros)]
macro_rules! print {
($($rest:tt)*) => {
#[cfg(feature = "disable-println")]
{
let _ = format!($($rest)*);
}
#[cfg(not(feature = "disable-println"))]
std::print!($($rest)*)
}
}
#[allow(unused_macros)]
macro_rules! eprint {
($($rest:tt)*) => {
#[cfg(feature = "disable-println")]
{
let _ = format!($($rest)*);
}
#[cfg(not(feature = "disable-println"))]
std::eprint!($($rest)*)
}
}
#[cfg(feature = "dhat-heap")]
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;
mod ast;
mod coredump;
mod docs;
@ -23,6 +66,7 @@ mod fs;
mod function_param;
mod kcl_value;
pub mod lint;
mod log;
mod lsp;
mod parser;
mod settings;
@ -48,7 +92,6 @@ pub use lsp::copilot::Backend as CopilotLspBackend;
pub use lsp::kcl::Backend as KclLspBackend;
pub use lsp::kcl::Server as KclLspServerSubCommand;
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
pub use token::lexer;
// Rather than make executor public and make lots of it pub(crate), just re-export into a new module.
// Ideally we wouldn't export these things at all, they should only be used for testing.
@ -72,6 +115,8 @@ pub mod std_utils {
pub use crate::std::utils::{get_tangential_arc_to_info, is_points_ccw_wasm, TangentialArcInfoInput};
}
#[allow(unused_imports)]
use crate::log::{log, logln};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]

116
src/wasm-lib/kcl/src/log.rs Normal file
View File

@ -0,0 +1,116 @@
#![allow(dead_code)]
#[cfg(feature = "dhat-heap")]
use dhat::{HeapStats, Profiler};
use std::env;
use web_time::Instant;
const LOG_ENV_VAR: &str = "ZOO_LOG";
lazy_static::lazy_static! {
static ref ENABLED: bool = {
let env_var = env::var(LOG_ENV_VAR);
let Ok(env_var) = env_var else {
return false;
};
!env_var.is_empty()
};
}
#[cfg(feature = "dhat-heap")]
lazy_static::lazy_static! {
static ref PROFILER: Profiler = Profiler::builder().testing().build();
}
/// Log a message
pub(crate) fn log(msg: impl Into<String>) {
if *ENABLED {
log_inner(msg.into());
}
}
#[allow(unused_macros)]
macro_rules! logln {
($($rest:tt)*) => {
crate::log::log(format!($($rest)*))
}
}
pub(crate) use logln;
#[cfg(not(feature = "disable-println"))]
#[inline]
fn log_inner(msg: String) {
eprintln!("{msg}");
}
#[cfg(all(feature = "disable-println", target_arch = "wasm32"))]
#[inline]
fn log_inner(msg: String) {
web_sys::console::log_1(&msg.into());
}
#[cfg(all(feature = "disable-println", not(target_arch = "wasm32")))]
#[inline]
fn log_inner(_msg: String) {}
/// A helper struct for recording and logging basic performance metrics.
///
/// It will log the metrics when dropped or if `log_now` is called.
pub(crate) struct LogPerfStats<'a> {
msg: &'a str,
start_time: Instant,
#[cfg(feature = "dhat-heap")]
start_stats: HeapStats,
cancelled: bool,
}
impl<'a> LogPerfStats<'a> {
#[cfg(not(feature = "dhat-heap"))]
pub fn new(msg: &'a str) -> Self {
LogPerfStats {
msg,
start_time: Instant::now(),
cancelled: false,
}
}
#[cfg(feature = "dhat-heap")]
pub fn new(msg: &'a str) -> Self {
lazy_static::initialize(&PROFILER);
LogPerfStats {
msg,
start_time: Instant::now(),
start_stats: HeapStats::get(),
cancelled: false,
}
}
pub fn log_now(&self) {
let time = Instant::now().duration_since(self.start_time).as_secs_f64() * 1000.0;
logln!("{}\n time: {time:.3}ms", self.msg);
#[cfg(feature = "dhat-heap")]
{
let stats = HeapStats::get();
let blocks = stats.total_blocks - self.start_stats.total_blocks;
let bytes = (stats.total_bytes - self.start_stats.total_bytes) as f64 / 1_000_000.0;
let cur = stats.curr_bytes as f64 / 1000.0;
let max = stats.curr_bytes as f64 / 1000.0;
logln!(" memory:");
logln!(" allocations: {bytes:.5} MB ({blocks} blocks)");
logln!(" currently allocated: {cur:.3} KB");
logln!(" max allocated: {max:.3} KB");
}
}
/// After `cancel`ing, this object will not log its stats on drop (you can still can `log_now`).
pub fn cancel(&mut self) {
self.cancelled = true;
}
}
impl Drop for LogPerfStats<'_> {
fn drop(&mut self) {
if !self.cancelled {
self.log_now();
}
}
}

View File

@ -81,11 +81,16 @@ pub fn parse_tokens(tokens: Vec<Token>) -> ParseResult {
/// Invariants:
/// - if there are no errors, then the Option will be Some
/// - if the Option is None, then there will be at least one error in the ParseContext.
#[derive(Debug, Clone)]
pub(crate) struct ParseResult(pub Result<(Option<Node<Program>>, ParseContext), KclError>);
impl ParseResult {
#[cfg(test)]
#[track_caller]
pub fn unwrap(self) -> Node<Program> {
if self.0.is_err() || self.0.as_ref().unwrap().0.is_none() {
eprint!("{self:#?}");
}
self.0.unwrap().0.unwrap()
}

View File

@ -18,12 +18,14 @@ use crate::{
ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, Shebang, TagDeclarator,
UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
},
docs::StdLibFn,
errors::{KclError, KclErrorDetails},
executor::SourceRange,
parser::{
math::BinaryExpressionToken, parser_impl::error::ContextError, PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR,
},
token::{Token, TokenType},
unparser::ExprContext,
};
pub(crate) mod error;
@ -36,6 +38,7 @@ thread_local! {
pub type TokenSlice<'slice, 'input> = &'slice mut &'input [Token];
pub fn run_parser(i: TokenSlice) -> super::ParseResult {
let _stats = crate::log::LogPerfStats::new("Parsing");
ParseContext::init();
let result = program.parse(i).save_err();
@ -940,26 +943,46 @@ fn if_expr(i: TokenSlice) -> PResult<BoxNode<IfExpression>> {
))
}
fn function_expr(i: TokenSlice) -> PResult<Expr> {
let fn_tok = opt(fun).parse_next(i)?;
ignore_whitespace(i);
let (result, has_arrow) = function_decl.parse_next(i)?;
if fn_tok.is_none() && !has_arrow {
let err = KclError::Syntax(KclErrorDetails {
source_ranges: result.as_source_ranges(),
message: "Anonymous function requires `fn` before `(`".to_owned(),
});
return Err(ErrMode::Cut(err.into()));
}
Ok(Expr::FunctionExpression(Box::new(result)))
}
// Looks like
// (arg0, arg1) => {
// (arg0, arg1) {
// const x = arg0 + arg1;
// return x
// }
fn function_expression(i: TokenSlice) -> PResult<Node<FunctionExpression>> {
fn function_decl(i: TokenSlice) -> PResult<(Node<FunctionExpression>, bool)> {
fn return_type(i: TokenSlice) -> PResult<FnArgType> {
colon(i)?;
ignore_whitespace(i);
argument_type(i)
}
let open = open_paren(i)?;
let start = open.start;
let params = parameters(i)?;
close_paren(i)?;
ignore_whitespace(i);
big_arrow(i)?;
let arrow = opt(big_arrow).parse_next(i)?;
ignore_whitespace(i);
// Optional type arguments.
let return_type = opt(argument_type).parse_next(i)?;
// Optional return type.
let return_type = opt(return_type).parse_next(i)?;
ignore_whitespace(i);
open_brace(i)?;
let body = function_body(i)?;
let end = close_brace(i)?.end;
Ok(Node::new(
let result = Node::new(
FunctionExpression {
params,
body,
@ -969,7 +992,21 @@ fn function_expression(i: TokenSlice) -> PResult<Node<FunctionExpression>> {
start,
end,
open.module_id,
))
);
let has_arrow = if let Some(arrow) = arrow {
ParseContext::warn(ParseError::with_suggestion(
arrow.as_source_range(),
Some(result.as_source_range()),
"Unnecessary `=>` in function declaration",
Some(""),
));
true
} else {
false
};
Ok((result, has_arrow))
}
/// E.g. `person.name`
@ -1478,7 +1515,7 @@ fn expr_allowed_in_pipe_expr(i: TokenSlice) -> PResult<Expr> {
array,
object.map(Box::new).map(Expr::ObjectExpression),
pipe_sub.map(Box::new).map(Expr::PipeSubstitution),
function_expression.map(Box::new).map(Expr::FunctionExpression),
function_expr,
if_expr.map(Expr::IfExpression),
unnecessarily_bracketed,
))
@ -1543,31 +1580,40 @@ fn declaration(i: TokenSlice) -> PResult<BoxNode<VariableDeclaration>> {
let (kind, mut start, dec_end, module_id) = if let Some((kind, token)) = &decl_token {
(*kind, token.start, token.end, token.module_id)
} else {
// TODO warn on const
(VariableKind::Const, id.start, id.end, id.module_id)
};
if let Some(token) = visibility_token {
start = token.start;
}
ignore_whitespace(i);
equals(i)?;
// After this point, the parser is DEFINITELY parsing a variable declaration, because
// `fn`, `let`, `const` etc are all unambiguous. If you've parsed one of those tokens --
// and we certainly have because `kind` was parsed above -- then the following tokens
// MUST continue the variable declaration, otherwise the program is invalid.
//
// This means, from here until this function returns, any errors should be ErrMode::Cut,
// not ErrMode::Backtrack. Because the parser is definitely parsing a variable declaration.
// If there's an error, there's no point backtracking -- instead the parser should fail.
ignore_whitespace(i);
let val = if kind == VariableKind::Fn {
function_expression
.map(Box::new)
let eq = opt(equals).parse_next(i)?;
ignore_whitespace(i);
let val = function_decl
.map(|t| Box::new(t.0))
.map(Expr::FunctionExpression)
.context(expected("a KCL function expression, like () => { return 1 }"))
.parse_next(i)
.context(expected("a KCL function expression, like () { return 1 }"))
.parse_next(i);
if let Some(t) = eq {
let ctxt_end = val.as_ref().map(|e| e.end()).unwrap_or(t.end);
ParseContext::warn(ParseError::with_suggestion(
t.as_source_range(),
Some(SourceRange([id.start, ctxt_end, module_id.as_usize()])),
"Unnecessary `=` in function declaration",
Some(""),
));
}
val
} else {
equals(i)?;
ignore_whitespace(i);
expression
.try_map(|val| {
// Function bodies can be used if and only if declaring a function.
@ -1956,6 +2002,17 @@ fn question_mark(i: TokenSlice) -> PResult<()> {
Ok(())
}
fn fun(i: TokenSlice) -> PResult<Token> {
any.try_map(|token: Token| match token.token_type {
TokenType::Keyword if token.value == "fn" => Ok(token),
_ => Err(KclError::Syntax(KclErrorDetails {
source_ranges: token.as_source_ranges(),
message: format!("expected 'fn', found {}", token.value.as_str(),),
})),
})
.parse_next(i)
}
/// Parse a comma, optionally followed by some whitespace.
fn comma_sep(i: TokenSlice) -> PResult<()> {
(opt(whitespace), comma, opt(whitespace))
@ -1979,6 +2036,7 @@ fn arguments(i: TokenSlice) -> PResult<Vec<Expr>> {
fn argument_type(i: TokenSlice) -> PResult<FnArgType> {
let type_ = alt((
// Object types
// TODO it is buggy to treat object fields like parameters since the parameters parser assumes a terminating `)`.
(open_brace, parameters, close_brace).map(|(_, params, _)| Ok(FnArgType::Object { properties: params })),
// Array types
(one_of(TokenType::Type), open_bracket, close_bracket).map(|(token, _, _)| {
@ -2009,13 +2067,11 @@ fn argument_type(i: TokenSlice) -> PResult<FnArgType> {
}
fn parameter(i: TokenSlice) -> PResult<(Token, std::option::Option<FnArgType>, bool)> {
let (arg_name, optional, _, _, _, type_) = (
let (arg_name, optional, _, type_) = (
any.verify(|token: &Token| !matches!(token.token_type, TokenType::Brace) || token.value != ")"),
opt(question_mark),
opt(whitespace),
opt(colon),
opt(whitespace),
opt(argument_type),
opt((colon, opt(whitespace), argument_type).map(|tup| tup.2)),
)
.parse_next(i)?;
Ok((arg_name, type_, optional.is_some()))
@ -2091,55 +2147,65 @@ fn binding_name(i: TokenSlice) -> PResult<Node<Identifier>> {
.parse_next(i)
}
fn typecheck_all(std_fn: Box<dyn StdLibFn>, args: &[Expr]) -> PResult<()> {
// Type check the arguments.
for (i, spec_arg) in std_fn.args(false).iter().enumerate() {
let Some(arg) = &args.get(i) else {
// The executor checks the number of arguments, so we don't need to check it here.
continue;
};
typecheck(spec_arg, arg)?;
}
Ok(())
}
fn typecheck(spec_arg: &crate::docs::StdLibFnArg, arg: &&Expr) -> PResult<()> {
match spec_arg.type_.as_ref() {
"TagNode" => match &arg {
Expr::Identifier(_) => {
// These are fine since we want someone to be able to map a variable to a tag declarator.
}
Expr::TagDeclarator(tag) => {
// TODO: Remove this check. It should be redundant.
tag.clone()
.into_valid_binding_name()
.map_err(|e| ErrMode::Cut(ContextError::from(e)))?;
}
e => {
return Err(ErrMode::Cut(
KclError::Syntax(KclErrorDetails {
source_ranges: vec![SourceRange::from(*arg)],
message: format!("Expected a tag declarator like `$name`, found {:?}", e),
})
.into(),
));
}
},
"TagIdentifier" => match &arg {
Expr::Identifier(_) => {}
Expr::MemberExpression(_) => {}
e => {
return Err(ErrMode::Cut(
KclError::Syntax(KclErrorDetails {
source_ranges: vec![SourceRange::from(*arg)],
message: format!("Expected a tag identifier like `tagName`, found {:?}", e),
})
.into(),
));
}
},
_ => {}
}
Ok(())
}
fn fn_call(i: TokenSlice) -> PResult<Node<CallExpression>> {
let fn_name = identifier(i)?;
opt(whitespace).parse_next(i)?;
let _ = terminated(open_paren, opt(whitespace)).parse_next(i)?;
let args = arguments(i)?;
if let Some(std_fn) = crate::std::get_stdlib_fn(&fn_name.name) {
// Type check the arguments.
for (i, spec_arg) in std_fn.args(false).iter().enumerate() {
let Some(arg) = &args.get(i) else {
// The executor checks the number of arguments, so we don't need to check it here.
continue;
};
match spec_arg.type_.as_ref() {
"TagNode" => match &arg {
Expr::Identifier(_) => {
// These are fine since we want someone to be able to map a variable to a tag declarator.
}
Expr::TagDeclarator(tag) => {
// TODO: Remove this check. It should be redundant.
tag.clone()
.into_valid_binding_name()
.map_err(|e| ErrMode::Cut(ContextError::from(e)))?;
}
e => {
return Err(ErrMode::Cut(
KclError::Syntax(KclErrorDetails {
source_ranges: vec![SourceRange::from(*arg)],
message: format!("Expected a tag declarator like `$name`, found {:?}", e),
})
.into(),
));
}
},
"TagIdentifier" => match &arg {
Expr::Identifier(_) => {}
Expr::MemberExpression(_) => {}
e => {
return Err(ErrMode::Cut(
KclError::Syntax(KclErrorDetails {
source_ranges: vec![SourceRange::from(*arg)],
message: format!("Expected a tag identifier like `tagName`, found {:?}", e),
})
.into(),
));
}
},
_ => {}
}
}
typecheck_all(std_fn, &args)?;
}
let end = preceded(opt(whitespace), close_paren).parse_next(i)?.end;
@ -2147,7 +2213,7 @@ fn fn_call(i: TokenSlice) -> PResult<Node<CallExpression>> {
// so we'll hack this in here.
if fn_name.name == "int" {
assert_eq!(args.len(), 1);
let mut arg_str = args[0].recast(&crate::FormatOptions::default(), 0, false);
let mut arg_str = args[0].recast(&crate::FormatOptions::default(), 0, ExprContext::Other);
if arg_str.contains('.') && !arg_str.ends_with(".0") {
arg_str = format!("round({arg_str})");
}
@ -2166,7 +2232,6 @@ fn fn_call(i: TokenSlice) -> PResult<Node<CallExpression>> {
inner: CallExpression {
callee: fn_name,
arguments: args,
optional: false,
digest: None,
},
})
@ -2221,7 +2286,7 @@ mod tests {
#[test]
fn weird_program_unclosed_paren() {
let tokens = crate::token::lexer("fn firstPrime=(", ModuleId::default()).unwrap();
let tokens = crate::token::lexer("fn firstPrime(", ModuleId::default()).unwrap();
let last = tokens.last().unwrap();
let err: super::error::ErrorKind = program.parse(&tokens).unwrap_err().into();
let err = err.unwrap_parse_error();
@ -2273,7 +2338,7 @@ mod tests {
#[test]
fn test_comments_in_function1() {
let test_program = r#"() => {
let test_program = r#"() {
// comment 0
const a = 1
// comment 1
@ -2283,7 +2348,7 @@ mod tests {
}"#;
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
let mut slice = tokens.as_slice();
let expr = function_expression.parse_next(&mut slice).unwrap();
let expr = function_decl.map(|t| t.0).parse_next(&mut slice).unwrap();
assert_eq!(expr.params, vec![]);
let comment_start = expr.body.non_code_meta.start_nodes.first().unwrap();
let comment0 = &expr.body.non_code_meta.non_code_nodes.get(&0).unwrap()[0];
@ -2295,13 +2360,13 @@ mod tests {
#[test]
fn test_comments_in_function2() {
let test_program = r#"() => {
let test_program = r#"() {
const yo = { a = { b = { c = '123' } } } /* block
comment */
}"#;
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
let mut slice = tokens.as_slice();
let expr = function_expression.parse_next(&mut slice).unwrap();
let expr = function_decl.map(|t| t.0).parse_next(&mut slice).unwrap();
let comment0 = &expr.body.non_code_meta.non_code_nodes.get(&0).unwrap()[0];
assert_eq!(comment0.value(), "block\ncomment");
}
@ -2353,25 +2418,25 @@ const mySk1 = startSketchAt([0, 0])"#;
#[test]
fn test_whitespace_in_function() {
let test_program = r#"() => {
let test_program = r#"() {
return sg
return sg
}"#;
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
let mut slice = tokens.as_slice();
let _expr = function_expression.parse_next(&mut slice).unwrap();
let _expr = function_decl.parse_next(&mut slice).unwrap();
}
#[test]
fn test_empty_lines_in_function() {
let test_program = "() => {
let test_program = "() {
return 2
}";
let module_id = ModuleId::from_usize(1);
let tokens = crate::token::lexer(test_program, module_id).unwrap();
let mut slice = tokens.as_slice();
let expr = function_expression.parse_next(&mut slice).unwrap();
let expr = function_decl.map(|t| t.0).parse_next(&mut slice).unwrap();
assert_eq!(
expr,
Node::new(
@ -2387,14 +2452,14 @@ const mySk1 = startSketchAt([0, 0])"#;
raw: "2".to_owned(),
digest: None,
},
32,
33,
29,
30,
module_id,
))),
digest: None,
},
25,
33,
22,
30,
module_id,
))],
non_code_meta: NonCodeMeta {
@ -2404,8 +2469,8 @@ const mySk1 = startSketchAt([0, 0])"#;
value: NonCodeValue::NewLine,
digest: None
},
7,
25,
4,
22,
module_id,
)],
digest: None,
@ -2413,15 +2478,15 @@ const mySk1 = startSketchAt([0, 0])"#;
shebang: None,
digest: None,
},
7,
47,
4,
44,
module_id,
),
return_type: None,
digest: None,
},
0,
47,
44,
module_id,
)
);
@ -2857,7 +2922,7 @@ const mySk1 = startSketchAt([0, 0])"#;
let test_fn = "(let) => { return 1 }";
let module_id = ModuleId::from_usize(2);
let tokens = crate::token::lexer(test_fn, module_id).unwrap();
let err = function_expression.parse(&tokens).unwrap_err().into_inner();
let err = function_decl.parse(&tokens).unwrap_err().into_inner();
let cause = err.cause.unwrap();
// This is the token `let`
assert_eq!(cause.source_ranges(), vec![SourceRange([1, 4, 2])]);
@ -2930,12 +2995,12 @@ const mySk1 = startSketchAt([0, 0])"#;
#[test]
fn test_user_function() {
let input = "() => {
let input = "() {
return 2
}";
let tokens = crate::token::lexer(input, ModuleId::default()).unwrap();
let actual = function_expression.parse(&tokens);
let actual = function_decl.parse(&tokens);
assert!(actual.is_ok(), "could not parse test function");
}
@ -3171,7 +3236,7 @@ const mySk1 = startSketchAt([0, 0])"#;
fn assert_no_err(p: &str) -> (Node<Program>, ParseContext) {
let result = crate::parser::top_level_parse(p);
let result = result.0.unwrap();
assert!(result.1.errors.is_empty());
assert!(result.1.errors.is_empty(), "found: {:#?}", result.1.errors);
(result.0.unwrap(), result.1)
}
@ -3256,6 +3321,16 @@ const height = [obj["a"] -1, 0]"#;
crate::parser::top_level_parse(code).unwrap();
}
#[test]
fn test_anon_fn() {
crate::parser::top_level_parse("foo(42, fn(x) { return x + 1 })").unwrap();
}
#[test]
fn test_anon_fn_no_fn() {
assert_err_contains("foo(42, (x) { return x + 1 })", "Anonymous function requires `fn`");
}
#[test]
fn test_parse_half_pipe() {
let code = "const height = 10
@ -3575,7 +3650,7 @@ e
#[test]
fn test_keyword_ok_in_fn_args_return() {
let some_program_string = r#"fn thing = (param) => {
let some_program_string = r#"fn thing(param) {
return true
}
@ -3727,6 +3802,25 @@ int(42.3)"#;
let replaced = ctxt.warnings[0].apply_suggestion(&replaced).unwrap();
assert_eq!(replaced, "1.0\nround(42.3)");
}
#[test]
fn warn_fn_decl() {
let some_program_string = r#"fn foo = () => {
return 0
}"#;
let (_, ctxt) = assert_no_err(some_program_string);
assert_eq!(ctxt.warnings.len(), 2);
let replaced = ctxt.warnings[0].apply_suggestion(some_program_string).unwrap();
let replaced = ctxt.warnings[1].apply_suggestion(&replaced).unwrap();
// Note the whitespace here is bad, but we're just testing the suggestion spans really. In
// real life we might reformat after applying suggestions.
assert_eq!(
replaced,
r#"fn foo () {
return 0
}"#
);
}
}
#[cfg(test)]

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3649
expression: actual
snapshot_kind: text
---
@ -52,7 +51,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 39,
"optional": false,
"start": 18,
"type": "CallExpression",
"type": "CallExpression"
@ -97,7 +95,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 63,
"optional": false,
"start": 47,
"type": "CallExpression",
"type": "CallExpression"
@ -149,7 +146,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 96,
"optional": false,
"start": 71,
"type": "CallExpression",
"type": "CallExpression"
@ -201,7 +197,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 121,
"optional": false,
"start": 104,
"type": "CallExpression",
"type": "CallExpression"
@ -230,7 +225,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 143,
"optional": false,
"start": 129,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3720
expression: actual
snapshot_kind: text
---
@ -68,7 +67,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 23,
"optional": false,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3722
expression: actual
snapshot_kind: text
---
@ -64,7 +63,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 80,
"optional": false,
"start": 62,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -83,7 +83,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 66,
"optional": false,
"start": 54,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3737
expression: actual
snapshot_kind: text
---
@ -52,7 +51,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 37,
"optional": false,
"start": 17,
"type": "CallExpression",
"type": "CallExpression"
@ -104,7 +102,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 75,
"optional": false,
"start": 49,
"type": "CallExpression",
"type": "CallExpression"
@ -149,7 +146,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 104,
"optional": false,
"start": 87,
"type": "CallExpression",
"type": "CallExpression"
@ -201,7 +197,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 145,
"optional": false,
"start": 116,
"type": "CallExpression",
"type": "CallExpression"
@ -222,7 +217,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 165,
"optional": false,
"start": 157,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3745
expression: actual
snapshot_kind: text
---
@ -52,7 +51,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 37,
"optional": false,
"start": 17,
"type": "CallExpression",
"type": "CallExpression"
@ -97,7 +95,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 58,
"optional": false,
"start": 41,
"type": "CallExpression",
"type": "CallExpression"
@ -118,7 +115,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 70,
"optional": false,
"start": 62,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -32,7 +32,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 30,
"optional": false,
"start": 14,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3750
expression: actual
snapshot_kind: text
---
@ -36,7 +35,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 18,
"optional": false,
"start": 14,
"type": "CallExpression",
"type": "CallExpression"
@ -65,7 +63,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 22,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3751
expression: actual
snapshot_kind: text
---
@ -35,7 +34,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 30,
"optional": false,
"start": 14,
"type": "CallExpression",
"type": "CallExpression"
@ -79,7 +77,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 49,
"optional": false,
"start": 34,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3752
expression: actual
snapshot_kind: text
---
@ -61,7 +60,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 22,
"optional": false,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3753
expression: actual
snapshot_kind: text
---
@ -96,7 +95,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 36,
"optional": false,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3754
expression: actual
snapshot_kind: text
---
@ -61,7 +60,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 19,
"optional": false,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3755
expression: actual
snapshot_kind: text
---
@ -96,7 +95,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 35,
"optional": false,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3756
expression: actual
snapshot_kind: text
---
@ -96,7 +95,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 35,
"optional": false,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3757
expression: actual
snapshot_kind: text
---
@ -50,7 +49,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 37,
"optional": false,
"start": 17,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3758
expression: actual
snapshot_kind: text
---
@ -41,7 +40,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 28,
"optional": false,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3760
expression: actual
snapshot_kind: text
---
@ -47,7 +46,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 15,
"optional": false,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3761
expression: actual
snapshot_kind: text
---
@ -36,7 +35,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 36,
"optional": false,
"start": 17,
"type": "CallExpression",
"type": "CallExpression"
@ -119,7 +117,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 85,
"optional": false,
"start": 44,
"type": "CallExpression",
"type": "CallExpression"
@ -148,7 +145,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 107,
"optional": false,
"start": 93,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3762
expression: actual
snapshot_kind: text
---
@ -45,7 +44,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 47,
"optional": false,
"start": 28,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3658
expression: actual
snapshot_kind: text
---
@ -53,7 +52,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 35,
"optional": false,
"start": 23,
"type": "CallExpression",
"type": "CallExpression"
@ -72,7 +70,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 36,
"optional": false,
"start": 14,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -35,7 +35,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 38,
"optional": false,
"start": 19,
"type": "CallExpression",
"type": "CallExpression"
@ -56,7 +55,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 132,
"optional": false,
"start": 115,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3822
expression: actual
snapshot_kind: text
---
@ -45,7 +44,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 64,
"optional": false,
"start": 52,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3660
expression: actual
snapshot_kind: text
---
@ -45,7 +44,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 31,
"optional": false,
"start": 19,
"type": "CallExpression",
"type": "CallExpression"
@ -72,7 +70,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 35,
"optional": false,
"start": 14,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3661
expression: actual
snapshot_kind: text
---
@ -65,7 +64,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 36,
"optional": false,
"start": 23,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,7 +1,6 @@
---
source: kcl/src/parser/parser_impl.rs
expression: actual
snapshot_kind: text
---
{
"body": [

View File

@ -32,7 +32,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 11,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/parser/parser_impl.rs
assertion_line: 3718
expression: actual
snapshot_kind: text
---
@ -35,7 +34,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 11,
"type": "CallExpression",
"type": "CallExpression"
@ -86,7 +84,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 53,
"optional": false,
"start": 33,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -31,7 +31,7 @@ pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
/// `[f(a), f(b), f(c)]`
/// ```no_run
/// const r = 10 // radius
/// fn drawCircle = (id) => {
/// fn drawCircle(id) {
/// return startSketchOn("XY")
/// |> circle({ center: [id * 2 * r, 0], radius: r}, %)
/// }
@ -49,7 +49,7 @@ pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
/// // Call `map`, using an anonymous function instead of a named one.
/// const circles = map(
/// [1..3],
/// (id) => {
/// fn(id) {
/// return startSketchOn("XY")
/// |> circle({ center: [id * 2 * r, 0], radius: r}, %)
/// }
@ -149,7 +149,7 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// // Use a `reduce` to draw the remaining decagon sides.
/// // For each number in the array 1..10, run the given function,
/// // which takes a partially-sketched decagon and adds one more edge to it.
/// fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) => {
/// fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {
/// // Draw one edge of the decagon.
/// let x = cos(stepAngle * i) * radius
/// let y = sin(stepAngle * i) * radius

View File

@ -27,7 +27,7 @@ pub async fn int(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
/// startSketchOn('XZ')
/// |> circle({ center = [0, 0], radius = 2 }, %)
/// |> extrude(5, %)
/// |> patternTransform(n, (id) => {
/// |> patternTransform(n, fn(id) {
/// return { translate = [4 * id, 0, 0] }
/// }, %)
/// ```

View File

@ -24,7 +24,6 @@ use crate::{
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(untagged)]
pub enum SketchOrSurface {
SketchSurface(SketchSurface),
Sketch(Box<Sketch>),

View File

@ -21,9 +21,8 @@ pub use tokeniser::Input;
pub(crate) use tokeniser::RESERVED_WORDS;
/// The types of tokens.
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize, ts_rs::TS, JsonSchema, FromStr, Display)]
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize, JsonSchema, FromStr, Display)]
#[cfg_attr(feature = "pyo3", pyo3::pyclass(eq, eq_int))]
#[ts(export)]
#[serde(rename_all = "camelCase")]
#[display(style = "camelCase")]
pub enum TokenType {
@ -156,9 +155,8 @@ impl TokenType {
}
}
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone, ts_rs::TS)]
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone)]
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
#[ts(export)]
pub struct Token {
#[serde(rename = "type")]
pub token_type: TokenType,

View File

@ -3,9 +3,9 @@ use std::fmt::Write;
use crate::{
ast::types::{
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression,
Expr, FormatOptions, FunctionExpression, IfExpression, ImportStatement, ItemVisibility, Literal,
Expr, FnArgType, FormatOptions, FunctionExpression, IfExpression, ImportStatement, ItemVisibility, Literal,
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NonCodeValue, ObjectExpression,
PipeExpression, Program, TagDeclarator, UnaryExpression, VariableDeclaration, VariableKind,
Parameter, PipeExpression, Program, TagDeclarator, UnaryExpression, VariableDeclaration, VariableKind,
},
parser::PIPE_OPERATOR,
};
@ -28,7 +28,7 @@ impl Program {
BodyItem::ExpressionStatement(expression_statement) => {
expression_statement
.expression
.recast(options, indentation_level, false)
.recast(options, indentation_level, ExprContext::Other)
}
BodyItem::VariableDeclaration(variable_declaration) => {
variable_declaration.recast(options, indentation_level)
@ -39,7 +39,7 @@ impl Program {
indentation,
return_statement
.argument
.recast(options, indentation_level, false)
.recast(options, indentation_level, ExprContext::Other)
.trim_start()
)
}
@ -140,22 +140,37 @@ impl ImportStatement {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum ExprContext {
Pipe,
Decl,
Other,
}
impl Expr {
pub(crate) fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
pub(crate) fn recast(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
match &self {
Expr::BinaryExpression(bin_exp) => bin_exp.recast(options),
Expr::ArrayExpression(array_exp) => array_exp.recast(options, indentation_level, is_in_pipe),
Expr::ArrayRangeExpression(range_exp) => range_exp.recast(options, indentation_level, is_in_pipe),
Expr::ObjectExpression(ref obj_exp) => obj_exp.recast(options, indentation_level, is_in_pipe),
Expr::ArrayExpression(array_exp) => array_exp.recast(options, indentation_level, ctxt),
Expr::ArrayRangeExpression(range_exp) => range_exp.recast(options, indentation_level, ctxt),
Expr::ObjectExpression(ref obj_exp) => obj_exp.recast(options, indentation_level, ctxt),
Expr::MemberExpression(mem_exp) => mem_exp.recast(),
Expr::Literal(literal) => literal.recast(),
Expr::FunctionExpression(func_exp) => func_exp.recast(options, indentation_level),
Expr::CallExpression(call_exp) => call_exp.recast(options, indentation_level, is_in_pipe),
Expr::FunctionExpression(func_exp) => {
let mut result = if ctxt == ExprContext::Decl {
String::new()
} else {
"fn".to_owned()
};
result += &func_exp.recast(options, indentation_level);
result
}
Expr::CallExpression(call_exp) => call_exp.recast(options, indentation_level, ctxt),
Expr::Identifier(ident) => ident.name.to_string(),
Expr::TagDeclarator(tag) => tag.recast(),
Expr::PipeExpression(pipe_exp) => pipe_exp.recast(options, indentation_level),
Expr::UnaryExpression(unary_exp) => unary_exp.recast(options),
Expr::IfExpression(e) => e.recast(options, indentation_level, is_in_pipe),
Expr::IfExpression(e) => e.recast(options, indentation_level, ctxt),
Expr::PipeSubstitution(_) => crate::parser::PIPE_SUBSTITUTION_OPERATOR.to_string(),
Expr::None(_) => {
unimplemented!("there is no literal None, see https://github.com/KittyCAD/modeling-app/issues/1115")
@ -170,19 +185,21 @@ impl BinaryPart {
BinaryPart::Literal(literal) => literal.recast(),
BinaryPart::Identifier(identifier) => identifier.name.to_string(),
BinaryPart::BinaryExpression(binary_expression) => binary_expression.recast(options),
BinaryPart::CallExpression(call_expression) => call_expression.recast(options, indentation_level, false),
BinaryPart::CallExpression(call_expression) => {
call_expression.recast(options, indentation_level, ExprContext::Other)
}
BinaryPart::UnaryExpression(unary_expression) => unary_expression.recast(options),
BinaryPart::MemberExpression(member_expression) => member_expression.recast(),
BinaryPart::IfExpression(e) => e.recast(options, indentation_level, false),
BinaryPart::IfExpression(e) => e.recast(options, indentation_level, ExprContext::Other),
}
}
}
impl CallExpression {
fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
fn recast(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
format!(
"{}{}({})",
if is_in_pipe {
if ctxt == ExprContext::Pipe {
"".to_string()
} else {
options.get_indentation(indentation_level)
@ -190,7 +207,7 @@ impl CallExpression {
self.callee.name,
self.arguments
.iter()
.map(|arg| arg.recast(options, indentation_level, is_in_pipe))
.map(|arg| arg.recast(options, indentation_level, ctxt))
.collect::<Vec<String>>()
.join(", ")
)
@ -205,16 +222,19 @@ impl VariableDeclaration {
ItemVisibility::Export => "export ".to_owned(),
};
self.declarations.iter().fold(output, |mut output, declaration| {
let keyword = match self.kind {
VariableKind::Fn => "fn ",
VariableKind::Const => "",
let (keyword, eq) = match self.kind {
VariableKind::Fn => ("fn ", ""),
VariableKind::Const => ("", " = "),
};
let _ = write!(
output,
"{}{keyword}{} = {}",
"{}{keyword}{}{eq}{}",
indentation,
declaration.id.name,
declaration.init.recast(options, indentation_level, false).trim()
declaration
.init
.recast(options, indentation_level, ExprContext::Decl)
.trim()
);
output
})
@ -248,7 +268,7 @@ impl TagDeclarator {
}
impl ArrayExpression {
fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
fn recast(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
// Reconstruct the order of items in the array.
// An item can be an element (i.e. an expression for a KCL value),
// or a non-code item (e.g. a comment)
@ -267,7 +287,7 @@ impl ArrayExpression {
.collect::<Vec<_>>()
} else {
let el = elems.next().unwrap();
let s = format!("{}, ", el.recast(options, 0, false));
let s = format!("{}, ", el.recast(options, 0, ExprContext::Other));
vec![s]
}
})
@ -290,7 +310,7 @@ impl ArrayExpression {
}
// Otherwise, we format a multi-line representation.
let inner_indentation = if is_in_pipe {
let inner_indentation = if ctxt == ExprContext::Pipe {
options.get_indentation_offset_pipe(indentation_level + 1)
} else {
options.get_indentation(indentation_level + 1)
@ -307,7 +327,7 @@ impl ArrayExpression {
.collect::<Vec<String>>()
.join("")
.to_owned();
let end_indent = if is_in_pipe {
let end_indent = if ctxt == ExprContext::Pipe {
options.get_indentation_offset_pipe(indentation_level)
} else {
options.get_indentation(indentation_level)
@ -336,9 +356,9 @@ fn expr_is_trivial(expr: &Expr) -> bool {
}
impl ArrayRangeExpression {
fn recast(&self, options: &FormatOptions, _: usize, _: bool) -> String {
let s1 = self.start_element.recast(options, 0, false);
let s2 = self.end_element.recast(options, 0, false);
fn recast(&self, options: &FormatOptions, _: usize, _: ExprContext) -> String {
let s1 = self.start_element.recast(options, 0, ExprContext::Other);
let s2 = self.end_element.recast(options, 0, ExprContext::Other);
// Format these items into a one-line array. Put spaces around the `..` if either expression
// is non-trivial. This is a bit arbitrary but people seem to like simple ranges to be formatted
@ -355,14 +375,14 @@ impl ArrayRangeExpression {
}
impl ObjectExpression {
fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
fn recast(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
if self
.non_code_meta
.non_code_nodes
.values()
.any(|nc| nc.iter().any(|nc| nc.value.should_cause_array_newline()))
{
return self.recast_multi_line(options, indentation_level, is_in_pipe);
return self.recast_multi_line(options, indentation_level, ctxt);
}
let flat_recast = format!(
"{{ {} }}",
@ -372,7 +392,7 @@ impl ObjectExpression {
format!(
"{} = {}",
prop.key.name,
prop.value.recast(options, indentation_level + 1, is_in_pipe).trim()
prop.value.recast(options, indentation_level + 1, ctxt).trim()
)
})
.collect::<Vec<String>>()
@ -383,12 +403,12 @@ impl ObjectExpression {
if !needs_multiple_lines {
return flat_recast;
}
self.recast_multi_line(options, indentation_level, is_in_pipe)
self.recast_multi_line(options, indentation_level, ctxt)
}
/// Recast, but always outputs the object with newlines between each property.
fn recast_multi_line(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
let inner_indentation = if is_in_pipe {
fn recast_multi_line(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
let inner_indentation = if ctxt == ExprContext::Pipe {
options.get_indentation_offset_pipe(indentation_level + 1)
} else {
options.get_indentation(indentation_level + 1)
@ -406,13 +426,13 @@ impl ObjectExpression {
let s = format!(
"{} = {}{comma}",
prop.key.name,
prop.value.recast(options, indentation_level + 1, is_in_pipe).trim()
prop.value.recast(options, indentation_level + 1, ctxt).trim()
);
vec![s]
}
})
.collect();
let end_indent = if is_in_pipe {
let end_indent = if ctxt == ExprContext::Pipe {
options.get_indentation_offset_pipe(indentation_level)
} else {
options.get_indentation(indentation_level)
@ -495,17 +515,17 @@ impl UnaryExpression {
}
impl IfExpression {
fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
fn recast(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
// We can calculate how many lines this will take, so let's do it and avoid growing the vec.
// Total lines = starting lines, else-if lines, ending lines.
let n = 2 + (self.else_ifs.len() * 2) + 3;
let mut lines = Vec::with_capacity(n);
let cond = self.cond.recast(options, indentation_level, is_in_pipe);
let cond = self.cond.recast(options, indentation_level, ctxt);
lines.push((0, format!("if {cond} {{")));
lines.push((1, self.then_val.recast(options, indentation_level + 1)));
for else_if in &self.else_ifs {
let cond = else_if.cond.recast(options, indentation_level, is_in_pipe);
let cond = else_if.cond.recast(options, indentation_level, ctxt);
lines.push((0, format!("}} else if {cond} {{")));
lines.push((1, else_if.then_val.recast(options, indentation_level + 1)));
}
@ -528,7 +548,7 @@ impl Node<PipeExpression> {
.enumerate()
.map(|(index, statement)| {
let indentation = options.get_indentation(indentation_level + 1);
let mut s = statement.recast(options, indentation_level + 1, true);
let mut s = statement.recast(options, indentation_level + 1, ExprContext::Pipe);
let non_code_meta = self.non_code_meta.clone();
if let Some(non_code_meta_value) = non_code_meta.non_code_nodes.get(&index) {
for val in non_code_meta_value {
@ -568,13 +588,55 @@ impl FunctionExpression {
let param_list = self
.params
.iter()
.map(|param| param.identifier.name.clone())
.map(|param| param.recast(options, indentation_level))
.collect::<Vec<String>>()
.join(", ");
let tab0 = options.get_indentation(indentation_level);
let tab1 = options.get_indentation(indentation_level + 1);
let return_type = match &self.return_type {
Some(rt) => format!(": {}", rt.recast(&new_options, indentation_level)),
None => String::new(),
};
let body = self.body.recast(&new_options, indentation_level + 1);
format!("({param_list}) => {{\n{tab1}{body}\n{tab0}}}")
format!("({param_list}){return_type} {{\n{tab1}{body}\n{tab0}}}")
}
}
impl Parameter {
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
let mut result = self.identifier.name.clone();
if let Some(ty) = &self.type_ {
result += ": ";
result += &ty.recast(options, indentation_level);
}
result
}
}
impl FnArgType {
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
match self {
FnArgType::Primitive(t) => t.to_string(),
FnArgType::Array(t) => format!("{t}[]"),
FnArgType::Object { properties } => {
let mut result = "{".to_owned();
for p in properties {
result += " ";
result += &p.recast(options, indentation_level);
result += ",";
}
if result.ends_with(',') {
result.pop();
result += " ";
}
result += "}";
result
}
}
}
}
@ -582,6 +644,7 @@ impl FunctionExpression {
mod tests {
use pretty_assertions::assert_eq;
use super::*;
use crate::ast::types::{FormatOptions, ModuleId};
#[test]
@ -639,7 +702,7 @@ import a as aaa, b as bbb from "a.kcl"
#[test]
fn test_recast_export_fn() {
let input = r#"export fn a = () => {
let input = r#"export fn a() {
return 0
}
"#;
@ -658,7 +721,7 @@ s = 1 // s = 1 -> height of Z is 13.4mm
// Depth
d = 1
fn rect = (x, y, w, h) => {
fn rect(x, y, w, h) {
startSketchOn('XY')
|> startProfileAt([x, y], %)
|> xLine(w, %)
@ -668,7 +731,7 @@ fn rect = (x, y, w, h) => {
|> extrude(d, %)
}
fn quad = (x1, y1, x2, y2, x3, y3, x4, y4) => {
fn quad(x1, y1, x2, y2, x3, y3, x4, y4) {
startSketchOn('XY')
|> startProfileAt([x1, y1], %)
|> lineTo([x2, y2], %)
@ -678,7 +741,7 @@ fn quad = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|> extrude(d, %)
}
fn crosshair = (x, y) => {
fn crosshair(x, y) {
startSketchOn('XY')
|> startProfileAt([x, y], %)
|> yLine(1, %)
@ -688,7 +751,7 @@ fn crosshair = (x, y) => {
|> xLine(-2, %)
}
fn z = (z_x, z_y) => {
fn z(z_x, z_y) {
z_end_w = s * 8.4
z_end_h = s * 3
z_corner = s * 2
@ -701,7 +764,7 @@ fn z = (z_x, z_y) => {
quad(z_x, z_y - z_h + z_corner, z_x + z_w - z_corner, z_y, z_x + z_w, z_y - z_corner, z_x + z_corner, z_y - z_h)
}
fn o = (c_x, c_y) => {
fn o(c_x, c_y) {
// Outer and inner radii
o_r = s * 6.95
i_r = 0.5652173913043478 * o_r
@ -758,7 +821,7 @@ fn o = (c_x, c_y) => {
|> extrude(d, %)
}
fn zoo = (x0, y0) => {
fn zoo(x0, y0) {
z(x0, y0)
o(x0 + s * 20, y0 - (s * 6.7))
o(x0 + s * 35, y0 - (s * 6.7))
@ -953,7 +1016,7 @@ thing ( 1 )
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"fn thing = (x) => {
r#"fn thing(x) {
return x + 1
}
@ -962,6 +1025,18 @@ thing(1)
);
}
#[test]
fn test_recast_typed_fn() {
let some_program_string = r#"fn thing(x: string, y: bool[]): number {
return x + 1
}
"#;
let program = crate::parser::top_level_parse(some_program_string).unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(recasted, some_program_string);
}
#[test]
fn test_recast_object_fn_in_array_weird_bracket() {
let some_program_string = r#"bing = { yo = 55 }
@ -1231,7 +1306,7 @@ depth = 45.0
thk = 5
hole_diam = 5
// define a rectangular shape func
fn rectShape = (pos, w, l) => {
fn rectShape(pos, w, l) {
rr = startSketchOn('xy')
|> startProfileAt([pos[0] - (w / 2), pos[1] - (l / 2)], %)
|> lineTo([pos[0] + w / 2, pos[1] - (l / 2)], %, $edge1)
@ -1254,7 +1329,7 @@ scarlett_body = rectShape([0, 0], width, length)
]
}, %)
// build the bracket sketch around the body
fn bracketSketch = (w, d, t) => {
fn bracketSketch(w, d, t) {
s = startSketchOn({
plane = {
origin = { x = 0, y = length / 2 + thk, z = 0 },
@ -1360,7 +1435,7 @@ tabs_l = startSketchOn({
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"fn cube = (pos, scale) => {
r#"fn cube(pos, scale) {
sg = startSketchOn('XY')
|> startProfileAt(pos, %)
|> line([0, scale], %)
@ -1447,7 +1522,7 @@ tabs_l = startSketchOn({
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"fn myFn = () => {
r#"fn myFn() {
// this is a comment
yo = { a = { b = { c = '123' } } } /* block
comment */
@ -1779,7 +1854,7 @@ blah = 1
foo = false
baz = { a = 1, part001 = "thing" }
fn ghi = (part001) => {
fn ghi(part001) {
return part001
}
"#
@ -1797,7 +1872,7 @@ fn ghi = (part001) => {
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"fn ghi = (newName, y, z) => {
r#"fn ghi(newName, y, z) {
return newName
}
"#
@ -1955,15 +2030,15 @@ thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
#[test]
fn recast_nested_fn() {
let some_program_string = r#"fn f = () => {
return () => {
return fn() => {
return 1
}
}"#;
let program = crate::parser::top_level_parse(some_program_string).unwrap();
let recasted = program.recast(&Default::default(), 0);
let expected = "\
fn f = () => {
return () => {
fn f() {
return fn() {
return 1
}
}";
@ -2057,7 +2132,7 @@ sketch002 = startSketchOn({
crate::parser::parser_impl::print_tokens(&tokens);
let expr = crate::parser::parser_impl::object.parse(&tokens).unwrap();
assert_eq!(
expr.recast(&FormatOptions::new(), 0, false),
expr.recast(&FormatOptions::new(), 0, ExprContext::Other),
expected,
"failed test {i}, which is testing that recasting {reason}"
);
@ -2154,7 +2229,7 @@ sketch002 = startSketchOn({
let tokens = crate::token::lexer(input, ModuleId::default()).unwrap();
let expr = crate::parser::parser_impl::array_elem_by_elem.parse(&tokens).unwrap();
assert_eq!(
expr.recast(&FormatOptions::new(), 0, false),
expr.recast(&FormatOptions::new(), 0, ExprContext::Other),
expected,
"failed test {i}, which is testing that recasting {reason}"
);

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
fn f = (i) => {
fn f(i) {
return i * 2
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing angled_line.kcl
snapshot_kind: text
---
@ -37,7 +36,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 10,
"type": "CallExpression",
"type": "CallExpression"
@ -82,7 +80,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 67,
"optional": false,
"start": 35,
"type": "CallExpression",
"type": "CallExpression"
@ -127,7 +124,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 94,
"optional": false,
"start": 73,
"type": "CallExpression",
"type": "CallExpression"
@ -186,7 +182,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 130,
"optional": false,
"start": 100,
"type": "CallExpression",
"type": "CallExpression"
@ -245,7 +240,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 159,
"optional": false,
"start": 136,
"type": "CallExpression",
"type": "CallExpression"
@ -271,7 +265,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 190,
"optional": false,
"start": 177,
"type": "CallExpression",
"type": "CallExpression"
@ -304,7 +297,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 202,
"optional": false,
"start": 165,
"type": "CallExpression",
"type": "CallExpression"
@ -356,7 +348,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 232,
"optional": false,
"start": 208,
"type": "CallExpression",
"type": "CallExpression"
@ -377,7 +368,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 246,
"optional": false,
"start": 238,
"type": "CallExpression",
"type": "CallExpression"
@ -406,7 +396,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 265,
"optional": false,
"start": 252,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing argument_error.kcl
snapshot_kind: text
---
@ -10,7 +9,7 @@ snapshot_kind: text
{
"declarations": [
{
"end": 28,
"end": 22,
"id": {
"end": 4,
"name": "f",
@ -22,36 +21,36 @@ snapshot_kind: text
"body": [
{
"argument": {
"end": 26,
"end": 20,
"raw": "5",
"start": 25,
"start": 19,
"type": "Literal",
"type": "Literal",
"value": 5.0
},
"end": 26,
"start": 18,
"end": 20,
"start": 12,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 28,
"start": 14
"end": 22,
"start": 8
},
"end": 28,
"end": 22,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 9,
"end": 6,
"name": "i",
"start": 8,
"start": 5,
"type": "Identifier"
},
"optional": false
}
],
"start": 7,
"start": 4,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
@ -59,72 +58,71 @@ snapshot_kind: text
"type": "VariableDeclarator"
}
],
"end": 28,
"end": 22,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"end": 44,
"end": 38,
"expression": {
"arguments": [
{
"end": 35,
"end": 29,
"name": "f",
"start": 34,
"start": 28,
"type": "Identifier",
"type": "Identifier"
},
{
"elements": [
{
"end": 39,
"end": 33,
"raw": "0",
"start": 38,
"start": 32,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 42,
"end": 36,
"raw": "1",
"start": 41,
"start": 35,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
],
"end": 43,
"start": 37,
"end": 37,
"start": 31,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
],
"callee": {
"end": 33,
"end": 27,
"name": "map",
"start": 30,
"start": 24,
"type": "Identifier"
},
"end": 44,
"optional": false,
"start": 30,
"end": 38,
"start": 24,
"type": "CallExpression",
"type": "CallExpression"
},
"start": 30,
"start": 24,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"end": 45,
"end": 39,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"end": 30,
"start": 28,
"end": 24,
"start": 22,
"type": "NonCodeNode",
"value": {
"type": "newLine"

View File

@ -1,4 +1,4 @@
fn f = (i) => {
fn f(i) {
return 5
}

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing array_elem_push.kcl
snapshot_kind: text
---
@ -94,7 +93,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 39,
"optional": false,
"start": 27,
"type": "CallExpression",
"type": "CallExpression"
@ -144,7 +142,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 68,
"optional": false,
"start": 51,
"type": "CallExpression",
"type": "CallExpression"
@ -217,7 +214,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 142,
"optional": false,
"start": 69,
"type": "CallExpression",
"type": "CallExpression"
@ -284,7 +280,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 216,
"optional": false,
"start": 143,
"type": "CallExpression",
"type": "CallExpression"
@ -351,7 +346,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 290,
"optional": false,
"start": 217,
"type": "CallExpression",
"type": "CallExpression"
@ -418,7 +412,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 366,
"optional": false,
"start": 291,
"type": "CallExpression",
"type": "CallExpression"
@ -485,7 +478,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 440,
"optional": false,
"start": 367,
"type": "CallExpression",
"type": "CallExpression"
@ -552,7 +544,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 514,
"optional": false,
"start": 441,
"type": "CallExpression",
"type": "CallExpression"
@ -619,7 +610,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 588,
"optional": false,
"start": 515,
"type": "CallExpression",
"type": "CallExpression"
@ -686,7 +676,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 664,
"optional": false,
"start": 589,
"type": "CallExpression",
"type": "CallExpression"
@ -753,7 +742,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 740,
"optional": false,
"start": 665,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing array_elem_push_fail.kcl
snapshot_kind: text
---
@ -94,7 +93,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 40,
"optional": false,
"start": 28,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,7 @@
---
source: kcl/src/simulation_tests.rs
description: Result of parsing array_range_expr.kcl
snapshot_kind: text
---
{
"Ok": {
@ -106,7 +107,6 @@ description: Result of parsing array_range_expr.kcl
"type": "Identifier"
},
"end": 70,
"optional": false,
"start": 12,
"type": "CallExpression",
"type": "CallExpression"
@ -270,7 +270,6 @@ description: Result of parsing array_range_expr.kcl
"type": "Identifier"
},
"end": 166,
"optional": false,
"start": 108,
"type": "CallExpression",
"type": "CallExpression"
@ -321,7 +320,6 @@ description: Result of parsing array_range_expr.kcl
"type": "Identifier"
},
"end": 188,
"optional": false,
"start": 175,
"type": "CallExpression",
"type": "CallExpression"
@ -435,7 +433,6 @@ description: Result of parsing array_range_expr.kcl
"type": "Identifier"
},
"end": 275,
"optional": false,
"start": 207,
"type": "CallExpression",
"type": "CallExpression"
@ -502,7 +499,6 @@ description: Result of parsing array_range_expr.kcl
"type": "Identifier"
},
"end": 334,
"optional": false,
"start": 276,
"type": "CallExpression",
"type": "CallExpression"
@ -555,7 +551,6 @@ description: Result of parsing array_range_expr.kcl
"type": "Identifier"
},
"end": 372,
"optional": false,
"start": 359,
"type": "CallExpression",
"type": "CallExpression"
@ -594,7 +589,6 @@ description: Result of parsing array_range_expr.kcl
"type": "Identifier"
},
"end": 355,
"optional": false,
"start": 342,
"type": "CallExpression",
"type": "CallExpression"
@ -670,7 +664,6 @@ description: Result of parsing array_range_expr.kcl
"type": "Identifier"
},
"end": 426,
"optional": false,
"start": 374,
"type": "CallExpression",
"type": "CallExpression"
@ -737,7 +730,6 @@ description: Result of parsing array_range_expr.kcl
"type": "Identifier"
},
"end": 488,
"optional": false,
"start": 427,
"type": "CallExpression",
"type": "CallExpression"
@ -804,7 +796,6 @@ description: Result of parsing array_range_expr.kcl
"type": "Identifier"
},
"end": 540,
"optional": false,
"start": 489,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,7 @@
---
source: kcl/src/simulation_tests.rs
description: Result of parsing array_range_negative_expr.kcl
snapshot_kind: text
---
{
"Ok": {
@ -52,7 +53,6 @@ description: Result of parsing array_range_negative_expr.kcl
"type": "Identifier"
},
"end": 13,
"optional": false,
"start": 6,
"type": "CallExpression",
"type": "CallExpression"
@ -135,7 +135,6 @@ description: Result of parsing array_range_negative_expr.kcl
"type": "Identifier"
},
"end": 72,
"optional": false,
"start": 20,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing basic_fillet_cube_close_opposite.kcl
snapshot_kind: text
---
@ -37,7 +36,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 10,
"type": "CallExpression",
"type": "CallExpression"
@ -82,7 +80,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 60,
"optional": false,
"start": 35,
"type": "CallExpression",
"type": "CallExpression"
@ -134,7 +131,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 90,
"optional": false,
"start": 66,
"type": "CallExpression",
"type": "CallExpression"
@ -179,7 +175,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 112,
"optional": false,
"start": 96,
"type": "CallExpression",
"type": "CallExpression"
@ -238,7 +233,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 144,
"optional": false,
"start": 118,
"type": "CallExpression",
"type": "CallExpression"
@ -266,7 +260,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 167,
"optional": false,
"start": 150,
"type": "CallExpression",
"type": "CallExpression"
@ -295,7 +288,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 187,
"optional": false,
"start": 173,
"type": "CallExpression",
"type": "CallExpression"
@ -360,7 +352,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 267,
"optional": false,
"start": 244,
"type": "CallExpression",
"type": "CallExpression"
@ -391,7 +382,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 279,
"optional": false,
"start": 193,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing basic_fillet_cube_end.kcl
snapshot_kind: text
---
@ -37,7 +36,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 10,
"type": "CallExpression",
"type": "CallExpression"
@ -82,7 +80,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 60,
"optional": false,
"start": 35,
"type": "CallExpression",
"type": "CallExpression"
@ -134,7 +131,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 90,
"optional": false,
"start": 66,
"type": "CallExpression",
"type": "CallExpression"
@ -179,7 +175,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 112,
"optional": false,
"start": 96,
"type": "CallExpression",
"type": "CallExpression"
@ -238,7 +233,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 144,
"optional": false,
"start": 118,
"type": "CallExpression",
"type": "CallExpression"
@ -259,7 +253,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 158,
"optional": false,
"start": 150,
"type": "CallExpression",
"type": "CallExpression"
@ -288,7 +281,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 178,
"optional": false,
"start": 164,
"type": "CallExpression",
"type": "CallExpression"
@ -353,7 +345,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 256,
"optional": false,
"start": 234,
"type": "CallExpression",
"type": "CallExpression"
@ -384,7 +375,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 268,
"optional": false,
"start": 184,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing basic_fillet_cube_next_adjacent.kcl
snapshot_kind: text
---
@ -37,7 +36,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 10,
"type": "CallExpression",
"type": "CallExpression"
@ -82,7 +80,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 60,
"optional": false,
"start": 35,
"type": "CallExpression",
"type": "CallExpression"
@ -134,7 +131,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 90,
"optional": false,
"start": 66,
"type": "CallExpression",
"type": "CallExpression"
@ -186,7 +182,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 121,
"optional": false,
"start": 96,
"type": "CallExpression",
"type": "CallExpression"
@ -245,7 +240,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 153,
"optional": false,
"start": 127,
"type": "CallExpression",
"type": "CallExpression"
@ -273,7 +267,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 176,
"optional": false,
"start": 159,
"type": "CallExpression",
"type": "CallExpression"
@ -302,7 +295,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 196,
"optional": false,
"start": 182,
"type": "CallExpression",
"type": "CallExpression"
@ -360,7 +352,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 272,
"optional": false,
"start": 245,
"type": "CallExpression",
"type": "CallExpression"
@ -391,7 +382,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 284,
"optional": false,
"start": 202,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing basic_fillet_cube_previous_adjacent.kcl
snapshot_kind: text
---
@ -37,7 +36,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 10,
"type": "CallExpression",
"type": "CallExpression"
@ -82,7 +80,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 60,
"optional": false,
"start": 35,
"type": "CallExpression",
"type": "CallExpression"
@ -134,7 +131,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 90,
"optional": false,
"start": 66,
"type": "CallExpression",
"type": "CallExpression"
@ -186,7 +182,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 121,
"optional": false,
"start": 96,
"type": "CallExpression",
"type": "CallExpression"
@ -245,7 +240,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 153,
"optional": false,
"start": 127,
"type": "CallExpression",
"type": "CallExpression"
@ -273,7 +267,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 176,
"optional": false,
"start": 159,
"type": "CallExpression",
"type": "CallExpression"
@ -302,7 +295,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 196,
"optional": false,
"start": 182,
"type": "CallExpression",
"type": "CallExpression"
@ -360,7 +352,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 276,
"optional": false,
"start": 245,
"type": "CallExpression",
"type": "CallExpression"
@ -391,7 +382,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 288,
"optional": false,
"start": 202,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing basic_fillet_cube_start.kcl
snapshot_kind: text
---
@ -37,7 +36,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 10,
"type": "CallExpression",
"type": "CallExpression"
@ -82,7 +80,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 60,
"optional": false,
"start": 35,
"type": "CallExpression",
"type": "CallExpression"
@ -134,7 +131,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 90,
"optional": false,
"start": 66,
"type": "CallExpression",
"type": "CallExpression"
@ -179,7 +175,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 112,
"optional": false,
"start": 96,
"type": "CallExpression",
"type": "CallExpression"
@ -238,7 +233,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 144,
"optional": false,
"start": 118,
"type": "CallExpression",
"type": "CallExpression"
@ -259,7 +253,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 158,
"optional": false,
"start": 150,
"type": "CallExpression",
"type": "CallExpression"
@ -288,7 +281,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 178,
"optional": false,
"start": 164,
"type": "CallExpression",
"type": "CallExpression"
@ -369,7 +361,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 233,
"optional": false,
"start": 184,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing big_number_angle_to_match_length_x.kcl
snapshot_kind: text
---
@ -37,7 +36,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 10,
"type": "CallExpression",
"type": "CallExpression"
@ -82,7 +80,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 60,
"optional": false,
"start": 35,
"type": "CallExpression",
"type": "CallExpression"
@ -134,7 +131,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 92,
"optional": false,
"start": 66,
"type": "CallExpression",
"type": "CallExpression"
@ -175,7 +171,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 146,
"optional": false,
"start": 114,
"type": "CallExpression",
"type": "CallExpression"
@ -214,7 +209,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 154,
"optional": false,
"start": 98,
"type": "CallExpression",
"type": "CallExpression"
@ -235,7 +229,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 168,
"optional": false,
"start": 160,
"type": "CallExpression",
"type": "CallExpression"
@ -264,7 +257,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 188,
"optional": false,
"start": 174,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing big_number_angle_to_match_length_y.kcl
snapshot_kind: text
---
@ -37,7 +36,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 29,
"optional": false,
"start": 10,
"type": "CallExpression",
"type": "CallExpression"
@ -82,7 +80,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 60,
"optional": false,
"start": 35,
"type": "CallExpression",
"type": "CallExpression"
@ -134,7 +131,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 92,
"optional": false,
"start": 66,
"type": "CallExpression",
"type": "CallExpression"
@ -175,7 +171,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 146,
"optional": false,
"start": 114,
"type": "CallExpression",
"type": "CallExpression"
@ -214,7 +209,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 154,
"optional": false,
"start": 98,
"type": "CallExpression",
"type": "CallExpression"
@ -235,7 +229,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 168,
"optional": false,
"start": 160,
"type": "CallExpression",
"type": "CallExpression"
@ -264,7 +257,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 188,
"optional": false,
"start": 174,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing circular_pattern3d_a_pattern.kcl
snapshot_kind: text
---
@ -37,7 +36,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 35,
"optional": false,
"start": 16,
"type": "CallExpression",
"type": "CallExpression"
@ -82,7 +80,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 66,
"optional": false,
"start": 41,
"type": "CallExpression",
"type": "CallExpression"
@ -127,7 +124,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 87,
"optional": false,
"start": 72,
"type": "CallExpression",
"type": "CallExpression"
@ -172,7 +168,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 108,
"optional": false,
"start": 93,
"type": "CallExpression",
"type": "CallExpression"
@ -224,7 +219,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 130,
"optional": false,
"start": 114,
"type": "CallExpression",
"type": "CallExpression"
@ -245,7 +239,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 144,
"optional": false,
"start": 136,
"type": "CallExpression",
"type": "CallExpression"
@ -274,7 +267,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 163,
"optional": false,
"start": 150,
"type": "CallExpression",
"type": "CallExpression"
@ -411,7 +403,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 261,
"optional": false,
"start": 174,
"type": "CallExpression",
"type": "CallExpression"
@ -625,7 +616,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 415,
"optional": false,
"start": 272,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing comparisons.kcl
snapshot_kind: text
---
@ -50,7 +49,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 26,
"optional": false,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"
@ -102,7 +100,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 67,
"optional": false,
"start": 27,
"type": "CallExpression",
"type": "CallExpression"
@ -154,7 +151,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 98,
"optional": false,
"start": 68,
"type": "CallExpression",
"type": "CallExpression"
@ -206,7 +202,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 143,
"optional": false,
"start": 99,
"type": "CallExpression",
"type": "CallExpression"
@ -258,7 +253,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 163,
"optional": false,
"start": 144,
"type": "CallExpression",
"type": "CallExpression"
@ -310,7 +304,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 201,
"optional": false,
"start": 164,
"type": "CallExpression",
"type": "CallExpression"
@ -362,7 +355,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 239,
"optional": false,
"start": 202,
"type": "CallExpression",
"type": "CallExpression"
@ -414,7 +406,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 259,
"optional": false,
"start": 240,
"type": "CallExpression",
"type": "CallExpression"
@ -466,7 +457,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 297,
"optional": false,
"start": 260,
"type": "CallExpression",
"type": "CallExpression"
@ -518,7 +508,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 335,
"optional": false,
"start": 298,
"type": "CallExpression",
"type": "CallExpression"
@ -570,7 +559,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 375,
"optional": false,
"start": 337,
"type": "CallExpression",
"type": "CallExpression"
@ -629,7 +617,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 428,
"optional": false,
"start": 376,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing comparisons_multiple.kcl
snapshot_kind: text
---
@ -65,7 +64,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 46,
"optional": false,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing computed_var.kcl
snapshot_kind: text
---
@ -179,7 +178,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 115,
"optional": false,
"start": 77,
"type": "CallExpression",
"type": "CallExpression"
@ -366,7 +364,6 @@ snapshot_kind: text
"type": "Identifier"
},
"end": 206,
"optional": false,
"start": 168,
"type": "CallExpression",
"type": "CallExpression"

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing cube.kcl
snapshot_kind: text
---
@ -10,7 +9,7 @@ snapshot_kind: text
{
"declarations": [
{
"end": 328,
"end": 322,
"id": {
"end": 7,
"name": "cube",
@ -23,434 +22,434 @@ snapshot_kind: text
{
"declarations": [
{
"end": 48,
"end": 42,
"id": {
"end": 35,
"end": 29,
"name": "l",
"start": 34,
"start": 28,
"type": "Identifier"
},
"init": {
"end": 48,
"end": 42,
"left": {
"end": 44,
"end": 38,
"name": "length",
"start": 38,
"start": 32,
"type": "Identifier",
"type": "Identifier"
},
"operator": "/",
"right": {
"end": 48,
"end": 42,
"raw": "2",
"start": 47,
"start": 41,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
"start": 38,
"start": 32,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 34,
"start": 28,
"type": "VariableDeclarator"
}
],
"end": 48,
"end": 42,
"kind": "const",
"start": 34,
"start": 28,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 64,
"end": 58,
"id": {
"end": 52,
"end": 46,
"name": "x",
"start": 51,
"start": 45,
"type": "Identifier"
},
"init": {
"computed": false,
"end": 64,
"end": 58,
"object": {
"end": 61,
"end": 55,
"name": "center",
"start": 55,
"start": 49,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 63,
"end": 57,
"raw": "0",
"start": 62,
"start": 56,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
"start": 55,
"start": 49,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 51,
"start": 45,
"type": "VariableDeclarator"
}
],
"end": 64,
"end": 58,
"kind": "const",
"start": 51,
"start": 45,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 80,
"end": 74,
"id": {
"end": 68,
"end": 62,
"name": "y",
"start": 67,
"start": 61,
"type": "Identifier"
},
"init": {
"computed": false,
"end": 80,
"end": 74,
"object": {
"end": 77,
"end": 71,
"name": "center",
"start": 71,
"start": 65,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 79,
"end": 73,
"raw": "1",
"start": 78,
"start": 72,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 71,
"start": 65,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 67,
"start": 61,
"type": "VariableDeclarator"
}
],
"end": 80,
"end": 74,
"kind": "const",
"start": 67,
"start": 61,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 104,
"end": 98,
"id": {
"end": 85,
"end": 79,
"name": "p0",
"start": 83,
"start": 77,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 95,
"end": 89,
"left": {
"argument": {
"end": 91,
"end": 85,
"name": "l",
"start": 90,
"start": 84,
"type": "Identifier",
"type": "Identifier"
},
"end": 91,
"end": 85,
"operator": "-",
"start": 89,
"start": 83,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
"operator": "+",
"right": {
"end": 95,
"end": 89,
"name": "x",
"start": 94,
"start": 88,
"type": "Identifier",
"type": "Identifier"
},
"start": 89,
"start": 83,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 103,
"end": 97,
"left": {
"argument": {
"end": 99,
"end": 93,
"name": "l",
"start": 98,
"start": 92,
"type": "Identifier",
"type": "Identifier"
},
"end": 99,
"end": 93,
"operator": "-",
"start": 97,
"start": 91,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
"operator": "+",
"right": {
"end": 103,
"end": 97,
"name": "y",
"start": 102,
"start": 96,
"type": "Identifier",
"type": "Identifier"
},
"start": 97,
"start": 91,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
],
"end": 104,
"start": 88,
"end": 98,
"start": 82,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 83,
"start": 77,
"type": "VariableDeclarator"
}
],
"end": 104,
"end": 98,
"kind": "const",
"start": 83,
"start": 77,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 127,
"end": 121,
"id": {
"end": 109,
"end": 103,
"name": "p1",
"start": 107,
"start": 101,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 119,
"end": 113,
"left": {
"argument": {
"end": 115,
"end": 109,
"name": "l",
"start": 114,
"start": 108,
"type": "Identifier",
"type": "Identifier"
},
"end": 115,
"end": 109,
"operator": "-",
"start": 113,
"start": 107,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
"operator": "+",
"right": {
"end": 119,
"end": 113,
"name": "x",
"start": 118,
"start": 112,
"type": "Identifier",
"type": "Identifier"
},
"start": 113,
"start": 107,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 126,
"end": 120,
"left": {
"end": 122,
"end": 116,
"name": "l",
"start": 121,
"start": 115,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 126,
"end": 120,
"name": "y",
"start": 125,
"start": 119,
"type": "Identifier",
"type": "Identifier"
},
"start": 121,
"start": 115,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
],
"end": 127,
"start": 112,
"end": 121,
"start": 106,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 107,
"start": 101,
"type": "VariableDeclarator"
}
],
"end": 127,
"end": 121,
"kind": "const",
"start": 107,
"start": 101,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 149,
"end": 143,
"id": {
"end": 132,
"end": 126,
"name": "p2",
"start": 130,
"start": 124,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 141,
"end": 135,
"left": {
"end": 137,
"end": 131,
"name": "l",
"start": 136,
"start": 130,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 141,
"end": 135,
"name": "x",
"start": 140,
"start": 134,
"type": "Identifier",
"type": "Identifier"
},
"start": 136,
"start": 130,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 148,
"end": 142,
"left": {
"end": 144,
"end": 138,
"name": "l",
"start": 143,
"start": 137,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 148,
"end": 142,
"name": "y",
"start": 147,
"start": 141,
"type": "Identifier",
"type": "Identifier"
},
"start": 143,
"start": 137,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
],
"end": 149,
"start": 135,
"end": 143,
"start": 129,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 130,
"start": 124,
"type": "VariableDeclarator"
}
],
"end": 149,
"end": 143,
"kind": "const",
"start": 130,
"start": 124,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 172,
"end": 166,
"id": {
"end": 154,
"end": 148,
"name": "p3",
"start": 152,
"start": 146,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 163,
"end": 157,
"left": {
"end": 159,
"end": 153,
"name": "l",
"start": 158,
"start": 152,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 163,
"end": 157,
"name": "x",
"start": 162,
"start": 156,
"type": "Identifier",
"type": "Identifier"
},
"start": 158,
"start": 152,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 171,
"end": 165,
"left": {
"argument": {
"end": 167,
"end": 161,
"name": "l",
"start": 166,
"start": 160,
"type": "Identifier",
"type": "Identifier"
},
"end": 167,
"end": 161,
"operator": "-",
"start": 165,
"start": 159,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
"operator": "+",
"right": {
"end": 171,
"end": 165,
"name": "y",
"start": 170,
"start": 164,
"type": "Identifier",
"type": "Identifier"
},
"start": 165,
"start": 159,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
],
"end": 172,
"start": 157,
"end": 166,
"start": 151,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 152,
"start": 146,
"type": "VariableDeclarator"
}
],
"end": 172,
"end": 166,
"kind": "const",
"start": 152,
"start": 146,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
@ -460,205 +459,198 @@ snapshot_kind: text
{
"arguments": [
{
"end": 199,
"end": 193,
"name": "p0",
"start": 197,
"start": 191,
"type": "Identifier",
"type": "Identifier"
}
],
"callee": {
"end": 196,
"end": 190,
"name": "startSketchAt",
"start": 183,
"start": 177,
"type": "Identifier"
},
"end": 200,
"optional": false,
"start": 183,
"end": 194,
"start": 177,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 217,
"end": 211,
"name": "p1",
"start": 215,
"start": 209,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 220,
"start": 219,
"end": 214,
"start": 213,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 214,
"end": 208,
"name": "lineTo",
"start": 208,
"start": 202,
"type": "Identifier"
},
"end": 221,
"optional": false,
"start": 208,
"end": 215,
"start": 202,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 238,
"end": 232,
"name": "p2",
"start": 236,
"start": 230,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 241,
"start": 240,
"end": 235,
"start": 234,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 235,
"end": 229,
"name": "lineTo",
"start": 229,
"start": 223,
"type": "Identifier"
},
"end": 242,
"optional": false,
"start": 229,
"end": 236,
"start": 223,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 259,
"end": 253,
"name": "p3",
"start": 257,
"start": 251,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 262,
"start": 261,
"end": 256,
"start": 255,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 256,
"end": 250,
"name": "lineTo",
"start": 250,
"start": 244,
"type": "Identifier"
},
"end": 263,
"optional": false,
"start": 250,
"end": 257,
"start": 244,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 280,
"end": 274,
"name": "p0",
"start": 278,
"start": 272,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 283,
"start": 282,
"end": 277,
"start": 276,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 277,
"end": 271,
"name": "lineTo",
"start": 271,
"start": 265,
"type": "Identifier"
},
"end": 284,
"optional": false,
"start": 271,
"end": 278,
"start": 265,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 299,
"start": 298,
"end": 293,
"start": 292,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 297,
"end": 291,
"name": "close",
"start": 292,
"start": 286,
"type": "Identifier"
},
"end": 300,
"optional": false,
"start": 292,
"end": 294,
"start": 286,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 322,
"end": 316,
"name": "length",
"start": 316,
"start": 310,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 325,
"start": 324,
"end": 319,
"start": 318,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 315,
"end": 309,
"name": "extrude",
"start": 308,
"start": 302,
"type": "Identifier"
},
"end": 326,
"optional": false,
"start": 308,
"end": 320,
"start": 302,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 326,
"start": 183,
"end": 320,
"start": 177,
"type": "PipeExpression",
"type": "PipeExpression"
},
"end": 326,
"start": 176,
"end": 320,
"start": 170,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 328,
"end": 322,
"nonCodeMeta": {
"nonCodeNodes": {
"6": [
{
"end": 176,
"start": 172,
"end": 170,
"start": 166,
"type": "NonCodeNode",
"value": {
"type": "newLine"
@ -668,16 +660,16 @@ snapshot_kind: text
},
"startNodes": []
},
"start": 30
"start": 24
},
"end": 328,
"end": 322,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 17,
"end": 14,
"name": "length",
"start": 11,
"start": 8,
"type": "Identifier"
},
"optional": false
@ -685,15 +677,15 @@ snapshot_kind: text
{
"type": "Parameter",
"identifier": {
"end": 25,
"end": 22,
"name": "center",
"start": 19,
"start": 16,
"type": "Identifier"
},
"optional": false
}
],
"start": 10,
"start": 7,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
@ -701,7 +693,7 @@ snapshot_kind: text
"type": "VariableDeclarator"
}
],
"end": 328,
"end": 322,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
@ -710,19 +702,19 @@ snapshot_kind: text
{
"declarations": [
{
"end": 355,
"end": 349,
"id": {
"end": 336,
"end": 330,
"name": "myCube",
"start": 330,
"start": 324,
"type": "Identifier"
},
"init": {
"arguments": [
{
"end": 346,
"end": 340,
"raw": "40",
"start": 344,
"start": 338,
"type": "Literal",
"type": "Literal",
"value": 40.0
@ -730,58 +722,57 @@ snapshot_kind: text
{
"elements": [
{
"end": 350,
"end": 344,
"raw": "0",
"start": 349,
"start": 343,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 353,
"end": 347,
"raw": "0",
"start": 352,
"start": 346,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 354,
"start": 348,
"end": 348,
"start": 342,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
],
"callee": {
"end": 343,
"end": 337,
"name": "cube",
"start": 339,
"start": 333,
"type": "Identifier"
},
"end": 355,
"optional": false,
"start": 339,
"end": 349,
"start": 333,
"type": "CallExpression",
"type": "CallExpression"
},
"start": 330,
"start": 324,
"type": "VariableDeclarator"
}
],
"end": 355,
"end": 349,
"kind": "const",
"start": 330,
"start": 324,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 356,
"end": 350,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"end": 330,
"start": 328,
"end": 324,
"start": 322,
"type": "NonCodeNode",
"value": {
"type": "newLine"

View File

@ -1,4 +1,4 @@
fn cube = (length, center) => {
fn cube(length, center) {
l = length / 2
x = center[0]
y = center[1]

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 92
description: Program memory after executing cube.kcl
snapshot_kind: text
---
@ -36,434 +35,434 @@ snapshot_kind: text
{
"declarations": [
{
"end": 48,
"end": 42,
"id": {
"end": 35,
"end": 29,
"name": "l",
"start": 34,
"start": 28,
"type": "Identifier"
},
"init": {
"end": 48,
"end": 42,
"left": {
"end": 44,
"end": 38,
"name": "length",
"start": 38,
"start": 32,
"type": "Identifier",
"type": "Identifier"
},
"operator": "/",
"right": {
"end": 48,
"end": 42,
"raw": "2",
"start": 47,
"start": 41,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
"start": 38,
"start": 32,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 34,
"start": 28,
"type": "VariableDeclarator"
}
],
"end": 48,
"end": 42,
"kind": "const",
"start": 34,
"start": 28,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 64,
"end": 58,
"id": {
"end": 52,
"end": 46,
"name": "x",
"start": 51,
"start": 45,
"type": "Identifier"
},
"init": {
"computed": false,
"end": 64,
"end": 58,
"object": {
"end": 61,
"end": 55,
"name": "center",
"start": 55,
"start": 49,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 63,
"end": 57,
"raw": "0",
"start": 62,
"start": 56,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
"start": 55,
"start": 49,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 51,
"start": 45,
"type": "VariableDeclarator"
}
],
"end": 64,
"end": 58,
"kind": "const",
"start": 51,
"start": 45,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 80,
"end": 74,
"id": {
"end": 68,
"end": 62,
"name": "y",
"start": 67,
"start": 61,
"type": "Identifier"
},
"init": {
"computed": false,
"end": 80,
"end": 74,
"object": {
"end": 77,
"end": 71,
"name": "center",
"start": 71,
"start": 65,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 79,
"end": 73,
"raw": "1",
"start": 78,
"start": 72,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 71,
"start": 65,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 67,
"start": 61,
"type": "VariableDeclarator"
}
],
"end": 80,
"end": 74,
"kind": "const",
"start": 67,
"start": 61,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 104,
"end": 98,
"id": {
"end": 85,
"end": 79,
"name": "p0",
"start": 83,
"start": 77,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 95,
"end": 89,
"left": {
"argument": {
"end": 91,
"end": 85,
"name": "l",
"start": 90,
"start": 84,
"type": "Identifier",
"type": "Identifier"
},
"end": 91,
"end": 85,
"operator": "-",
"start": 89,
"start": 83,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
"operator": "+",
"right": {
"end": 95,
"end": 89,
"name": "x",
"start": 94,
"start": 88,
"type": "Identifier",
"type": "Identifier"
},
"start": 89,
"start": 83,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 103,
"end": 97,
"left": {
"argument": {
"end": 99,
"end": 93,
"name": "l",
"start": 98,
"start": 92,
"type": "Identifier",
"type": "Identifier"
},
"end": 99,
"end": 93,
"operator": "-",
"start": 97,
"start": 91,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
"operator": "+",
"right": {
"end": 103,
"end": 97,
"name": "y",
"start": 102,
"start": 96,
"type": "Identifier",
"type": "Identifier"
},
"start": 97,
"start": 91,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
],
"end": 104,
"start": 88,
"end": 98,
"start": 82,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 83,
"start": 77,
"type": "VariableDeclarator"
}
],
"end": 104,
"end": 98,
"kind": "const",
"start": 83,
"start": 77,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 127,
"end": 121,
"id": {
"end": 109,
"end": 103,
"name": "p1",
"start": 107,
"start": 101,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 119,
"end": 113,
"left": {
"argument": {
"end": 115,
"end": 109,
"name": "l",
"start": 114,
"start": 108,
"type": "Identifier",
"type": "Identifier"
},
"end": 115,
"end": 109,
"operator": "-",
"start": 113,
"start": 107,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
"operator": "+",
"right": {
"end": 119,
"end": 113,
"name": "x",
"start": 118,
"start": 112,
"type": "Identifier",
"type": "Identifier"
},
"start": 113,
"start": 107,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 126,
"end": 120,
"left": {
"end": 122,
"end": 116,
"name": "l",
"start": 121,
"start": 115,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 126,
"end": 120,
"name": "y",
"start": 125,
"start": 119,
"type": "Identifier",
"type": "Identifier"
},
"start": 121,
"start": 115,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
],
"end": 127,
"start": 112,
"end": 121,
"start": 106,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 107,
"start": 101,
"type": "VariableDeclarator"
}
],
"end": 127,
"end": 121,
"kind": "const",
"start": 107,
"start": 101,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 149,
"end": 143,
"id": {
"end": 132,
"end": 126,
"name": "p2",
"start": 130,
"start": 124,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 141,
"end": 135,
"left": {
"end": 137,
"end": 131,
"name": "l",
"start": 136,
"start": 130,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 141,
"end": 135,
"name": "x",
"start": 140,
"start": 134,
"type": "Identifier",
"type": "Identifier"
},
"start": 136,
"start": 130,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 148,
"end": 142,
"left": {
"end": 144,
"end": 138,
"name": "l",
"start": 143,
"start": 137,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 148,
"end": 142,
"name": "y",
"start": 147,
"start": 141,
"type": "Identifier",
"type": "Identifier"
},
"start": 143,
"start": 137,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
],
"end": 149,
"start": 135,
"end": 143,
"start": 129,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 130,
"start": 124,
"type": "VariableDeclarator"
}
],
"end": 149,
"end": 143,
"kind": "const",
"start": 130,
"start": 124,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 172,
"end": 166,
"id": {
"end": 154,
"end": 148,
"name": "p3",
"start": 152,
"start": 146,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 163,
"end": 157,
"left": {
"end": 159,
"end": 153,
"name": "l",
"start": 158,
"start": 152,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 163,
"end": 157,
"name": "x",
"start": 162,
"start": 156,
"type": "Identifier",
"type": "Identifier"
},
"start": 158,
"start": 152,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 171,
"end": 165,
"left": {
"argument": {
"end": 167,
"end": 161,
"name": "l",
"start": 166,
"start": 160,
"type": "Identifier",
"type": "Identifier"
},
"end": 167,
"end": 161,
"operator": "-",
"start": 165,
"start": 159,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
"operator": "+",
"right": {
"end": 171,
"end": 165,
"name": "y",
"start": 170,
"start": 164,
"type": "Identifier",
"type": "Identifier"
},
"start": 165,
"start": 159,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
],
"end": 172,
"start": 157,
"end": 166,
"start": 151,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 152,
"start": 146,
"type": "VariableDeclarator"
}
],
"end": 172,
"end": 166,
"kind": "const",
"start": 152,
"start": 146,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
@ -473,205 +472,198 @@ snapshot_kind: text
{
"arguments": [
{
"end": 199,
"end": 193,
"name": "p0",
"start": 197,
"start": 191,
"type": "Identifier",
"type": "Identifier"
}
],
"callee": {
"end": 196,
"end": 190,
"name": "startSketchAt",
"start": 183,
"start": 177,
"type": "Identifier"
},
"end": 200,
"optional": false,
"start": 183,
"end": 194,
"start": 177,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 217,
"end": 211,
"name": "p1",
"start": 215,
"start": 209,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 220,
"start": 219,
"end": 214,
"start": 213,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 214,
"end": 208,
"name": "lineTo",
"start": 208,
"start": 202,
"type": "Identifier"
},
"end": 221,
"optional": false,
"start": 208,
"end": 215,
"start": 202,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 238,
"end": 232,
"name": "p2",
"start": 236,
"start": 230,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 241,
"start": 240,
"end": 235,
"start": 234,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 235,
"end": 229,
"name": "lineTo",
"start": 229,
"start": 223,
"type": "Identifier"
},
"end": 242,
"optional": false,
"start": 229,
"end": 236,
"start": 223,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 259,
"end": 253,
"name": "p3",
"start": 257,
"start": 251,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 262,
"start": 261,
"end": 256,
"start": 255,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 256,
"end": 250,
"name": "lineTo",
"start": 250,
"start": 244,
"type": "Identifier"
},
"end": 263,
"optional": false,
"start": 250,
"end": 257,
"start": 244,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 280,
"end": 274,
"name": "p0",
"start": 278,
"start": 272,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 283,
"start": 282,
"end": 277,
"start": 276,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 277,
"end": 271,
"name": "lineTo",
"start": 271,
"start": 265,
"type": "Identifier"
},
"end": 284,
"optional": false,
"start": 271,
"end": 278,
"start": 265,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 299,
"start": 298,
"end": 293,
"start": 292,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 297,
"end": 291,
"name": "close",
"start": 292,
"start": 286,
"type": "Identifier"
},
"end": 300,
"optional": false,
"start": 292,
"end": 294,
"start": 286,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 322,
"end": 316,
"name": "length",
"start": 316,
"start": 310,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 325,
"start": 324,
"end": 319,
"start": 318,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 315,
"end": 309,
"name": "extrude",
"start": 308,
"start": 302,
"type": "Identifier"
},
"end": 326,
"optional": false,
"start": 308,
"end": 320,
"start": 302,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 326,
"start": 183,
"end": 320,
"start": 177,
"type": "PipeExpression",
"type": "PipeExpression"
},
"end": 326,
"start": 176,
"end": 320,
"start": 170,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 328,
"end": 322,
"nonCodeMeta": {
"nonCodeNodes": {
"6": [
{
"end": 176,
"start": 172,
"end": 170,
"start": 166,
"type": "NonCodeNode",
"value": {
"type": "newLine"
@ -681,16 +673,16 @@ snapshot_kind: text
},
"startNodes": []
},
"start": 30
"start": 24
},
"end": 328,
"end": 322,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 17,
"end": 14,
"name": "length",
"start": 11,
"start": 8,
"type": "Identifier"
},
"optional": false
@ -698,15 +690,15 @@ snapshot_kind: text
{
"type": "Parameter",
"identifier": {
"end": 25,
"end": 22,
"name": "center",
"start": 19,
"start": 16,
"type": "Identifier"
},
"optional": false
}
],
"start": 10,
"start": 7,
"type": "FunctionExpression"
},
"memory": {
@ -743,8 +735,8 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
10,
328,
7,
322,
0
]
}
@ -759,8 +751,8 @@ snapshot_kind: text
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
208,
221,
202,
215,
0
],
"tag": null,
@ -770,8 +762,8 @@ snapshot_kind: text
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
229,
242,
223,
236,
0
],
"tag": null,
@ -781,8 +773,8 @@ snapshot_kind: text
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
250,
263,
244,
257,
0
],
"tag": null,
@ -792,8 +784,8 @@ snapshot_kind: text
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
271,
284,
265,
278,
0
],
"tag": null,
@ -808,8 +800,8 @@ snapshot_kind: text
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
208,
221,
202,
215,
0
]
},
@ -828,8 +820,8 @@ snapshot_kind: text
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
229,
242,
223,
236,
0
]
},
@ -848,8 +840,8 @@ snapshot_kind: text
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
250,
263,
244,
257,
0
]
},
@ -868,8 +860,8 @@ snapshot_kind: text
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
271,
284,
265,
278,
0
]
},
@ -888,8 +880,8 @@ snapshot_kind: text
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
292,
300,
286,
294,
0
]
},
@ -944,8 +936,8 @@ snapshot_kind: text
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
183,
200,
177,
194,
0
]
}
@ -953,8 +945,8 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
183,
200,
177,
194,
0
]
}
@ -966,8 +958,8 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
183,
200,
177,
194,
0
]
}

View File

@ -1,6 +1,5 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 52
description: Result of parsing double_map_fn.kcl
snapshot_kind: text
---
@ -10,7 +9,7 @@ snapshot_kind: text
{
"declarations": [
{
"end": 40,
"end": 34,
"id": {
"end": 12,
"name": "increment",
@ -22,50 +21,50 @@ snapshot_kind: text
"body": [
{
"argument": {
"end": 38,
"end": 32,
"left": {
"end": 34,
"end": 28,
"name": "i",
"start": 33,
"start": 27,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 38,
"end": 32,
"raw": "1",
"start": 37,
"start": 31,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 33,
"start": 27,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"end": 38,
"start": 26,
"end": 32,
"start": 20,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 40,
"start": 22
"end": 34,
"start": 16
},
"end": 40,
"end": 34,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 17,
"end": 14,
"name": "i",
"start": 16,
"start": 13,
"type": "Identifier"
},
"optional": false
}
],
"start": 15,
"start": 12,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
@ -73,7 +72,7 @@ snapshot_kind: text
"type": "VariableDeclarator"
}
],
"end": 40,
"end": 34,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
@ -82,29 +81,29 @@ snapshot_kind: text
{
"declarations": [
{
"end": 53,
"end": 47,
"id": {
"end": 44,
"end": 38,
"name": "xs",
"start": 42,
"start": 36,
"type": "Identifier"
},
"init": {
"end": 53,
"end": 47,
"endElement": {
"end": 52,
"end": 46,
"raw": "2",
"start": 51,
"start": 45,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
"endInclusive": true,
"start": 47,
"start": 41,
"startElement": {
"end": 49,
"end": 43,
"raw": "0",
"start": 48,
"start": 42,
"type": "Literal",
"type": "Literal",
"value": 0.0
@ -112,115 +111,113 @@ snapshot_kind: text
"type": "ArrayRangeExpression",
"type": "ArrayRangeExpression"
},
"start": 42,
"start": 36,
"type": "VariableDeclarator"
}
],
"end": 53,
"end": 47,
"kind": "const",
"start": 42,
"start": 36,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declarations": [
{
"end": 107,
"end": 101,
"id": {
"end": 56,
"end": 50,
"name": "ys",
"start": 54,
"start": 48,
"type": "Identifier"
},
"init": {
"body": [
{
"end": 61,
"end": 55,
"name": "xs",
"start": 59,
"start": 53,
"type": "Identifier",
"type": "Identifier"
},
{
"arguments": [
{
"end": 72,
"start": 71,
"end": 66,
"start": 65,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
},
{
"end": 83,
"end": 77,
"name": "increment",
"start": 74,
"start": 68,
"type": "Identifier",
"type": "Identifier"
}
],
"callee": {
"end": 70,
"end": 64,
"name": "map",
"start": 67,
"start": 61,
"type": "Identifier"
},
"end": 84,
"optional": false,
"start": 67,
"end": 78,
"start": 61,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 95,
"start": 94,
"end": 89,
"start": 88,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
},
{
"end": 106,
"end": 100,
"name": "increment",
"start": 97,
"start": 91,
"type": "Identifier",
"type": "Identifier"
}
],
"callee": {
"end": 93,
"end": 87,
"name": "map",
"start": 90,
"start": 84,
"type": "Identifier"
},
"end": 107,
"optional": false,
"start": 90,
"end": 101,
"start": 84,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 107,
"start": 59,
"end": 101,
"start": 53,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 54,
"start": 48,
"type": "VariableDeclarator"
}
],
"end": 107,
"end": 101,
"kind": "const",
"start": 54,
"start": 48,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 108,
"end": 102,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"end": 42,
"start": 40,
"end": 36,
"start": 34,
"type": "NonCodeNode",
"value": {
"type": "newLine"

View File

@ -1,4 +1,4 @@
fn increment = (i) => {
fn increment(i) {
return i + 1
}

View File

@ -1,8 +1,6 @@
---
source: kcl/src/simulation_tests.rs
assertion_line: 92
description: Program memory after executing double_map_fn.kcl
snapshot_kind: text
---
{
"environments": [
@ -35,50 +33,50 @@ snapshot_kind: text
"body": [
{
"argument": {
"end": 38,
"end": 32,
"left": {
"end": 34,
"end": 28,
"name": "i",
"start": 33,
"start": 27,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 38,
"end": 32,
"raw": "1",
"start": 37,
"start": 31,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 33,
"start": 27,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"end": 38,
"start": 26,
"end": 32,
"start": 20,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 40,
"start": 22
"end": 34,
"start": 16
},
"end": 40,
"end": 34,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 17,
"end": 14,
"name": "i",
"start": 16,
"start": 13,
"type": "Identifier"
},
"optional": false
}
],
"start": 15,
"start": 12,
"type": "FunctionExpression"
},
"memory": {
@ -115,8 +113,8 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
15,
40,
12,
34,
0
]
}
@ -131,8 +129,8 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
41,
47,
53,
0
]
}
@ -144,8 +142,8 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
41,
47,
53,
0
]
}
@ -157,8 +155,8 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
41,
47,
53,
0
]
}
@ -168,8 +166,8 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
41,
47,
53,
0
]
}
@ -184,22 +182,22 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
41,
47,
53,
0
]
},
{
"sourceRange": [
37,
38,
31,
32,
0
]
},
{
"sourceRange": [
37,
38,
31,
32,
0
]
}
@ -211,22 +209,22 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
41,
47,
53,
0
]
},
{
"sourceRange": [
37,
38,
31,
32,
0
]
},
{
"sourceRange": [
37,
38,
31,
32,
0
]
}
@ -238,22 +236,22 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
41,
47,
53,
0
]
},
{
"sourceRange": [
37,
38,
31,
32,
0
]
},
{
"sourceRange": [
37,
38,
31,
32,
0
]
}
@ -263,8 +261,8 @@ snapshot_kind: text
"__meta": [
{
"sourceRange": [
90,
107,
84,
101,
0
]
}

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