Compare commits

...

94 Commits

Author SHA1 Message Date
9407162543 Merge branch 'main' into pierremtb/issue4472-Add-shell-point-and-click-operation-backup 2024-12-09 14:04:18 -05:00
067b83b468 Manual resolution of snapshot conflicts 2024-12-09 13:56:33 -05:00
ff2103d493 Bump node to v22.12.0 (LTS) (#4706) 2024-12-09 13:37:01 -05:00
2dfa8f2176 Add installation instructions for all platforms (#4592)
* Add installation instructions for all platforms
Fixes #4511

* Typo

* Typo2

* Improve linux instructions, thanks @TomPridham

Co-authored-by: Tom Pridham <pridham.tom@gmail.com>

---------

Co-authored-by: Tom Pridham <pridham.tom@gmail.com>
2024-12-09 12:30:24 -05:00
29ed330326 Add some more warnings (#4697) 2024-12-10 06:27:04 +13:00
c5b30341eb Add unit tests for doesSceneHaveExtrudedSketch 2024-12-09 12:26:21 -05:00
ca2cc825a6 Invalidate nightly bucket files after publish (#4627)
* Invalidate nightly bucket files after publish

* Fix conflict resolution
2024-12-09 11:45:32 -05:00
3e6441b563 Fix test annotations 2024-12-09 11:36:46 -05:00
acafcf2d4d Apply suggestions from Frank's review
Co-authored-by: Frank Noirot <frank@zoo.dev>
2024-12-09 11:33:41 -05:00
83fe1b7ce0 Fixup for review comment from #4677 (#4696)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2024-12-09 10:42:03 -05:00
157b76cc78 KCL refactor: combine two fields into one struct (#4689) 2024-12-06 19:11:31 -06:00
cf957d880e KCL refactor: Fix argument names (#4690)
Does not change behaviour. It just clarifies whether JS is passing a string containing KCL source code, or containing a JSON-stringified KCL AST.
2024-12-06 17:20:06 -06:00
dfc3d19677 remove clearScene from TS side (#4684)
* updates

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

* fix lint

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

* add failing tests

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

* more tests

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

* the scene is cleared

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

* create new clear scene and bust cache function from rust side

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

* pull thru

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

* set that we switched files

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

* updates

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

* updates

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

* updates

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

* fix two dirties

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

* fix

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-12-06 22:56:53 +00:00
dd370a9365 AST: Allow unlabeled kw args (#4686)
When declaring a function, its first parameter is allowed to be prefixed with `@`. This means that when users call this function, they don't have to label this argument.

Only the first parameter is allowed this prefix, no others.

Part of https://github.com/KittyCAD/modeling-app/issues/4600
2024-12-06 15:44:39 -06:00
2274d6459c Bump @types/react-dom from 18.3.0 to 18.3.1 (#4411)
Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 18.3.0 to 18.3.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

---
updated-dependencies:
- dependency-name: "@types/react-dom"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Adam Chalmers <adam.chalmers@zoo.dev>
2024-12-06 15:21:28 -06:00
32ce857119 Update bracket KCL variable syntax in onboarding (#4685) 2024-12-06 16:04:17 -05:00
88b51da417 Run external contributor branch through CI (#4679)
* fix: make variable declaration errors Cut instead of Backtrace

* fix: clippy, move comma to empty case and add test

* fix: add missing TokenType case

* fix: incorrect fn args after merge

* fix: clippy lint

* fix: update error message being looked for in e2e test

---------

Co-authored-by: Tom Pridham <pridham.tom@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2024-12-06 14:12:20 -05:00
b9f31d94d5 Merge branch 'main' into pierremtb/issue4472-Add-shell-point-and-click-operation 2024-12-05 18:43:44 -05:00
9e03b58ae5 Cap and wall pw test 2024-12-05 18:21:20 -05:00
c591f73c70 Working multi-face shell across types 2024-12-05 17:56:18 -05:00
9330aaba13 Trigger CI 2024-12-05 17:27:16 -05:00
5a14f0189e Look at this (photo)Graph *in the voice of Nickelback* 2024-12-05 22:14:20 +00:00
9af001f22e Lint 2024-12-05 17:04:02 -05:00
435d1ea52e WIP circular dep 2024-12-05 16:53:57 -05:00
54847139f2 Merge branch 'main' into pierremtb/issue4472-Add-shell-point-and-click-operation 2024-12-05 15:00:34 -05:00
9369a17ea7 WIP mutliple faces 2024-12-05 14:59:44 -05:00
9ee2e7c3b0 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-05 19:42:39 +00:00
28ae261e5f Lint fix 2024-12-05 14:38:23 -05:00
e4b0de0ead Add selection guard and clean up 2024-12-05 14:06:09 -05:00
12859598a3 Merge branch 'main' into pierremtb/issue4472-Add-shell-point-and-click-operation 2024-12-05 13:02:35 -05:00
e9a334f433 Fix lint 2024-12-05 13:02:22 -05:00
e470a7b4af Add shell wall test 2024-12-05 12:33:46 -05:00
602c39f63c Add pw tests for cap shell 2024-12-05 12:03:45 -05:00
4994aa6f61 Handle walls 2024-12-05 11:38:03 -05:00
4aa07b81db Add extrude lookup for more generic shell 2024-12-05 10:26:58 -05:00
89ef4b3243 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-05 14:55:04 +00:00
001c9a8219 Update from main 2024-12-05 09:49:14 -05:00
4dde3f60e0 WIP: first time working shell mod 2024-12-04 18:41:45 -05:00
f407c53032 WIP: closer 2024-12-04 17:53:04 -05:00
9ba584487a WIP: more additions 2024-12-04 17:01:11 -05:00
96b66d6bca Merge branch 'pierremtb/issue4470-loft-ui' into pierremtb/issue4472-Add-shell-point-and-click-operation 2024-12-04 16:44:40 -05:00
8d66f3ffad Rollback pw values to pre cam change 2024-12-04 16:43:29 -05:00
f4c54cbbe4 WIP: initial shell code addition 2024-12-04 16:38:44 -05:00
5156b847f3 Trigger CI 2024-12-04 15:56:48 -05:00
ded9f2c56b A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-12-04 20:54:59 +00:00
5db5f79f9a Merge branch 'main' into pierremtb/issue4470-loft-ui 2024-12-04 15:46:18 -05:00
6a883f4a8d A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-04 20:43:39 +00:00
07b91f0fb1 Trigger CI 2024-12-04 15:39:26 -05:00
1b2e213afe A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-12-04 20:37:28 +00:00
f48a23c35e A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-04 20:36:20 +00:00
7e1d102496 Revert snapshots 2024-12-04 15:32:14 -05:00
94cb2535c0 Fix typo 2024-12-04 15:21:03 -05:00
9e08ec9096 A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-12-04 20:00:36 +00:00
17bd8ec32a A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-04 19:58:28 +00:00
d0f12e85e5 Trigger CI 2024-12-04 14:54:05 -05:00
94d185944e A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-12-04 19:52:22 +00:00
c38b2270c3 A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-12-04 19:51:34 +00:00
967ad66c98 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-04 19:47:04 +00:00
afeca9ca39 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-04 19:46:25 +00:00
61242282f0 Trigger CI 2024-12-04 14:42:00 -05:00
0065df13ce A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-12-04 16:55:39 +00:00
01c8d45c13 Remove comments 2024-12-04 11:44:49 -05:00
8b25527f21 Move error logic out of loftSketches, fix pw tests 2024-12-04 11:39:35 -05:00
2abd980de9 Move to fromPromise-based Actor 2024-12-04 11:02:51 -05:00
f783deb706 A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-12-04 15:25:38 +00:00
f4dd295ca1 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-04 15:17:19 +00:00
ceaa85fe3f A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-04 15:16:16 +00:00
3991bd9173 Trigger CI 2024-12-04 10:11:47 -05:00
b8f9da36c0 A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-12-04 10:54:14 +00:00
283315b5d2 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-04 10:44:01 +00:00
e204dfe564 A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-12-04 10:43:15 +00:00
208a36196b Merge branch 'main' into pierremtb/issue4470-loft-ui 2024-12-04 05:38:43 -05:00
660a349588 Add pw test for preselected sketches 2024-12-03 15:17:21 -05:00
56c37da317 Clean up loftSketches function 2024-12-03 15:02:56 -05:00
a46734b76d Add test for doesSceneHaveSweepableSketch with count = 2 2024-12-03 14:39:06 -05:00
4347e0cf84 Clean up and working pw test 2024-12-03 13:49:26 -05:00
df3e541cdf A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-03 17:51:57 +00:00
b1cec443b9 Merge branch 'main' into pierremtb/issue4470-loft-ui 2024-12-03 12:48:32 -05:00
967f49055d Lint 2024-12-02 14:30:43 -05:00
fe977524b5 First point-click loft test (not working locally, loft gets inserted at the wrong place) 2024-12-02 14:19:32 -05:00
d6f271fb0f Enable multiple selections after the button click 2024-12-01 07:00:13 -05:00
e851b2bcc4 Merge branch 'main' into pierremtb/issue4470-loft-ui 2024-11-29 20:01:32 -05:00
be569c91de Clean up 2024-11-29 19:51:56 -05:00
5080e304b9 Appends the loft line after the 'last' sketch in the code 2024-11-29 18:30:15 -05:00
f4e75b7b4f More checks 2024-11-29 13:38:52 -05:00
31cbc90f56 WIP selections 2024-11-29 12:37:53 -05:00
a7d3552472 WIP selections 2024-11-29 11:17:31 -05:00
e984b20664 First pass at handling more than 2 sketches 2024-11-29 10:09:10 -05:00
0c2cd24bda Working loft for two sketches in the right hardcoded order 2024-11-28 20:28:37 -05:00
9b2de237b8 Add selection guard 2024-11-28 16:45:41 -05:00
c79c02f18e A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-11-28 20:25:44 +00:00
4851aa2d71 A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) 2024-11-28 20:17:17 +00:00
76fafa6fd0 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-11-28 20:11:35 +00:00
647ca11e08 WIP: experimenting with Loft UI
Relates to #4470
2024-11-27 15:55:01 -05:00
94 changed files with 2986 additions and 1351 deletions

View File

@ -365,7 +365,7 @@ jobs:
- name: Set more complete nightly release notes
if: ${{ env.IS_NIGHTLY == 'true' }}
run: |
# Note: prefered going this way instead of a full clone in the checkout step,
# Note: preferred going this way instead of a full clone in the checkout step,
# see https://github.com/actions/checkout/issues/1471
git fetch --prune --unshallow --tags
export TAG="nightly-${VERSION}"
@ -394,6 +394,10 @@ jobs:
parent: false
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
- name: Invalidate bucket cache on latest*.yml and last_download.json files
if: ${{ env.IS_NIGHTLY == 'true' }}
run: yarn files:invalidate-bucket:nightly
- name: Tag nightly commit
if: ${{ env.IS_NIGHTLY == 'true' }}
uses: actions/github-script@v7

View File

@ -126,11 +126,7 @@ jobs:
destination: 'dl.kittycad.io/releases/modeling-app'
- name: Invalidate bucket cache on latest*.yml and last_download.json files
run: |
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/last_download.json" --async
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-linux-arm64.yml" --async
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
run: yarn files:invalidate-bucket
- name: Upload release files to Github
if: ${{ github.event_name == 'release' }}

2
.nvmrc
View File

@ -1 +1 @@
v21.7.3
v22.12.0

43
INSTALL.md Normal file
View File

@ -0,0 +1,43 @@
# Setting Up Zoo Modeling App
Compared to other CAD software, getting Zoo Modeling App up and running is quick and straightforward across platforms. It's about 100MB to download and is quick to install.
## Windows
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Windows and for your processor type.
2. Once downloaded, run the installer `Zoo Modeling App-{version}-{arch}-win.exe` which should take a few seconds.
3. The installation happens at `C:\Program Files\Zoo Modeling App`. A shortcut in the start menu is also created so you can run the app easily by clicking on it.
## macOS
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for macOS and for your processor type.
2. Once downloaded, open the disk image `Zoo Modeling App-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
3. You can then open your `Applications` directory and double-click on `Zoo Modeling App` to open.
## Linux
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Linux and for your processor type.
2. Install the dependencies needed to run the [AppImage format](https://appimage.org/).
- On Ubuntu, install the FUSE library with these commands in a terminal.
```bash
sudo apt update
sudo apt install libfuse2
```
- Optionally, follow [these steps](https://github.com/probonopd/go-appimage/blob/master/src/appimaged/README.md#initial-setup) to install `appimaged`. It is a daemon that makes interacting with AppImage files more seamless.
- Once installed, copy the downloaded `Zoo Modeling App-{version}-{arch}-linux.AppImage` to the directory of your choice, for instance `~/Applications`.
- `appimaged` should automatically find it and make it executable. If not, run:
```bash
chmod a+x ~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
```
3. You can double-click on the AppImage to run it, or in a terminal with this command:
```bash
~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
```

View File

@ -458,8 +458,8 @@ test.describe('Editor tests', () => {
/* add the following code to the editor ($ error is not a valid line)
$ error
const topAng = 30
const bottomAng = 25
topAng = 30
bottomAng = 25
*/
await u.codeLocator.click()
await page.keyboard.type('$ error')
@ -474,12 +474,14 @@ test.describe('Editor tests', () => {
await page.keyboard.type('bottomAng = 25')
await page.keyboard.press('Enter')
// error in guter
// error in gutter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
await expect(page.getByText('Unexpected token: $').first()).toBeVisible()
await expect(
page.getByText('Tag names must not be empty').first()
).toBeVisible()
// select the line that's causing the error and delete it
await page.getByText('$ error').click()

View File

@ -7,6 +7,7 @@ export class ToolbarFixture {
extrudeButton!: Locator
loftButton!: Locator
shellButton!: Locator
offsetPlaneButton!: Locator
startSketchBtn!: Locator
lineBtn!: Locator
@ -28,6 +29,7 @@ export class ToolbarFixture {
this.page = page
this.extrudeButton = page.getByTestId('extrude')
this.loftButton = page.getByTestId('loft')
this.shellButton = page.getByTestId('shell')
this.offsetPlaneButton = page.getByTestId('plane-offset')
this.startSketchBtn = page.getByTestId('sketch')
this.lineBtn = page.getByTestId('line')

View File

@ -768,3 +768,166 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
})
})
})
const shellPointAndClickCapCases = [
{ shouldPreselect: true },
{ shouldPreselect: false },
]
shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({
app,
scene,
editor,
toolbar,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 30 }, %)
extrude001 = extrude(30, sketch001)
`
await app.initialise(initialCode)
// One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 }
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const shellDeclaration =
"shell001 = shell({ faces = ['end'], thickness = 5 }, extrude001)"
await test.step(`Look for the grey of the shape`, async () => {
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
})
if (!shouldPreselect) {
await test.step(`Go through the command bar flow without preselected faces`, async () => {
await toolbar.shellButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'selection',
currentArgValue: '',
headerArguments: {
Selection: '',
Thickness: '',
},
highlightedHeaderArg: 'selection',
commandName: 'Shell',
})
await clickOnCap()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Selection: '1 cap',
Thickness: '5',
},
commandName: 'Shell',
})
await cmdBar.progressCmdBar()
})
} else {
await test.step(`Preselect the cap`, async () => {
await clickOnCap()
})
await test.step(`Go through the command bar flow with a preselected face (cap)`, async () => {
await toolbar.shellButton.click()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Selection: '1 cap',
Thickness: '5',
},
commandName: 'Shell',
})
await cmdBar.progressCmdBar()
})
}
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
await editor.expectEditor.toContain(shellDeclaration)
await editor.expectState({
diagnostics: [],
activeLines: [shellDeclaration],
highlightedCode: '',
})
await scene.expectPixelColor([146, 146, 146], testPoint, 15)
})
})
})
test('Shell point-and-click wall', async ({
app,
page,
scene,
editor,
toolbar,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn('XY')
|> startProfileAt([-20, 20], %)
|> xLine(40, %)
|> yLine(-60, %)
|> xLine(-40, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
extrude001 = extrude(40, sketch001)
`
await app.initialise(initialCode)
// One dumb hardcoded screen pixel value
const testPoint = { x: 580, y: 180 }
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
const mutatedCode = 'xLine(-40, %, $seg01)'
const shellDeclaration =
"shell001 = shell({ faces = ['end', seg01], thickness = 5}, extrude001)"
const formattedOutLastLine = '}, extrude001)'
await test.step(`Look for the grey of the shape`, async () => {
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
})
await test.step(`Go through the command bar flow, selecting a wall and keeping default thickness`, async () => {
await toolbar.shellButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'selection',
currentArgValue: '',
headerArguments: {
Selection: '',
Thickness: '',
},
highlightedHeaderArg: 'selection',
commandName: 'Shell',
})
await clickOnCap()
await page.keyboard.down('Shift')
await clickOnWall()
await app.page.waitForTimeout(500)
await page.keyboard.up('Shift')
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Selection: '1 cap, 1 face',
Thickness: '5',
},
commandName: 'Shell',
})
await cmdBar.progressCmdBar()
})
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
await editor.expectEditor.toContain(mutatedCode)
await editor.expectEditor.toContain(shellDeclaration)
await editor.expectState({
diagnostics: [],
activeLines: [formattedOutLastLine],
highlightedCode: '',
})
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
})
})

View File

@ -136,6 +136,335 @@ test(
}
)
test(
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const { electronApp, page } = await setupElectron({
testInfo,
folderSetupFn: async (dir) => {
const bracketDir = join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
join(bracketDir, 'main.kcl')
)
const errorDir = join(dir, 'broken-code')
await fsp.mkdir(errorDir, { recursive: true })
await fsp.copyFile(
executorInputPath('broken-code-test.kcl'),
join(errorDir, 'main.kcl')
)
},
})
await page.setViewportSize({ width: 1200, height: 500 })
const u = await getUtils(page)
page.on('console', console.log)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
await page.getByTestId('app-logo').click()
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
await expect(page.getByText('broken-code')).toBeVisible()
await expect(page.getByText('bracket')).toBeVisible()
await expect(page.getByText('New Project')).toBeVisible()
})
await test.step('opening broken code project should clear the scene and show the error', async () => {
// Go back home.
await expect(page.getByText('broken-code')).toBeVisible()
await page.getByText('broken-code').click()
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
const crypticErrorText = `Expected a tag declarator`
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
// black pixel means the scene has been cleared.
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await electronApp.close()
}
)
test(
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const { electronApp, page } = await setupElectron({
testInfo,
folderSetupFn: async (dir) => {
const bracketDir = join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
join(bracketDir, 'main.kcl')
)
const emptyDir = join(dir, 'empty')
await fsp.mkdir(emptyDir, { recursive: true })
await fsp.writeFile(join(emptyDir, 'main.kcl'), '')
},
})
await page.setViewportSize({ width: 1200, height: 500 })
const u = await getUtils(page)
page.on('console', console.log)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
await page.getByTestId('app-logo').click()
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
await expect(page.getByText('empty')).toBeVisible()
await expect(page.getByText('bracket')).toBeVisible()
await expect(page.getByText('New Project')).toBeVisible()
})
await test.step('opening empty code project should clear the scene', async () => {
// Go back home.
await expect(page.getByText('empty')).toBeVisible()
await page.getByText('empty').click()
// Ensure the code is empty.
await expect(u.codeLocator).toContainText('')
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
// planes colors means the scene has been cleared.
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await electronApp.close()
}
)
test(
'open a file in a project works and renders, open empty file, it should clear the scene',
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const { electronApp, page } = await setupElectron({
testInfo,
folderSetupFn: async (dir) => {
const bracketDir = join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
join(bracketDir, 'main.kcl')
)
await fsp.writeFile(join(bracketDir, 'empty.kcl'), '')
},
})
await page.setViewportSize({ width: 1200, height: 500 })
const u = await getUtils(page)
page.on('console', console.log)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await test.step('creating a empty file should clear the scene', async () => {
// open the file pane.
await page.getByTestId('files-pane-button').click()
// OPen the other file.
const file = page.getByRole('button', { name: 'empty.kcl' })
await expect(file).toBeVisible()
await file.click()
// planes colors means the scene has been cleared.
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
timeout: 10_000,
})
.toBeLessThan(15)
// Ensure the code is empty.
await expect(u.codeLocator).toContainText('')
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
})
await electronApp.close()
}
)
test(
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const { electronApp, page } = await setupElectron({
testInfo,
folderSetupFn: async (dir) => {
const bracketDir = join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
join(bracketDir, 'main.kcl')
)
await fsp.copyFile(
executorInputPath('broken-code-test.kcl'),
join(bracketDir, 'broken-code-test.kcl')
)
},
})
await page.setViewportSize({ width: 1200, height: 500 })
const u = await getUtils(page)
page.on('console', console.log)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await test.step('opening broken code file should clear the scene and show the error', async () => {
// open the file pane.
await page.getByTestId('files-pane-button').click()
// OPen the other file.
const file = page.getByRole('button', { name: 'broken-code-test.kcl' })
await expect(file).toBeVisible()
await file.click()
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
const crypticErrorText = `Expected a tag declarator`
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
// black pixel means the scene has been cleared.
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await electronApp.close()
}
)
test(
'when code with error first loads you get errors in console',
{ tag: '@electron' },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -81,6 +81,7 @@
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",
"simpleserver:stop": "kill-port 3000",
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
"fetch:wasm": "./get-latest-wasm-bundle.sh",
@ -95,6 +96,8 @@
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
"files:set-notes": "./scripts/set-files-notes.sh",
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
"files:invalidate-bucket": "./scripts/invalidate-files-bucket.sh",
"files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly",
"postinstall": "yarn fetch:samples && yarn xstate:typegen && ./node_modules/.bin/electron-rebuild",
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
"make:dev": "make dev",
@ -171,7 +174,7 @@
"@types/pixelmatch": "^5.2.6",
"@types/pngjs": "^6.0.4",
"@types/react": "^18.3.4",
"@types/react-dom": "^18.2.25",
"@types/react-dom": "^18.3.1",
"@types/react-modal": "^3.16.3",
"@types/three": "^0.163.0",
"@types/ua-parser-js": "^0.7.39",

View File

@ -0,0 +1,11 @@
#!/bin/bash
base_dir="/releases/modeling-app"
if [[ $1 = "--nightly" ]]; then
base_dir="/releases/modeling-app/nightly"
fi
echo "Invalidating json and yml files at $base_dir in the download bucket"
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/last_download.json" --async
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/latest-linux-arm64.yml" --async
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/latest-mac.yml" --async
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/latest.yml" --async

View File

@ -266,6 +266,7 @@ const FileTreeItem = ({
// Let the lsp servers know we closed a file.
onFileClose(currentFile?.path || null, project?.path || null)
onFileOpen(fileOrDir.path, project?.path || null)
kclManager.switchedFiles = true
// Open kcl files
navigate(`${PATHS.FILE}/${encodeURIComponent(fileOrDir.path)}`)

View File

@ -51,6 +51,7 @@ import {
Selections,
updateSelections,
canLoftSelection,
canShellSelection,
} from 'lib/selections'
import { applyConstraintIntersect } from './Toolbar/Intersect'
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
@ -69,6 +70,7 @@ import {
} from 'lang/modifyAst'
import { Program, parse, recast, resultIsOk } from 'lang/wasm'
import {
doesSceneHaveExtrudedSketch,
doesSceneHaveSweepableSketch,
getNodePathFromSourceRange,
isSingleCursorInPipe,
@ -585,6 +587,24 @@ export const ModelingMachineProvider = ({
if (err(canLoft)) return false
return canLoft
},
'has valid shell selection': ({
context: { selectionRanges },
event,
}) => {
const hasNoSelection =
selectionRanges.graphSelections.length === 0 ||
isRangeBetweenCharacters(selectionRanges) ||
isSelectionLastLine(selectionRanges, codeManager.code)
if (hasNoSelection) {
return doesSceneHaveExtrudedSketch(kclManager.ast)
}
const canShell = canShellSelection(selectionRanges)
console.log('canShellSelection', canShellSelection(selectionRanges))
if (err(canShell)) return false
return canShell
},
'has valid selection for deletion': ({
context: { selectionRanges },
}) => {

View File

@ -40,7 +40,9 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
<Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-100 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50">
<Menu.Item>
<button
onClick={() => kclManager.format()}
onClick={() => {
kclManager.format().catch(reportRejection)
}}
className={styles.button}
>
<span>Format code</span>

View File

@ -10,7 +10,7 @@ import { APP_NAME } from 'lib/constants'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { CustomIcon } from './CustomIcon'
import { useLspContext } from './LspProvider'
import { engineCommandManager } from 'lib/singletons'
import { engineCommandManager, kclManager } from 'lib/singletons'
import { MachineManagerContext } from 'components/MachineManagerProvider'
import usePlatform from 'hooks/usePlatform'
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
@ -68,8 +68,7 @@ function AppLogoLink({
data-testid="app-logo"
onClick={() => {
onProjectClose(file || null, project?.path || null, false)
// Clear the scene.
engineCommandManager.clearScene()
kclManager.switchedFiles = true
}}
to={PATHS.HOME}
className={wrapperClassName + ' hover:before:brightness-110'}
@ -190,8 +189,7 @@ function ProjectMenuPopover({
className: !isDesktop() ? 'hidden' : '',
onClick: () => {
onProjectClose(file || null, project?.path || null, true)
// Clear the scene.
engineCommandManager.clearScene()
kclManager.switchedFiles = true
},
},
].filter(

View File

@ -12,6 +12,7 @@ import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from 'lib/constants'
import {
CallExpression,
clearSceneAndBustCache,
emptyExecState,
ExecState,
initPromise,
@ -60,6 +61,7 @@ export class KclManager {
private _executeIsStale: ExecuteArgs | null = null
private _wasmInitFailed = true
private _hasErrors = false
private _switchedFiles = false
engineCommandManager: EngineCommandManager
@ -79,6 +81,10 @@ export class KclManager {
this._astCallBack(ast)
}
set switchedFiles(switchedFiles: boolean) {
this._switchedFiles = switchedFiles
}
get programMemory() {
return this._programMemory
}
@ -166,8 +172,12 @@ export class KclManager {
this.engineCommandManager = engineCommandManager
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.ensureWasmInit().then(() => {
this.ast = this.safeParse(codeManager.code) || this.ast
this.ensureWasmInit().then(async () => {
await this.safeParse(codeManager.code).then((ast) => {
if (ast) {
this.ast = ast
}
})
})
}
@ -211,7 +221,25 @@ export class KclManager {
}
}
safeParse(code: string): Node<Program> | null {
// (jess) I'm not in love with this, but it ensures we clear the scene and
// bust the cache on
// errors from parsing when opening new files.
// Why not just clear the cache on all parse errors, you ask? well its actually
// really nice to keep the cache on parse errors within the same file, and
// only bust on engine errors esp if they take a long time to execute and
// you hit the wrong key!
private async checkIfSwitchedFilesShouldClear() {
// If we were switching files and we hit an error on parse we need to bust
// the cache and clear the scene.
if (this._hasErrors && this._switchedFiles) {
await clearSceneAndBustCache(this.engineCommandManager)
} else if (this._switchedFiles) {
// Reset the switched files boolean.
this._switchedFiles = false
}
}
async safeParse(code: string): Promise<Node<Program> | null> {
const result = parse(code)
this.diagnostics = []
this._hasErrors = false
@ -220,6 +248,8 @@ export class KclManager {
const kclerror: KCLError = result as KCLError
this.diagnostics = kclErrorsToDiagnostics([kclerror])
this._hasErrors = true
await this.checkIfSwitchedFilesShouldClear()
return null
}
@ -228,6 +258,7 @@ export class KclManager {
if (result.errors.length > 0) {
this._hasErrors = true
await this.checkIfSwitchedFilesShouldClear()
return null
}
@ -353,7 +384,7 @@ export class KclManager {
console.error(newCode)
return
}
const newAst = this.safeParse(newCode)
const newAst = await this.safeParse(newCode)
if (!newAst) {
this.clearAst()
return
@ -408,7 +439,7 @@ export class KclManager {
})
}
async executeCode(zoomToFit?: boolean): Promise<void> {
const ast = this.safeParse(codeManager.code)
const ast = await this.safeParse(codeManager.code)
if (!ast) {
this.clearAst()
return
@ -416,9 +447,9 @@ export class KclManager {
this.ast = { ...ast }
return this.executeAst({ zoomToFit })
}
format() {
async format() {
const originalCode = codeManager.code
const ast = this.safeParse(originalCode)
const ast = await this.safeParse(originalCode)
if (!ast) {
this.clearAst()
return
@ -458,7 +489,7 @@ export class KclManager {
const newCode = recast(ast)
if (err(newCode)) return Promise.reject(newCode)
const astWithUpdatedSource = this.safeParse(newCode)
const astWithUpdatedSource = await this.safeParse(newCode)
if (!astWithUpdatedSource) return Promise.reject(new Error('bad ast'))
let returnVal: Selections | undefined = undefined

View File

@ -22,7 +22,7 @@ import {
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
import { createLiteral } from 'lang/modifyAst'
import { err } from 'lib/trap'
import { Selections } from 'lib/selections'
import { Selection, Selections } from 'lib/selections'
import { engineCommandManager, kclManager } from 'lib/singletons'
import { VITE_KC_DEV_TOKEN } from 'env'
import { isOverlap } from 'lib/utils'
@ -118,13 +118,8 @@ const runGetPathToExtrudeForSegmentSelectionTest = async (
code.indexOf(selectedSegmentSnippet) + selectedSegmentSnippet.length,
true,
]
const selection: Selections = {
graphSelections: [
{
codeRef: codeRefFromRange(segmentRange, ast),
},
],
otherSelections: [],
const selection: Selection = {
codeRef: codeRefFromRange(segmentRange, ast),
}
// executeAst and artifactGraph

View File

@ -29,7 +29,7 @@ import {
sketchLineHelperMap,
} from '../std/sketch'
import { err, trap } from 'lib/trap'
import { Selections } from 'lib/selections'
import { Selection, Selections } from 'lib/selections'
import { KclCommandValue } from 'lib/commandTypes'
import {
Artifact,
@ -99,14 +99,9 @@ export function modifyAstWithEdgeTreatmentAndTag(
const lookupMap: Map<string, PathToNode> = new Map() // work around for Map key comparison
for (const selection of selections.graphSelections) {
const singleSelection = {
graphSelections: [selection],
otherSelections: [],
}
const result = getPathToExtrudeForSegmentSelection(
clonedAstForGetExtrude,
singleSelection,
selection,
artifactGraph
)
if (err(result)) return result
@ -259,12 +254,12 @@ function insertParametersIntoAst(
export function getPathToExtrudeForSegmentSelection(
ast: Program,
selection: Selections,
selection: Selection,
artifactGraph: ArtifactGraph
): { pathToSegmentNode: PathToNode; pathToExtrudeNode: PathToNode } | Error {
const pathToSegmentNode = getNodePathFromSourceRange(
ast,
selection.graphSelections[0]?.codeRef?.range
selection.codeRef?.range
)
const varDecNode = getNodeFromPath<VariableDeclaration>(
@ -308,7 +303,7 @@ async function updateAstAndFocus(
}
}
function mutateAstWithTagForSketchSegment(
export function mutateAstWithTagForSketchSegment(
astClone: Node<Program>,
pathToSegmentNode: PathToNode
): { modifiedAst: Program; tag: string } | Error {

View File

@ -0,0 +1,124 @@
import { ArtifactGraph } from 'lang/std/artifactGraph'
import { Selections } from 'lib/selections'
import { Expr } from 'wasm-lib/kcl/bindings/Expr'
import { Program } from 'wasm-lib/kcl/bindings/Program'
import { Node } from 'wasm-lib/kcl/bindings/Node'
import { PathToNode, VariableDeclarator } from 'lang/wasm'
import {
getPathToExtrudeForSegmentSelection,
mutateAstWithTagForSketchSegment,
} from './addEdgeTreatment'
import { getNodeFromPath } from 'lang/queryAst'
import { err } from 'lib/trap'
import {
createLiteral,
createIdentifier,
findUniqueName,
createCallExpressionStdLib,
createObjectExpression,
createArrayExpression,
createVariableDeclaration,
} from 'lang/modifyAst'
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
export function addShell({
node,
selection,
artifactGraph,
thickness,
}: {
node: Node<Program>
selection: Selections
artifactGraph: ArtifactGraph
thickness: Expr
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
const modifiedAst = structuredClone(node)
// Look up the corresponding extrude
const clonedAstForGetExtrude = structuredClone(modifiedAst)
const expressions: Expr[] = []
let pathToExtrudeNode: PathToNode | undefined = undefined
for (const graphSelection of selection.graphSelections) {
const extrudeLookupResult = getPathToExtrudeForSegmentSelection(
clonedAstForGetExtrude,
graphSelection,
artifactGraph
)
if (err(extrudeLookupResult)) {
return new Error("Couldn't find extrude")
}
pathToExtrudeNode = extrudeLookupResult.pathToExtrudeNode
// Get the sketch ref from the selection
// TODO: this assumes the segment is piped directly from the sketch, with no intermediate `VariableDeclarator` between.
// We must find a technique for these situations that is robust to intermediate declarations
const sketchNode = getNodeFromPath<VariableDeclarator>(
modifiedAst,
graphSelection.codeRef.pathToNode,
'VariableDeclarator'
)
if (err(sketchNode)) {
return sketchNode
}
const selectedArtifact = graphSelection.artifact
if (!selectedArtifact) {
return new Error('Bad artifact')
}
// Check on the selection, and handle the wall vs cap casees
let expr: Expr
if (selectedArtifact.type === 'cap') {
expr = createLiteral(selectedArtifact.subType)
} else if (selectedArtifact.type === 'wall') {
const tagResult = mutateAstWithTagForSketchSegment(
modifiedAst,
extrudeLookupResult.pathToSegmentNode
)
if (err(tagResult)) return tagResult
const { tag } = tagResult
expr = createIdentifier(tag)
} else {
continue
}
expressions.push(expr)
}
if (!pathToExtrudeNode) return new Error('No extrude found')
const extrudeNode = getNodeFromPath<VariableDeclarator>(
modifiedAst,
pathToExtrudeNode,
'VariableDeclarator'
)
if (err(extrudeNode)) {
return extrudeNode
}
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SHELL)
const shell = createCallExpressionStdLib('shell', [
createObjectExpression({
faces: createArrayExpression(expressions),
thickness,
}),
createIdentifier(extrudeNode.node.id.name),
])
const declaration = createVariableDeclaration(name, shell)
// TODO: check if we should append at the end like here or right after the extrude
modifiedAst.body.push(declaration)
const pathToNode: PathToNode = [
['body', ''],
[modifiedAst.body.length - 1, 'index'],
['declarations', 'VariableDeclaration'],
['0', 'index'],
['init', 'VariableDeclarator'],
['arguments', 'CallExpression'],
[0, 'index'],
]
return {
modifiedAst,
pathToNode,
}
}

View File

@ -17,6 +17,7 @@ import {
doesSceneHaveSweepableSketch,
traverse,
getNodeFromPath,
doesSceneHaveExtrudedSketch,
} from './queryAst'
import { enginelessExecutor } from '../lib/testHelpers'
import {
@ -654,6 +655,38 @@ extrude001 = extrude(10, sketch001)
})
})
describe('Testing doesSceneHaveExtrudedSketch', () => {
it('finds extruded sketch as variable', async () => {
const exampleCode = `sketch001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 1 }, %)
extrude001 = extrude(1, sketch001)
`
const ast = parse(exampleCode)
if (err(ast)) throw ast
const extrudable = doesSceneHaveExtrudedSketch(ast)
expect(extrudable).toBeTruthy()
})
it('finds extruded sketch in pipe', async () => {
const exampleCode = `extrude001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 1 }, %)
|> extrude(1, %)
`
const ast = parse(exampleCode)
if (err(ast)) throw ast
const extrudable = doesSceneHaveExtrudedSketch(ast)
expect(extrudable).toBeTruthy()
})
it('finds no extrusion with sketch only', async () => {
const exampleCode = `extrude001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 1 }, %)
`
const ast = parse(exampleCode)
if (err(ast)) throw ast
const extrudable = doesSceneHaveExtrudedSketch(ast)
expect(extrudable).toBeFalsy()
})
})
describe('Testing traverse and pathToNode', () => {
it.each([
['basic', '2.73'],

View File

@ -1064,6 +1064,35 @@ export function doesSceneHaveSweepableSketch(ast: Node<Program>, count = 1) {
return Object.keys(theMap).length >= count
}
export function doesSceneHaveExtrudedSketch(ast: Node<Program>) {
const theMap: any = {}
traverse(ast as any, {
enter(node) {
if (
node.type === 'VariableDeclarator' &&
node.init?.type === 'PipeExpression'
) {
for (const pipe of node.init.body) {
if (
pipe.type === 'CallExpression' &&
pipe.callee.name === 'extrude'
) {
theMap[node.id.name] = true
break
}
}
} else if (
node.type === 'CallExpression' &&
node.callee.name === 'extrude' &&
node.arguments[1]?.type === 'Identifier'
) {
theMap[node.moduleId] = true
}
},
})
return Object.keys(theMap).length > 0
}
export function getObjExprProperty(
node: ObjectExpression,
propName: string

View File

@ -1879,17 +1879,6 @@ export class EngineCommandManager extends EventTarget {
}
return JSON.stringify(this.defaultPlanes)
}
clearScene(): void {
const deleteCmd: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'scene_clear_all',
},
}
this.clearDefaultPlanes()
this.engineConnection?.send(deleteCmd)
}
addCommandLog(message: CommandLog) {
if (this.commandLogs.length > 500) {
this.commandLogs.shift()

View File

@ -16,6 +16,7 @@ import init, {
parse_project_settings,
default_project_settings,
base64_decode,
clear_scene_and_bust_cache,
} from '../wasm-lib/pkg/wasm_lib'
import { KCLError } from './errors'
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
@ -698,6 +699,21 @@ export function defaultAppSettings(): DeepPartial<Configuration> | Error {
return default_app_settings()
}
export async function clearSceneAndBustCache(
engineCommandManager: EngineCommandManager
): Promise<null | Error> {
try {
await clear_scene_and_bust_cache(engineCommandManager)
} catch (e: any) {
console.error('clear_scene_and_bust_cache: error', e)
return Promise.reject(
new Error(`Error on clear_scene_and_bust_cache: ${e}`)
)
}
return null
}
export function parseAppSettings(
toml: string
): DeepPartial<Configuration> | Error {

View File

@ -34,6 +34,10 @@ export type ModelingCommandSchema = {
Loft: {
selection: Selections
}
Shell: {
selection: Selections
thickness: KclCommandValue
}
Revolve: {
selection: Selections
angle: KclCommandValue
@ -277,6 +281,25 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
},
},
},
Shell: {
description: 'Hollow out a 3D solid.',
icon: 'shell',
needsReview: true,
args: {
selection: {
inputType: 'selection',
selectionTypes: ['cap', 'wall'],
multiple: true,
required: true,
skip: false,
},
thickness: {
inputType: 'kcl',
defaultValue: KCL_DEFAULT_LENGTH,
required: true,
},
},
},
// TODO: Update this configuration, copied from extrude for MVP of revolve, specifically the args.selection
Revolve: {
description: 'Create a 3D body by rotating a sketch region about an axis.',

View File

@ -53,6 +53,7 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
SKETCH: 'sketch',
EXTRUDE: 'extrude',
LOFT: 'loft',
SHELL: 'shell',
SEGMENT: 'seg',
REVOLVE: 'revolve',
PLANE: 'plane',

View File

@ -13,7 +13,6 @@ import {
listProjects,
readAppSettingsFile,
} from './desktop'
import { engineCommandManager } from './singletons'
export const isHidden = (fileOrDir: FileEntry) =>
!!fileOrDir.name?.startsWith('.')
@ -116,9 +115,6 @@ export async function createAndOpenNewTutorialProject({
) => void
navigate: (path: string) => void
}) {
// Clear the scene.
engineCommandManager.clearScene()
// Create a new project with the onboarding project name
const configuration = await readAppSettingsFile()
const projects = await listProjects(configuration)

View File

@ -3,27 +3,27 @@ export const bracket = `// Shelf Bracket
// Define constants
const sigmaAllow = 35000 // psi (6061-T6 aluminum)
const width = 6 // inch
const p = 300 // Force on shelf - lbs
const factorOfSafety = 1.2 // FOS of 1.2
const shelfMountL = 5 // inches
const wallMountL = 2 // inches
const shelfDepth = 12 // Shelf is 12 inches in depth from the wall
const moment = shelfDepth * p // assume the force is applied at the end of the shelf to be conservative (lb-in)
sigmaAllow = 35000 // psi (6061-T6 aluminum)
width = 6 // inch
p = 300 // Force on shelf - lbs
factorOfSafety = 1.2 // FOS of 1.2
shelfMountL = 5 // inches
wallMountL = 2 // inches
shelfDepth = 12 // Shelf is 12 inches in depth from the wall
moment = shelfDepth * p // assume the force is applied at the end of the shelf to be conservative (lb-in)
const filletRadius = .375 // inches
const extFilletRadius = .25 // inches
const mountingHoleDiameter = 0.5 // inches
filletRadius = .375 // inches
extFilletRadius = .25 // inches
mountingHoleDiameter = 0.5 // inches
// Calculate required thickness of bracket
const thickness = sqrt(moment * factorOfSafety * 6 / (sigmaAllow * width)) // this is the calculation of two brackets holding up the shelf (inches)
thickness = sqrt(moment * factorOfSafety * 6 / (sigmaAllow * width)) // this is the calculation of two brackets holding up the shelf (inches)
// Sketch the bracket body and fillet the inner and outer edges of the bend
const bracketLeg1Sketch = startSketchOn('XY')
bracketLeg1Sketch = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([shelfMountL - filletRadius, 0], %, $fillet1)
|> line([0, width], %, $fillet2)
@ -47,7 +47,7 @@ const bracketLeg1Sketch = startSketchOn('XY')
}, %), %)
// Extrude the leg 2 bracket sketch
const bracketLeg1Extrude = extrude(thickness, bracketLeg1Sketch)
bracketLeg1Extrude = extrude(thickness, bracketLeg1Sketch)
|> fillet({
radius = extFilletRadius,
tags = [
@ -57,7 +57,7 @@ const bracketLeg1Extrude = extrude(thickness, bracketLeg1Sketch)
}, %)
// Sketch the fillet arc
const filletSketch = startSketchOn('XZ')
filletSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([0, thickness], %)
|> arc({
@ -73,10 +73,10 @@ const filletSketch = startSketchOn('XZ')
}, %)
// Sketch the bend
const filletExtrude = extrude(-width, filletSketch)
filletExtrude = extrude(-width, filletSketch)
// Create a custom plane for the leg that sits on the wall
const customPlane = {
customPlane = {
plane = {
origin = { x = -filletRadius, y = 0, z = 0 },
xAxis = { x = 0, y = 1, z = 0 },
@ -86,7 +86,7 @@ const customPlane = {
}
// Create a sketch for the second leg
const bracketLeg2Sketch = startSketchOn(customPlane)
bracketLeg2Sketch = startSketchOn(customPlane)
|> startProfileAt([0, -filletRadius], %)
|> line([width, 0], %)
|> line([0, -wallMountL], %, $fillet3)
@ -102,7 +102,7 @@ const bracketLeg2Sketch = startSketchOn(customPlane)
}, %), %)
// Extrude the second leg
const bracketLeg2Extrude = extrude(-thickness, bracketLeg2Sketch)
bracketLeg2Extrude = extrude(-thickness, bracketLeg2Sketch)
|> fillet({
radius = extFilletRadius,
tags = [
@ -135,8 +135,8 @@ function findLineInExampleCode({
}
export const bracketWidthConstantLine = findLineInExampleCode({
searchText: 'const width',
searchText: 'width =',
})
export const bracketThicknessCalculationLine = findLineInExampleCode({
searchText: 'const thickness',
searchText: 'thickness =',
})

View File

@ -5,7 +5,7 @@ import { isDesktop } from './isDesktop'
import { FILE_EXT, PROJECT_SETTINGS_FILE_NAME } from './constants'
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
import { parseProjectSettings } from 'lang/wasm'
import { err } from './trap'
import { err, reportRejection } from './trap'
import { projectConfigurationToSettingsPayload } from './settings/settingsUtils'
interface OnSubmitProps {
@ -28,7 +28,7 @@ export function kclCommands(
groupId: 'code',
icon: 'code',
onSubmit: () => {
kclManager.format()
kclManager.format().catch(reportRejection)
},
},
{

View File

@ -585,6 +585,17 @@ export function canLoftSelection(selection: Selections) {
)
}
export function canShellSelection(selection: Selections) {
const commonNodes = selection.graphSelections.map((_, i) =>
buildCommonNodeFromSelection(selection, i)
)
return commonNodes.every(
(n) =>
n.selection.artifact?.type === 'cap' ||
n.selection.artifact?.type === 'wall'
)
}
// This accounts for non-geometry selections under "other"
export type ResolvedSelectionType = Artifact['type'] | 'other'
export type SelectionCountsByType = Map<ResolvedSelectionType, number>

View File

@ -190,9 +190,15 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
},
{
id: 'shell',
onClick: () => console.error('Shell not yet implemented'),
onClick: ({ commandBarSend }) => {
commandBarSend({
type: 'Find and select command',
data: { name: 'Shell', groupId: 'modeling' },
})
},
disabled: (state) => !state.can({ type: 'Shell' }),
icon: 'shell',
status: 'kcl-only',
status: 'available',
title: 'Shell',
description: 'Hollow out a 3D solid.',
links: [{ label: 'KCL docs', url: 'https://zoo.dev/docs/kcl/shell' }],

View File

@ -80,6 +80,7 @@ import { ToolbarModeName } from 'lib/toolbar'
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
import { Vector3 } from 'three'
import { MachineManager } from 'components/MachineManagerProvider'
import { addShell } from 'lang/modifyAst/addShell'
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
@ -260,6 +261,7 @@ export type ModelingMachineEvent =
| { type: 'Make'; data: ModelingCommandSchema['Make'] }
| { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] }
| { type: 'Loft'; data?: ModelingCommandSchema['Loft'] }
| { type: 'Shell'; data?: ModelingCommandSchema['Shell'] }
| { type: 'Revolve'; data?: ModelingCommandSchema['Revolve'] }
| { type: 'Fillet'; data?: ModelingCommandSchema['Fillet'] }
| { type: 'Offset plane'; data: ModelingCommandSchema['Offset plane'] }
@ -392,6 +394,7 @@ export const modelingMachine = setup({
'Selection is on face': () => false,
'has valid sweep selection': () => false,
'has valid loft selection': () => false,
'has valid shell selection': () => false,
'has valid edge treatment selection': () => false,
'Has exportable geometry': () => false,
'has valid selection for deletion': () => false,
@ -1584,6 +1587,66 @@ export const modelingMachine = setup({
updateAstResult.newAst
)
if (updateAstResult?.selections) {
editorManager.selectRange(updateAstResult?.selections)
}
}
),
shellAstMod: fromPromise(
async ({
input,
}: {
input: ModelingCommandSchema['Shell'] | undefined
}) => {
if (!input) {
return new Error('No input provided')
}
// Extract inputs
const ast = kclManager.ast
const { selection, thickness } = input
// Insert the thickness variable if it exists
if (
'variableName' in thickness &&
thickness.variableName &&
thickness.insertIndex !== undefined
) {
const newBody = [...ast.body]
newBody.splice(
thickness.insertIndex,
0,
thickness.variableDeclarationAst
)
ast.body = newBody
}
// Perform the shell op
const shellResult = addShell({
node: ast,
selection,
artifactGraph: engineCommandManager.artifactGraph,
thickness:
'variableName' in thickness
? thickness.variableIdentifierAst
: thickness.valueAst,
})
if (err(shellResult)) {
return err(shellResult)
}
const updateAstResult = await kclManager.updateAst(
shellResult.modifiedAst,
true,
{
focusPath: [shellResult.pathToNode],
}
)
await codeManager.updateEditorWithAstAndWriteToFile(
updateAstResult.newAst
)
if (updateAstResult?.selections) {
editorManager.selectRange(updateAstResult?.selections)
}
@ -1627,6 +1690,13 @@ export const modelingMachine = setup({
Loft: {
target: 'Applying loft',
guard: 'has valid loft selection',
reenter: true,
},
Shell: {
target: 'Applying shell',
guard: 'has valid shell selection',
reenter: true,
},
@ -2391,6 +2461,19 @@ export const modelingMachine = setup({
onError: ['idle'],
},
},
'Applying shell': {
invoke: {
src: 'shellAstMod',
id: 'shellAstMod',
input: ({ event }) => {
if (event.type !== 'Shell') return undefined
return event.data
},
onDone: ['idle'],
onError: ['idle'],
},
},
},
initial: 'idle',

View File

@ -15,7 +15,6 @@ redo-kcl-stdlib-docs:
TWENTY_TWENTY=overwrite {{cnr}} -p kcl-lib kcl_test_example
EXPECTORATE=overwrite {{cnr}} -p kcl-lib docs::gen_std_tests::test_generate_stdlib
# Copy a test KCL file from executor tests into a new simulation test.
copy-exec-test-into-sim-test test_name:
mkdir -p kcl/tests/{{test_name}}

View File

@ -13,7 +13,10 @@ use crate::{
ObjectExpression, PipeExpression, TagDeclarator, UnaryExpression, UnaryOperator,
},
source_range::SourceRange,
std::{args::Arg, FunctionKind},
std::{
args::{Arg, KwArgs},
FunctionKind,
},
};
const FLOAT_TO_INT_MAX_DELTA: f64 = 0.01;
@ -388,7 +391,14 @@ impl Node<CallExpressionKw> {
None
};
let args = crate::std::Args::new_kw(fn_args, unlabeled, self.into(), ctx.clone());
let args = crate::std::Args::new_kw(
KwArgs {
unlabeled,
labeled: fn_args,
},
self.into(),
ctx.clone(),
);
match ctx.stdlib.get_either(fn_name) {
FunctionKind::Core(func) => {
// Attempt to call the function.

View File

@ -645,7 +645,7 @@ async fn test_kcl_lsp_completions() {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"const thing= 1
text: r#"thing= 1
st"#
.to_string(),
},
@ -688,7 +688,7 @@ async fn test_kcl_lsp_completions_empty_in_comment() {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"const thing= 1 // st"#.to_string(),
text: r#"thing= 1 // st"#.to_string(),
},
})
.await;
@ -700,7 +700,7 @@ async fn test_kcl_lsp_completions_empty_in_comment() {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
},
position: tower_lsp::lsp_types::Position { line: 0, character: 19 },
position: tower_lsp::lsp_types::Position { line: 0, character: 13 },
},
context: None,
partial_result_params: Default::default(),
@ -723,7 +723,7 @@ async fn test_kcl_lsp_completions_tags() {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"const part001 = startSketchOn('XY')
text: r#"part001 = startSketchOn('XY')
|> startProfileAt([11.19, 28.35], %)
|> line([28.67, -13.25], %, $here)
|> line([-4.12, -22.81], %)
@ -1058,7 +1058,7 @@ async fn test_kcl_lsp_semantic_tokens_with_modifiers() {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"const part001 = startSketchOn('XY')
text: r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
@ -1066,8 +1066,8 @@ async fn test_kcl_lsp_semantic_tokens_with_modifiers() {
|> close(%)
|> extrude(3.14, %)
const thing = {blah: "foo"}
const bar = thing.blah
thing = {blah: "foo"}
bar = thing.blah
fn myFn = (param1) => {
return param1
@ -1230,7 +1230,7 @@ async fn test_kcl_lsp_semantic_tokens_multiple_comments() {
// A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads.
// Define constants like ball diameter, inside diameter, overhange length, and thickness
const sphereDia = 0.5"#
sphereDia = 0.5"#
.to_string(),
},
})
@ -1251,7 +1251,7 @@ const sphereDia = 0.5"#
// Check the semantic tokens.
if let tower_lsp::lsp_types::SemanticTokensResult::Tokens(semantic_tokens) = semantic_tokens {
assert_eq!(semantic_tokens.data.len(), 7);
assert_eq!(semantic_tokens.data.len(), 6);
assert_eq!(semantic_tokens.data[0].length, 15);
assert_eq!(semantic_tokens.data[0].delta_start, 0);
assert_eq!(semantic_tokens.data[0].delta_line, 0);
@ -1279,36 +1279,27 @@ const sphereDia = 0.5"#
.get_semantic_token_type_index(&SemanticTokenType::COMMENT)
.unwrap()
);
assert_eq!(semantic_tokens.data[3].length, 5);
assert_eq!(semantic_tokens.data[3].length, 9);
assert_eq!(semantic_tokens.data[3].delta_start, 0);
assert_eq!(semantic_tokens.data[3].delta_line, 1);
assert_eq!(
semantic_tokens.data[3].token_type,
server
.get_semantic_token_type_index(&SemanticTokenType::KEYWORD)
.unwrap()
);
assert_eq!(semantic_tokens.data[4].length, 9);
assert_eq!(semantic_tokens.data[4].delta_start, 6);
assert_eq!(semantic_tokens.data[4].delta_line, 0);
assert_eq!(
semantic_tokens.data[4].token_type,
server
.get_semantic_token_type_index(&SemanticTokenType::VARIABLE)
.unwrap()
);
assert_eq!(semantic_tokens.data[5].length, 1);
assert_eq!(semantic_tokens.data[5].delta_start, 10);
assert_eq!(semantic_tokens.data[4].length, 1);
assert_eq!(semantic_tokens.data[4].delta_start, 10);
assert_eq!(
semantic_tokens.data[5].token_type,
semantic_tokens.data[4].token_type,
server
.get_semantic_token_type_index(&SemanticTokenType::OPERATOR)
.unwrap()
);
assert_eq!(semantic_tokens.data[6].length, 3);
assert_eq!(semantic_tokens.data[6].delta_start, 2);
assert_eq!(semantic_tokens.data[5].length, 3);
assert_eq!(semantic_tokens.data[5].delta_start, 2);
assert_eq!(
semantic_tokens.data[6].token_type,
semantic_tokens.data[5].token_type,
server
.get_semantic_token_type_index(&SemanticTokenType::NUMBER)
.unwrap()
@ -1329,7 +1320,7 @@ async fn test_kcl_lsp_document_symbol() {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"const myVar = 1
text: r#"myVar = 1
startSketchOn('XY')"#
.to_string(),
},
@ -1369,7 +1360,7 @@ async fn test_kcl_lsp_document_symbol_tag() {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"const part001 = startSketchOn('XY')
text: r#"part001 = startSketchOn('XY')
|> startProfileAt([11.19, 28.35], %)
|> line([28.67, -13.25], %, $here)
|> line([-4.12, -22.81], %)
@ -1466,13 +1457,13 @@ async fn test_kcl_lsp_formatting_extra_parens() {
// A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads.
// Define constants like ball diameter, inside diameter, overhange length, and thickness
const sphereDia = 0.5
const insideDia = 1
const thickness = 0.25
const overHangLength = .4
sphereDia = 0.5
insideDia = 1
thickness = 0.25
overHangLength = .4
// Sketch and revolve the inside bearing piece
const insideRevolve = startSketchOn('XZ')
insideRevolve = startSketchOn('XZ')
|> startProfileAt([insideDia / 2, 0], %)
|> line([0, thickness + sphereDia / 2], %)
|> line([overHangLength, 0], %)
@ -1486,7 +1477,7 @@ const insideRevolve = startSketchOn('XZ')
|> revolve({ axis: 'y' }, %)
// Sketch and revolve one of the balls and duplicate it using a circular pattern. (This is currently a workaround, we have a bug with rotating on a sketch that touches the rotation axis)
const sphere = startSketchOn('XZ')
sphere = startSketchOn('XZ')
|> startProfileAt([
0.05 + insideDia / 2 + thickness,
0 - 0.05
@ -1508,7 +1499,7 @@ const sphere = startSketchOn('XZ')
}, %)
// Sketch and revolve the outside bearing
const outsideRevolve = startSketchOn('XZ')
outsideRevolve = startSketchOn('XZ')
|> startProfileAt([
insideDia / 2 + thickness + sphereDia,
0
@ -1638,7 +1629,7 @@ async fn test_kcl_lsp_rename() {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"const thing= 1"#.to_string(),
text: r#"thing= 1"#.to_string(),
},
})
.await;
@ -1650,7 +1641,7 @@ async fn test_kcl_lsp_rename() {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
},
position: tower_lsp::lsp_types::Position { line: 0, character: 8 },
position: tower_lsp::lsp_types::Position { line: 0, character: 2 },
},
new_name: "newName".to_string(),
work_done_progress_params: Default::default(),
@ -1667,7 +1658,7 @@ async fn test_kcl_lsp_rename() {
vec![tower_lsp::lsp_types::TextEdit {
range: tower_lsp::lsp_types::Range {
start: tower_lsp::lsp_types::Position { line: 0, character: 0 },
end: tower_lsp::lsp_types::Position { line: 0, character: 13 }
end: tower_lsp::lsp_types::Position { line: 0, character: 7 }
},
new_text: "newName = 1\n".to_string()
}]
@ -1773,7 +1764,7 @@ async fn test_kcl_lsp_diagnostic_has_lints() {
uri: "file:///testlint.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"let THING = 10"#.to_string(),
text: r#"THING = 10"#.to_string(),
},
})
.await;
@ -1859,7 +1850,7 @@ async fn test_copilot_lsp_completions_raw() {
let completions = server
.get_completions(
"kcl".to_string(),
r#"const bracket = startSketchOn('XY')
r#"bracket = startSketchOn('XY')
|> startProfileAt([0, 0], %)
"#
.to_string(),
@ -1878,7 +1869,7 @@ async fn test_copilot_lsp_completions_raw() {
let completions_hit_cache = server
.get_completions(
"kcl".to_string(),
r#"const bracket = startSketchOn('XY')
r#"bracket = startSketchOn('XY')
|> startProfileAt([0, 0], %)
"#
.to_string(),
@ -1918,7 +1909,7 @@ async fn test_copilot_lsp_completions() {
path: "file:///test.copilot".to_string(),
position: crate::lsp::copilot::types::CopilotPosition { line: 3, character: 3 },
relative_path: "test.copilot".to_string(),
source: r#"const bracket = startSketchOn('XY')
source: r#"bracket = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> close(%)
@ -2066,7 +2057,7 @@ async fn test_lsp_initialized() {
async fn test_kcl_lsp_on_change_update_ast() {
let server = kcl_lsp_server(false).await.unwrap();
let same_text = r#"const thing = 1"#.to_string();
let same_text = r#"thing = 1"#.to_string();
// Send open file.
server
@ -2102,7 +2093,7 @@ async fn test_kcl_lsp_on_change_update_ast() {
assert_eq!(ast, server.ast_map.get("file:///test.kcl").unwrap().clone());
// Update the text.
let new_text = r#"const thing = 2"#.to_string();
let new_text = r#"thing = 2"#.to_string();
// Send change file.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
@ -2128,7 +2119,7 @@ async fn test_kcl_lsp_on_change_update_ast() {
async fn kcl_test_kcl_lsp_on_change_update_memory() {
let server = kcl_lsp_server(true).await.unwrap();
let same_text = r#"const thing = 1"#.to_string();
let same_text = r#"thing = 1"#.to_string();
// Send open file.
server
@ -2164,7 +2155,7 @@ async fn kcl_test_kcl_lsp_on_change_update_memory() {
assert_eq!(memory, server.memory_map.get("file:///test.kcl").unwrap().clone());
// Update the text.
let new_text = r#"const thing = 2"#.to_string();
let new_text = r#"thing = 2"#.to_string();
// Send change file.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
@ -2188,7 +2179,7 @@ async fn kcl_test_kcl_lsp_update_units() {
let server = kcl_lsp_server(true).await.unwrap();
let same_text = r#"fn cube = (pos, scale) => {
const sg = startSketchOn('XY')
sg = startSketchOn('XY')
|> startProfileAt(pos, %)
|> line([0, scale], %)
|> line([scale, 0], %)
@ -2196,7 +2187,7 @@ async fn kcl_test_kcl_lsp_update_units() {
return sg
}
const part001 = cube([0,0], 20)
part001 = cube([0,0], 20)
|> close(%)
|> extrude(20, %)"#
.to_string();
@ -2215,7 +2206,7 @@ const part001 = cube([0,0], 20)
// Get the tokens.
let tokens = server.token_map.get("file:///test.kcl").unwrap().clone();
assert_eq!(tokens.len(), 124);
assert_eq!(tokens.len(), 120);
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
@ -2305,7 +2296,7 @@ async fn test_kcl_lsp_diagnostics_on_parse_error() {
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
// Update the text.
let new_text = r#"const thing = 2"#.to_string();
let new_text = r#"thing = 2"#.to_string();
// Send change file.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
@ -2336,7 +2327,7 @@ async fn kcl_test_kcl_lsp_diagnostics_on_execution_error() {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"const part001 = startSketchOn('XY')
text: r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -2356,7 +2347,7 @@ async fn kcl_test_kcl_lsp_diagnostics_on_execution_error() {
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
// Update the text.
let new_text = r#"const part001 = startSketchOn('XY')
let new_text = r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -2394,7 +2385,7 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"const part001 = startSketchOn('XY')
text: r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -2443,7 +2434,7 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const part001 = startSketchOn('XY')
let code = r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -2536,7 +2527,7 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const part001 = startSketchOn('XY')
let code = r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -2624,7 +2615,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexecute_on_unit_change() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const part001 = startSketchOn('XY')
let code = r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -2715,7 +2706,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_on_unit_change() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const part001 = startSketchOn('XY')
let code = r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -2785,7 +2776,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_
async fn kcl_test_kcl_lsp_cant_execute_set() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const part001 = startSketchOn('XY')
let code = r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -2982,7 +2973,7 @@ async fn test_kcl_lsp_folding() {
async fn kcl_test_kcl_lsp_code_with_parse_error_and_ast_unchanged_but_has_diagnostics_reparse() {
let server = kcl_lsp_server(false).await.unwrap();
let code = r#"const part001 = startSketchOn('XY')
let code = r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -3036,8 +3027,8 @@ async fn kcl_test_kcl_lsp_code_with_parse_error_and_ast_unchanged_but_has_diagno
async fn kcl_test_kcl_lsp_code_with_lint_and_ast_unchanged_but_has_diagnostics_reparse() {
let server = kcl_lsp_server(false).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
let code = r#"LINT = 1
part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -3090,8 +3081,8 @@ const part001 = startSketchOn('XY')
async fn kcl_test_kcl_lsp_code_with_lint_and_parse_error_and_ast_unchanged_but_has_diagnostics_reparse() {
let server = kcl_lsp_server(false).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
let code = r#"LINT = 1
part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -3145,8 +3136,8 @@ const part001 = startSketchOn('XY')
async fn kcl_test_kcl_lsp_code_lint_and_ast_unchanged_but_has_diagnostics_reexecute() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
let code = r#"LINT = 1
part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
@ -3210,8 +3201,8 @@ const part001 = startSketchOn('XY')
async fn kcl_test_kcl_lsp_code_lint_reexecute_new_lint() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
let code = r#"LINT = 1
part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
@ -3253,14 +3244,14 @@ const part001 = startSketchOn('XY')
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: r#"const part001 = startSketchOn('XY')
text: r#"part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
|> line([-20, 0], %, $seg01)
|> close(%)
|> extrude(3.14, %)
const NEW_LINT = 1"#
NEW_LINT = 1"#
.to_string(),
}],
})
@ -3283,8 +3274,8 @@ const NEW_LINT = 1"#
async fn kcl_test_kcl_lsp_code_lint_reexecute_new_ast_error() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
let code = r#"LINT = 1
part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
@ -3326,14 +3317,14 @@ const part001 = startSketchOn('XY')
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: r#"const part001 = startSketchOn('XY')
text: r#"part001 = startSketchOn('XY')
|> ^^^^startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
|> line([-20, 0], %, $seg01)
|> close(%)
|> extrude(3.14, %)
const NEW_LINT = 1"#
NEW_LINT = 1"#
.to_string(),
}],
})
@ -3356,8 +3347,8 @@ const NEW_LINT = 1"#
async fn kcl_test_kcl_lsp_code_lint_reexecute_had_lint_new_parse_error() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
let code = r#"LINT = 1
part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -3408,14 +3399,14 @@ const part001 = startSketchOn('XY')
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: r#"const part001 = startSketchOn('XY')
text: r#"part001 = startSketchOn('XY')
|> ^^^^startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
|> extrude(3.14, %)
const NEW_LINT = 1"#
NEW_LINT = 1"#
.to_string(),
}],
})
@ -3447,8 +3438,8 @@ const NEW_LINT = 1"#
async fn kcl_test_kcl_lsp_code_lint_reexecute_had_lint_new_execution_error() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
let code = r#"LINT = 1
part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
@ -3503,8 +3494,8 @@ const part001 = startSketchOn('XY')
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: r#"const LINT = 1
const part001 = startSketchOn('XY')
text: r#"LINT = 1
part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %, $seg01)
|> line([0, 20], %, $seg01)
@ -3552,7 +3543,7 @@ async fn kcl_test_kcl_lsp_completions_number_literal() {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: "const thing = 10".to_string(),
text: "thing = 10".to_string(),
},
})
.await;
@ -3563,7 +3554,7 @@ async fn kcl_test_kcl_lsp_completions_number_literal() {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
},
position: tower_lsp::lsp_types::Position { line: 0, character: 15 },
position: tower_lsp::lsp_types::Position { line: 0, character: 10 },
},
context: None,
partial_result_params: Default::default(),

View File

@ -3,8 +3,10 @@ pub(crate) mod digest;
pub mod modify;
pub mod types;
use crate::parsing::ast::types::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject};
use crate::source_range::ModuleId;
use crate::{
parsing::ast::types::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject},
source_range::ModuleId,
};
impl BodyItem {
pub fn module_id(&self) -> ModuleId {

View File

@ -8,7 +8,6 @@ use std::{
};
use anyhow::Result;
use parse_display::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -1229,6 +1228,7 @@ impl ImportSelector {
pub struct ImportStatement {
pub selector: ImportSelector,
pub path: String,
#[serde(default, skip_serializing_if = "ItemVisibility::is_default")]
pub visibility: ItemVisibility,
#[serde(default, skip_serializing_if = "Option::is_none")]
@ -2834,6 +2834,18 @@ impl Parameter {
}
}
impl From<&Parameter> for SourceRange {
fn from(p: &Parameter) -> Self {
let sr = Self::from(&p.identifier);
// If it's unlabelled, the span should start 1 char earlier than the identifier,
// to include the '@' symbol.
if !p.labeled {
return Self::new(sr.start() - 1, sr.end(), sr.module_id());
}
sr
}
}
fn is_true(b: &bool) -> bool {
*b
}

View File

@ -1517,6 +1517,11 @@ fn import_stmt(i: TokenSlice) -> PResult<BoxNode<ImportStatement>> {
*a = Some(alias);
}
ParseContext::warn(CompilationError::err(
SourceRange::new(start, path.end, path.module_id),
"Importing a whole module is experimental, likely to be buggy, and likely to change",
));
if a.is_none()
&& (!path_string.ends_with(".kcl")
|| path_string.starts_with("_")
@ -1717,11 +1722,10 @@ fn declaration(i: TokenSlice) -> PResult<BoxNode<VariableDeclaration>> {
"an identifier, which becomes name you're binding the value to",
))
.parse_next(i)?;
let (kind, mut start, dec_end, module_id) = if let Some((kind, token)) = &decl_token {
(*kind, token.start, token.end, token.module_id)
let (kind, mut start, dec_end) = if let Some((kind, token)) = &decl_token {
(*kind, token.start, token.end)
} else {
// TODO warn on const
(VariableKind::Const, id.start, id.end, id.module_id)
(VariableKind::Const, id.start, id.end)
};
if let Some(token) = visibility_token {
start = token.start;
@ -1743,7 +1747,7 @@ fn declaration(i: TokenSlice) -> PResult<BoxNode<VariableDeclaration>> {
let ctxt_end = val.as_ref().map(|e| e.end()).unwrap_or(t.end);
ParseContext::warn(CompilationError::with_suggestion(
t.as_source_range(),
Some(SourceRange::new(id.start, ctxt_end, module_id)),
Some(SourceRange::new(id.start, ctxt_end, id.module_id)),
"Unnecessary `=` in function declaration",
Some(("Remove `=`", "")),
Tag::Unnecessary,
@ -1755,30 +1759,52 @@ fn declaration(i: TokenSlice) -> PResult<BoxNode<VariableDeclaration>> {
equals(i)?;
ignore_whitespace(i);
expression
let val = expression
.try_map(|val| {
// Function bodies can be used if and only if declaring a function.
// Check the 'if' direction:
if matches!(val, Expr::FunctionExpression(_)) {
return Err(CompilationError::fatal(
SourceRange::new(start, dec_end, module_id),
SourceRange::new(start, dec_end, id.module_id),
format!("Expected a `fn` variable kind, found: `{}`", kind),
));
}
Ok(val)
})
.context(expected("a KCL value, which is being bound to a variable"))
.parse_next(i)
.parse_next(i);
if let Some((_, tok)) = decl_token {
ParseContext::warn(CompilationError::with_suggestion(
tok.as_source_range(),
Some(SourceRange::new(
id.start,
val.as_ref().map(|e| e.end()).unwrap_or(dec_end),
id.module_id,
)),
format!(
"Using `{}` to declare constants is deprecated; no keyword is required",
tok.value
),
Some((format!("Remove `{}`", tok.value), "")),
Tag::Deprecated,
));
}
val
}
.map_err(|e| e.cut())?;
let end = val.end();
Ok(Box::new(Node {
start,
end,
module_id: id.module_id,
inner: VariableDeclaration {
declaration: Node {
start: id.start,
end,
module_id,
module_id: id.module_id,
inner: VariableDeclarator {
id,
init: val,
@ -1789,9 +1815,6 @@ fn declaration(i: TokenSlice) -> PResult<BoxNode<VariableDeclaration>> {
kind,
digest: None,
},
start,
end,
module_id,
}))
}
@ -1855,22 +1878,59 @@ impl TryFrom<Token> for Node<TagDeclarator> {
type Error = CompilationError;
fn try_from(token: Token) -> Result<Self, Self::Error> {
if token.token_type == TokenType::Word {
Ok(Node::new(
TagDeclarator {
// We subtract 1 from the start because the tag starts with a `$`.
name: token.value,
digest: None,
},
token.start - 1,
token.end,
token.module_id,
))
} else {
Err(CompilationError::fatal(
match token.token_type {
TokenType::Word => {
Ok(Node::new(
TagDeclarator {
// We subtract 1 from the start because the tag starts with a `$`.
name: token.value,
digest: None,
},
token.start - 1,
token.end,
token.module_id,
))
}
TokenType::Number => Err(CompilationError::fatal(
token.as_source_range(),
format!(
"Tag names must not start with a number. Tag starts with `{}`",
token.value.as_str()
),
)),
// e.g. `line(%, $)` or `line(%, $ , 5)`
TokenType::Brace | TokenType::Whitespace | TokenType::Comma => Err(CompilationError::fatal(
token.as_source_range(),
"Tag names must not be empty".to_string(),
)),
TokenType::Type => Err(CompilationError::fatal(
token.as_source_range(),
format!("Cannot assign a tag to a reserved keyword: {}", token.value.as_str()),
))
)),
TokenType::Bang
| TokenType::At
| TokenType::Hash
| TokenType::Colon
| TokenType::Period
| TokenType::Operator
| TokenType::DoublePeriod
| TokenType::QuestionMark
| TokenType::BlockComment
| TokenType::Function
| TokenType::String
| TokenType::Dollar
| TokenType::Keyword
| TokenType::Unknown
| TokenType::LineComment => Err(CompilationError::fatal(
token.as_source_range(),
// this is `start with` because if most of these cases are in the middle, it ends
// up hitting a different error path(e.g. including a bang) or being valid(e.g. including a comment) since it will get broken up into
// multiple tokens
format!("Tag names must not start with a {}", token.token_type),
)),
}
}
}
@ -1894,7 +1954,8 @@ fn tag(i: TokenSlice) -> PResult<Node<TagDeclarator>> {
let tag_declarator = any
.try_map(Node::<TagDeclarator>::try_from)
.context(expected("a tag, e.g. '$seg01' or '$line01'"))
.parse_next(i)?;
.parse_next(i)
.map_err(|e| e.cut())?;
// Now that we've parsed a tag declarator, verify that it's not a stdlib
// name. If it is, stop backtracking.
tag_declarator
@ -2142,6 +2203,11 @@ fn question_mark(i: TokenSlice) -> PResult<()> {
Ok(())
}
fn at_sign(i: TokenSlice) -> PResult<()> {
TokenType::At.parse_from(i)?;
Ok(())
}
fn fun(i: TokenSlice) -> PResult<Token> {
any.try_map(|token: Token| match token.token_type {
TokenType::Keyword if token.value == "fn" => Ok(token),
@ -2214,13 +2280,15 @@ fn argument_type(i: TokenSlice) -> PResult<FnArgType> {
}
struct ParamDescription {
labeled: bool,
arg_name: Token,
type_: std::option::Option<FnArgType>,
is_optional: bool,
}
fn parameter(i: TokenSlice) -> PResult<ParamDescription> {
let (arg_name, optional, _, type_) = (
let (found_at_sign, arg_name, optional, _, type_) = (
opt(at_sign),
any.verify(|token: &Token| !matches!(token.token_type, TokenType::Brace) || token.value != ")"),
opt(question_mark),
opt(whitespace),
@ -2228,6 +2296,7 @@ fn parameter(i: TokenSlice) -> PResult<ParamDescription> {
)
.parse_next(i)?;
Ok(ParamDescription {
labeled: found_at_sign.is_none(),
arg_name,
type_,
is_optional: optional.is_some(),
@ -2246,6 +2315,7 @@ fn parameters(i: TokenSlice) -> PResult<Vec<Parameter>> {
.into_iter()
.map(
|ParamDescription {
labeled,
arg_name,
type_,
is_optional,
@ -2261,7 +2331,7 @@ fn parameters(i: TokenSlice) -> PResult<Vec<Parameter>> {
} else {
None
},
labeled: true,
labeled,
digest: None,
})
},
@ -2269,6 +2339,15 @@ fn parameters(i: TokenSlice) -> PResult<Vec<Parameter>> {
.collect::<Result<_, _>>()
.map_err(|e: CompilationError| ErrMode::Backtrack(ContextError::from(e)))?;
// Make sure the only unlabeled parameter is the first one.
if let Some(param) = params.iter().skip(1).find(|param| !param.labeled) {
let source_range = SourceRange::from(param);
return Err(ErrMode::Cut(ContextError::from(CompilationError::fatal(
source_range,
"Only the first parameter can be declared unlabeled",
))));
}
// Make sure optional parameters are last.
if let Err(e) = optional_after_required(&params) {
return Err(ErrMode::Cut(ContextError::from(e)));
@ -2372,6 +2451,7 @@ fn fn_call(i: TokenSlice) -> PResult<Node<CallExpression>> {
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) {
let just_args: Vec<_> = args.iter().collect();
typecheck_all(std_fn, &just_args)?;
@ -2536,9 +2616,9 @@ mod tests {
fn test_comments_in_function1() {
let test_program = r#"() {
// comment 0
const a = 1
a = 1
// comment 1
const b = 2
b = 2
// comment 2
return 1
}"#;
@ -2557,7 +2637,7 @@ mod tests {
#[test]
fn test_comments_in_function2() {
let test_program = r#"() {
const yo = { a = { b = { c = '123' } } } /* block
yo = { a = { b = { c = '123' } } } /* block
comment */
}"#;
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
@ -2572,7 +2652,7 @@ comment */
let test_program = r#"
/* comment at start */
const mySk1 = startSketchAt([0, 0])"#;
mySk1 = startSketchAt([0, 0])"#;
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
let program = program.parse(&tokens).unwrap();
let mut starting_comments = program.inner.non_code_meta.start_nodes;
@ -2591,7 +2671,7 @@ const mySk1 = startSketchAt([0, 0])"#;
#[test]
fn test_comment_in_pipe() {
let tokens = crate::parsing::token::lexer(r#"const x = y() |> /*hi*/ z(%)"#, ModuleId::default()).unwrap();
let tokens = crate::parsing::token::lexer(r#"x = y() |> /*hi*/ z(%)"#, ModuleId::default()).unwrap();
let mut body = program.parse(&tokens).unwrap().inner.body;
let BodyItem::VariableDeclaration(item) = body.remove(0) else {
panic!("expected vardec");
@ -2717,10 +2797,10 @@ const mySk1 = startSketchAt([0, 0])"#;
#[test]
fn many_comments() {
let test_program = r#"// this is a comment
const yo = { a = { b = { c = '123' } } } /* block
yo = { a = { b = { c = '123' } } } /* block
comment */
const key = 'c'
key = 'c'
// this is also a comment
return things
"#;
@ -2754,8 +2834,8 @@ const mySk1 = startSketchAt([0, 0])"#;
},
digest: None,
},
63,
85,
57,
79,
module_id,
),
Node::new(
@ -2763,8 +2843,8 @@ const mySk1 = startSketchAt([0, 0])"#;
value: NonCodeValue::NewLine,
digest: None,
},
85,
89,
79,
83,
module_id,
)
]),
@ -2780,8 +2860,8 @@ const mySk1 = startSketchAt([0, 0])"#;
},
digest: None,
},
106,
132,
94,
120,
module_id,
)]),
non_code_meta.non_code_nodes.get(&1),
@ -2790,7 +2870,7 @@ const mySk1 = startSketchAt([0, 0])"#;
#[test]
fn inline_block_comments() {
let test_program = r#"const yo = 3 /* block
let test_program = r#"yo = 3 /* block
comment */
return 1"#;
@ -2855,10 +2935,10 @@ const mySk1 = startSketchAt([0, 0])"#;
#[test]
fn assign_brackets() {
for (i, test_input) in [
"const thickness_squared = (1 + 1)",
"const thickness_squared = ( 1 + 1)",
"const thickness_squared = (1 + 1 )",
"const thickness_squared = ( 1 + 1 )",
"thickness_squared = (1 + 1)",
"thickness_squared = ( 1 + 1)",
"thickness_squared = (1 + 1 )",
"thickness_squared = ( 1 + 1 )",
]
.into_iter()
.enumerate()
@ -2880,7 +2960,7 @@ const mySk1 = startSketchAt([0, 0])"#;
#[test]
fn test_function_call() {
for (i, test_input) in ["const x = f(1)", "const x = f( 1 )"].into_iter().enumerate() {
for (i, test_input) in ["x = f(1)", "x = f( 1 )"].into_iter().enumerate() {
let tokens = crate::parsing::token::lexer(test_input, ModuleId::default()).unwrap();
let _actual = match declaration.parse(&tokens) {
Err(e) => panic!("Could not parse test {i}: {e:#?}"),
@ -3202,7 +3282,7 @@ const mySk1 = startSketchAt([0, 0])"#;
#[test]
fn test_declaration() {
let tests = ["const myVar = 5", "const myVar=5", "const myVar =5", "const myVar= 5"];
let tests = ["myVar = 5", "myVar=5", "myVar =5", "myVar= 5"];
for test in tests {
// Run the original parser
let tokens = crate::parsing::token::lexer(test, ModuleId::default()).unwrap();
@ -3436,12 +3516,18 @@ const mySk1 = startSketchAt([0, 0])"#;
}
#[track_caller]
fn assert_err(p: &str, msg: &str, src: [usize; 2]) {
fn assert_err(p: &str, msg: &str, src_expected: [usize; 2]) {
let result = crate::parsing::top_level_parse(p);
let err = result.unwrap_errs().next().unwrap();
assert_eq!(err.message, msg);
assert_eq!(err.source_range.start(), src[0]);
assert_eq!(err.source_range.end(), src[1]);
let src_actual = [err.source_range.start(), err.source_range.end()];
assert_eq!(
src_expected,
src_actual,
"expected error would highlight {} but it actually highlighted {}",
&p[src_expected[0]..src_expected[1]],
&p[src_actual[0]..src_actual[1]],
);
}
#[track_caller]
@ -3454,7 +3540,7 @@ const mySk1 = startSketchAt([0, 0])"#;
#[test]
fn test_parse_half_pipe_small() {
assert_err_contains(
"const secondExtrude = startSketchOn('XY')
"secondExtrude = startSketchOn('XY')
|> startProfileAt([0,0], %)
|",
"Unexpected token: |",
@ -3463,20 +3549,20 @@ const mySk1 = startSketchAt([0, 0])"#;
#[test]
fn test_parse_member_expression_double_nested_braces() {
let code = r#"const prop = yo["one"][two]"#;
let code = r#"prop = yo["one"][two]"#;
crate::parsing::top_level_parse(code).unwrap();
}
#[test]
fn test_parse_member_expression_binary_expression_period_number_first() {
let code = r#"const obj = { a: 1, b: 2 }
const height = 1 - obj.a"#;
let code = r#"obj = { a: 1, b: 2 }
height = 1 - obj.a"#;
crate::parsing::top_level_parse(code).unwrap();
}
#[test]
fn test_parse_member_expression_allowed_type_in_expression() {
let code = r#"const obj = { thing: 1 }
let code = r#"obj = { thing: 1 }
startSketchOn(obj.sketch)"#;
crate::parsing::top_level_parse(code).unwrap();
@ -3484,36 +3570,36 @@ startSketchOn(obj.sketch)"#;
#[test]
fn test_parse_member_expression_binary_expression_brace_number_first() {
let code = r#"const obj = { a: 1, b: 2 }
const height = 1 - obj["a"]"#;
let code = r#"obj = { a: 1, b: 2 }
height = 1 - obj["a"]"#;
crate::parsing::top_level_parse(code).unwrap();
}
#[test]
fn test_parse_member_expression_binary_expression_brace_number_second() {
let code = r#"const obj = { a: 1, b: 2 }
const height = obj["a"] - 1"#;
let code = r#"obj = { a: 1, b: 2 }
height = obj["a"] - 1"#;
crate::parsing::top_level_parse(code).unwrap();
}
#[test]
fn test_parse_member_expression_binary_expression_in_array_number_first() {
let code = r#"const obj = { a: 1, b: 2 }
const height = [1 - obj["a"], 0]"#;
let code = r#"obj = { a: 1, b: 2 }
height = [1 - obj["a"], 0]"#;
crate::parsing::top_level_parse(code).unwrap();
}
#[test]
fn test_parse_member_expression_binary_expression_in_array_number_second() {
let code = r#"const obj = { a: 1, b: 2 }
const height = [obj["a"] - 1, 0]"#;
let code = r#"obj = { a: 1, b: 2 }
height = [obj["a"] - 1, 0]"#;
crate::parsing::top_level_parse(code).unwrap();
}
#[test]
fn test_parse_member_expression_binary_expression_in_array_number_second_missing_space() {
let code = r#"const obj = { a: 1, b: 2 }
const height = [obj["a"] -1, 0]"#;
let code = r#"obj = { a: 1, b: 2 }
height = [obj["a"] -1, 0]"#;
crate::parsing::top_level_parse(code).unwrap();
}
@ -3529,9 +3615,9 @@ const height = [obj["a"] -1, 0]"#;
#[test]
fn test_parse_half_pipe() {
let code = "const height = 10
let code = "height = 10
const firstExtrude = startSketchOn('XY')
firstExtrude = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, 8], %)
|> line([20, 0], %)
@ -3539,7 +3625,7 @@ const firstExtrude = startSketchOn('XY')
|> close(%)
|> extrude(2, %)
const secondExtrude = startSketchOn('XY')
secondExtrude = startSketchOn('XY')
|> startProfileAt([0,0], %)
|";
assert_err_contains(code, "Unexpected token: |");
@ -3550,6 +3636,20 @@ const secondExtrude = startSketchOn('XY')
assert_err(">!", "Unexpected token: >", [0, 1]);
}
#[test]
fn test_parse_unlabeled_param_not_allowed() {
assert_err(
"fn f(@x, @y) { return 1 }",
"Only the first parameter can be declared unlabeled",
[9, 11],
);
assert_err(
"fn f(x, @y) { return 1 }",
"Only the first parameter can be declared unlabeled",
[8, 10],
);
}
#[test]
fn test_parse_z_percent_parens() {
assert_err("z%)", "Unexpected token: %", [1, 2]);
@ -3570,10 +3670,10 @@ const secondExtrude = startSketchOn('XY')
#[test]
fn test_parse_negative_in_array_binary_expression() {
let code = r#"const leg1 = 5
const thickness = 0.56
let code = r#"leg1 = 5
thickness = 0.56
const bracket = [-leg2 + thickness, 0]
bracket = [-leg2 + thickness, 0]
"#;
crate::parsing::top_level_parse(code).unwrap();
}
@ -3805,6 +3905,13 @@ e
);
}
#[test]
fn warn_import() {
let some_program_string = r#"import "foo.kcl""#;
let (_, errs) = assert_no_err(some_program_string);
assert_eq!(errs.len(), 1);
}
#[test]
fn zero_param_function() {
let code = r#"
@ -3932,7 +4039,7 @@ thing(false)
#[test]
fn random_words_fail() {
let test_program = r#"const part001 = startSketchOn('-XZ')
let test_program = r#"part001 = startSketchOn('-XZ')
|> startProfileAt([8.53, 11.8], %)
asdasd asdasd
|> line([11.12, -14.82], %)
@ -3946,7 +4053,7 @@ thing(false)
#[test]
fn test_member_expression_sketch() {
let some_program_string = r#"fn cube = (pos, scale) => {
const sg = startSketchOn('XY')
sg = startSketchOn('XY')
|> startProfileAt(pos, %)
|> line([0, scale], %)
|> line([scale, 0], %)
@ -3955,18 +4062,18 @@ thing(false)
return sg
}
const b1 = cube([0,0], 10)
const b2 = cube([3,3], 4)
b1 = cube([0,0], 10)
b2 = cube([3,3], 4)
const pt1 = b1[0]
const pt2 = b2[0]
pt1 = b1[0]
pt2 = b2[0]
"#;
crate::parsing::top_level_parse(some_program_string).unwrap();
}
#[test]
fn test_math_with_stdlib() {
let some_program_string = r#"const d2r = pi() / 2
let some_program_string = r#"d2r = pi() / 2
let other_thing = 2 * cos(3)"#;
crate::parsing::top_level_parse(some_program_string).unwrap();
}
@ -3974,7 +4081,7 @@ let other_thing = 2 * cos(3)"#;
#[test]
fn test_negative_arguments() {
let some_program_string = r#"fn box = (p, h, l, w) => {
const myBox = startSketchOn('XY')
myBox = startSketchOn('XY')
|> startProfileAt(p, %)
|> line([0, l], %)
|> line([w, 0], %)
@ -4035,12 +4142,108 @@ let myBox = box([0,0], -3, -16, -10)
}
#[test]
fn test_parse_empty_tag() {
fn test_parse_empty_tag_brace() {
let some_program_string = r#"startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([5, 5], %, $)
"#;
assert_err(some_program_string, "Unexpected token: |>", [57, 59]);
|> line(%, $)
"#;
assert_err(some_program_string, "Tag names must not be empty", [69, 70]);
}
#[test]
fn test_parse_empty_tag_whitespace() {
let some_program_string = r#"startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line(%, $ ,01)
"#;
assert_err(some_program_string, "Tag names must not be empty", [69, 70]);
}
#[test]
fn test_parse_empty_tag_comma() {
let some_program_string = r#"startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line(%, $,)
"#;
assert_err(some_program_string, "Tag names must not be empty", [69, 70]);
}
#[test]
fn test_parse_tag_starting_with_digit() {
let some_program_string = r#"
startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line(%, $01)"#;
assert_err(
some_program_string,
"Tag names must not start with a number. Tag starts with `01`",
[74, 76],
);
}
#[test]
fn test_parse_tag_including_digit() {
let some_program_string = r#"
startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line(%, $var01)"#;
assert_no_err(some_program_string);
}
#[test]
fn test_parse_tag_starting_with_bang() {
let some_program_string = r#"startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line(%, $!var,01)
"#;
assert_err(some_program_string, "Tag names must not start with a bang", [69, 70]);
}
#[test]
fn test_parse_tag_starting_with_dollar() {
let some_program_string = r#"startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line(%, $$,01)
"#;
assert_err(some_program_string, "Tag names must not start with a dollar", [69, 70]);
}
#[test]
fn test_parse_tag_starting_with_fn() {
let some_program_string = r#"startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line(%, $fn,01)
"#;
assert_err(some_program_string, "Tag names must not start with a keyword", [69, 71]);
}
#[test]
fn test_parse_tag_starting_with_a_comment() {
let some_program_string = r#"startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line(%, $//
,01)
"#;
assert_err(
some_program_string,
"Tag names must not start with a lineComment",
[69, 71],
);
}
#[test]
fn test_parse_tag_starting_with_reserved_type() {
let some_program_string = r#"
startSketchOn('XY')
|> line(%, $sketch)
"#;
assert_err(
some_program_string,
"Cannot assign a tag to a reserved keyword: sketch",
[41, 47],
);
}
#[test]
fn test_parse_tag_with_reserved_in_middle_works() {
let some_program_string = r#"
startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([5, 5], %, $sketching)
"#;
assert_no_err(some_program_string);
}
#[test]
@ -4080,6 +4283,26 @@ int(42.3)"#;
}"#
);
}
#[test]
fn warn_const() {
let some_program_string = r#"const foo = 0
let bar = 1
var baz = 2
"#;
let (_, errs) = assert_no_err(some_program_string);
assert_eq!(errs.len(), 3);
let replaced = errs[2].apply_suggestion(some_program_string).unwrap();
let replaced = errs[1].apply_suggestion(&replaced).unwrap();
let replaced = errs[0].apply_suggestion(&replaced).unwrap();
assert_eq!(
replaced,
r#" foo = 0
bar = 1
baz = 2
"#
);
}
}
#[cfg(test)]
@ -4151,19 +4374,19 @@ mod snapshot_tests {
snapshot_test!(
a,
r#"const boxSketch = startSketchAt([0, 0])
r#"boxSketch = startSketchAt([0, 0])
|> line([0, 10], %)
|> tangentialArc([-5, 5], %)
|> line([5, -15], %)
|> extrude(10, %)
"#
);
snapshot_test!(b, "const myVar = min(5 , -legLen(5, 4))"); // Space before comma
snapshot_test!(b, "myVar = min(5 , -legLen(5, 4))"); // Space before comma
snapshot_test!(c, "const myVar = min(-legLen(5, 4), 5)");
snapshot_test!(d, "const myVar = 5 + 6 |> myFunc(45, %)");
snapshot_test!(c, "myVar = min(-legLen(5, 4), 5)");
snapshot_test!(d, "myVar = 5 + 6 |> myFunc(45, %)");
snapshot_test!(e, "let x = 1 * (3 - 4)");
snapshot_test!(f, r#"const x = 1 // this is an inline comment"#);
snapshot_test!(f, r#"x = 1 // this is an inline comment"#);
snapshot_test!(
g,
r#"fn x = () => {
@ -4171,57 +4394,57 @@ mod snapshot_tests {
return sg
}"#
);
snapshot_test!(d2, r#"const x = -leg2 + thickness"#);
snapshot_test!(d2, r#"x = -leg2 + thickness"#);
snapshot_test!(
h,
r#"const obj = { a: 1, b: 2 }
const height = 1 - obj.a"#
r#"obj = { a: 1, b: 2 }
height = 1 - obj.a"#
);
snapshot_test!(
i,
r#"const obj = { a: 1, b: 2 }
const height = 1 - obj["a"]"#
r#"obj = { a: 1, b: 2 }
height = 1 - obj["a"]"#
);
snapshot_test!(
j,
r#"const obj = { a: 1, b: 2 }
const height = obj["a"] - 1"#
r#"obj = { a: 1, b: 2 }
height = obj["a"] - 1"#
);
snapshot_test!(
k,
r#"const obj = { a: 1, b: 2 }
const height = [1 - obj["a"], 0]"#
r#"obj = { a: 1, b: 2 }
height = [1 - obj["a"], 0]"#
);
snapshot_test!(
l,
r#"const obj = { a: 1, b: 2 }
const height = [obj["a"] - 1, 0]"#
r#"obj = { a: 1, b: 2 }
height = [obj["a"] - 1, 0]"#
);
snapshot_test!(
m,
r#"const obj = { a: 1, b: 2 }
const height = [obj["a"] -1, 0]"#
r#"obj = { a: 1, b: 2 }
height = [obj["a"] -1, 0]"#
);
snapshot_test!(n, "const height = 1 - obj.a");
snapshot_test!(o, "const six = 1 + 2 + 3");
snapshot_test!(p, "const five = 3 * 1 + 2");
snapshot_test!(q, r#"const height = [ obj["a"], 0 ]"#);
snapshot_test!(n, "height = 1 - obj.a");
snapshot_test!(o, "six = 1 + 2 + 3");
snapshot_test!(p, "five = 3 * 1 + 2");
snapshot_test!(q, r#"height = [ obj["a"], 0 ]"#);
snapshot_test!(
r,
r#"const obj = { a: 1, b: 2 }
const height = obj["a"]"#
r#"obj = { a: 1, b: 2 }
height = obj["a"]"#
);
snapshot_test!(s, r#"const prop = yo["one"][two]"#);
snapshot_test!(t, r#"const pt1 = b1[x]"#);
snapshot_test!(u, "const prop = yo.one.two.three.four");
snapshot_test!(v, r#"const pt1 = b1[0]"#);
snapshot_test!(w, r#"const pt1 = b1['zero']"#);
snapshot_test!(x, r#"const pt1 = b1.zero"#);
snapshot_test!(y, "const sg = startSketchAt(pos)");
snapshot_test!(z, "const sg = startSketchAt(pos) |> line([0, -scale], %)");
snapshot_test!(aa, r#"const sg = -scale"#);
snapshot_test!(s, r#"prop = yo["one"][two]"#);
snapshot_test!(t, r#"pt1 = b1[x]"#);
snapshot_test!(u, "prop = yo.one.two.three.four");
snapshot_test!(v, r#"pt1 = b1[0]"#);
snapshot_test!(w, r#"pt1 = b1['zero']"#);
snapshot_test!(x, r#"pt1 = b1.zero"#);
snapshot_test!(y, "sg = startSketchAt(pos)");
snapshot_test!(z, "sg = startSketchAt(pos) |> line([0, -scale], %)");
snapshot_test!(aa, r#"sg = -scale"#);
snapshot_test!(ab, "lineTo({ to: [0, -1] })");
snapshot_test!(ac, "const myArray = [0..10]");
snapshot_test!(ac, "myArray = [0..10]");
snapshot_test!(
ad,
r#"
@ -4239,25 +4462,22 @@ mod snapshot_tests {
);
snapshot_test!(
af,
r#"const mySketch = startSketchAt([0,0])
r#"mySketch = startSketchAt([0,0])
|> lineTo([0, 1], %, $myPath)
|> lineTo([1, 1], %)
|> lineTo([1, 0], %, $rightPath)
|> close(%)"#
);
snapshot_test!(
ag,
"const mySketch = startSketchAt([0,0]) |> lineTo([1, 1], %) |> close(%)"
);
snapshot_test!(ah, "const myBox = startSketchAt(p)");
snapshot_test!(ai, r#"const myBox = f(1) |> g(2, %)"#);
snapshot_test!(aj, r#"const myBox = startSketchAt(p) |> line([0, l], %)"#);
snapshot_test!(ag, "mySketch = startSketchAt([0,0]) |> lineTo([1, 1], %) |> close(%)");
snapshot_test!(ah, "myBox = startSketchAt(p)");
snapshot_test!(ai, r#"myBox = f(1) |> g(2, %)"#);
snapshot_test!(aj, r#"myBox = startSketchAt(p) |> line([0, l], %)"#);
snapshot_test!(ak, "lineTo({ to: [0, 1] })");
snapshot_test!(al, "lineTo({ to: [0, 1], from: [3, 3] })");
snapshot_test!(am, "lineTo({to:[0, 1]})");
snapshot_test!(an, "lineTo({ to: [0, 1], from: [3, 3]})");
snapshot_test!(ao, "lineTo({ to: [0, 1],from: [3, 3] })");
snapshot_test!(ap, "const mySketch = startSketchAt([0,0])");
snapshot_test!(ap, "mySketch = startSketchAt([0,0])");
snapshot_test!(aq, "log(5, \"hello\", aIdentifier)");
snapshot_test!(ar, r#"5 + "a""#);
snapshot_test!(at, "line([0, l], %)");
@ -4300,7 +4520,7 @@ mod snapshot_tests {
snapshot_test!(
ba,
r#"
const sketch001 = startSketchOn('XY')
sketch001 = startSketchOn('XY')
// |> arc({
// angleEnd: 270,
// angleStart: 450,
@ -4311,12 +4531,12 @@ const sketch001 = startSketchOn('XY')
snapshot_test!(
bb,
r#"
const my14 = 4 ^ 2 - 3 ^ 2 * 2
my14 = 4 ^ 2 - 3 ^ 2 * 2
"#
);
snapshot_test!(
bc,
r#"const x = if true {
r#"x = if true {
3
} else {
4
@ -4324,7 +4544,7 @@ const my14 = 4 ^ 2 - 3 ^ 2 * 2
);
snapshot_test!(
bd,
r#"const x = if true {
r#"x = if true {
3
} else if func(radius) {
4
@ -4335,7 +4555,7 @@ const my14 = 4 ^ 2 - 3 ^ 2 * 2
snapshot_test!(be, "let x = 3 == 3");
snapshot_test!(bf, "let x = 3 != 3");
snapshot_test!(bg, r#"x = 4"#);
snapshot_test!(bh, "const obj = {center : [10, 10], radius: 5}");
snapshot_test!(bh, "obj = {center : [10, 10], radius: 5}");
snapshot_test!(
bi,
r#"x = 3
@ -4343,6 +4563,8 @@ const my14 = 4 ^ 2 - 3 ^ 2 * 2
);
snapshot_test!(kw_function_unnamed_first, r#"val = foo(x, y: z)"#);
snapshot_test!(kw_function_all_named, r#"val = foo(x: a, y: b)"#);
snapshot_test!(kw_function_decl_all_labeled, r#"fn foo(x, y) { return 1 }"#);
snapshot_test!(kw_function_decl_first_unlabeled, r#"fn foo(@x, y) { return 1 }"#);
}
#[allow(unused)]

View File

@ -6,11 +6,11 @@ expression: actual
"body": [
{
"declaration": {
"end": 143,
"end": 137,
"id": {
"end": 15,
"end": 9,
"name": "boxSketch",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
@ -20,36 +20,36 @@ expression: actual
{
"elements": [
{
"end": 34,
"end": 28,
"raw": "0",
"start": 33,
"start": 27,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 37,
"end": 31,
"raw": "0",
"start": 36,
"start": 30,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 38,
"start": 32,
"end": 32,
"start": 26,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
],
"callee": {
"end": 31,
"end": 25,
"name": "startSketchAt",
"start": 18,
"start": 12,
"type": "Identifier"
},
"end": 39,
"start": 18,
"end": 33,
"start": 12,
"type": "CallExpression",
"type": "CallExpression"
},
@ -58,42 +58,42 @@ expression: actual
{
"elements": [
{
"end": 54,
"end": 48,
"raw": "0",
"start": 53,
"start": 47,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 58,
"end": 52,
"raw": "10",
"start": 56,
"start": 50,
"type": "Literal",
"type": "Literal",
"value": 10.0
}
],
"end": 59,
"start": 52,
"end": 53,
"start": 46,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 62,
"start": 61,
"end": 56,
"start": 55,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 51,
"end": 45,
"name": "line",
"start": 47,
"start": 41,
"type": "Identifier"
},
"end": 63,
"start": 47,
"end": 57,
"start": 41,
"type": "CallExpression",
"type": "CallExpression"
},
@ -103,48 +103,48 @@ expression: actual
"elements": [
{
"argument": {
"end": 88,
"end": 82,
"raw": "5",
"start": 87,
"start": 81,
"type": "Literal",
"type": "Literal",
"value": 5.0
},
"end": 88,
"end": 82,
"operator": "-",
"start": 86,
"start": 80,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
{
"end": 91,
"end": 85,
"raw": "5",
"start": 90,
"start": 84,
"type": "Literal",
"type": "Literal",
"value": 5.0
}
],
"end": 92,
"start": 85,
"end": 86,
"start": 79,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 95,
"start": 94,
"end": 89,
"start": 88,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 84,
"end": 78,
"name": "tangentialArc",
"start": 71,
"start": 65,
"type": "Identifier"
},
"end": 96,
"start": 71,
"end": 90,
"start": 65,
"type": "CallExpression",
"type": "CallExpression"
},
@ -153,96 +153,96 @@ expression: actual
{
"elements": [
{
"end": 111,
"end": 105,
"raw": "5",
"start": 110,
"start": 104,
"type": "Literal",
"type": "Literal",
"value": 5.0
},
{
"argument": {
"end": 116,
"end": 110,
"raw": "15",
"start": 114,
"start": 108,
"type": "Literal",
"type": "Literal",
"value": 15.0
},
"end": 116,
"end": 110,
"operator": "-",
"start": 113,
"start": 107,
"type": "UnaryExpression",
"type": "UnaryExpression"
}
],
"end": 117,
"start": 109,
"end": 111,
"start": 103,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 120,
"start": 119,
"end": 114,
"start": 113,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 108,
"end": 102,
"name": "line",
"start": 104,
"start": 98,
"type": "Identifier"
},
"end": 121,
"start": 104,
"end": 115,
"start": 98,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 139,
"end": 133,
"raw": "10",
"start": 137,
"start": 131,
"type": "Literal",
"type": "Literal",
"value": 10.0
},
{
"end": 142,
"start": 141,
"end": 136,
"start": 135,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 136,
"end": 130,
"name": "extrude",
"start": 129,
"start": 123,
"type": "Identifier"
},
"end": 143,
"start": 129,
"end": 137,
"start": 123,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 143,
"start": 18,
"end": 137,
"start": 12,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 143,
"end": 137,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 144,
"end": 138,
"start": 0
}

View File

@ -6,37 +6,37 @@ expression: actual
"body": [
{
"declaration": {
"end": 17,
"end": 11,
"id": {
"end": 8,
"end": 2,
"name": "sg",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"argument": {
"end": 17,
"end": 11,
"name": "scale",
"start": 12,
"start": 6,
"type": "Identifier",
"type": "Identifier"
},
"end": 17,
"end": 11,
"operator": "-",
"start": 11,
"start": 5,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 17,
"end": 11,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 17,
"end": 11,
"start": 0
}

View File

@ -6,29 +6,29 @@ expression: actual
"body": [
{
"declaration": {
"end": 23,
"end": 17,
"id": {
"end": 13,
"end": 7,
"name": "myArray",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 23,
"end": 17,
"endElement": {
"end": 22,
"end": 16,
"raw": "10",
"start": 20,
"start": 14,
"type": "Literal",
"type": "Literal",
"value": 10.0
},
"endInclusive": true,
"start": 16,
"start": 10,
"startElement": {
"end": 18,
"end": 12,
"raw": "0",
"start": 17,
"start": 11,
"type": "Literal",
"type": "Literal",
"value": 0.0
@ -36,16 +36,16 @@ expression: actual
"type": "ArrayRangeExpression",
"type": "ArrayRangeExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 23,
"end": 17,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 23,
"end": 17,
"start": 0
}

View File

@ -6,11 +6,11 @@ expression: actual
"body": [
{
"declaration": {
"end": 165,
"end": 159,
"id": {
"end": 14,
"end": 8,
"name": "mySketch",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
@ -20,36 +20,36 @@ expression: actual
{
"elements": [
{
"end": 33,
"end": 27,
"raw": "0",
"start": 32,
"start": 26,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 35,
"end": 29,
"raw": "0",
"start": 34,
"start": 28,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 36,
"start": 31,
"end": 30,
"start": 25,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
],
"callee": {
"end": 30,
"end": 24,
"name": "startSketchAt",
"start": 17,
"start": 11,
"type": "Identifier"
},
"end": 37,
"start": 17,
"end": 31,
"start": 11,
"type": "CallExpression",
"type": "CallExpression"
},
@ -58,49 +58,49 @@ expression: actual
{
"elements": [
{
"end": 58,
"end": 52,
"raw": "0",
"start": 57,
"start": 51,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 61,
"end": 55,
"raw": "1",
"start": 60,
"start": 54,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
],
"end": 62,
"start": 56,
"end": 56,
"start": 50,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 65,
"start": 64,
"end": 59,
"start": 58,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
},
{
"end": 74,
"start": 67,
"end": 68,
"start": 61,
"type": "TagDeclarator",
"type": "TagDeclarator",
"value": "myPath"
}
],
"callee": {
"end": 55,
"end": 49,
"name": "lineTo",
"start": 49,
"start": 43,
"type": "Identifier"
},
"end": 75,
"start": 49,
"end": 69,
"start": 43,
"type": "CallExpression",
"type": "CallExpression"
},
@ -109,42 +109,42 @@ expression: actual
{
"elements": [
{
"end": 96,
"end": 90,
"raw": "1",
"start": 95,
"start": 89,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
{
"end": 99,
"end": 93,
"raw": "1",
"start": 98,
"start": 92,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
],
"end": 100,
"start": 94,
"end": 94,
"start": 88,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 103,
"start": 102,
"end": 97,
"start": 96,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 93,
"end": 87,
"name": "lineTo",
"start": 87,
"start": 81,
"type": "Identifier"
},
"end": 104,
"start": 87,
"end": 98,
"start": 81,
"type": "CallExpression",
"type": "CallExpression"
},
@ -153,88 +153,88 @@ expression: actual
{
"elements": [
{
"end": 125,
"end": 119,
"raw": "1",
"start": 124,
"start": 118,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
{
"end": 128,
"end": 122,
"raw": "0",
"start": 127,
"start": 121,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 129,
"start": 123,
"end": 123,
"start": 117,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 132,
"start": 131,
"end": 126,
"start": 125,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
},
{
"end": 144,
"start": 134,
"end": 138,
"start": 128,
"type": "TagDeclarator",
"type": "TagDeclarator",
"value": "rightPath"
}
],
"callee": {
"end": 122,
"end": 116,
"name": "lineTo",
"start": 116,
"start": 110,
"type": "Identifier"
},
"end": 145,
"start": 116,
"end": 139,
"start": 110,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 164,
"start": 163,
"end": 158,
"start": 157,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 162,
"end": 156,
"name": "close",
"start": 157,
"start": 151,
"type": "Identifier"
},
"end": 165,
"start": 157,
"end": 159,
"start": 151,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 165,
"start": 17,
"end": 159,
"start": 11,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 165,
"end": 159,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 165,
"end": 159,
"start": 0
}

View File

@ -6,11 +6,11 @@ expression: actual
"body": [
{
"declaration": {
"end": 70,
"end": 64,
"id": {
"end": 14,
"end": 8,
"name": "mySketch",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
@ -20,36 +20,36 @@ expression: actual
{
"elements": [
{
"end": 33,
"end": 27,
"raw": "0",
"start": 32,
"start": 26,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 35,
"end": 29,
"raw": "0",
"start": 34,
"start": 28,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 36,
"start": 31,
"end": 30,
"start": 25,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
],
"callee": {
"end": 30,
"end": 24,
"name": "startSketchAt",
"start": 17,
"start": 11,
"type": "Identifier"
},
"end": 37,
"start": 17,
"end": 31,
"start": 11,
"type": "CallExpression",
"type": "CallExpression"
},
@ -58,81 +58,81 @@ expression: actual
{
"elements": [
{
"end": 50,
"end": 44,
"raw": "1",
"start": 49,
"start": 43,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
{
"end": 53,
"end": 47,
"raw": "1",
"start": 52,
"start": 46,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
],
"end": 54,
"start": 48,
"end": 48,
"start": 42,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 57,
"start": 56,
"end": 51,
"start": 50,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 47,
"end": 41,
"name": "lineTo",
"start": 41,
"start": 35,
"type": "Identifier"
},
"end": 58,
"start": 41,
"end": 52,
"start": 35,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 69,
"start": 68,
"end": 63,
"start": 62,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 67,
"end": 61,
"name": "close",
"start": 62,
"start": 56,
"type": "Identifier"
},
"end": 70,
"start": 62,
"end": 64,
"start": 56,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 70,
"start": 17,
"end": 64,
"start": 11,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 70,
"end": 64,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 70,
"end": 64,
"start": 0
}

View File

@ -6,44 +6,44 @@ expression: actual
"body": [
{
"declaration": {
"end": 30,
"end": 24,
"id": {
"end": 11,
"end": 5,
"name": "myBox",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"arguments": [
{
"end": 29,
"end": 23,
"name": "p",
"start": 28,
"start": 22,
"type": "Identifier",
"type": "Identifier"
}
],
"callee": {
"end": 27,
"end": 21,
"name": "startSketchAt",
"start": 14,
"start": 8,
"type": "Identifier"
},
"end": 30,
"start": 14,
"end": 24,
"start": 8,
"type": "CallExpression",
"type": "CallExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 30,
"end": 24,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 30,
"end": 24,
"start": 0
}

View File

@ -6,11 +6,11 @@ expression: actual
"body": [
{
"declaration": {
"end": 29,
"end": 23,
"id": {
"end": 11,
"end": 5,
"name": "myBox",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
@ -18,69 +18,69 @@ expression: actual
{
"arguments": [
{
"end": 17,
"end": 11,
"raw": "1",
"start": 16,
"start": 10,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
],
"callee": {
"end": 15,
"end": 9,
"name": "f",
"start": 14,
"start": 8,
"type": "Identifier"
},
"end": 18,
"start": 14,
"end": 12,
"start": 8,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 25,
"end": 19,
"raw": "2",
"start": 24,
"start": 18,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
{
"end": 28,
"start": 27,
"end": 22,
"start": 21,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 23,
"end": 17,
"name": "g",
"start": 22,
"start": 16,
"type": "Identifier"
},
"end": 29,
"start": 22,
"end": 23,
"start": 16,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 29,
"start": 14,
"end": 23,
"start": 8,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 29,
"end": 23,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 29,
"end": 23,
"start": 0
}

View File

@ -6,11 +6,11 @@ expression: actual
"body": [
{
"declaration": {
"end": 49,
"end": 43,
"id": {
"end": 11,
"end": 5,
"name": "myBox",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
@ -18,21 +18,21 @@ expression: actual
{
"arguments": [
{
"end": 29,
"end": 23,
"name": "p",
"start": 28,
"start": 22,
"type": "Identifier",
"type": "Identifier"
}
],
"callee": {
"end": 27,
"end": 21,
"name": "startSketchAt",
"start": 14,
"start": 8,
"type": "Identifier"
},
"end": 30,
"start": 14,
"end": 24,
"start": 8,
"type": "CallExpression",
"type": "CallExpression"
},
@ -41,60 +41,60 @@ expression: actual
{
"elements": [
{
"end": 41,
"end": 35,
"raw": "0",
"start": 40,
"start": 34,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 44,
"end": 38,
"name": "l",
"start": 43,
"start": 37,
"type": "Identifier",
"type": "Identifier"
}
],
"end": 45,
"start": 39,
"end": 39,
"start": 33,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 48,
"start": 47,
"end": 42,
"start": 41,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 38,
"end": 32,
"name": "line",
"start": 34,
"start": 28,
"type": "Identifier"
},
"end": 49,
"start": 34,
"end": 43,
"start": 28,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 49,
"start": 14,
"end": 43,
"start": 8,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 49,
"end": 43,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 49,
"end": 43,
"start": 0
}

View File

@ -6,11 +6,11 @@ expression: actual
"body": [
{
"declaration": {
"end": 37,
"end": 31,
"id": {
"end": 14,
"end": 8,
"name": "mySketch",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
@ -18,49 +18,49 @@ expression: actual
{
"elements": [
{
"end": 33,
"end": 27,
"raw": "0",
"start": 32,
"start": 26,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 35,
"end": 29,
"raw": "0",
"start": 34,
"start": 28,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 36,
"start": 31,
"end": 30,
"start": 25,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
],
"callee": {
"end": 30,
"end": 24,
"name": "startSketchAt",
"start": 17,
"start": 11,
"type": "Identifier"
},
"end": 37,
"start": 17,
"end": 31,
"start": 11,
"type": "CallExpression",
"type": "CallExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 37,
"end": 31,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 37,
"end": 31,
"start": 0
}

View File

@ -6,19 +6,19 @@ expression: actual
"body": [
{
"declaration": {
"end": 36,
"end": 30,
"id": {
"end": 11,
"end": 5,
"name": "myVar",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"arguments": [
{
"end": 19,
"end": 13,
"raw": "5",
"start": 18,
"start": 12,
"type": "Literal",
"type": "Literal",
"value": 5.0
@ -27,61 +27,61 @@ expression: actual
"argument": {
"arguments": [
{
"end": 31,
"end": 25,
"raw": "5",
"start": 30,
"start": 24,
"type": "Literal",
"type": "Literal",
"value": 5.0
},
{
"end": 34,
"end": 28,
"raw": "4",
"start": 33,
"start": 27,
"type": "Literal",
"type": "Literal",
"value": 4.0
}
],
"callee": {
"end": 29,
"end": 23,
"name": "legLen",
"start": 23,
"start": 17,
"type": "Identifier"
},
"end": 35,
"start": 23,
"end": 29,
"start": 17,
"type": "CallExpression",
"type": "CallExpression"
},
"end": 35,
"end": 29,
"operator": "-",
"start": 22,
"start": 16,
"type": "UnaryExpression",
"type": "UnaryExpression"
}
],
"callee": {
"end": 17,
"end": 11,
"name": "min",
"start": 14,
"start": 8,
"type": "Identifier"
},
"end": 36,
"start": 14,
"end": 30,
"start": 8,
"type": "CallExpression",
"type": "CallExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 36,
"end": 30,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 36,
"end": 30,
"start": 0
}

View File

@ -6,11 +6,11 @@ expression: actual
"body": [
{
"declaration": {
"end": 132,
"end": 126,
"id": {
"end": 16,
"end": 10,
"name": "sketch001",
"start": 7,
"start": 1,
"type": "Identifier"
},
"init": {
@ -18,53 +18,53 @@ expression: actual
{
"arguments": [
{
"end": 37,
"end": 31,
"raw": "'XY'",
"start": 33,
"start": 27,
"type": "Literal",
"type": "Literal",
"value": "XY"
}
],
"callee": {
"end": 32,
"end": 26,
"name": "startSketchOn",
"start": 19,
"start": 13,
"type": "Identifier"
},
"end": 38,
"start": 19,
"end": 32,
"start": 13,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 131,
"start": 130,
"end": 125,
"start": 124,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 129,
"end": 123,
"name": "startProfileAt",
"start": 115,
"start": 109,
"type": "Identifier"
},
"end": 132,
"start": 115,
"end": 126,
"start": 109,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 132,
"end": 126,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"end": 52,
"start": 41,
"end": 46,
"start": 35,
"type": "NonCodeNode",
"value": {
"type": "blockComment",
@ -73,8 +73,8 @@ expression: actual
}
},
{
"end": 74,
"start": 55,
"end": 68,
"start": 49,
"type": "NonCodeNode",
"value": {
"type": "blockComment",
@ -83,8 +83,8 @@ expression: actual
}
},
{
"end": 98,
"start": 77,
"end": 92,
"start": 71,
"type": "NonCodeNode",
"value": {
"type": "blockComment",
@ -93,8 +93,8 @@ expression: actual
}
},
{
"end": 109,
"start": 101,
"end": 103,
"start": 95,
"type": "NonCodeNode",
"value": {
"type": "blockComment",
@ -106,20 +106,20 @@ expression: actual
},
"startNodes": []
},
"start": 19,
"start": 13,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 7,
"start": 1,
"type": "VariableDeclarator"
},
"end": 132,
"end": 126,
"kind": "const",
"start": 1,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 133,
"end": 127,
"start": 0
}

View File

@ -6,91 +6,91 @@ expression: actual
"body": [
{
"declaration": {
"end": 31,
"end": 25,
"id": {
"end": 11,
"end": 5,
"name": "my14",
"start": 7,
"start": 1,
"type": "Identifier"
},
"init": {
"end": 31,
"end": 25,
"left": {
"end": 19,
"end": 13,
"left": {
"end": 15,
"end": 9,
"raw": "4",
"start": 14,
"start": 8,
"type": "Literal",
"type": "Literal",
"value": 4.0
},
"operator": "^",
"right": {
"end": 19,
"end": 13,
"raw": "2",
"start": 18,
"start": 12,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
"start": 14,
"start": 8,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"operator": "-",
"right": {
"end": 31,
"end": 25,
"left": {
"end": 27,
"end": 21,
"left": {
"end": 23,
"end": 17,
"raw": "3",
"start": 22,
"start": 16,
"type": "Literal",
"type": "Literal",
"value": 3.0
},
"operator": "^",
"right": {
"end": 27,
"end": 21,
"raw": "2",
"start": 26,
"start": 20,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
"start": 22,
"start": 16,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"operator": "*",
"right": {
"end": 31,
"end": 25,
"raw": "2",
"start": 30,
"start": 24,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
"start": 22,
"start": 16,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 14,
"start": 8,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 7,
"start": 1,
"type": "VariableDeclarator"
},
"end": 31,
"end": 25,
"kind": "const",
"start": 1,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 32,
"end": 26,
"start": 0
}

View File

@ -6,79 +6,79 @@ expression: actual
"body": [
{
"declaration": {
"end": 74,
"end": 68,
"id": {
"end": 7,
"end": 1,
"name": "x",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"cond": {
"end": 17,
"end": 11,
"raw": "true",
"start": 13,
"start": 7,
"type": "Literal",
"type": "Literal",
"value": true
},
"digest": null,
"else_ifs": [],
"end": 74,
"end": 68,
"final_else": {
"body": [
{
"end": 64,
"end": 58,
"expression": {
"end": 64,
"end": 58,
"raw": "4",
"start": 63,
"start": 57,
"type": "Literal",
"type": "Literal",
"value": 4.0
},
"start": 63,
"start": 57,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"end": 73,
"start": 63
"end": 67,
"start": 57
},
"start": 10,
"start": 4,
"then_val": {
"body": [
{
"end": 33,
"end": 27,
"expression": {
"end": 33,
"end": 27,
"raw": "3",
"start": 32,
"start": 26,
"type": "Literal",
"type": "Literal",
"value": 3.0
},
"start": 32,
"start": 26,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"end": 42,
"start": 32
"end": 36,
"start": 26
},
"type": "IfExpression",
"type": "IfExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 74,
"end": 68,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 74,
"end": 68,
"start": 0
}

View File

@ -6,18 +6,18 @@ expression: actual
"body": [
{
"declaration": {
"end": 121,
"end": 115,
"id": {
"end": 7,
"end": 1,
"name": "x",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"cond": {
"end": 17,
"end": 11,
"raw": "true",
"start": 13,
"start": 7,
"type": "Literal",
"type": "Literal",
"value": true
@ -28,105 +28,105 @@ expression: actual
"cond": {
"arguments": [
{
"end": 63,
"end": 57,
"name": "radius",
"start": 57,
"start": 51,
"type": "Identifier",
"type": "Identifier"
}
],
"callee": {
"end": 56,
"end": 50,
"name": "func",
"start": 52,
"start": 46,
"type": "Identifier"
},
"end": 64,
"start": 52,
"end": 58,
"start": 46,
"type": "CallExpression",
"type": "CallExpression"
},
"digest": null,
"end": 90,
"start": 44,
"end": 84,
"start": 38,
"then_val": {
"body": [
{
"end": 80,
"end": 74,
"expression": {
"end": 80,
"end": 74,
"raw": "4",
"start": 79,
"start": 73,
"type": "Literal",
"type": "Literal",
"value": 4.0
},
"start": 79,
"start": 73,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"end": 89,
"start": 65
"end": 83,
"start": 59
},
"type": "ElseIf"
}
],
"end": 121,
"end": 115,
"final_else": {
"body": [
{
"end": 111,
"end": 105,
"expression": {
"end": 111,
"end": 105,
"raw": "5",
"start": 110,
"start": 104,
"type": "Literal",
"type": "Literal",
"value": 5.0
},
"start": 110,
"start": 104,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"end": 120,
"start": 110
"end": 114,
"start": 104
},
"start": 10,
"start": 4,
"then_val": {
"body": [
{
"end": 33,
"end": 27,
"expression": {
"end": 33,
"end": 27,
"raw": "3",
"start": 32,
"start": 26,
"type": "Literal",
"type": "Literal",
"value": 3.0
},
"start": 32,
"start": 26,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"end": 42,
"start": 32
"end": 36,
"start": 26
},
"type": "IfExpression",
"type": "IfExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 121,
"end": 115,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 121,
"end": 115,
"start": 0
}

View File

@ -6,85 +6,85 @@ expression: actual
"body": [
{
"declaration": {
"end": 42,
"end": 36,
"id": {
"end": 9,
"end": 3,
"name": "obj",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 42,
"end": 36,
"properties": [
{
"end": 30,
"end": 24,
"key": {
"end": 19,
"end": 13,
"name": "center",
"start": 13,
"start": 7,
"type": "Identifier"
},
"start": 13,
"start": 7,
"type": "ObjectProperty",
"value": {
"elements": [
{
"end": 25,
"end": 19,
"raw": "10",
"start": 23,
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 10.0
},
{
"end": 29,
"end": 23,
"raw": "10",
"start": 27,
"start": 21,
"type": "Literal",
"type": "Literal",
"value": 10.0
}
],
"end": 30,
"start": 22,
"end": 24,
"start": 16,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
},
{
"end": 41,
"end": 35,
"key": {
"end": 38,
"end": 32,
"name": "radius",
"start": 32,
"start": 26,
"type": "Identifier"
},
"start": 32,
"start": 26,
"type": "ObjectProperty",
"value": {
"end": 41,
"end": 35,
"raw": "5",
"start": 40,
"start": 34,
"type": "Literal",
"type": "Literal",
"value": 5.0
}
}
],
"start": 12,
"start": 6,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 42,
"end": 36,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 42,
"end": 36,
"start": 0
}

View File

@ -6,11 +6,11 @@ expression: actual
"body": [
{
"declaration": {
"end": 35,
"end": 29,
"id": {
"end": 11,
"end": 5,
"name": "myVar",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
@ -19,69 +19,69 @@ expression: actual
"argument": {
"arguments": [
{
"end": 27,
"end": 21,
"raw": "5",
"start": 26,
"start": 20,
"type": "Literal",
"type": "Literal",
"value": 5.0
},
{
"end": 30,
"end": 24,
"raw": "4",
"start": 29,
"start": 23,
"type": "Literal",
"type": "Literal",
"value": 4.0
}
],
"callee": {
"end": 25,
"end": 19,
"name": "legLen",
"start": 19,
"start": 13,
"type": "Identifier"
},
"end": 31,
"start": 19,
"end": 25,
"start": 13,
"type": "CallExpression",
"type": "CallExpression"
},
"end": 31,
"end": 25,
"operator": "-",
"start": 18,
"start": 12,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
{
"end": 34,
"end": 28,
"raw": "5",
"start": 33,
"start": 27,
"type": "Literal",
"type": "Literal",
"value": 5.0
}
],
"callee": {
"end": 17,
"end": 11,
"name": "min",
"start": 14,
"start": 8,
"type": "Identifier"
},
"end": 35,
"start": 14,
"end": 29,
"start": 8,
"type": "CallExpression",
"type": "CallExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 35,
"end": 29,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 35,
"end": 29,
"start": 0
}

View File

@ -6,82 +6,82 @@ expression: actual
"body": [
{
"declaration": {
"end": 36,
"end": 30,
"id": {
"end": 11,
"end": 5,
"name": "myVar",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"body": [
{
"end": 19,
"end": 13,
"left": {
"end": 15,
"end": 9,
"raw": "5",
"start": 14,
"start": 8,
"type": "Literal",
"type": "Literal",
"value": 5.0
},
"operator": "+",
"right": {
"end": 19,
"end": 13,
"raw": "6",
"start": 18,
"start": 12,
"type": "Literal",
"type": "Literal",
"value": 6.0
},
"start": 14,
"start": 8,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"arguments": [
{
"end": 32,
"end": 26,
"raw": "45",
"start": 30,
"start": 24,
"type": "Literal",
"type": "Literal",
"value": 45.0
},
{
"end": 35,
"start": 34,
"end": 29,
"start": 28,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 29,
"end": 23,
"name": "myFunc",
"start": 23,
"start": 17,
"type": "Identifier"
},
"end": 36,
"start": 23,
"end": 30,
"start": 17,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 36,
"start": 14,
"end": 30,
"start": 8,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 36,
"end": 30,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 36,
"end": 30,
"start": 0
}

View File

@ -6,51 +6,51 @@ expression: actual
"body": [
{
"declaration": {
"end": 27,
"end": 21,
"id": {
"end": 7,
"end": 1,
"name": "x",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 27,
"end": 21,
"left": {
"argument": {
"end": 15,
"end": 9,
"name": "leg2",
"start": 11,
"start": 5,
"type": "Identifier",
"type": "Identifier"
},
"end": 15,
"end": 9,
"operator": "-",
"start": 10,
"start": 4,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
"operator": "+",
"right": {
"end": 27,
"end": 21,
"name": "thickness",
"start": 18,
"start": 12,
"type": "Identifier",
"type": "Identifier"
},
"start": 10,
"start": 4,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 27,
"end": 21,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 27,
"end": 21,
"start": 0
}

View File

@ -6,38 +6,38 @@ expression: actual
"body": [
{
"declaration": {
"end": 11,
"end": 5,
"id": {
"end": 7,
"end": 1,
"name": "x",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 11,
"end": 5,
"raw": "1",
"start": 10,
"start": 4,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 11,
"end": 5,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 40,
"end": 34,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"end": 40,
"start": 11,
"end": 34,
"start": 5,
"type": "NonCodeNode",
"value": {
"type": "inlineComment",

View File

@ -6,21 +6,40 @@ expression: actual
"body": [
{
"declaration": {
"end": 26,
"end": 20,
"id": {
"end": 9,
"end": 3,
"name": "obj",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 26,
"end": 20,
"properties": [
{
"end": 12,
"key": {
"end": 9,
"name": "a",
"start": 8,
"type": "Identifier"
},
"start": 8,
"type": "ObjectProperty",
"value": {
"end": 12,
"raw": "1",
"start": 11,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 18,
"key": {
"end": 15,
"name": "a",
"name": "b",
"start": 14,
"type": "Identifier"
},
@ -28,41 +47,22 @@ expression: actual
"type": "ObjectProperty",
"value": {
"end": 18,
"raw": "1",
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 24,
"key": {
"end": 21,
"name": "b",
"start": 20,
"type": "Identifier"
},
"start": 20,
"type": "ObjectProperty",
"value": {
"end": 24,
"raw": "2",
"start": 23,
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 2.0
}
}
],
"start": 12,
"start": 6,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 26,
"end": 20,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
@ -70,19 +70,19 @@ expression: actual
},
{
"declaration": {
"end": 55,
"end": 43,
"id": {
"end": 43,
"end": 31,
"name": "height",
"start": 37,
"start": 25,
"type": "Identifier"
},
"init": {
"end": 55,
"end": 43,
"left": {
"end": 47,
"end": 35,
"raw": "1",
"start": 46,
"start": 34,
"type": "Literal",
"type": "Literal",
"value": 1.0
@ -90,39 +90,39 @@ expression: actual
"operator": "-",
"right": {
"computed": false,
"end": 55,
"end": 43,
"object": {
"end": 53,
"end": 41,
"name": "obj",
"start": 50,
"start": 38,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 55,
"end": 43,
"name": "a",
"start": 54,
"start": 42,
"type": "Identifier",
"type": "Identifier"
},
"start": 50,
"start": 38,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 46,
"start": 34,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 37,
"start": 25,
"type": "VariableDeclarator"
},
"end": 55,
"end": 43,
"kind": "const",
"start": 31,
"start": 25,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 55,
"end": 43,
"start": 0
}

View File

@ -6,21 +6,40 @@ expression: actual
"body": [
{
"declaration": {
"end": 26,
"end": 20,
"id": {
"end": 9,
"end": 3,
"name": "obj",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 26,
"end": 20,
"properties": [
{
"end": 12,
"key": {
"end": 9,
"name": "a",
"start": 8,
"type": "Identifier"
},
"start": 8,
"type": "ObjectProperty",
"value": {
"end": 12,
"raw": "1",
"start": 11,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 18,
"key": {
"end": 15,
"name": "a",
"name": "b",
"start": 14,
"type": "Identifier"
},
@ -28,41 +47,22 @@ expression: actual
"type": "ObjectProperty",
"value": {
"end": 18,
"raw": "1",
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 24,
"key": {
"end": 21,
"name": "b",
"start": 20,
"type": "Identifier"
},
"start": 20,
"type": "ObjectProperty",
"value": {
"end": 24,
"raw": "2",
"start": 23,
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 2.0
}
}
],
"start": 12,
"start": 6,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 26,
"end": 20,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
@ -70,19 +70,19 @@ expression: actual
},
{
"declaration": {
"end": 59,
"end": 47,
"id": {
"end": 44,
"end": 32,
"name": "height",
"start": 38,
"start": 26,
"type": "Identifier"
},
"init": {
"end": 59,
"end": 47,
"left": {
"end": 48,
"end": 36,
"raw": "1",
"start": 47,
"start": 35,
"type": "Literal",
"type": "Literal",
"value": 1.0
@ -90,40 +90,40 @@ expression: actual
"operator": "-",
"right": {
"computed": false,
"end": 59,
"end": 47,
"object": {
"end": 54,
"end": 42,
"name": "obj",
"start": 51,
"start": 39,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 58,
"end": 46,
"raw": "\"a\"",
"start": 55,
"start": 43,
"type": "Literal",
"type": "Literal",
"value": "a"
},
"start": 51,
"start": 39,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 47,
"start": 35,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 38,
"start": 26,
"type": "VariableDeclarator"
},
"end": 59,
"end": 47,
"kind": "const",
"start": 32,
"start": 26,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 59,
"end": 47,
"start": 0
}

View File

@ -6,21 +6,40 @@ expression: actual
"body": [
{
"declaration": {
"end": 26,
"end": 20,
"id": {
"end": 9,
"end": 3,
"name": "obj",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 26,
"end": 20,
"properties": [
{
"end": 12,
"key": {
"end": 9,
"name": "a",
"start": 8,
"type": "Identifier"
},
"start": 8,
"type": "ObjectProperty",
"value": {
"end": 12,
"raw": "1",
"start": 11,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 18,
"key": {
"end": 15,
"name": "a",
"name": "b",
"start": 14,
"type": "Identifier"
},
@ -28,41 +47,22 @@ expression: actual
"type": "ObjectProperty",
"value": {
"end": 18,
"raw": "1",
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 24,
"key": {
"end": 21,
"name": "b",
"start": 20,
"type": "Identifier"
},
"start": 20,
"type": "ObjectProperty",
"value": {
"end": 24,
"raw": "2",
"start": 23,
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 2.0
}
}
],
"start": 12,
"start": 6,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 26,
"end": 20,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
@ -70,60 +70,60 @@ expression: actual
},
{
"declaration": {
"end": 58,
"end": 46,
"id": {
"end": 43,
"end": 31,
"name": "height",
"start": 37,
"start": 25,
"type": "Identifier"
},
"init": {
"end": 58,
"end": 46,
"left": {
"computed": false,
"end": 54,
"end": 42,
"object": {
"end": 49,
"end": 37,
"name": "obj",
"start": 46,
"start": 34,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 53,
"end": 41,
"raw": "\"a\"",
"start": 50,
"start": 38,
"type": "Literal",
"type": "Literal",
"value": "a"
},
"start": 46,
"start": 34,
"type": "MemberExpression",
"type": "MemberExpression"
},
"operator": "-",
"right": {
"end": 58,
"end": 46,
"raw": "1",
"start": 57,
"start": 45,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 46,
"start": 34,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 37,
"start": 25,
"type": "VariableDeclarator"
},
"end": 58,
"end": 46,
"kind": "const",
"start": 31,
"start": 25,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 58,
"end": 46,
"start": 0
}

View File

@ -6,21 +6,40 @@ expression: actual
"body": [
{
"declaration": {
"end": 26,
"end": 20,
"id": {
"end": 9,
"end": 3,
"name": "obj",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 26,
"end": 20,
"properties": [
{
"end": 12,
"key": {
"end": 9,
"name": "a",
"start": 8,
"type": "Identifier"
},
"start": 8,
"type": "ObjectProperty",
"value": {
"end": 12,
"raw": "1",
"start": 11,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 18,
"key": {
"end": 15,
"name": "a",
"name": "b",
"start": 14,
"type": "Identifier"
},
@ -28,41 +47,22 @@ expression: actual
"type": "ObjectProperty",
"value": {
"end": 18,
"raw": "1",
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 24,
"key": {
"end": 21,
"name": "b",
"start": 20,
"type": "Identifier"
},
"start": 20,
"type": "ObjectProperty",
"value": {
"end": 24,
"raw": "2",
"start": 23,
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 2.0
}
}
],
"start": 12,
"start": 6,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 26,
"end": 20,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
@ -70,21 +70,21 @@ expression: actual
},
{
"declaration": {
"end": 63,
"end": 51,
"id": {
"end": 43,
"end": 31,
"name": "height",
"start": 37,
"start": 25,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 59,
"end": 47,
"left": {
"end": 48,
"end": 36,
"raw": "1",
"start": 47,
"start": 35,
"type": "Literal",
"type": "Literal",
"value": 1.0
@ -92,54 +92,54 @@ expression: actual
"operator": "-",
"right": {
"computed": false,
"end": 59,
"end": 47,
"object": {
"end": 54,
"end": 42,
"name": "obj",
"start": 51,
"start": 39,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 58,
"end": 46,
"raw": "\"a\"",
"start": 55,
"start": 43,
"type": "Literal",
"type": "Literal",
"value": "a"
},
"start": 51,
"start": 39,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 47,
"start": 35,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 62,
"end": 50,
"raw": "0",
"start": 61,
"start": 49,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 63,
"start": 46,
"end": 51,
"start": 34,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 37,
"start": 25,
"type": "VariableDeclarator"
},
"end": 63,
"end": 51,
"kind": "const",
"start": 31,
"start": 25,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 63,
"end": 51,
"start": 0
}

View File

@ -0,0 +1,75 @@
---
source: kcl/src/parsing/parser.rs
expression: actual
snapshot_kind: text
---
{
"body": [
{
"declaration": {
"end": 25,
"id": {
"end": 6,
"name": "foo",
"start": 3,
"type": "Identifier"
},
"init": {
"body": {
"body": [
{
"argument": {
"end": 23,
"raw": "1",
"start": 22,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"end": 23,
"start": 15,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 25,
"start": 13
},
"end": 25,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 8,
"name": "x",
"start": 7,
"type": "Identifier"
}
},
{
"type": "Parameter",
"identifier": {
"end": 11,
"name": "y",
"start": 10,
"type": "Identifier"
}
}
],
"start": 6,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
"start": 3,
"type": "VariableDeclarator"
},
"end": 25,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 25,
"start": 0
}

View File

@ -0,0 +1,76 @@
---
source: kcl/src/parsing/parser.rs
expression: actual
snapshot_kind: text
---
{
"body": [
{
"declaration": {
"end": 26,
"id": {
"end": 6,
"name": "foo",
"start": 3,
"type": "Identifier"
},
"init": {
"body": {
"body": [
{
"argument": {
"end": 24,
"raw": "1",
"start": 23,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"end": 24,
"start": 16,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 26,
"start": 14
},
"end": 26,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 9,
"name": "x",
"start": 8,
"type": "Identifier"
},
"labeled": false
},
{
"type": "Parameter",
"identifier": {
"end": 12,
"name": "y",
"start": 11,
"type": "Identifier"
}
}
],
"start": 6,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
"start": 3,
"type": "VariableDeclarator"
},
"end": 26,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 26,
"start": 0
}

View File

@ -6,21 +6,40 @@ expression: actual
"body": [
{
"declaration": {
"end": 26,
"end": 20,
"id": {
"end": 9,
"end": 3,
"name": "obj",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 26,
"end": 20,
"properties": [
{
"end": 12,
"key": {
"end": 9,
"name": "a",
"start": 8,
"type": "Identifier"
},
"start": 8,
"type": "ObjectProperty",
"value": {
"end": 12,
"raw": "1",
"start": 11,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 18,
"key": {
"end": 15,
"name": "a",
"name": "b",
"start": 14,
"type": "Identifier"
},
@ -28,41 +47,22 @@ expression: actual
"type": "ObjectProperty",
"value": {
"end": 18,
"raw": "1",
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 24,
"key": {
"end": 21,
"name": "b",
"start": 20,
"type": "Identifier"
},
"start": 20,
"type": "ObjectProperty",
"value": {
"end": 24,
"raw": "2",
"start": 23,
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 2.0
}
}
],
"start": 12,
"start": 6,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 26,
"end": 20,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
@ -70,76 +70,76 @@ expression: actual
},
{
"declaration": {
"end": 63,
"end": 51,
"id": {
"end": 43,
"end": 31,
"name": "height",
"start": 37,
"start": 25,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 59,
"end": 47,
"left": {
"computed": false,
"end": 55,
"end": 43,
"object": {
"end": 50,
"end": 38,
"name": "obj",
"start": 47,
"start": 35,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 54,
"end": 42,
"raw": "\"a\"",
"start": 51,
"start": 39,
"type": "Literal",
"type": "Literal",
"value": "a"
},
"start": 47,
"start": 35,
"type": "MemberExpression",
"type": "MemberExpression"
},
"operator": "-",
"right": {
"end": 59,
"end": 47,
"raw": "1",
"start": 58,
"start": 46,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 47,
"start": 35,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 62,
"end": 50,
"raw": "0",
"start": 61,
"start": 49,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 63,
"start": 46,
"end": 51,
"start": 34,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 37,
"start": 25,
"type": "VariableDeclarator"
},
"end": 63,
"end": 51,
"kind": "const",
"start": 31,
"start": 25,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 63,
"end": 51,
"start": 0
}

View File

@ -6,21 +6,40 @@ expression: actual
"body": [
{
"declaration": {
"end": 26,
"end": 20,
"id": {
"end": 9,
"end": 3,
"name": "obj",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 26,
"end": 20,
"properties": [
{
"end": 12,
"key": {
"end": 9,
"name": "a",
"start": 8,
"type": "Identifier"
},
"start": 8,
"type": "ObjectProperty",
"value": {
"end": 12,
"raw": "1",
"start": 11,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 18,
"key": {
"end": 15,
"name": "a",
"name": "b",
"start": 14,
"type": "Identifier"
},
@ -28,41 +47,22 @@ expression: actual
"type": "ObjectProperty",
"value": {
"end": 18,
"raw": "1",
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 24,
"key": {
"end": 21,
"name": "b",
"start": 20,
"type": "Identifier"
},
"start": 20,
"type": "ObjectProperty",
"value": {
"end": 24,
"raw": "2",
"start": 23,
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 2.0
}
}
],
"start": 12,
"start": 6,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 26,
"end": 20,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
@ -70,76 +70,76 @@ expression: actual
},
{
"declaration": {
"end": 62,
"end": 50,
"id": {
"end": 43,
"end": 31,
"name": "height",
"start": 37,
"start": 25,
"type": "Identifier"
},
"init": {
"elements": [
{
"end": 58,
"end": 46,
"left": {
"computed": false,
"end": 55,
"end": 43,
"object": {
"end": 50,
"end": 38,
"name": "obj",
"start": 47,
"start": 35,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 54,
"end": 42,
"raw": "\"a\"",
"start": 51,
"start": 39,
"type": "Literal",
"type": "Literal",
"value": "a"
},
"start": 47,
"start": 35,
"type": "MemberExpression",
"type": "MemberExpression"
},
"operator": "-",
"right": {
"end": 58,
"end": 46,
"raw": "1",
"start": 57,
"start": 45,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 47,
"start": 35,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 61,
"end": 49,
"raw": "0",
"start": 60,
"start": 48,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 62,
"start": 46,
"end": 50,
"start": 34,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 37,
"start": 25,
"type": "VariableDeclarator"
},
"end": 62,
"end": 50,
"kind": "const",
"start": 31,
"start": 25,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 62,
"end": 50,
"start": 0
}

View File

@ -6,19 +6,19 @@ expression: actual
"body": [
{
"declaration": {
"end": 24,
"end": 18,
"id": {
"end": 12,
"end": 6,
"name": "height",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 24,
"end": 18,
"left": {
"end": 16,
"end": 10,
"raw": "1",
"start": 15,
"start": 9,
"type": "Literal",
"type": "Literal",
"value": 1.0
@ -26,39 +26,39 @@ expression: actual
"operator": "-",
"right": {
"computed": false,
"end": 24,
"end": 18,
"object": {
"end": 22,
"end": 16,
"name": "obj",
"start": 19,
"start": 13,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 24,
"end": 18,
"name": "a",
"start": 23,
"start": 17,
"type": "Identifier",
"type": "Identifier"
},
"start": 19,
"start": 13,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 15,
"start": 9,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 24,
"end": 18,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 24,
"end": 18,
"start": 0
}

View File

@ -6,61 +6,61 @@ expression: actual
"body": [
{
"declaration": {
"end": 21,
"end": 15,
"id": {
"end": 9,
"end": 3,
"name": "six",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 21,
"end": 15,
"left": {
"end": 17,
"end": 11,
"left": {
"end": 13,
"end": 7,
"raw": "1",
"start": 12,
"start": 6,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"operator": "+",
"right": {
"end": 17,
"end": 11,
"raw": "2",
"start": 16,
"start": 10,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
"start": 12,
"start": 6,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"operator": "+",
"right": {
"end": 21,
"end": 15,
"raw": "3",
"start": 20,
"start": 14,
"type": "Literal",
"type": "Literal",
"value": 3.0
},
"start": 12,
"start": 6,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 21,
"end": 15,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 21,
"end": 15,
"start": 0
}

View File

@ -6,61 +6,61 @@ expression: actual
"body": [
{
"declaration": {
"end": 22,
"end": 16,
"id": {
"end": 10,
"end": 4,
"name": "five",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 22,
"end": 16,
"left": {
"end": 18,
"end": 12,
"left": {
"end": 14,
"end": 8,
"raw": "3",
"start": 13,
"start": 7,
"type": "Literal",
"type": "Literal",
"value": 3.0
},
"operator": "*",
"right": {
"end": 18,
"end": 12,
"raw": "1",
"start": 17,
"start": 11,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 13,
"start": 7,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"operator": "+",
"right": {
"end": 22,
"end": 16,
"raw": "2",
"start": 21,
"start": 15,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
"start": 13,
"start": 7,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 22,
"end": 16,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 22,
"end": 16,
"start": 0
}

View File

@ -6,61 +6,61 @@ expression: actual
"body": [
{
"declaration": {
"end": 30,
"end": 24,
"id": {
"end": 12,
"end": 6,
"name": "height",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"elements": [
{
"computed": false,
"end": 25,
"end": 19,
"object": {
"end": 20,
"end": 14,
"name": "obj",
"start": 17,
"start": 11,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 24,
"end": 18,
"raw": "\"a\"",
"start": 21,
"start": 15,
"type": "Literal",
"type": "Literal",
"value": "a"
},
"start": 17,
"start": 11,
"type": "MemberExpression",
"type": "MemberExpression"
},
{
"end": 28,
"end": 22,
"raw": "0",
"start": 27,
"start": 21,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 30,
"start": 15,
"end": 24,
"start": 9,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 30,
"end": 24,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 30,
"end": 24,
"start": 0
}

View File

@ -6,21 +6,40 @@ expression: actual
"body": [
{
"declaration": {
"end": 26,
"end": 20,
"id": {
"end": 9,
"end": 3,
"name": "obj",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"end": 26,
"end": 20,
"properties": [
{
"end": 12,
"key": {
"end": 9,
"name": "a",
"start": 8,
"type": "Identifier"
},
"start": 8,
"type": "ObjectProperty",
"value": {
"end": 12,
"raw": "1",
"start": 11,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 18,
"key": {
"end": 15,
"name": "a",
"name": "b",
"start": 14,
"type": "Identifier"
},
@ -28,41 +47,22 @@ expression: actual
"type": "ObjectProperty",
"value": {
"end": 18,
"raw": "1",
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
},
{
"end": 24,
"key": {
"end": 21,
"name": "b",
"start": 20,
"type": "Identifier"
},
"start": 20,
"type": "ObjectProperty",
"value": {
"end": 24,
"raw": "2",
"start": 23,
"start": 17,
"type": "Literal",
"type": "Literal",
"value": 2.0
}
}
],
"start": 12,
"start": 6,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 26,
"end": 20,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
@ -70,45 +70,45 @@ expression: actual
},
{
"declaration": {
"end": 54,
"end": 42,
"id": {
"end": 43,
"end": 31,
"name": "height",
"start": 37,
"start": 25,
"type": "Identifier"
},
"init": {
"computed": false,
"end": 54,
"end": 42,
"object": {
"end": 49,
"end": 37,
"name": "obj",
"start": 46,
"start": 34,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 53,
"end": 41,
"raw": "\"a\"",
"start": 50,
"start": 38,
"type": "Literal",
"type": "Literal",
"value": "a"
},
"start": 46,
"start": 34,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 37,
"start": 25,
"type": "VariableDeclarator"
},
"end": 54,
"end": 42,
"kind": "const",
"start": 31,
"start": 25,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 54,
"end": 42,
"start": 0
}

View File

@ -6,59 +6,59 @@ expression: actual
"body": [
{
"declaration": {
"end": 27,
"end": 21,
"id": {
"end": 10,
"end": 4,
"name": "prop",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"computed": true,
"end": 27,
"end": 21,
"object": {
"computed": false,
"end": 22,
"end": 16,
"object": {
"end": 15,
"end": 9,
"name": "yo",
"start": 13,
"start": 7,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 21,
"end": 15,
"raw": "\"one\"",
"start": 16,
"start": 10,
"type": "Literal",
"type": "Literal",
"value": "one"
},
"start": 13,
"start": 7,
"type": "MemberExpression",
"type": "MemberExpression"
},
"property": {
"end": 26,
"end": 20,
"name": "two",
"start": 23,
"start": 17,
"type": "Identifier",
"type": "Identifier"
},
"start": 13,
"start": 7,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 27,
"end": 21,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 27,
"end": 21,
"start": 0
}

View File

@ -6,44 +6,44 @@ expression: actual
"body": [
{
"declaration": {
"end": 17,
"end": 11,
"id": {
"end": 9,
"end": 3,
"name": "pt1",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"computed": true,
"end": 17,
"end": 11,
"object": {
"end": 14,
"end": 8,
"name": "b1",
"start": 12,
"start": 6,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 16,
"end": 10,
"name": "x",
"start": 15,
"start": 9,
"type": "Identifier",
"type": "Identifier"
},
"start": 12,
"start": 6,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 17,
"end": 11,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 17,
"end": 11,
"start": 0
}

View File

@ -6,86 +6,86 @@ expression: actual
"body": [
{
"declaration": {
"end": 34,
"end": 28,
"id": {
"end": 10,
"end": 4,
"name": "prop",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"computed": false,
"end": 34,
"end": 28,
"object": {
"computed": false,
"end": 29,
"end": 23,
"object": {
"computed": false,
"end": 23,
"end": 17,
"object": {
"computed": false,
"end": 19,
"end": 13,
"object": {
"end": 15,
"end": 9,
"name": "yo",
"start": 13,
"start": 7,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 19,
"end": 13,
"name": "one",
"start": 16,
"start": 10,
"type": "Identifier",
"type": "Identifier"
},
"start": 13,
"start": 7,
"type": "MemberExpression",
"type": "MemberExpression"
},
"property": {
"end": 23,
"end": 17,
"name": "two",
"start": 20,
"start": 14,
"type": "Identifier",
"type": "Identifier"
},
"start": 13,
"start": 7,
"type": "MemberExpression",
"type": "MemberExpression"
},
"property": {
"end": 29,
"end": 23,
"name": "three",
"start": 24,
"start": 18,
"type": "Identifier",
"type": "Identifier"
},
"start": 13,
"start": 7,
"type": "MemberExpression",
"type": "MemberExpression"
},
"property": {
"end": 34,
"end": 28,
"name": "four",
"start": 30,
"start": 24,
"type": "Identifier",
"type": "Identifier"
},
"start": 13,
"start": 7,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 34,
"end": 28,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 34,
"end": 28,
"start": 0
}

View File

@ -6,45 +6,45 @@ expression: actual
"body": [
{
"declaration": {
"end": 17,
"end": 11,
"id": {
"end": 9,
"end": 3,
"name": "pt1",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"computed": false,
"end": 17,
"end": 11,
"object": {
"end": 14,
"end": 8,
"name": "b1",
"start": 12,
"start": 6,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 16,
"end": 10,
"raw": "0",
"start": 15,
"start": 9,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
"start": 12,
"start": 6,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 17,
"end": 11,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 17,
"end": 11,
"start": 0
}

View File

@ -6,45 +6,45 @@ expression: actual
"body": [
{
"declaration": {
"end": 22,
"end": 16,
"id": {
"end": 9,
"end": 3,
"name": "pt1",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"computed": false,
"end": 22,
"end": 16,
"object": {
"end": 14,
"end": 8,
"name": "b1",
"start": 12,
"start": 6,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 21,
"end": 15,
"raw": "'zero'",
"start": 15,
"start": 9,
"type": "Literal",
"type": "Literal",
"value": "zero"
},
"start": 12,
"start": 6,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 22,
"end": 16,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 22,
"end": 16,
"start": 0
}

View File

@ -6,44 +6,44 @@ expression: actual
"body": [
{
"declaration": {
"end": 19,
"end": 13,
"id": {
"end": 9,
"end": 3,
"name": "pt1",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"computed": false,
"end": 19,
"end": 13,
"object": {
"end": 14,
"end": 8,
"name": "b1",
"start": 12,
"start": 6,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"end": 19,
"end": 13,
"name": "zero",
"start": 15,
"start": 9,
"type": "Identifier",
"type": "Identifier"
},
"start": 12,
"start": 6,
"type": "MemberExpression",
"type": "MemberExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 19,
"end": 13,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 19,
"end": 13,
"start": 0
}

View File

@ -6,44 +6,44 @@ expression: actual
"body": [
{
"declaration": {
"end": 29,
"end": 23,
"id": {
"end": 8,
"end": 2,
"name": "sg",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
"arguments": [
{
"end": 28,
"end": 22,
"name": "pos",
"start": 25,
"start": 19,
"type": "Identifier",
"type": "Identifier"
}
],
"callee": {
"end": 24,
"end": 18,
"name": "startSketchAt",
"start": 11,
"start": 5,
"type": "Identifier"
},
"end": 29,
"start": 11,
"end": 23,
"start": 5,
"type": "CallExpression",
"type": "CallExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 29,
"end": 23,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 29,
"end": 23,
"start": 0
}

View File

@ -6,11 +6,11 @@ expression: actual
"body": [
{
"declaration": {
"end": 53,
"end": 47,
"id": {
"end": 8,
"end": 2,
"name": "sg",
"start": 6,
"start": 0,
"type": "Identifier"
},
"init": {
@ -18,21 +18,21 @@ expression: actual
{
"arguments": [
{
"end": 28,
"end": 22,
"name": "pos",
"start": 25,
"start": 19,
"type": "Identifier",
"type": "Identifier"
}
],
"callee": {
"end": 24,
"end": 18,
"name": "startSketchAt",
"start": 11,
"start": 5,
"type": "Identifier"
},
"end": 29,
"start": 11,
"end": 23,
"start": 5,
"type": "CallExpression",
"type": "CallExpression"
},
@ -41,67 +41,67 @@ expression: actual
{
"elements": [
{
"end": 40,
"end": 34,
"raw": "0",
"start": 39,
"start": 33,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"argument": {
"end": 48,
"end": 42,
"name": "scale",
"start": 43,
"start": 37,
"type": "Identifier",
"type": "Identifier"
},
"end": 48,
"end": 42,
"operator": "-",
"start": 42,
"start": 36,
"type": "UnaryExpression",
"type": "UnaryExpression"
}
],
"end": 49,
"start": 38,
"end": 43,
"start": 32,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 52,
"start": 51,
"end": 46,
"start": 45,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 37,
"end": 31,
"name": "line",
"start": 33,
"start": 27,
"type": "Identifier"
},
"end": 53,
"start": 33,
"end": 47,
"start": 27,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 53,
"start": 11,
"end": 47,
"start": 5,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 6,
"start": 0,
"type": "VariableDeclarator"
},
"end": 53,
"end": 47,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 53,
"end": 47,
"start": 0
}

View File

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

View File

@ -42,14 +42,19 @@ impl Arg {
}
}
#[derive(Debug, Clone, Default)]
pub struct KwArgs {
/// Unlabeled keyword args. Currently only the first arg can be unlabeled.
pub unlabeled: Option<Arg>,
/// Labeled args.
pub labeled: HashMap<String, Arg>,
}
#[derive(Debug, Clone)]
pub struct Args {
/// Positional args.
pub args: Vec<Arg>,
/// Keyword args.
pub kw_args: HashMap<String, Arg>,
/// Unlabeled keyword args. Currently only the first arg can be unlabeled.
pub unlabeled_kw_arg: Option<Arg>,
pub kw_args: KwArgs,
pub source_range: SourceRange,
pub ctx: ExecutorContext,
}
@ -59,23 +64,16 @@ impl Args {
Self {
args,
kw_args: Default::default(),
unlabeled_kw_arg: Default::default(),
source_range,
ctx,
}
}
/// Collect the given keyword arguments.
pub fn new_kw(
kw_args: HashMap<String, Arg>,
unlabeled_kw_arg: Option<Arg>,
source_range: SourceRange,
ctx: ExecutorContext,
) -> Self {
pub fn new_kw(kw_args: KwArgs, source_range: SourceRange, ctx: ExecutorContext) -> Self {
Self {
args: Default::default(),
kw_args,
unlabeled_kw_arg,
source_range,
ctx,
}
@ -88,7 +86,6 @@ impl Args {
Ok(Self {
args: Vec::new(),
kw_args: Default::default(),
unlabeled_kw_arg: Default::default(),
source_range: SourceRange::default(),
ctx: ExecutorContext {
engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)),
@ -105,7 +102,10 @@ impl Args {
where
T: FromKclValue<'a>,
{
self.kw_args.get(label).and_then(|arg| T::from_kcl_val(&arg.value))
self.kw_args
.labeled
.get(label)
.and_then(|arg| T::from_kcl_val(&arg.value))
}
/// Get a keyword argument. If not set, returns Err.
@ -126,7 +126,7 @@ impl Args {
where
T: FromKclValue<'a>,
{
let Some(ref arg) = self.unlabeled_kw_arg else {
let Some(ref arg) = self.kw_args.unlabeled else {
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![self.source_range],
message: format!("This function requires a value for the special unlabeled first parameter, '{label}'"),

View File

@ -659,7 +659,11 @@ impl FunctionExpression {
impl Parameter {
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
let mut result = self.identifier.name.clone();
let mut result = format!(
"{}{}",
if self.labeled { "" } else { "@" },
self.identifier.name.clone()
);
if let Some(ty) = &self.type_ {
result += ": ";
result += &ty.recast(options, indentation_level);

View File

@ -27,8 +27,7 @@ description: Result of parsing import_constant.kcl
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement",
"visibility": "default"
"type": "ImportStatement"
}
],
"end": 40,

View File

@ -27,8 +27,7 @@ description: Result of parsing import_cycle1.kcl
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement",
"visibility": "default"
"type": "ImportStatement"
},
{
"declaration": {

View File

@ -27,8 +27,7 @@ description: Result of parsing import_export.kcl
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement",
"visibility": "default"
"type": "ImportStatement"
}
],
"end": 33,

View File

@ -15,8 +15,7 @@ description: Result of parsing import_glob.kcl
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement",
"visibility": "default"
"type": "ImportStatement"
}
],
"end": 36,

View File

@ -27,8 +27,7 @@ description: Result of parsing import_side_effect.kcl
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement",
"visibility": "default"
"type": "ImportStatement"
}
],
"end": 41,

View File

@ -0,0 +1,138 @@
---
source: kcl/src/simulation_tests.rs
description: Result of parsing kw_fn.kcl
snapshot_kind: text
---
{
"Ok": {
"body": [
{
"declaration": {
"end": 35,
"id": {
"end": 12,
"name": "increment",
"start": 3,
"type": "Identifier"
},
"init": {
"body": {
"body": [
{
"argument": {
"end": 33,
"left": {
"end": 29,
"name": "x",
"start": 28,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 33,
"raw": "1",
"start": 32,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 28,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"end": 33,
"start": 21,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 35,
"start": 17
},
"end": 35,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 15,
"name": "x",
"start": 14,
"type": "Identifier"
},
"labeled": false
}
],
"start": 12,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
"start": 3,
"type": "VariableDeclarator"
},
"end": 35,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"declaration": {
"end": 55,
"id": {
"end": 40,
"name": "two",
"start": 37,
"type": "Identifier"
},
"init": {
"arguments": [
{
"end": 54,
"raw": "1",
"start": 53,
"type": "Literal",
"type": "Literal",
"value": 1.0
}
],
"callee": {
"end": 52,
"name": "increment",
"start": 43,
"type": "Identifier"
},
"end": 55,
"start": 43,
"type": "CallExpression",
"type": "CallExpression"
},
"start": 37,
"type": "VariableDeclarator"
},
"end": 55,
"kind": "const",
"start": 37,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 56,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"end": 37,
"start": 35,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,5 @@
fn increment(@x) {
return x + 1
}
two = increment(1)

View File

@ -0,0 +1,150 @@
---
source: kcl/src/simulation_tests.rs
description: Program memory after executing kw_fn.kcl
snapshot_kind: text
---
{
"environments": [
{
"bindings": {
"HALF_TURN": {
"type": "Number",
"value": 180.0,
"__meta": []
},
"QUARTER_TURN": {
"type": "Number",
"value": 90.0,
"__meta": []
},
"THREE_QUARTER_TURN": {
"type": "Number",
"value": 270.0,
"__meta": []
},
"ZERO": {
"type": "Number",
"value": 0.0,
"__meta": []
},
"increment": {
"type": "Function",
"expression": {
"body": {
"body": [
{
"argument": {
"end": 33,
"left": {
"end": 29,
"name": "x",
"start": 28,
"type": "Identifier",
"type": "Identifier"
},
"operator": "+",
"right": {
"end": 33,
"raw": "1",
"start": 32,
"type": "Literal",
"type": "Literal",
"value": 1.0
},
"start": 28,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"end": 33,
"start": 21,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 35,
"start": 17
},
"end": 35,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 15,
"name": "x",
"start": 14,
"type": "Identifier"
},
"labeled": false
}
],
"start": 12,
"type": "FunctionExpression"
},
"memory": {
"environments": [
{
"bindings": {
"HALF_TURN": {
"type": "Number",
"value": 180.0,
"__meta": []
},
"QUARTER_TURN": {
"type": "Number",
"value": 90.0,
"__meta": []
},
"THREE_QUARTER_TURN": {
"type": "Number",
"value": 270.0,
"__meta": []
},
"ZERO": {
"type": "Number",
"value": 0.0,
"__meta": []
}
},
"parent": null
}
],
"currentEnv": 0,
"return": null
},
"__meta": [
{
"sourceRange": [
12,
35,
0
]
}
]
},
"two": {
"type": "Number",
"value": 2.0,
"__meta": [
{
"sourceRange": [
53,
54,
0
]
},
{
"sourceRange": [
32,
33,
0
]
}
]
}
},
"parent": null
}
],
"currentEnv": 0,
"return": null
}

View File

@ -4,7 +4,9 @@ use std::{str::FromStr, sync::Arc};
use futures::stream::TryStreamExt;
use gloo_utils::format::JsValueSerdeExt;
use kcl_lib::{CacheInformation, CoreDump, EngineManager, ExecState, ModuleId, OldAstState, Program};
use kcl_lib::{
exec::IdGenerator, CacheInformation, CoreDump, EngineManager, ExecState, ModuleId, OldAstState, Program,
};
use tokio::sync::RwLock;
use tower_lsp::{LspService, Server};
use wasm_bindgen::prelude::*;
@ -22,10 +24,40 @@ async fn read_old_ast_memory() -> Option<OldAstState> {
lock.clone()
}
async fn bust_cache() {
// We don't use the cache in mock mode.
let mut current_cache = OLD_AST_MEMORY.write().await;
// Set the cache to None.
*current_cache = None;
}
// wasm_bindgen wrapper for clearing the scene and busting the cache.
#[wasm_bindgen]
pub async fn clear_scene_and_bust_cache(
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
) -> Result<(), String> {
console_error_panic_hook::set_once();
// Bust the cache.
bust_cache().await;
let engine = kcl_lib::wasm_engine::EngineConnection::new(engine_manager)
.await
.map_err(|e| format!("{:?}", e))?;
let mut id_generator: IdGenerator = Default::default();
engine
.clear_scene(&mut id_generator, Default::default())
.await
.map_err(|e| e.to_string())?;
Ok(())
}
// wasm_bindgen wrapper for execute
#[wasm_bindgen]
pub async fn execute_wasm(
program_str: &str,
program_ast_json: &str,
program_memory_override_str: &str,
units: &str,
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
@ -33,7 +65,7 @@ pub async fn execute_wasm(
) -> Result<JsValue, String> {
console_error_panic_hook::set_once();
let program: Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| e.to_string())?;
let program_memory_override: Option<kcl_lib::exec::ProgramMemory> =
serde_json::from_str(program_memory_override_str).map_err(|e| e.to_string())?;
@ -74,10 +106,7 @@ pub async fn execute_wasm(
.map_err(String::from)
{
if !is_mock {
// We don't use the cache in mock mode.
let mut current_cache = OLD_AST_MEMORY.write().await;
// Set the cache to None.
*current_cache = None;
bust_cache().await;
}
// Throw the error.
@ -106,10 +135,10 @@ pub async fn execute_wasm(
// wasm_bindgen wrapper for execute
#[wasm_bindgen]
pub async fn kcl_lint(program_str: &str) -> Result<JsValue, JsValue> {
pub async fn kcl_lint(program_ast_json: &str) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();
let program: Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| e.to_string())?;
let mut findings = vec![];
for discovered_finding in program.lint_all().into_iter().flatten() {
findings.push(discovered_finding);
@ -160,14 +189,14 @@ pub async fn modify_grid(
#[wasm_bindgen]
pub async fn modify_ast_for_sketch_wasm(
manager: kcl_lib::wasm_engine::EngineCommandManager,
program_str: &str,
program_ast_json: &str,
sketch_name: &str,
plane_type: &str,
sketch_id: &str,
) -> Result<JsValue, String> {
console_error_panic_hook::set_once();
let mut program: Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
let mut program: Program = serde_json::from_str(program_ast_json).map_err(|e| e.to_string())?;
let plane: kcl_lib::exec::PlaneType = serde_json::from_str(plane_type).map_err(|e| e.to_string())?;
@ -214,10 +243,10 @@ pub fn deserialize_files(data: &[u8]) -> Result<JsValue, JsError> {
}
#[wasm_bindgen]
pub fn parse_wasm(js: &str) -> Result<JsValue, String> {
pub fn parse_wasm(kcl_program_source: &str) -> Result<JsValue, String> {
console_error_panic_hook::set_once();
let (program, errs) = Program::parse(js).map_err(String::from)?;
let (program, errs) = Program::parse(kcl_program_source).map_err(String::from)?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead.
JsValue::from_serde(&(program, errs)).map_err(|e| e.to_string())

View File

@ -2704,10 +2704,10 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6"
integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==
"@types/react-dom@^18.0.0", "@types/react-dom@^18.2.25":
version "18.3.0"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0"
integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==
"@types/react-dom@^18.0.0", "@types/react-dom@^18.3.1":
version "18.3.1"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07"
integrity sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==
dependencies:
"@types/react" "*"