Compare commits
52 Commits
v0.11.0
...
achalmers/
Author | SHA1 | Date | |
---|---|---|---|
dec538590a | |||
9523eef52c | |||
20ae39dec3 | |||
8ebba16796 | |||
038de47e5d | |||
61563bee97 | |||
3b3b5371eb | |||
3ea77f8e1e | |||
4fa7c07e54 | |||
c66a96a333 | |||
4196ff91ac | |||
cf66b93963 | |||
0b0219b810 | |||
36c7fcf6d7 | |||
023c3cbb90 | |||
387f7e0912 | |||
9b55b1fd12 | |||
4b6662169c | |||
d36abfcb3d | |||
9002ae9efb | |||
4deea25394 | |||
b5940d2cb7 | |||
932b467c1e | |||
7c7f5c81c4 | |||
066b4f3e06 | |||
c6067bfc7a | |||
2018f0d517 | |||
74aae3d15f | |||
812f419e75 | |||
5ec8cc69db | |||
a5302b6e0e | |||
2114cc0d94 | |||
2471ce1aba | |||
35772475b9 | |||
86c592c0f6 | |||
0e98973cfa | |||
7dd16fe6de | |||
478b636049 | |||
c779311a56 | |||
ca02ec1151 | |||
b271d5060e | |||
19f11fe55a | |||
f6f1574982 | |||
6dc4fbc808 | |||
8843d02380 | |||
3578ec07e6 | |||
db35f73e41 | |||
5cfc2b7941 | |||
318e4a0cc7 | |||
1e23be8f08 | |||
ef547e7db8 | |||
71b48bbd89 |
58
.github/workflows/ci.yml
vendored
58
.github/workflows/ci.yml
vendored
@ -17,22 +17,23 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
jobs:
|
jobs:
|
||||||
check-format:
|
check-format:
|
||||||
runs-on: 'ubuntu-20.04'
|
runs-on: 'ubuntu-latest'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn fmt-check
|
- run: yarn fmt-check
|
||||||
|
|
||||||
|
|
||||||
check-types:
|
check-types:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
@ -44,12 +45,13 @@ jobs:
|
|||||||
- run: yarn build:wasm
|
- run: yarn build:wasm
|
||||||
- run: yarn tsc
|
- run: yarn tsc
|
||||||
|
|
||||||
|
|
||||||
build-test-web:
|
build-test-web:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
@ -68,14 +70,15 @@ jobs:
|
|||||||
|
|
||||||
- run: yarn test:cov
|
- run: yarn test:cov
|
||||||
|
|
||||||
|
|
||||||
prepare-json-files:
|
prepare-json-files:
|
||||||
runs-on: ubuntu-20.04 # seperate job on Ubuntu for easy string manipulations (compared to Windows)
|
runs-on: ubuntu-latest # seperate job on Ubuntu for easy string manipulations (compared to Windows)
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.export_version.outputs.version }}
|
version: ${{ steps.export_version.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
@ -84,7 +87,7 @@ jobs:
|
|||||||
if: github.event_name == 'schedule'
|
if: github.event_name == 'schedule'
|
||||||
run: |
|
run: |
|
||||||
VERSION=$(date +'%-y.%-m.%-d') yarn bump-jsons
|
VERSION=$(date +'%-y.%-m.%-d') yarn bump-jsons
|
||||||
echo "$(jq --arg url 'https://dl.kittycad.io/releases/modeling-app/test/nightly/last_update.json' \
|
echo "$(jq --arg url 'https://dl.kittycad.io/releases/modeling-app/nightly/last_update.json' \
|
||||||
'.tauri.updater.endpoints[]=$url' src-tauri/tauri.conf.json --indent 2)" > src-tauri/tauri.conf.json
|
'.tauri.updater.endpoints[]=$url' src-tauri/tauri.conf.json --indent 2)" > src-tauri/tauri.conf.json
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
@ -97,12 +100,13 @@ jobs:
|
|||||||
- id: export_version
|
- id: export_version
|
||||||
run: echo "version=`cat package.json | jq -r '.version'`" >> "$GITHUB_OUTPUT"
|
run: echo "version=`cat package.json | jq -r '.version'`" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
build-apps:
|
|
||||||
|
build-test-apps:
|
||||||
needs: [check-format, build-test-web, prepare-json-files, check-types]
|
needs: [check-format, build-test-web, prepare-json-files, check-types]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, ubuntu-20.04, windows-latest]
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -116,13 +120,19 @@ jobs:
|
|||||||
cp artifact/src-tauri/tauri.conf.json src-tauri/tauri.conf.json
|
cp artifact/src-tauri/tauri.conf.json src-tauri/tauri.conf.json
|
||||||
|
|
||||||
- name: install ubuntu system dependencies
|
- name: install ubuntu system dependencies
|
||||||
if: matrix.os == 'ubuntu-20.04'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: |
|
run: >
|
||||||
sudo apt-get update
|
sudo apt-get update &&
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev
|
sudo apt-get install -y
|
||||||
|
libgtk-3-dev
|
||||||
|
libgtksourceview-3.0-dev
|
||||||
|
webkit2gtk-4.0
|
||||||
|
libappindicator3-dev
|
||||||
|
webkit2gtk-driver
|
||||||
|
xvfb
|
||||||
|
|
||||||
- name: Sync node version and setup cache
|
- name: Sync node version and setup cache
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn' # Set this to npm, yarn or pnpm.
|
cache: 'yarn' # Set this to npm, yarn or pnpm.
|
||||||
@ -201,10 +211,22 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ${{ matrix.os == 'macos-latest' && 'src-tauri/target/universal-apple-darwin/release/bundle/*/*' || 'src-tauri/target/release/bundle/*/*' }}
|
path: ${{ matrix.os == 'macos-latest' && 'src-tauri/target/universal-apple-darwin/release/bundle/*/*' || 'src-tauri/target/release/bundle/*/*' }}
|
||||||
|
|
||||||
|
- name: Install tauri-driver for e2e tests
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: install
|
||||||
|
args: tauri-driver
|
||||||
|
|
||||||
|
- name: Run e2e tests
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: xvfb-run yarn test:e2e
|
||||||
|
|
||||||
|
|
||||||
publish-apps-release:
|
publish-apps-release:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
|
if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
|
||||||
needs: [build-test-web, prepare-json-files, build-apps]
|
needs: [build-test-web, prepare-json-files, build-test-apps]
|
||||||
env:
|
env:
|
||||||
VERSION_NO_V: ${{ needs.prepare-json-files.outputs.version }}
|
VERSION_NO_V: ${{ needs.prepare-json-files.outputs.version }}
|
||||||
VERSION: ${{ github.event_name == 'release' && format('v{0}', needs.prepare-json-files.outputs.version) || needs.prepare-json-files.outputs.version }}
|
VERSION: ${{ github.event_name == 'release' && format('v{0}', needs.prepare-json-files.outputs.version) || needs.prepare-json-files.outputs.version }}
|
||||||
|
@ -3812,7 +3812,7 @@
|
|||||||
"args": [
|
"args": [
|
||||||
{
|
{
|
||||||
"name": "data",
|
"name": "data",
|
||||||
"type": "AngeledLineThatIntersectsData",
|
"type": "AngledLineThatIntersectsData",
|
||||||
"schema": {
|
"schema": {
|
||||||
"description": "Data for drawing an angled line that intersects with a given line.",
|
"description": "Data for drawing an angled line that intersects with a given line.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
@ -763,12 +763,12 @@ Draw an angled line that intersects with a given line.
|
|||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
angledLineThatIntersects(data: AngeledLineThatIntersectsData, sketch_group: SketchGroup) -> SketchGroup
|
angledLineThatIntersects(data: AngledLineThatIntersectsData, sketch_group: SketchGroup) -> SketchGroup
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Arguments
|
#### Arguments
|
||||||
|
|
||||||
* `data`: `AngeledLineThatIntersectsData` - Data for drawing an angled line that intersects with a given line.
|
* `data`: `AngledLineThatIntersectsData` - Data for drawing an angled line that intersects with a given line.
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
// The angle of the line.
|
// The angle of the line.
|
||||||
|
11
e2e/tauri/specs/signin.e2e.js
Normal file
11
e2e/tauri/specs/signin.e2e.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
describe('Modeling App', () => {
|
||||||
|
it('open the sign in page', async () => {
|
||||||
|
const button = await $('#signin')
|
||||||
|
expect(button).toHaveText('Sign in')
|
||||||
|
|
||||||
|
// Workaround for .click(), see https://github.com/tauri-apps/tauri/issues/6541
|
||||||
|
await button.waitForClickable()
|
||||||
|
await browser.execute('arguments[0].click();', button)
|
||||||
|
// TODO: handle auth
|
||||||
|
})
|
||||||
|
})
|
41
package.json
41
package.json
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "untitled-app",
|
"name": "untitled-app",
|
||||||
"version": "0.11.0",
|
"version": "0.11.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.9.0",
|
"@codemirror/autocomplete": "^6.10.2",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@headlessui/react": "^1.7.13",
|
"@headlessui/react": "^1.7.13",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@kittycad/lib": "^0.0.43",
|
"@kittycad/lib": "^0.0.45",
|
||||||
"@lezer/javascript": "^1.4.7",
|
"@lezer/javascript": "^1.4.7",
|
||||||
"@open-rpc/client-js": "^1.8.1",
|
"@open-rpc/client-js": "^1.8.1",
|
||||||
"@react-hook/resize-observer": "^1.2.6",
|
"@react-hook/resize-observer": "^1.2.6",
|
||||||
@ -18,16 +18,16 @@
|
|||||||
"@sentry/react": "^7.65.0",
|
"@sentry/react": "^7.65.0",
|
||||||
"@tauri-apps/api": "^1.5.0",
|
"@tauri-apps/api": "^1.5.0",
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
"@testing-library/react": "^13.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
"@testing-library/user-event": "^13.2.1",
|
"@testing-library/user-event": "^14.5.1",
|
||||||
"@ts-stack/markdown": "^1.5.0",
|
"@ts-stack/markdown": "^1.5.0",
|
||||||
"@types/node": "^16.7.13",
|
"@types/node": "^16.7.13",
|
||||||
"@types/react": "^18.0.0",
|
"@types/react": "^18.0.0",
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.0.0",
|
||||||
"@uiw/react-codemirror": "^4.21.13",
|
"@uiw/react-codemirror": "^4.21.20",
|
||||||
"@xstate/inspect": "^0.8.0",
|
"@xstate/inspect": "^0.8.0",
|
||||||
"@xstate/react": "^3.2.2",
|
"@xstate/react": "^3.2.2",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.2.0",
|
||||||
"debounce-promise": "^3.1.2",
|
"debounce-promise": "^3.1.2",
|
||||||
"formik": "^2.4.3",
|
"formik": "^2.4.3",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
@ -43,20 +43,20 @@
|
|||||||
"react-modal-promise": "^1.0.2",
|
"react-modal-promise": "^1.0.2",
|
||||||
"react-router-dom": "^6.14.2",
|
"react-router-dom": "^6.14.2",
|
||||||
"sketch-helpers": "^0.0.4",
|
"sketch-helpers": "^0.0.4",
|
||||||
"swr": "^2.0.4",
|
"swr": "^2.2.2",
|
||||||
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
|
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
|
||||||
"toml": "^3.0.0",
|
"toml": "^3.0.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.4.2",
|
"typescript": "^5.2.2",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.1",
|
||||||
"vitest": "^0.34.6",
|
"vitest": "^0.34.6",
|
||||||
"vscode-jsonrpc": "^8.1.0",
|
"vscode-jsonrpc": "^8.1.0",
|
||||||
"vscode-languageserver-protocol": "^3.17.3",
|
"vscode-languageserver-protocol": "^3.17.3",
|
||||||
"wasm-pack": "^0.12.1",
|
"wasm-pack": "^0.12.1",
|
||||||
"web-vitals": "^2.1.0",
|
"web-vitals": "^3.5.0",
|
||||||
"ws": "^8.13.0",
|
"ws": "^8.13.0",
|
||||||
"xstate": "^4.38.2",
|
"xstate": "^4.38.2",
|
||||||
"zustand": "^4.1.4"
|
"zustand": "^4.4.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
@ -69,6 +69,7 @@
|
|||||||
"test:nowatch": "vitest run --mode development",
|
"test:nowatch": "vitest run --mode development",
|
||||||
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
|
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
|
||||||
"test:cov": "vitest run --coverage --mode development",
|
"test:cov": "vitest run --coverage --mode development",
|
||||||
|
"test:e2e": "wdio run wdio.conf.js",
|
||||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||||
"fmt": "prettier --write ./src",
|
"fmt": "prettier --write ./src",
|
||||||
@ -102,12 +103,12 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"@babel/preset-env": "^7.22.9",
|
"@babel/preset-env": "^7.22.9",
|
||||||
"@tauri-apps/cli": "^1.5.0",
|
"@tauri-apps/cli": "^1.5.6",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/debounce-promise": "^3.1.6",
|
"@types/debounce-promise": "^3.1.8",
|
||||||
"@types/isomorphic-fetch": "^0.0.36",
|
"@types/isomorphic-fetch": "^0.0.36",
|
||||||
"@types/react-modal": "^3.16.0",
|
"@types/react-modal": "^3.16.0",
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.4",
|
||||||
"@types/wicg-file-system-access": "^2020.9.6",
|
"@types/wicg-file-system-access": "^2020.9.6",
|
||||||
"@types/ws": "^8.5.5",
|
"@types/ws": "^8.5.5",
|
||||||
"@vitejs/plugin-react": "^4.0.3",
|
"@vitejs/plugin-react": "^4.0.3",
|
||||||
@ -122,9 +123,13 @@
|
|||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
"setimmediate": "^1.0.5",
|
"setimmediate": "^1.0.5",
|
||||||
"tailwindcss": "^3.2.4",
|
"tailwindcss": "^3.2.4",
|
||||||
"vite": "^4.4.3",
|
"vite": "^4.5.0",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-tsconfig-paths": "^4.2.0",
|
"vite-tsconfig-paths": "^4.2.1",
|
||||||
"yarn": "^1.22.19"
|
"yarn": "^1.22.19",
|
||||||
|
"@wdio/cli": "^7.7.3",
|
||||||
|
"@wdio/local-runner": "^7.7.3",
|
||||||
|
"@wdio/mocha-framework": "^7.7.3",
|
||||||
|
"@wdio/spec-reporter": "^7.7.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
69
src-tauri/Cargo.lock
generated
69
src-tauri/Cargo.lock
generated
@ -122,6 +122,12 @@ dependencies = [
|
|||||||
"system-deps 6.1.0",
|
"system-deps 6.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@ -1567,7 +1573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.3.1",
|
"hermit-abi 0.3.1",
|
||||||
"rustix 0.38.13",
|
"rustix 0.38.21",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1658,9 +1664,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.2.33"
|
version = "0.2.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d341a81a4dfef43460d395c87d86c17e24affb96db0e7f4a35e8688f0e092344"
|
checksum = "874914cd40bfd43674406683bb3f0924d41780698a4ade96f2e180a73678bdd1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1759,9 +1765,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.7"
|
version = "0.4.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
|
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
@ -2833,9 +2839,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.11.20"
|
version = "0.11.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1"
|
checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.2",
|
"base64 0.21.2",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -2862,6 +2868,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
@ -3011,9 +3018,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.37.19"
|
version = "0.37.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
|
checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"errno",
|
"errno",
|
||||||
@ -3025,14 +3032,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.13"
|
version = "0.38.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662"
|
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"bitflags 2.4.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.4.7",
|
"linux-raw-sys 0.4.10",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3208,9 +3215,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.189"
|
version = "1.0.190"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
|
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@ -3226,9 +3233,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.189"
|
version = "1.0.190"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
|
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3248,9 +3255,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.107"
|
version = "1.0.108"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
|
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa 1.0.6",
|
"itoa 1.0.6",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -3600,6 +3607,27 @@ dependencies = [
|
|||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-configuration"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"core-foundation",
|
||||||
|
"system-configuration-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-configuration-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-deps"
|
name = "system-deps"
|
||||||
version = "5.0.0"
|
version = "5.0.0"
|
||||||
@ -3828,7 +3856,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-fs-extra"
|
name = "tauri-plugin-fs-extra"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#9b20f28d747f6ec3ba5a80bfcd5edc1d573b4c90"
|
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#11048fd9975bf89e9bc2f192b735ac339f6bb43b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
@ -3927,7 +3955,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"redox_syscall 0.3.5",
|
"redox_syscall 0.3.5",
|
||||||
"rustix 0.37.19",
|
"rustix 0.37.27",
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4292,6 +4320,7 @@ version = "1.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
|
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"atomic",
|
||||||
"getrandom 0.2.9",
|
"getrandom 0.2.9",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
description = "A Tauri App"
|
description = "A Tauri App"
|
||||||
authors = ["you"]
|
authors = ["you"]
|
||||||
license = ""
|
license = ""
|
||||||
repository = ""
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
default-run = "app"
|
default-run = "app"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.60"
|
rust-version = "1.60"
|
||||||
@ -16,7 +16,7 @@ tauri-build = { version = "1.5.0", features = [] }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
kittycad = "0.2.33"
|
kittycad = "0.2.41"
|
||||||
oauth2 = "4.4.2"
|
oauth2 = "4.4.2"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
@ -129,10 +129,10 @@ async fn get_user(
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.setup(|app| {
|
.setup(|_app| {
|
||||||
#[cfg(debug_assertions)] // only include this code on debug builds
|
#[cfg(debug_assertions)] // only include this code on debug builds
|
||||||
{
|
{
|
||||||
let window = app.get_window("main").unwrap();
|
let window = _app.get_window("main").unwrap();
|
||||||
// comment out the below if you don't devtools to open everytime.
|
// comment out the below if you don't devtools to open everytime.
|
||||||
// it's useful because otherwise devtools shuts everytime rust code changes.
|
// it's useful because otherwise devtools shuts everytime rust code changes.
|
||||||
window.open_devtools();
|
window.open_devtools();
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "kittycad-modeling",
|
"productName": "kittycad-modeling",
|
||||||
"version": "0.11.0"
|
"version": "0.11.1"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { ToolTip } from './useStore'
|
|
||||||
import { Fragment, WheelEvent, useRef, useMemo } from 'react'
|
import { Fragment, WheelEvent, useRef, useMemo } from 'react'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faSearch, faX } from '@fortawesome/free-solid-svg-icons'
|
import { faSearch, faX } from '@fortawesome/free-solid-svg-icons'
|
||||||
@ -15,23 +14,6 @@ export const sketchButtonClassnames = {
|
|||||||
icon: 'text-fern-20 h-auto group-hover:text-fern-10 hover:text-fern-10 dark:text-chalkboard-100 dark:group-hover:text-chalkboard-100 dark:hover:text-chalkboard-100 group-disabled:bg-chalkboard-60 hover:group-disabled:text-inherit',
|
icon: 'text-fern-20 h-auto group-hover:text-fern-10 hover:text-fern-10 dark:text-chalkboard-100 dark:group-hover:text-chalkboard-100 dark:hover:text-chalkboard-100 group-disabled:bg-chalkboard-60 hover:group-disabled:text-inherit',
|
||||||
}
|
}
|
||||||
|
|
||||||
const sketchFnLabels: Record<ToolTip | 'sketch_line' | 'move', string> = {
|
|
||||||
sketch_line: 'Line',
|
|
||||||
line: 'Line',
|
|
||||||
move: 'Move',
|
|
||||||
angledLine: 'Angled Line',
|
|
||||||
angledLineThatIntersects: 'Angled Line That Intersects',
|
|
||||||
angledLineOfXLength: 'Angled Line Of X Length',
|
|
||||||
angledLineOfYLength: 'Angled Line Of Y Length',
|
|
||||||
angledLineToX: 'Angled Line To X',
|
|
||||||
angledLineToY: 'Angled Line To Y',
|
|
||||||
lineTo: 'Line to Point',
|
|
||||||
xLine: 'Horizontal Line',
|
|
||||||
yLine: 'Vertical Line',
|
|
||||||
xLineTo: 'Horizontal Line to Point',
|
|
||||||
yLineTo: 'Vertical Line to Point',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Toolbar = () => {
|
export const Toolbar = () => {
|
||||||
const { state, send, context } = useModelingContext()
|
const { state, send, context } = useModelingContext()
|
||||||
const toolbarButtonsRef = useRef<HTMLSpanElement>(null)
|
const toolbarButtonsRef = useRef<HTMLSpanElement>(null)
|
||||||
|
@ -184,6 +184,7 @@ function DisplayObj({
|
|||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
</span>
|
</span>
|
||||||
|
@ -2,7 +2,7 @@ import { IndexLoaderData, paths } from 'Router'
|
|||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import Tooltip from './Tooltip'
|
import Tooltip from './Tooltip'
|
||||||
import { FileEntry } from '@tauri-apps/api/fs'
|
import { FileEntry } from '@tauri-apps/api/fs'
|
||||||
import { Dispatch, useEffect, useRef, useState } from 'react'
|
import { Dispatch, useRef, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Dialog, Disclosure } from '@headlessui/react'
|
import { Dialog, Disclosure } from '@headlessui/react'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
@ -20,7 +20,6 @@ import {
|
|||||||
recast,
|
recast,
|
||||||
parse,
|
parse,
|
||||||
Program,
|
Program,
|
||||||
VariableDeclarator,
|
|
||||||
PipeExpression,
|
PipeExpression,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
|
@ -267,11 +267,11 @@ export const Stream = ({ className = '' }) => {
|
|||||||
}
|
}
|
||||||
engineCommandManager.sendSceneCommand(command).then(async () => {
|
engineCommandManager.sendSceneCommand(command).then(async () => {
|
||||||
if (!context.sketchPathToNode) return
|
if (!context.sketchPathToNode) return
|
||||||
const varDec = getNodeFromPath<VariableDeclarator>(
|
getNodeFromPath<VariableDeclarator>(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
context.sketchPathToNode,
|
context.sketchPathToNode,
|
||||||
'VariableDeclarator'
|
'VariableDeclarator'
|
||||||
).node
|
)
|
||||||
// Get the current plane string for plane we are on.
|
// Get the current plane string for plane we are on.
|
||||||
let currentPlaneString = ''
|
let currentPlaneString = ''
|
||||||
if (context.sketchPlaneId === kclManager.getPlaneId('xy')) {
|
if (context.sketchPlaneId === kclManager.getPlaneId('xy')) {
|
||||||
|
@ -69,7 +69,7 @@ export function applyConstraintEqualLength({
|
|||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
} {
|
} {
|
||||||
const { enabled, transforms } = setEqualLengthInfo({ selectionRanges })
|
const { transforms } = setEqualLengthInfo({ selectionRanges })
|
||||||
const { modifiedAst, pathToNodeMap } = transformSecondarySketchLinesTagFirst({
|
const { modifiedAst, pathToNodeMap } = transformSecondarySketchLinesTagFirst({
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -108,6 +108,7 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
messageString += message
|
messageString += message
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
@ -2,8 +2,6 @@ import { useEffect } from 'react'
|
|||||||
import { useStore } from 'useStore'
|
import { useStore } from 'useStore'
|
||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||||
import { useModelingContext } from './useModelingContext'
|
import { useModelingContext } from './useModelingContext'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import { SourceRange } from 'lang/wasm'
|
|
||||||
import { getEventForSelectWithPoint } from 'lib/selections'
|
import { getEventForSelectWithPoint } from 'lib/selections'
|
||||||
|
|
||||||
export function useEngineConnectionSubscriptions() {
|
export function useEngineConnectionSubscriptions() {
|
||||||
@ -13,11 +11,6 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
}))
|
}))
|
||||||
const { send, context } = useModelingContext()
|
const { send, context } = useModelingContext()
|
||||||
|
|
||||||
interface RangeAndId {
|
|
||||||
id: string
|
|
||||||
range: SourceRange
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!engineCommandManager) return
|
if (!engineCommandManager) return
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useLayoutEffect, useEffect, useRef } from 'react'
|
import { useLayoutEffect, useEffect, useRef } from 'react'
|
||||||
import { _executor, parse } from '../lang/wasm'
|
import { parse } from '../lang/wasm'
|
||||||
import { useStore } from '../useStore'
|
import { useStore } from '../useStore'
|
||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||||
import { deferExecution } from 'lib/utils'
|
import { deferExecution } from 'lib/utils'
|
||||||
|
@ -20,7 +20,7 @@ describe('testing AST', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 1,
|
end: 1,
|
||||||
value: 5,
|
value: { type: 'i_integer', data: 5 },
|
||||||
raw: '5',
|
raw: '5',
|
||||||
},
|
},
|
||||||
operator: '+',
|
operator: '+',
|
||||||
@ -28,7 +28,7 @@ describe('testing AST', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 3,
|
start: 3,
|
||||||
end: 4,
|
end: 4,
|
||||||
value: 6,
|
value: { type: 'i_integer', data: 6 },
|
||||||
raw: '6',
|
raw: '6',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -58,7 +58,7 @@ describe('testing AST', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 14,
|
start: 14,
|
||||||
end: 15,
|
end: 15,
|
||||||
value: 5,
|
value: { type: 'i_integer', data: 5 },
|
||||||
raw: '5',
|
raw: '5',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -92,7 +92,7 @@ const newVar = myVar + 1
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 14,
|
start: 14,
|
||||||
end: 15,
|
end: 15,
|
||||||
value: 5,
|
value: { type: 'i_integer', data: 5 },
|
||||||
raw: '5',
|
raw: '5',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -129,7 +129,7 @@ const newVar = myVar + 1
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 39,
|
start: 39,
|
||||||
end: 40,
|
end: 40,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -320,14 +320,14 @@ const myVar = funcN(1, 2)`
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 58,
|
start: 58,
|
||||||
end: 59,
|
end: 59,
|
||||||
value: 1,
|
value: { data: 1, type: 'i_integer' },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 61,
|
start: 61,
|
||||||
end: 62,
|
end: 62,
|
||||||
value: 2,
|
value: { data: 2, type: 'i_integer' },
|
||||||
raw: '2',
|
raw: '2',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -390,14 +390,14 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 32,
|
start: 32,
|
||||||
end: 33,
|
end: 33,
|
||||||
value: 0,
|
value: { type: 'i_integer', data: 0 },
|
||||||
raw: '0',
|
raw: '0',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 35,
|
start: 35,
|
||||||
end: 36,
|
end: 36,
|
||||||
value: 0,
|
value: { type: 'i_integer', data: 0 },
|
||||||
raw: '0',
|
raw: '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -426,14 +426,14 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 52,
|
start: 52,
|
||||||
end: 53,
|
end: 53,
|
||||||
value: 2,
|
value: { type: 'i_integer', data: 2 },
|
||||||
raw: '2',
|
raw: '2',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 55,
|
start: 55,
|
||||||
end: 56,
|
end: 56,
|
||||||
value: 3,
|
value: { type: 'i_integer', data: 3 },
|
||||||
raw: '3',
|
raw: '3',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -478,14 +478,14 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 81,
|
start: 81,
|
||||||
end: 82,
|
end: 82,
|
||||||
value: 0,
|
value: { type: 'i_integer', data: 0 },
|
||||||
raw: '0',
|
raw: '0',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 84,
|
start: 84,
|
||||||
end: 85,
|
end: 85,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -505,7 +505,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 93,
|
start: 93,
|
||||||
end: 101,
|
end: 101,
|
||||||
value: 'myPath',
|
value: { type: 'string', data: 'myPath' },
|
||||||
raw: '"myPath"',
|
raw: '"myPath"',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -536,14 +536,14 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 121,
|
start: 121,
|
||||||
end: 122,
|
end: 122,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 124,
|
start: 124,
|
||||||
end: 125,
|
end: 125,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -568,7 +568,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 139,
|
start: 139,
|
||||||
end: 141,
|
end: 141,
|
||||||
value: 45,
|
value: { type: 'i_integer', data: 45 },
|
||||||
raw: '45',
|
raw: '45',
|
||||||
},
|
},
|
||||||
{ type: 'PipeSubstitution', start: 143, end: 144 },
|
{ type: 'PipeSubstitution', start: 143, end: 144 },
|
||||||
@ -619,7 +619,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 14,
|
start: 14,
|
||||||
end: 15,
|
end: 15,
|
||||||
value: 5,
|
value: { type: 'i_integer', data: 5 },
|
||||||
raw: '5',
|
raw: '5',
|
||||||
},
|
},
|
||||||
operator: '+',
|
operator: '+',
|
||||||
@ -627,7 +627,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 18,
|
start: 18,
|
||||||
end: 19,
|
end: 19,
|
||||||
value: 6,
|
value: { type: 'i_integer', data: 6 },
|
||||||
raw: '6',
|
raw: '6',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -646,7 +646,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 30,
|
start: 30,
|
||||||
end: 32,
|
end: 32,
|
||||||
value: 45,
|
value: { type: 'i_integer', data: 45 },
|
||||||
raw: '45',
|
raw: '45',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -696,14 +696,14 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 12,
|
start: 12,
|
||||||
end: 13,
|
end: 13,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 15,
|
start: 15,
|
||||||
end: 18,
|
end: 18,
|
||||||
value: '2',
|
value: { type: 'string', data: '2' },
|
||||||
raw: "'2'",
|
raw: "'2'",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -720,7 +720,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 27,
|
start: 27,
|
||||||
end: 28,
|
end: 28,
|
||||||
value: 4,
|
value: { type: 'i_integer', data: 4 },
|
||||||
raw: '4',
|
raw: '4',
|
||||||
},
|
},
|
||||||
operator: '+',
|
operator: '+',
|
||||||
@ -728,7 +728,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 31,
|
start: 31,
|
||||||
end: 32,
|
end: 32,
|
||||||
value: 5,
|
value: { type: 'i_integer', data: 5 },
|
||||||
raw: '5',
|
raw: '5',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -766,7 +766,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 14,
|
start: 14,
|
||||||
end: 15,
|
end: 15,
|
||||||
value: 3,
|
value: { type: 'i_integer', data: 3 },
|
||||||
raw: '3',
|
raw: '3',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -807,7 +807,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 34,
|
start: 34,
|
||||||
end: 39,
|
end: 39,
|
||||||
value: 'str',
|
value: { type: 'string', data: 'str' },
|
||||||
raw: "'str'",
|
raw: "'str'",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -825,7 +825,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 47,
|
start: 47,
|
||||||
end: 48,
|
end: 48,
|
||||||
value: 2,
|
value: { type: 'i_integer', data: 2 },
|
||||||
raw: '2',
|
raw: '2',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -864,7 +864,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 77,
|
start: 77,
|
||||||
end: 78,
|
end: 78,
|
||||||
value: 4,
|
value: { type: 'i_integer', data: 4 },
|
||||||
raw: '4',
|
raw: '4',
|
||||||
},
|
},
|
||||||
operator: '+',
|
operator: '+',
|
||||||
@ -872,7 +872,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 81,
|
start: 81,
|
||||||
end: 82,
|
end: 82,
|
||||||
value: 5,
|
value: { type: 'i_integer', data: 5 },
|
||||||
raw: '5',
|
raw: '5',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -940,7 +940,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 27,
|
start: 27,
|
||||||
end: 34,
|
end: 34,
|
||||||
value: 'value',
|
value: { type: 'string', data: 'value' },
|
||||||
raw: "'value'",
|
raw: "'value'",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -998,14 +998,14 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 18,
|
start: 18,
|
||||||
end: 19,
|
end: 19,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 21,
|
start: 21,
|
||||||
end: 24,
|
end: 24,
|
||||||
value: '2',
|
value: { type: 'string', data: '2' },
|
||||||
raw: "'2'",
|
raw: "'2'",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -1120,7 +1120,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 20,
|
start: 20,
|
||||||
end: 25,
|
end: 25,
|
||||||
value: 'two',
|
value: { type: 'string', data: 'two' },
|
||||||
raw: '"two"',
|
raw: '"two"',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1169,7 +1169,7 @@ describe('testing pipe operator special', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 16,
|
start: 16,
|
||||||
end: 21,
|
end: 21,
|
||||||
value: 'one',
|
value: { type: 'string', data: 'one' },
|
||||||
raw: '"one"',
|
raw: '"one"',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1215,7 +1215,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 11,
|
start: 11,
|
||||||
end: 12,
|
end: 12,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
operator: '+',
|
operator: '+',
|
||||||
@ -1223,7 +1223,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 15,
|
start: 15,
|
||||||
end: 16,
|
end: 16,
|
||||||
value: 2,
|
value: { type: 'i_integer', data: 2 },
|
||||||
raw: '2',
|
raw: '2',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1263,7 +1263,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 11,
|
start: 11,
|
||||||
end: 12,
|
end: 12,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
operator: '*',
|
operator: '*',
|
||||||
@ -1271,7 +1271,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 15,
|
start: 15,
|
||||||
end: 16,
|
end: 16,
|
||||||
value: 2,
|
value: { type: 'i_integer', data: 2 },
|
||||||
raw: '2',
|
raw: '2',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1280,7 +1280,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 19,
|
start: 19,
|
||||||
end: 20,
|
end: 20,
|
||||||
value: 3,
|
value: { type: 'i_integer', data: 3 },
|
||||||
raw: '3',
|
raw: '3',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1316,7 +1316,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 11,
|
start: 11,
|
||||||
end: 12,
|
end: 12,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
operator: '+',
|
operator: '+',
|
||||||
@ -1328,7 +1328,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 15,
|
start: 15,
|
||||||
end: 16,
|
end: 16,
|
||||||
value: 2,
|
value: { type: 'i_integer', data: 2 },
|
||||||
raw: '2',
|
raw: '2',
|
||||||
},
|
},
|
||||||
operator: '*',
|
operator: '*',
|
||||||
@ -1336,7 +1336,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 19,
|
start: 19,
|
||||||
end: 20,
|
end: 20,
|
||||||
value: 3,
|
value: { type: 'i_integer', data: 3 },
|
||||||
raw: '3',
|
raw: '3',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1360,7 +1360,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 11,
|
start: 11,
|
||||||
end: 12,
|
end: 12,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
operator: '+',
|
operator: '+',
|
||||||
@ -1368,7 +1368,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 15,
|
start: 15,
|
||||||
end: 16,
|
end: 16,
|
||||||
value: 2,
|
value: { type: 'i_integer', data: 2 },
|
||||||
raw: '2',
|
raw: '2',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1377,7 +1377,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 19,
|
start: 19,
|
||||||
end: 20,
|
end: 20,
|
||||||
value: 3,
|
value: { type: 'i_integer', data: 3 },
|
||||||
raw: '3',
|
raw: '3',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -1397,7 +1397,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 11,
|
start: 11,
|
||||||
end: 12,
|
end: 12,
|
||||||
value: 1,
|
value: { type: 'i_integer', data: 1 },
|
||||||
raw: '1',
|
raw: '1',
|
||||||
},
|
},
|
||||||
operator: '*',
|
operator: '*',
|
||||||
@ -1405,7 +1405,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 15,
|
start: 15,
|
||||||
end: 16,
|
end: 16,
|
||||||
value: 2,
|
value: { type: 'i_integer', data: 2 },
|
||||||
raw: '2',
|
raw: '2',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1414,7 +1414,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 19,
|
start: 19,
|
||||||
end: 20,
|
end: 20,
|
||||||
value: 3,
|
value: { type: 'i_integer', data: 3 },
|
||||||
raw: '3',
|
raw: '3',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -1433,7 +1433,13 @@ describe('nests binary expressions correctly', () => {
|
|||||||
operator: '+',
|
operator: '+',
|
||||||
start: 11,
|
start: 11,
|
||||||
end: 30,
|
end: 30,
|
||||||
left: { type: 'Literal', value: 1, raw: '1', start: 11, end: 12 },
|
left: {
|
||||||
|
type: 'Literal',
|
||||||
|
value: { type: 'i_integer', data: 1 },
|
||||||
|
raw: '1',
|
||||||
|
start: 11,
|
||||||
|
end: 12,
|
||||||
|
},
|
||||||
right: {
|
right: {
|
||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
operator: '/',
|
operator: '/',
|
||||||
@ -1443,27 +1449,51 @@ describe('nests binary expressions correctly', () => {
|
|||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
operator: '*',
|
operator: '*',
|
||||||
start: 15,
|
start: 15,
|
||||||
end: 26,
|
end: 25,
|
||||||
left: { type: 'Literal', value: 2, raw: '2', start: 15, end: 16 },
|
left: {
|
||||||
|
type: 'Literal',
|
||||||
|
value: { type: 'i_integer', data: 2 },
|
||||||
|
raw: '2',
|
||||||
|
start: 15,
|
||||||
|
end: 16,
|
||||||
|
},
|
||||||
right: {
|
right: {
|
||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
operator: '-',
|
operator: '-',
|
||||||
start: 20,
|
start: 20,
|
||||||
end: 25,
|
end: 25,
|
||||||
left: { type: 'Literal', value: 3, raw: '3', start: 20, end: 21 },
|
left: {
|
||||||
|
type: 'Literal',
|
||||||
|
value: { type: 'i_integer', data: 3 },
|
||||||
|
raw: '3',
|
||||||
|
start: 20,
|
||||||
|
end: 21,
|
||||||
|
},
|
||||||
right: {
|
right: {
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
value: 4,
|
value: { type: 'i_integer', data: 4 },
|
||||||
raw: '4',
|
raw: '4',
|
||||||
start: 24,
|
start: 24,
|
||||||
end: 25,
|
end: 25,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
right: { type: 'Literal', value: 5, raw: '5', start: 29, end: 30 },
|
right: {
|
||||||
|
type: 'Literal',
|
||||||
|
value: { type: 'i_integer', data: 5 },
|
||||||
|
raw: '5',
|
||||||
|
start: 29,
|
||||||
|
end: 30,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
right: { type: 'Literal', value: 6, raw: '6', start: 33, end: 34 },
|
},
|
||||||
|
right: {
|
||||||
|
type: 'Literal',
|
||||||
|
value: { type: 'i_integer', data: 6 },
|
||||||
|
raw: '6',
|
||||||
|
start: 33,
|
||||||
|
end: 34,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1481,7 +1511,7 @@ const key = 'c'`
|
|||||||
value: {
|
value: {
|
||||||
type: 'blockComment',
|
type: 'blockComment',
|
||||||
style: 'line',
|
style: 'line',
|
||||||
value: 'this is a comment',
|
value: { type: 'string', data: 'this is a comment' },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const { nonCodeMeta } = parse(code)
|
const { nonCodeMeta } = parse(code)
|
||||||
@ -1564,8 +1594,20 @@ describe('test UnaryExpression', () => {
|
|||||||
end: 26,
|
end: 26,
|
||||||
callee: { type: 'Identifier', start: 15, end: 18, name: 'min' },
|
callee: { type: 'Identifier', start: 15, end: 18, name: 'min' },
|
||||||
arguments: [
|
arguments: [
|
||||||
{ type: 'Literal', start: 19, end: 20, value: 4, raw: '4' },
|
{
|
||||||
{ type: 'Literal', start: 22, end: 25, value: 100, raw: '100' },
|
type: 'Literal',
|
||||||
|
start: 19,
|
||||||
|
end: 20,
|
||||||
|
value: { type: 'i_integer', data: 4 },
|
||||||
|
raw: '4',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'Literal',
|
||||||
|
start: 22,
|
||||||
|
end: 25,
|
||||||
|
value: { type: 'i_integer', data: 100 },
|
||||||
|
raw: '100',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
function: expect.any(Object),
|
function: expect.any(Object),
|
||||||
optional: false,
|
optional: false,
|
||||||
@ -1585,21 +1627,45 @@ describe('testing nested call expressions', () => {
|
|||||||
end: 40,
|
end: 40,
|
||||||
callee: { type: 'Identifier', start: 14, end: 17, name: 'min' },
|
callee: { type: 'Identifier', start: 14, end: 17, name: 'min' },
|
||||||
arguments: [
|
arguments: [
|
||||||
{ type: 'Literal', start: 18, end: 21, value: 100, raw: '100' },
|
{
|
||||||
|
type: 'Literal',
|
||||||
|
start: 18,
|
||||||
|
end: 21,
|
||||||
|
value: { type: 'i_integer', data: 100 },
|
||||||
|
raw: '100',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
operator: '+',
|
operator: '+',
|
||||||
start: 23,
|
start: 23,
|
||||||
end: 39,
|
end: 39,
|
||||||
left: { type: 'Literal', value: 1, raw: '1', start: 23, end: 24 },
|
left: {
|
||||||
|
type: 'Literal',
|
||||||
|
value: { type: 'i_integer', data: 1 },
|
||||||
|
raw: '1',
|
||||||
|
start: 23,
|
||||||
|
end: 24,
|
||||||
|
},
|
||||||
right: {
|
right: {
|
||||||
type: 'CallExpression',
|
type: 'CallExpression',
|
||||||
start: 27,
|
start: 27,
|
||||||
end: 39,
|
end: 39,
|
||||||
callee: { type: 'Identifier', start: 27, end: 33, name: 'legLen' },
|
callee: { type: 'Identifier', start: 27, end: 33, name: 'legLen' },
|
||||||
arguments: [
|
arguments: [
|
||||||
{ type: 'Literal', start: 34, end: 35, value: 5, raw: '5' },
|
{
|
||||||
{ type: 'Literal', start: 37, end: 38, value: 3, raw: '3' },
|
type: 'Literal',
|
||||||
|
start: 34,
|
||||||
|
end: 35,
|
||||||
|
value: { type: 'i_integer', data: 5 },
|
||||||
|
raw: '5',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'Literal',
|
||||||
|
start: 37,
|
||||||
|
end: 38,
|
||||||
|
value: { type: 'i_integer', data: 3 },
|
||||||
|
raw: '3',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
function: expect.any(Object),
|
function: expect.any(Object),
|
||||||
optional: false,
|
optional: false,
|
||||||
@ -1633,7 +1699,7 @@ describe('should recognise callExpresions in binaryExpressions', () => {
|
|||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 16,
|
start: 16,
|
||||||
end: 23,
|
end: 23,
|
||||||
value: 'seg02',
|
value: { type: 'string', data: 'seg02' },
|
||||||
raw: "'seg02'",
|
raw: "'seg02'",
|
||||||
},
|
},
|
||||||
{ type: 'PipeSubstitution', start: 25, end: 26 },
|
{ type: 'PipeSubstitution', start: 25, end: 26 },
|
||||||
@ -1641,7 +1707,13 @@ describe('should recognise callExpresions in binaryExpressions', () => {
|
|||||||
function: expect.any(Object),
|
function: expect.any(Object),
|
||||||
optional: false,
|
optional: false,
|
||||||
},
|
},
|
||||||
right: { type: 'Literal', value: 1, raw: '1', start: 30, end: 31 },
|
right: {
|
||||||
|
type: 'Literal',
|
||||||
|
value: { type: 'i_integer', data: 1 },
|
||||||
|
raw: '1',
|
||||||
|
start: 30,
|
||||||
|
end: 31,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{ type: 'PipeSubstitution', start: 33, end: 34 },
|
{ type: 'PipeSubstitution', start: 33, end: 34 },
|
||||||
])
|
])
|
||||||
|
@ -21,7 +21,7 @@ describe('Testing createLiteral', () => {
|
|||||||
it('should create a literal', () => {
|
it('should create a literal', () => {
|
||||||
const result = createLiteral(5)
|
const result = createLiteral(5)
|
||||||
expect(result.type).toBe('Literal')
|
expect(result.type).toBe('Literal')
|
||||||
expect(result.value).toBe(5)
|
expect(result.value.data).toBe(5)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('Testing createIdentifier', () => {
|
describe('Testing createIdentifier', () => {
|
||||||
@ -38,7 +38,7 @@ describe('Testing createCallExpression', () => {
|
|||||||
expect(result.callee.type).toBe('Identifier')
|
expect(result.callee.type).toBe('Identifier')
|
||||||
expect(result.callee.name).toBe('myFunc')
|
expect(result.callee.name).toBe('myFunc')
|
||||||
expect(result.arguments[0].type).toBe('Literal')
|
expect(result.arguments[0].type).toBe('Literal')
|
||||||
expect((result.arguments[0] as any).value).toBe(5)
|
expect((result.arguments[0] as any).value.data).toBe(5)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('Testing createObjectExpression', () => {
|
describe('Testing createObjectExpression', () => {
|
||||||
@ -50,7 +50,7 @@ describe('Testing createObjectExpression', () => {
|
|||||||
expect(result.properties[0].type).toBe('ObjectProperty')
|
expect(result.properties[0].type).toBe('ObjectProperty')
|
||||||
expect(result.properties[0].key.name).toBe('myProp')
|
expect(result.properties[0].key.name).toBe('myProp')
|
||||||
expect(result.properties[0].value.type).toBe('Literal')
|
expect(result.properties[0].value.type).toBe('Literal')
|
||||||
expect((result.properties[0].value as any).value).toBe(5)
|
expect((result.properties[0].value as any).value.data).toBe(5)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('Testing createArrayExpression', () => {
|
describe('Testing createArrayExpression', () => {
|
||||||
@ -58,7 +58,7 @@ describe('Testing createArrayExpression', () => {
|
|||||||
const result = createArrayExpression([createLiteral(5)])
|
const result = createArrayExpression([createLiteral(5)])
|
||||||
expect(result.type).toBe('ArrayExpression')
|
expect(result.type).toBe('ArrayExpression')
|
||||||
expect(result.elements[0].type).toBe('Literal')
|
expect(result.elements[0].type).toBe('Literal')
|
||||||
expect((result.elements[0] as any).value).toBe(5)
|
expect((result.elements[0] as any).value.data).toBe(5)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('Testing createPipeSubstitution', () => {
|
describe('Testing createPipeSubstitution', () => {
|
||||||
@ -75,7 +75,7 @@ describe('Testing createVariableDeclaration', () => {
|
|||||||
expect(result.declarations[0].id.type).toBe('Identifier')
|
expect(result.declarations[0].id.type).toBe('Identifier')
|
||||||
expect(result.declarations[0].id.name).toBe('myVar')
|
expect(result.declarations[0].id.name).toBe('myVar')
|
||||||
expect(result.declarations[0].init.type).toBe('Literal')
|
expect(result.declarations[0].init.type).toBe('Literal')
|
||||||
expect((result.declarations[0].init as any).value).toBe(5)
|
expect((result.declarations[0].init as any).value.data).toBe(5)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('Testing createPipeExpression', () => {
|
describe('Testing createPipeExpression', () => {
|
||||||
@ -83,7 +83,7 @@ describe('Testing createPipeExpression', () => {
|
|||||||
const result = createPipeExpression([createLiteral(5)])
|
const result = createPipeExpression([createLiteral(5)])
|
||||||
expect(result.type).toBe('PipeExpression')
|
expect(result.type).toBe('PipeExpression')
|
||||||
expect(result.body[0].type).toBe('Literal')
|
expect(result.body[0].type).toBe('Literal')
|
||||||
expect((result.body[0] as any).value).toBe(5)
|
expect((result.body[0] as any).value.data).toBe(5)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -310,7 +310,7 @@ export function extrudeSketch(
|
|||||||
const name = findUniqueName(node, 'part')
|
const name = findUniqueName(node, 'part')
|
||||||
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
||||||
let showCallIndex = getShowIndex(_node)
|
let showCallIndex = getShowIndex(_node)
|
||||||
if (showCallIndex == -1) {
|
if (showCallIndex === -1) {
|
||||||
// We didn't find a show, so let's just append everything
|
// We didn't find a show, so let's just append everything
|
||||||
showCallIndex = _node.body.length
|
showCallIndex = _node.body.length
|
||||||
}
|
}
|
||||||
@ -440,11 +440,15 @@ export function splitPathAtPipeExpression(pathToNode: PathToNode): {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createLiteral(value: string | number): Literal {
|
export function createLiteral(value: string | number): Literal {
|
||||||
|
const literalValue =
|
||||||
|
typeof value === 'string'
|
||||||
|
? ({ type: 'string', data: value } as const)
|
||||||
|
: ({ type: 'fractional', data: value } as const)
|
||||||
return {
|
return {
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
value,
|
value: literalValue,
|
||||||
raw: `${value}`,
|
raw: `${value}`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -641,7 +645,7 @@ export function giveSketchFnCallTag(
|
|||||||
const isTagExisting = !!firstArg.tag
|
const isTagExisting = !!firstArg.tag
|
||||||
const tagValue = (firstArg.tag ||
|
const tagValue = (firstArg.tag ||
|
||||||
createLiteral(tag || findUniqueName(ast, 'seg', 2))) as Literal
|
createLiteral(tag || findUniqueName(ast, 'seg', 2))) as Literal
|
||||||
const tagStr = String(tagValue.value)
|
const tagStr = String(tagValue.value.data)
|
||||||
const newFirstArg = createFirstArg(
|
const newFirstArg = createFirstArg(
|
||||||
primaryCallExp.callee.name as ToolTip,
|
primaryCallExp.callee.name as ToolTip,
|
||||||
firstArg.val,
|
firstArg.val,
|
||||||
|
@ -982,7 +982,7 @@ export class EngineCommandManager {
|
|||||||
if (parseCommand.type === 'modeling_cmd_req')
|
if (parseCommand.type === 'modeling_cmd_req')
|
||||||
return this.handlePendingCommand(id, parseCommand?.cmd, range)
|
return this.handlePendingCommand(id, parseCommand?.cmd, range)
|
||||||
}
|
}
|
||||||
throw 'shouldnt reach here'
|
throw Error('shouldnt reach here')
|
||||||
}
|
}
|
||||||
handlePendingCommand(
|
handlePendingCommand(
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -20,7 +20,6 @@ import {
|
|||||||
import { isLiteralArrayOrStatic } from './sketchcombos'
|
import { isLiteralArrayOrStatic } from './sketchcombos'
|
||||||
import { toolTips, ToolTip } from '../../useStore'
|
import { toolTips, ToolTip } from '../../useStore'
|
||||||
import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
|
import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
|
||||||
import { generateUuidFromHashSeed } from '../../lib/uuid'
|
|
||||||
|
|
||||||
import { SketchLineHelper, ModifyAstBase, TransformCallback } from './stdTypes'
|
import { SketchLineHelper, ModifyAstBase, TransformCallback } from './stdTypes'
|
||||||
|
|
||||||
@ -92,18 +91,12 @@ export function createFirstArg(
|
|||||||
throw new Error('all sketch line types should have been covered')
|
throw new Error('all sketch line types should have been covered')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
type LineData = {
|
type LineData = {
|
||||||
from: [number, number, number]
|
from: [number, number, number]
|
||||||
to: [number, number, number]
|
to: [number, number, number]
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeId(seed: string | any) {
|
|
||||||
if (typeof seed === 'string') {
|
|
||||||
return generateUuidFromHashSeed(seed)
|
|
||||||
}
|
|
||||||
return generateUuidFromHashSeed(JSON.stringify(seed))
|
|
||||||
}
|
|
||||||
|
|
||||||
export const lineTo: SketchLineHelper = {
|
export const lineTo: SketchLineHelper = {
|
||||||
add: ({
|
add: ({
|
||||||
node,
|
node,
|
||||||
@ -250,9 +243,6 @@ export const line: SketchLineHelper = {
|
|||||||
])
|
])
|
||||||
|
|
||||||
if (callExpression.arguments?.[0].type === 'ObjectExpression') {
|
if (callExpression.arguments?.[0].type === 'ObjectExpression') {
|
||||||
const toProp = callExpression.arguments?.[0].properties?.find(
|
|
||||||
({ key }) => key.name === 'to'
|
|
||||||
)
|
|
||||||
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
||||||
} else {
|
} else {
|
||||||
mutateArrExp(callExpression.arguments?.[0], toArrExp)
|
mutateArrExp(callExpression.arguments?.[0], toArrExp)
|
||||||
@ -974,14 +964,12 @@ export function addNewSketchLn({
|
|||||||
const node = JSON.parse(JSON.stringify(_node))
|
const node = JSON.parse(JSON.stringify(_node))
|
||||||
const { add, updateArgs } = sketchLineHelperMap?.[fnName] || {}
|
const { add, updateArgs } = sketchLineHelperMap?.[fnName] || {}
|
||||||
if (!add || !updateArgs) throw new Error('not a sketch line helper')
|
if (!add || !updateArgs) throw new Error('not a sketch line helper')
|
||||||
const { node: varDec } = getNodeFromPath<VariableDeclarator>(
|
getNodeFromPath<VariableDeclarator>(node, pathToNode, 'VariableDeclarator')
|
||||||
|
getNodeFromPath<PipeExpression | CallExpression>(
|
||||||
node,
|
node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'VariableDeclarator'
|
'PipeExpression'
|
||||||
)
|
)
|
||||||
const { node: pipeExp, shallowPath: pipePath } = getNodeFromPath<
|
|
||||||
PipeExpression | CallExpression
|
|
||||||
>(node, pathToNode, 'PipeExpression')
|
|
||||||
return add({
|
return add({
|
||||||
node,
|
node,
|
||||||
previousProgramMemory,
|
previousProgramMemory,
|
||||||
|
@ -1426,7 +1426,7 @@ export function transformAstSketchLines({
|
|||||||
referenceSegName ||
|
referenceSegName ||
|
||||||
(_referencedSegmentNameVal &&
|
(_referencedSegmentNameVal &&
|
||||||
_referencedSegmentNameVal.type === 'Literal' &&
|
_referencedSegmentNameVal.type === 'Literal' &&
|
||||||
String(_referencedSegmentNameVal.value)) ||
|
String(_referencedSegmentNameVal.value.data)) ||
|
||||||
''
|
''
|
||||||
const [varValA, varValB] = Array.isArray(val) ? val : [val, val]
|
const [varValA, varValB] = Array.isArray(val) ? val : [val, val]
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import { v4 as uuidv4 } from 'uuid'
|
|||||||
type WebSocketResponse = Models['OkWebSocketResponseData_type']
|
type WebSocketResponse = Models['OkWebSocketResponseData_type']
|
||||||
|
|
||||||
class MockEngineCommandManager {
|
class MockEngineCommandManager {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
||||||
constructor(mockParams: {
|
constructor(mockParams: {
|
||||||
setIsStreamReady: (isReady: boolean) => void
|
setIsStreamReady: (isReady: boolean) => void
|
||||||
setMediaStream: (stream: MediaStream) => void
|
setMediaStream: (stream: MediaStream) => void
|
||||||
|
@ -882,7 +882,7 @@ export const modelingMachine = createMachine(
|
|||||||
// TODO implement source ranges for all of these constraints
|
// TODO implement source ranges for all of these constraints
|
||||||
// need to make the async like the modal constraints
|
// need to make the async like the modal constraints
|
||||||
'Make selection horizontal': ({ selectionRanges }) => {
|
'Make selection horizontal': ({ selectionRanges }) => {
|
||||||
const { modifiedAst, pathToNodeMap } = applyConstraintHorzVert(
|
const { modifiedAst } = applyConstraintHorzVert(
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
'horizontal',
|
'horizontal',
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
@ -891,7 +891,7 @@ export const modelingMachine = createMachine(
|
|||||||
kclManager.updateAst(modifiedAst, true)
|
kclManager.updateAst(modifiedAst, true)
|
||||||
},
|
},
|
||||||
'Make selection vertical': ({ selectionRanges }) => {
|
'Make selection vertical': ({ selectionRanges }) => {
|
||||||
const { modifiedAst, pathToNodeMap } = applyConstraintHorzVert(
|
const { modifiedAst } = applyConstraintHorzVert(
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
'vertical',
|
'vertical',
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
@ -900,33 +900,33 @@ export const modelingMachine = createMachine(
|
|||||||
kclManager.updateAst(modifiedAst, true)
|
kclManager.updateAst(modifiedAst, true)
|
||||||
},
|
},
|
||||||
'Constrain horizontally align': ({ selectionRanges }) => {
|
'Constrain horizontally align': ({ selectionRanges }) => {
|
||||||
const { modifiedAst, pathToNodeMap } = applyConstraintHorzVertAlign({
|
const { modifiedAst } = applyConstraintHorzVertAlign({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
constraint: 'setVertDistance',
|
constraint: 'setVertDistance',
|
||||||
})
|
})
|
||||||
kclManager.updateAst(modifiedAst, true)
|
kclManager.updateAst(modifiedAst, true)
|
||||||
},
|
},
|
||||||
'Constrain vertically align': ({ selectionRanges }) => {
|
'Constrain vertically align': ({ selectionRanges }) => {
|
||||||
const { modifiedAst, pathToNodeMap } = applyConstraintHorzVertAlign({
|
const { modifiedAst } = applyConstraintHorzVertAlign({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
constraint: 'setHorzDistance',
|
constraint: 'setHorzDistance',
|
||||||
})
|
})
|
||||||
kclManager.updateAst(modifiedAst, true)
|
kclManager.updateAst(modifiedAst, true)
|
||||||
},
|
},
|
||||||
'Constrain equal length': ({ selectionRanges }) => {
|
'Constrain equal length': ({ selectionRanges }) => {
|
||||||
const { modifiedAst, pathToNodeMap } = applyConstraintEqualLength({
|
const { modifiedAst } = applyConstraintEqualLength({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
kclManager.updateAst(modifiedAst, true)
|
kclManager.updateAst(modifiedAst, true)
|
||||||
},
|
},
|
||||||
'Constrain parallel': ({ selectionRanges }) => {
|
'Constrain parallel': ({ selectionRanges }) => {
|
||||||
const { modifiedAst, pathToNodeMap } = applyConstraintEqualAngle({
|
const { modifiedAst } = applyConstraintEqualAngle({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
kclManager.updateAst(modifiedAst, true)
|
kclManager.updateAst(modifiedAst, true)
|
||||||
},
|
},
|
||||||
'Constrain remove constraints': ({ selectionRanges }) => {
|
'Constrain remove constraints': ({ selectionRanges }) => {
|
||||||
const { modifiedAst, pathToNodeMap } = applyRemoveConstrainingValues({
|
const { modifiedAst } = applyRemoveConstrainingValues({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
kclManager.updateAst(modifiedAst, true)
|
kclManager.updateAst(modifiedAst, true)
|
||||||
|
@ -67,6 +67,7 @@ const SignIn = () => {
|
|||||||
onClick={signInTauri}
|
onClick={signInTauri}
|
||||||
icon={{ icon: faSignInAlt }}
|
icon={{ icon: faSignInAlt }}
|
||||||
className="w-fit mt-4"
|
className="w-fit mt-4"
|
||||||
|
id="signin"
|
||||||
>
|
>
|
||||||
Sign in
|
Sign in
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
545
src/wasm-lib/Cargo.lock
generated
545
src/wasm-lib/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
|||||||
name = "wasm-lib"
|
name = "wasm-lib"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[lib]
|
[lib]
|
||||||
@ -11,28 +12,28 @@ crate-type = ["cdylib"]
|
|||||||
bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
|
bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
|
||||||
gloo-utils = "0.2.0"
|
gloo-utils = "0.2.0"
|
||||||
kcl-lib = { path = "kcl" }
|
kcl-lib = { path = "kcl" }
|
||||||
kittycad = { version = "0.2.33", default-features = false, features = ["js"] }
|
kittycad = { workspace = true }
|
||||||
serde_json = "1.0.107"
|
serde_json = "1.0.108"
|
||||||
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
|
uuid = { version = "1.5.0", features = ["v4", "js", "serde"] }
|
||||||
wasm-bindgen = "0.2.87"
|
wasm-bindgen = "0.2.87"
|
||||||
wasm-bindgen-futures = "0.4.37"
|
wasm-bindgen-futures = "0.4.37"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
image = "0.24.7"
|
image = "0.24.7"
|
||||||
kittycad = "0.2.33"
|
kittycad = { workspace = true, default-features = true }
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
reqwest = { version = "0.11.22", default-features = false }
|
reqwest = { version = "0.11.22", default-features = false }
|
||||||
tokio = { version = "1.33.0", features = ["rt-multi-thread", "macros", "time"] }
|
tokio = { version = "1.33.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||||
twenty-twenty = "0.6.1"
|
twenty-twenty = "0.6.1"
|
||||||
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
|
uuid = { version = "1.5.0", features = ["v4", "js", "serde"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
futures = "0.3.28"
|
futures = "0.3.29"
|
||||||
js-sys = "0.3.64"
|
js-sys = "0.3.64"
|
||||||
tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] }
|
tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] }
|
||||||
wasm-bindgen-futures = { version = "0.4.37", features = ["futures-core-03-stream"] }
|
wasm-bindgen-futures = { version = "0.4.37", features = ["futures-core-03-stream"] }
|
||||||
wasm-streams = "0.3.0"
|
wasm-streams = "0.4.0"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
|
[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
|
||||||
version = "0.3.57"
|
version = "0.3.57"
|
||||||
@ -53,6 +54,9 @@ members = [
|
|||||||
"kcl",
|
"kcl",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
kittycad = { version = "0.2.41", default-features = false, features = ["js"] }
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "executor"
|
name = "executor"
|
||||||
path = "tests/executor/main.rs"
|
path = "tests/executor/main.rs"
|
||||||
|
@ -4,6 +4,7 @@ description = "A tool for generating documentation from Rust derive macros"
|
|||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ proc-macro = true
|
|||||||
convert_case = "0.6.0"
|
convert_case = "0.6.0"
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
serde = { version = "1.0.189", features = ["derive"] }
|
serde = { version = "1.0.190", features = ["derive"] }
|
||||||
serde_tokenstream = "0.2"
|
serde_tokenstream = "0.2"
|
||||||
syn = { version = "2.0.38", features = ["full"] }
|
syn = { version = "2.0.38", features = ["full"] }
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ description = "KittyCAD Language"
|
|||||||
version = "0.1.35"
|
version = "0.1.35"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -11,20 +12,20 @@ license = "MIT"
|
|||||||
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
||||||
async-recursion = "1.0.5"
|
async-recursion = "1.0.5"
|
||||||
async-trait = "0.1.73"
|
async-trait = "0.1.73"
|
||||||
clap = { version = "4.4.6", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
clap = { version = "4.4.7", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
||||||
dashmap = "5.5.3"
|
dashmap = "5.5.3"
|
||||||
derive-docs = { version = "0.1.4" }
|
derive-docs = { version = "0.1.4" }
|
||||||
#derive-docs = { path = "../derive-docs" }
|
#derive-docs = { path = "../derive-docs" }
|
||||||
kittycad = { version = "0.2.33", default-features = false, features = ["js"] }
|
kittycad = { workspace = true }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
parse-display = "0.8.2"
|
parse-display = "0.8.2"
|
||||||
schemars = { version = "0.8", features = ["impl_json_schema", "url", "uuid1"] }
|
schemars = { version = "0.8", features = ["impl_json_schema", "url", "uuid1"] }
|
||||||
serde = { version = "1.0.189", features = ["derive"] }
|
serde = { version = "1.0.190", features = ["derive"] }
|
||||||
serde_json = "1.0.107"
|
serde_json = "1.0.108"
|
||||||
thiserror = "1.0.49"
|
thiserror = "1.0.50"
|
||||||
ts-rs = { version = "7", package = "ts-rs-json-value", features = ["serde-json-impl", "schemars-impl", "uuid-impl"] }
|
ts-rs = { version = "7", package = "ts-rs-json-value", features = ["serde-json-impl", "schemars-impl", "uuid-impl"] }
|
||||||
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
|
uuid = { version = "1.5.0", features = ["v4", "js", "serde"] }
|
||||||
winnow = "0.5.16"
|
winnow = "0.5.18"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
js-sys = { version = "0.3.64" }
|
js-sys = { version = "0.3.64" }
|
||||||
@ -35,7 +36,7 @@ web-sys = { version = "0.3.64", features = ["console"] }
|
|||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
|
bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
|
||||||
futures = { version = "0.3.28" }
|
futures = { version = "0.3.29" }
|
||||||
reqwest = { version = "0.11.22", default-features = false }
|
reqwest = { version = "0.11.22", default-features = false }
|
||||||
tokio = { version = "1.33.0", features = ["full"] }
|
tokio = { version = "1.33.0", features = ["full"] }
|
||||||
tokio-tungstenite = { version = "0.20.0", features = ["rustls-tls-native-roots"] }
|
tokio-tungstenite = { version = "0.20.0", features = ["rustls-tls-native-roots"] }
|
||||||
@ -50,6 +51,9 @@ engine = []
|
|||||||
panic = "abort"
|
panic = "abort"
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
[profile.bench]
|
||||||
|
debug = true # Flamegraphs of benchmarks require accurate debug symbols
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.5.1"
|
criterion = "0.5.1"
|
||||||
expectorate = "1.1.0"
|
expectorate = "1.1.0"
|
||||||
|
@ -11,6 +11,7 @@ pub fn bench_parse(c: &mut Criterion) {
|
|||||||
("pipes_on_pipes", PIPES_PROGRAM),
|
("pipes_on_pipes", PIPES_PROGRAM),
|
||||||
("big_kitt", KITT_PROGRAM),
|
("big_kitt", KITT_PROGRAM),
|
||||||
("cube", CUBE_PROGRAM),
|
("cube", CUBE_PROGRAM),
|
||||||
|
("math", MATH_PROGRAM),
|
||||||
] {
|
] {
|
||||||
let tokens = kcl_lib::token::lexer(file);
|
let tokens = kcl_lib::token::lexer(file);
|
||||||
c.bench_function(&format!("parse_{name}"), move |b| {
|
c.bench_function(&format!("parse_{name}"), move |b| {
|
||||||
@ -33,3 +34,4 @@ criterion_main!(benches);
|
|||||||
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
|
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
|
||||||
const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl");
|
const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl");
|
||||||
const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
|
const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
|
||||||
|
const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl");
|
||||||
|
@ -3,6 +3,7 @@ name = "kcl-lib-fuzz"
|
|||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
publish = false
|
publish = false
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
cargo-fuzz = true
|
cargo-fuzz = true
|
||||||
|
@ -136,7 +136,7 @@ pub async fn modify_ast_for_sketch(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut additional_lines = Vec::new();
|
let mut additional_lines = Vec::new();
|
||||||
let mut last_point = first_control_points.points[1].clone();
|
let mut last_point = first_control_points.points[1];
|
||||||
for control_point in control_points[1..].iter() {
|
for control_point in control_points[1..].iter() {
|
||||||
additional_lines.push([
|
additional_lines.push([
|
||||||
(control_point.points[1].x - last_point.x),
|
(control_point.points[1].x - last_point.x),
|
||||||
|
@ -7,14 +7,18 @@ use parse_display::{Display, FromStr};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Map;
|
use serde_json::Map;
|
||||||
|
use serde_json::Value as JValue;
|
||||||
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, DocumentSymbol, Range as LspRange, SymbolKind};
|
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, DocumentSymbol, Range as LspRange, SymbolKind};
|
||||||
|
|
||||||
|
pub use self::literal_value::LiteralValue;
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{ExecutorContext, MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal},
|
executor::{ExecutorContext, MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal},
|
||||||
parser::PIPE_OPERATOR,
|
parser::PIPE_OPERATOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod literal_value;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@ -371,13 +375,13 @@ impl BodyItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BodyItem> for crate::executor::SourceRange {
|
impl From<BodyItem> for SourceRange {
|
||||||
fn from(item: BodyItem) -> Self {
|
fn from(item: BodyItem) -> Self {
|
||||||
Self([item.start(), item.end()])
|
Self([item.start(), item.end()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&BodyItem> for crate::executor::SourceRange {
|
impl From<&BodyItem> for SourceRange {
|
||||||
fn from(item: &BodyItem) -> Self {
|
fn from(item: &BodyItem) -> Self {
|
||||||
Self([item.start(), item.end()])
|
Self([item.start(), item.end()])
|
||||||
}
|
}
|
||||||
@ -534,13 +538,13 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Value> for crate::executor::SourceRange {
|
impl From<Value> for SourceRange {
|
||||||
fn from(value: Value) -> Self {
|
fn from(value: Value) -> Self {
|
||||||
Self([value.start(), value.end()])
|
Self([value.start(), value.end()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Value> for crate::executor::SourceRange {
|
impl From<&Value> for SourceRange {
|
||||||
fn from(value: &Value) -> Self {
|
fn from(value: &Value) -> Self {
|
||||||
Self([value.start(), value.end()])
|
Self([value.start(), value.end()])
|
||||||
}
|
}
|
||||||
@ -558,13 +562,13 @@ pub enum BinaryPart {
|
|||||||
MemberExpression(Box<MemberExpression>),
|
MemberExpression(Box<MemberExpression>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BinaryPart> for crate::executor::SourceRange {
|
impl From<BinaryPart> for SourceRange {
|
||||||
fn from(value: BinaryPart) -> Self {
|
fn from(value: BinaryPart) -> Self {
|
||||||
Self([value.start(), value.end()])
|
Self([value.start(), value.end()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&BinaryPart> for crate::executor::SourceRange {
|
impl From<&BinaryPart> for SourceRange {
|
||||||
fn from(value: &BinaryPart) -> Self {
|
fn from(value: &BinaryPart) -> Self {
|
||||||
Self([value.start(), value.end()])
|
Self([value.start(), value.end()])
|
||||||
}
|
}
|
||||||
@ -640,7 +644,7 @@ impl BinaryPart {
|
|||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
ctx: &ExecutorContext,
|
ctx: &ExecutorContext,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
// We DO NOT set this globally because if we did and this was called inside a pipe it would
|
||||||
// stop the execution of the pipe.
|
// stop the execution of the pipe.
|
||||||
// THIS IS IMPORTANT.
|
// THIS IS IMPORTANT.
|
||||||
let mut new_pipe_info = pipe_info.clone();
|
let mut new_pipe_info = pipe_info.clone();
|
||||||
@ -930,7 +934,7 @@ impl CallExpression {
|
|||||||
binary_expression.get_result(memory, pipe_info, ctx).await?
|
binary_expression.get_result(memory, pipe_info, ctx).await?
|
||||||
}
|
}
|
||||||
Value::CallExpression(call_expression) => {
|
Value::CallExpression(call_expression) => {
|
||||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
// We DO NOT set this globally because if we did and this was called inside a pipe it would
|
||||||
// stop the execution of the pipe.
|
// stop the execution of the pipe.
|
||||||
// THIS IS IMPORTANT.
|
// THIS IS IMPORTANT.
|
||||||
let mut new_pipe_info = pipe_info.clone();
|
let mut new_pipe_info = pipe_info.clone();
|
||||||
@ -1312,24 +1316,18 @@ impl VariableDeclarator {
|
|||||||
pub struct Literal {
|
pub struct Literal {
|
||||||
pub start: usize,
|
pub start: usize,
|
||||||
pub end: usize,
|
pub end: usize,
|
||||||
pub value: serde_json::Value,
|
pub value: LiteralValue,
|
||||||
pub raw: String,
|
pub raw: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_value_meta!(Literal);
|
impl_value_meta!(Literal);
|
||||||
|
|
||||||
impl From<Literal> for Value {
|
|
||||||
fn from(literal: Literal) -> Self {
|
|
||||||
Value::Literal(Box::new(literal))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Literal {
|
impl Literal {
|
||||||
pub fn new(value: serde_json::Value) -> Self {
|
pub fn new(value: LiteralValue) -> Self {
|
||||||
Self {
|
Self {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
raw: value.to_string(),
|
raw: JValue::from(value.clone()).to_string(),
|
||||||
value,
|
value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1343,11 +1341,19 @@ impl Literal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn recast(&self) -> String {
|
fn recast(&self) -> String {
|
||||||
if let serde_json::Value::String(value) = &self.value {
|
match self.value {
|
||||||
let quote = if self.raw.trim().starts_with('"') { '"' } else { '\'' };
|
LiteralValue::Fractional(x) => {
|
||||||
format!("{}{}{}", quote, value, quote)
|
if x.fract() == 0.0 {
|
||||||
|
format!("{x:?}")
|
||||||
} else {
|
} else {
|
||||||
self.value.to_string()
|
self.raw.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LiteralValue::IInteger(_) => self.raw.clone(),
|
||||||
|
LiteralValue::String(ref s) => {
|
||||||
|
let quote = if self.raw.trim().starts_with('"') { '"' } else { '\'' };
|
||||||
|
format!("{quote}{s}{quote}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1355,7 +1361,7 @@ impl Literal {
|
|||||||
impl From<Literal> for MemoryItem {
|
impl From<Literal> for MemoryItem {
|
||||||
fn from(literal: Literal) -> Self {
|
fn from(literal: Literal) -> Self {
|
||||||
MemoryItem::UserVal(UserVal {
|
MemoryItem::UserVal(UserVal {
|
||||||
value: literal.value.clone(),
|
value: JValue::from(literal.value.clone()),
|
||||||
meta: vec![Metadata {
|
meta: vec![Metadata {
|
||||||
source_range: literal.into(),
|
source_range: literal.into(),
|
||||||
}],
|
}],
|
||||||
@ -1366,7 +1372,7 @@ impl From<Literal> for MemoryItem {
|
|||||||
impl From<&Box<Literal>> for MemoryItem {
|
impl From<&Box<Literal>> for MemoryItem {
|
||||||
fn from(literal: &Box<Literal>) -> Self {
|
fn from(literal: &Box<Literal>) -> Self {
|
||||||
MemoryItem::UserVal(UserVal {
|
MemoryItem::UserVal(UserVal {
|
||||||
value: literal.value.clone(),
|
value: JValue::from(literal.value.clone()),
|
||||||
meta: vec![Metadata {
|
meta: vec![Metadata {
|
||||||
source_range: literal.into(),
|
source_range: literal.into(),
|
||||||
}],
|
}],
|
||||||
@ -1552,7 +1558,7 @@ impl ArrayExpression {
|
|||||||
binary_expression.get_result(memory, pipe_info, ctx).await?
|
binary_expression.get_result(memory, pipe_info, ctx).await?
|
||||||
}
|
}
|
||||||
Value::CallExpression(call_expression) => {
|
Value::CallExpression(call_expression) => {
|
||||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
// We DO NOT set this globally because if we did and this was called inside a pipe it would
|
||||||
// stop the execution of the pipe.
|
// stop the execution of the pipe.
|
||||||
// THIS IS IMPORTANT.
|
// THIS IS IMPORTANT.
|
||||||
let mut new_pipe_info = pipe_info.clone();
|
let mut new_pipe_info = pipe_info.clone();
|
||||||
@ -1703,7 +1709,7 @@ impl ObjectExpression {
|
|||||||
binary_expression.get_result(memory, pipe_info, ctx).await?
|
binary_expression.get_result(memory, pipe_info, ctx).await?
|
||||||
}
|
}
|
||||||
Value::CallExpression(call_expression) => {
|
Value::CallExpression(call_expression) => {
|
||||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
// We DO NOT set this globally because if we did and this was called inside a pipe it would
|
||||||
// stop the execution of the pipe.
|
// stop the execution of the pipe.
|
||||||
// THIS IS IMPORTANT.
|
// THIS IS IMPORTANT.
|
||||||
let mut new_pipe_info = pipe_info.clone();
|
let mut new_pipe_info = pipe_info.clone();
|
||||||
@ -1831,13 +1837,13 @@ impl MemberObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MemberObject> for crate::executor::SourceRange {
|
impl From<MemberObject> for SourceRange {
|
||||||
fn from(obj: MemberObject) -> Self {
|
fn from(obj: MemberObject) -> Self {
|
||||||
Self([obj.start(), obj.end()])
|
Self([obj.start(), obj.end()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&MemberObject> for crate::executor::SourceRange {
|
impl From<&MemberObject> for SourceRange {
|
||||||
fn from(obj: &MemberObject) -> Self {
|
fn from(obj: &MemberObject) -> Self {
|
||||||
Self([obj.start(), obj.end()])
|
Self([obj.start(), obj.end()])
|
||||||
}
|
}
|
||||||
@ -1867,13 +1873,13 @@ impl LiteralIdentifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LiteralIdentifier> for crate::executor::SourceRange {
|
impl From<LiteralIdentifier> for SourceRange {
|
||||||
fn from(id: LiteralIdentifier) -> Self {
|
fn from(id: LiteralIdentifier) -> Self {
|
||||||
Self([id.start(), id.end()])
|
Self([id.start(), id.end()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&LiteralIdentifier> for crate::executor::SourceRange {
|
impl From<&LiteralIdentifier> for SourceRange {
|
||||||
fn from(id: &LiteralIdentifier) -> Self {
|
fn from(id: &LiteralIdentifier) -> Self {
|
||||||
Self([id.start(), id.end()])
|
Self([id.start(), id.end()])
|
||||||
}
|
}
|
||||||
@ -1967,17 +1973,21 @@ impl MemberExpression {
|
|||||||
LiteralIdentifier::Identifier(identifier) => identifier.name.to_string(),
|
LiteralIdentifier::Identifier(identifier) => identifier.name.to_string(),
|
||||||
LiteralIdentifier::Literal(literal) => {
|
LiteralIdentifier::Literal(literal) => {
|
||||||
let value = literal.value.clone();
|
let value = literal.value.clone();
|
||||||
// Parse this as a string.
|
match value {
|
||||||
if let serde_json::Value::String(string) = value {
|
LiteralValue::IInteger(x) if x >= 0 => return self.get_result_array(memory, x as usize),
|
||||||
string
|
LiteralValue::IInteger(x) => {
|
||||||
} else if let serde_json::Value::Number(_) = &value {
|
return Err(KclError::Syntax(KclErrorDetails {
|
||||||
// It can also be a number if we are getting a member of an array.
|
source_ranges: vec![self.into()],
|
||||||
return self.get_result_array(memory, parse_json_number_as_usize(&value, self.into())?);
|
message: format!("invalid index: {x}"),
|
||||||
} else {
|
}))
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
}
|
||||||
message: format!("Expected string literal or number for property name, found {:?}", value),
|
LiteralValue::Fractional(x) => {
|
||||||
source_ranges: vec![literal.into()],
|
return Err(KclError::Syntax(KclErrorDetails {
|
||||||
}));
|
source_ranges: vec![self.into()],
|
||||||
|
message: format!("invalid index: {x}"),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
LiteralValue::String(s) => s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -2133,7 +2143,7 @@ impl BinaryExpression {
|
|||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
ctx: &ExecutorContext,
|
ctx: &ExecutorContext,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
// We DO NOT set this globally because if we did and this was called inside a pipe it would
|
||||||
// stop the execution of the pipe.
|
// stop the execution of the pipe.
|
||||||
// THIS IS IMPORTANT.
|
// THIS IS IMPORTANT.
|
||||||
let mut new_pipe_info = pipe_info.clone();
|
let mut new_pipe_info = pipe_info.clone();
|
||||||
@ -2175,6 +2185,7 @@ impl BinaryExpression {
|
|||||||
BinaryOperator::Mul => (left * right).into(),
|
BinaryOperator::Mul => (left * right).into(),
|
||||||
BinaryOperator::Div => (left / right).into(),
|
BinaryOperator::Div => (left / right).into(),
|
||||||
BinaryOperator::Mod => (left % right).into(),
|
BinaryOperator::Mod => (left % right).into(),
|
||||||
|
BinaryOperator::Pow => (left.powf(right)).into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(MemoryItem::UserVal(UserVal {
|
Ok(MemoryItem::UserVal(UserVal {
|
||||||
@ -2208,22 +2219,6 @@ pub fn parse_json_number_as_f64(j: &serde_json::Value, source_range: SourceRange
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_json_number_as_usize(j: &serde_json::Value, source_range: SourceRange) -> Result<usize, KclError> {
|
|
||||||
if let serde_json::Value::Number(n) = &j {
|
|
||||||
Ok(n.as_i64().ok_or_else(|| {
|
|
||||||
KclError::Syntax(KclErrorDetails {
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
message: format!("Invalid index: {}", j),
|
|
||||||
})
|
|
||||||
})? as usize)
|
|
||||||
} else {
|
|
||||||
Err(KclError::Syntax(KclErrorDetails {
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
message: format!("Invalid index: {}", j),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_json_value_as_string(j: &serde_json::Value) -> Option<String> {
|
pub fn parse_json_value_as_string(j: &serde_json::Value) -> Option<String> {
|
||||||
if let serde_json::Value::String(n) = &j {
|
if let serde_json::Value::String(n) = &j {
|
||||||
Some(n.clone())
|
Some(n.clone())
|
||||||
@ -2257,13 +2252,46 @@ pub enum BinaryOperator {
|
|||||||
#[serde(rename = "%")]
|
#[serde(rename = "%")]
|
||||||
#[display("%")]
|
#[display("%")]
|
||||||
Mod,
|
Mod,
|
||||||
|
/// Raise a number to a power.
|
||||||
|
#[serde(rename = "^")]
|
||||||
|
#[display("^")]
|
||||||
|
Pow,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mathematical associativity.
|
||||||
|
/// Should a . b . c be read as (a . b) . c, or a . (b . c)
|
||||||
|
/// See <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#precedence_and_associativity> for more.
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||||
|
pub enum Associativity {
|
||||||
|
/// Read a . b . c as (a . b) . c
|
||||||
|
Left,
|
||||||
|
/// Read a . b . c as a . (b . c)
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Associativity {
|
||||||
|
pub fn is_left(&self) -> bool {
|
||||||
|
matches!(self, Self::Left)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BinaryOperator {
|
impl BinaryOperator {
|
||||||
|
/// Follow JS definitions of each operator.
|
||||||
|
/// Taken from <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table>
|
||||||
pub fn precedence(&self) -> u8 {
|
pub fn precedence(&self) -> u8 {
|
||||||
match &self {
|
match &self {
|
||||||
BinaryOperator::Add | BinaryOperator::Sub => 11,
|
BinaryOperator::Add | BinaryOperator::Sub => 11,
|
||||||
BinaryOperator::Mul | BinaryOperator::Div | BinaryOperator::Mod => 12,
|
BinaryOperator::Mul | BinaryOperator::Div | BinaryOperator::Mod => 12,
|
||||||
|
BinaryOperator::Pow => 6,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Follow JS definitions of each operator.
|
||||||
|
/// Taken from <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table>
|
||||||
|
pub fn associativity(&self) -> Associativity {
|
||||||
|
match self {
|
||||||
|
Self::Add | Self::Sub | Self::Mul | Self::Div | Self::Mod => Associativity::Left,
|
||||||
|
Self::Pow => Associativity::Right,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2307,7 +2335,7 @@ impl UnaryExpression {
|
|||||||
pipe_info: &mut PipeInfo,
|
pipe_info: &mut PipeInfo,
|
||||||
ctx: &ExecutorContext,
|
ctx: &ExecutorContext,
|
||||||
) -> Result<MemoryItem, KclError> {
|
) -> Result<MemoryItem, KclError> {
|
||||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
// We DO NOT set this globally because if we did and this was called inside a pipe it would
|
||||||
// stop the execution of the pipe.
|
// stop the execution of the pipe.
|
||||||
// THIS IS IMPORTANT.
|
// THIS IS IMPORTANT.
|
||||||
let mut new_pipe_info = pipe_info.clone();
|
let mut new_pipe_info = pipe_info.clone();
|
||||||
@ -3255,4 +3283,40 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
|
|||||||
let recasted = program.recast(&Default::default(), 0);
|
let recasted = program.recast(&Default::default(), 0);
|
||||||
assert_eq!(recasted.trim(), some_program_string);
|
assert_eq!(recasted.trim(), some_program_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recast_literal() {
|
||||||
|
use winnow::Parser;
|
||||||
|
for (i, (raw, expected, reason)) in [
|
||||||
|
(
|
||||||
|
"5.0",
|
||||||
|
"5.0",
|
||||||
|
"fractional numbers should stay fractional, i.e. don't reformat this to '5'",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"5",
|
||||||
|
"5",
|
||||||
|
"integers should stay integral, i.e. don't reformat this to '5.0'",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"5.0000000",
|
||||||
|
"5.0",
|
||||||
|
"if the number is f64 but not fractional, use its canonical format",
|
||||||
|
),
|
||||||
|
("5.1", "5.1", "straightforward case works"),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let tokens = crate::token::lexer(raw);
|
||||||
|
let literal = crate::parser::parser_impl::unsigned_number_literal
|
||||||
|
.parse(&tokens)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
literal.recast(),
|
||||||
|
expected,
|
||||||
|
"failed test {i}, which is testing that {reason}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
70
src/wasm-lib/kcl/src/ast/types/literal_value.rs
Normal file
70
src/wasm-lib/kcl/src/ast/types/literal_value.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value as JValue;
|
||||||
|
|
||||||
|
use super::{Literal, Value};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||||
|
pub enum LiteralValue {
|
||||||
|
IInteger(i64),
|
||||||
|
Fractional(f64),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Literal> for Value {
|
||||||
|
fn from(literal: Literal) -> Self {
|
||||||
|
Value::Literal(Box::new(literal))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LiteralValue> for JValue {
|
||||||
|
fn from(value: LiteralValue) -> Self {
|
||||||
|
match value {
|
||||||
|
LiteralValue::IInteger(x) => x.into(),
|
||||||
|
LiteralValue::Fractional(x) => x.into(),
|
||||||
|
LiteralValue::String(x) => x.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f64> for LiteralValue {
|
||||||
|
fn from(value: f64) -> Self {
|
||||||
|
Self::Fractional(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for LiteralValue {
|
||||||
|
fn from(value: i64) -> Self {
|
||||||
|
Self::IInteger(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for LiteralValue {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self::String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for LiteralValue {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
Self::IInteger(value as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<u16> for LiteralValue {
|
||||||
|
fn from(value: u16) -> Self {
|
||||||
|
Self::IInteger(value as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<u8> for LiteralValue {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
Self::IInteger(value as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&'static str> for LiteralValue {
|
||||||
|
fn from(value: &'static str) -> Self {
|
||||||
|
// TODO: Make this Cow<str>
|
||||||
|
Self::String(value.to_owned())
|
||||||
|
}
|
||||||
|
}
|
@ -183,7 +183,7 @@ impl DefaultPlanes {
|
|||||||
SourceRange::default(),
|
SourceRange::default(),
|
||||||
ModelingCmd::MakePlane {
|
ModelingCmd::MakePlane {
|
||||||
clobber: false,
|
clobber: false,
|
||||||
origin: default_origin.clone(),
|
origin: default_origin,
|
||||||
size: default_size,
|
size: default_size,
|
||||||
x_axis: Point3D { x: 1.0, y: 0.0, z: 0.0 },
|
x_axis: Point3D { x: 1.0, y: 0.0, z: 0.0 },
|
||||||
y_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
|
y_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
|
||||||
@ -216,7 +216,7 @@ impl DefaultPlanes {
|
|||||||
SourceRange::default(),
|
SourceRange::default(),
|
||||||
ModelingCmd::MakePlane {
|
ModelingCmd::MakePlane {
|
||||||
clobber: false,
|
clobber: false,
|
||||||
origin: default_origin.clone(),
|
origin: default_origin,
|
||||||
size: default_size,
|
size: default_size,
|
||||||
x_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
|
x_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
|
||||||
y_axis: Point3D { x: 0.0, y: 0.0, z: 1.0 },
|
y_axis: Point3D { x: 0.0, y: 0.0, z: 1.0 },
|
||||||
|
@ -5,7 +5,6 @@ pub mod docs;
|
|||||||
pub mod engine;
|
pub mod engine;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
pub mod math_parser;
|
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod std;
|
pub mod std;
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
134
src/wasm-lib/kcl/src/parser/math.rs
Normal file
134
src/wasm-lib/kcl/src/parser/math.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
use crate::ast::types::{BinaryExpression, BinaryOperator, BinaryPart};
|
||||||
|
|
||||||
|
/// Parses a list of tokens (in infix order, i.e. as the user typed them)
|
||||||
|
/// into a binary expression tree.
|
||||||
|
pub fn parse(infix_tokens: Vec<BinaryExpressionToken>) -> BinaryExpression {
|
||||||
|
let rpn = postfix(infix_tokens);
|
||||||
|
evaluate(rpn)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a list of tokens (in postfix order) into a binary expression tree.
|
||||||
|
fn evaluate(rpn: Vec<BinaryExpressionToken>) -> BinaryExpression {
|
||||||
|
let mut operand_stack = Vec::new();
|
||||||
|
for item in rpn {
|
||||||
|
let expr = match item {
|
||||||
|
BinaryExpressionToken::Operator(operator) => {
|
||||||
|
let right: BinaryPart = operand_stack.pop().unwrap();
|
||||||
|
let left = operand_stack.pop().unwrap();
|
||||||
|
BinaryPart::BinaryExpression(Box::new(BinaryExpression {
|
||||||
|
start: left.start(),
|
||||||
|
end: right.end(),
|
||||||
|
operator,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
BinaryExpressionToken::Operand(o) => o,
|
||||||
|
};
|
||||||
|
operand_stack.push(expr)
|
||||||
|
}
|
||||||
|
if let BinaryPart::BinaryExpression(expr) = operand_stack.pop().unwrap() {
|
||||||
|
*expr
|
||||||
|
} else {
|
||||||
|
panic!("Last expression was not a binary expression")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reorders tokens from infix order to postfix order.
|
||||||
|
fn postfix(infix: Vec<BinaryExpressionToken>) -> Vec<BinaryExpressionToken> {
|
||||||
|
let mut operator_stack: Vec<BinaryOperator> = Vec::with_capacity(infix.len());
|
||||||
|
let mut output = Vec::with_capacity(infix.len());
|
||||||
|
for token in infix {
|
||||||
|
match token {
|
||||||
|
BinaryExpressionToken::Operator(o1) => {
|
||||||
|
// From https://en.wikipedia.org/wiki/Shunting_yard_algorithm:
|
||||||
|
// while (
|
||||||
|
// there is an operator o2 at the top of the operator stack which is not a left parenthesis,
|
||||||
|
// and (o2 has greater precedence than o1 or (o1 and o2 have the same precedence and o1 is left-associative))
|
||||||
|
// )
|
||||||
|
while operator_stack
|
||||||
|
.last()
|
||||||
|
.map(|o2| {
|
||||||
|
(o2.precedence() > o1.precedence())
|
||||||
|
|| o1.precedence() == o2.precedence() && o1.associativity().is_left()
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
output.push(BinaryExpressionToken::Operator(operator_stack.pop().unwrap()));
|
||||||
|
}
|
||||||
|
operator_stack.push(o1);
|
||||||
|
}
|
||||||
|
o @ BinaryExpressionToken::Operand(_) => output.push(o),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// After the while loop, pop the remaining items from the operator stack into the output queue.
|
||||||
|
output.extend(operator_stack.into_iter().rev().map(BinaryExpressionToken::Operator));
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expressions are made up of operators and operands.
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum BinaryExpressionToken {
|
||||||
|
Operator(BinaryOperator),
|
||||||
|
Operand(BinaryPart),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BinaryPart> for BinaryExpressionToken {
|
||||||
|
fn from(value: BinaryPart) -> Self {
|
||||||
|
Self::Operand(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BinaryOperator> for BinaryExpressionToken {
|
||||||
|
fn from(value: BinaryOperator) -> Self {
|
||||||
|
Self::Operator(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::ast::types::Literal;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_and_evaluate() {
|
||||||
|
/// Make a literal
|
||||||
|
fn lit(n: i64) -> BinaryPart {
|
||||||
|
BinaryPart::Literal(Box::new(Literal {
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
value: n.into(),
|
||||||
|
raw: n.to_string(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
let tests: Vec<Vec<BinaryExpressionToken>> = vec![
|
||||||
|
// 3 + 4 × 2 ÷ ( 1 − 5 ) ^ 2 ^ 3
|
||||||
|
vec![
|
||||||
|
lit(3).into(),
|
||||||
|
BinaryOperator::Add.into(),
|
||||||
|
lit(4).into(),
|
||||||
|
BinaryOperator::Mul.into(),
|
||||||
|
lit(2).into(),
|
||||||
|
BinaryOperator::Div.into(),
|
||||||
|
BinaryPart::BinaryExpression(Box::new(BinaryExpression {
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
operator: BinaryOperator::Sub,
|
||||||
|
left: lit(1),
|
||||||
|
right: lit(5),
|
||||||
|
}))
|
||||||
|
.into(),
|
||||||
|
BinaryOperator::Pow.into(),
|
||||||
|
lit(2).into(),
|
||||||
|
BinaryOperator::Pow.into(),
|
||||||
|
lit(3).into(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
for infix_input in tests {
|
||||||
|
let rpn = postfix(infix_input);
|
||||||
|
let tree = evaluate(rpn);
|
||||||
|
dbg!(tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,28 @@
|
|||||||
use serde_json::{Number as JNumber, Value as JValue};
|
|
||||||
use winnow::{
|
use winnow::{
|
||||||
combinator::{alt, delimited, opt, peek, preceded, repeat, separated0, terminated},
|
combinator::{alt, delimited, opt, peek, preceded, repeat, separated0, terminated},
|
||||||
dispatch,
|
dispatch,
|
||||||
error::{ErrMode, StrContext, StrContextValue},
|
error::{ErrMode, StrContext, StrContextValue},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
token::any,
|
token::{any, one_of},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
ArrayExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression, CommentStyle,
|
ArrayExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression, CommentStyle,
|
||||||
ExpressionStatement, FunctionExpression, Identifier, Literal, LiteralIdentifier, MemberExpression,
|
ExpressionStatement, FunctionExpression, Identifier, Literal, LiteralIdentifier, LiteralValue,
|
||||||
MemberObject, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty, PipeExpression,
|
MemberExpression, MemberObject, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty,
|
||||||
PipeSubstitution, Program, ReturnStatement, UnaryExpression, UnaryOperator, Value, VariableDeclaration,
|
PipeExpression, PipeSubstitution, Program, ReturnStatement, UnaryExpression, UnaryOperator, Value,
|
||||||
VariableDeclarator, VariableKind,
|
VariableDeclaration, VariableDeclarator, VariableKind,
|
||||||
},
|
},
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::SourceRange,
|
executor::SourceRange,
|
||||||
math_parser::MathParser,
|
|
||||||
parser::parser_impl::error::ContextError,
|
parser::parser_impl::error::ContextError,
|
||||||
std::StdLib,
|
std::StdLib,
|
||||||
token::{Token, TokenType},
|
token::{Token, TokenType},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::{math::BinaryExpressionToken, PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR};
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
type PResult<O, E = error::ContextError> = winnow::prelude::PResult<O, E>;
|
type PResult<O, E = error::ContextError> = winnow::prelude::PResult<O, E>;
|
||||||
@ -77,8 +77,7 @@ fn non_code_node(i: TokenSlice) -> PResult<NonCodeNode> {
|
|||||||
/// Matches one case of NonCodeValue
|
/// Matches one case of NonCodeValue
|
||||||
/// See docstring on [NonCodeValue::NewLineBlockComment] for why that case is different to the others.
|
/// See docstring on [NonCodeValue::NewLineBlockComment] for why that case is different to the others.
|
||||||
fn non_code_node_leading_whitespace(i: TokenSlice) -> PResult<NonCodeNode> {
|
fn non_code_node_leading_whitespace(i: TokenSlice) -> PResult<NonCodeNode> {
|
||||||
let leading_whitespace = any
|
let leading_whitespace = one_of(TokenType::Whitespace)
|
||||||
.verify(|token: &Token| token.token_type == TokenType::Whitespace)
|
|
||||||
.context(expected("whitespace, with a newline"))
|
.context(expected("whitespace, with a newline"))
|
||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
let has_empty_line = count_in('\n', &leading_whitespace.value) >= 2;
|
let has_empty_line = count_in('\n', &leading_whitespace.value) >= 2;
|
||||||
@ -216,7 +215,7 @@ pub fn string_literal(i: TokenSlice) -> PResult<Literal> {
|
|||||||
.try_map(|token: Token| match token.token_type {
|
.try_map(|token: Token| match token.token_type {
|
||||||
TokenType::String => {
|
TokenType::String => {
|
||||||
let s = token.value[1..token.value.len() - 1].to_string();
|
let s = token.value[1..token.value.len() - 1].to_string();
|
||||||
Ok((JValue::String(s), token))
|
Ok((LiteralValue::from(s), token))
|
||||||
}
|
}
|
||||||
_ => Err(KclError::Syntax(KclErrorDetails {
|
_ => Err(KclError::Syntax(KclErrorDetails {
|
||||||
source_ranges: token.as_source_ranges(),
|
source_ranges: token.as_source_ranges(),
|
||||||
@ -234,12 +233,12 @@ pub fn string_literal(i: TokenSlice) -> PResult<Literal> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a KCL literal number, with no - sign.
|
/// Parse a KCL literal number, with no - sign.
|
||||||
fn unsigned_number_literal(i: TokenSlice) -> PResult<Literal> {
|
pub(crate) fn unsigned_number_literal(i: TokenSlice) -> PResult<Literal> {
|
||||||
let (value, token) = any
|
let (value, token) = any
|
||||||
.try_map(|token: Token| match token.token_type {
|
.try_map(|token: Token| match token.token_type {
|
||||||
TokenType::Number => {
|
TokenType::Number => {
|
||||||
if let Ok(x) = token.value.parse::<i64>() {
|
if let Ok(x) = token.value.parse::<u64>() {
|
||||||
return Ok((JValue::Number(JNumber::from(x)), token));
|
return Ok((LiteralValue::IInteger(x as i64), token));
|
||||||
}
|
}
|
||||||
let x: f64 = token.value.parse().map_err(|_| {
|
let x: f64 = token.value.parse().map_err(|_| {
|
||||||
KclError::Syntax(KclErrorDetails {
|
KclError::Syntax(KclErrorDetails {
|
||||||
@ -248,13 +247,7 @@ fn unsigned_number_literal(i: TokenSlice) -> PResult<Literal> {
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
match JNumber::from_f64(x) {
|
Ok((LiteralValue::Fractional(x), token))
|
||||||
Some(n) => Ok((JValue::Number(n), token)),
|
|
||||||
None => Err(KclError::Syntax(KclErrorDetails {
|
|
||||||
source_ranges: token.as_source_ranges(),
|
|
||||||
message: format!("Invalid float: {}", token.value),
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => Err(KclError::Syntax(KclErrorDetails {
|
_ => Err(KclError::Syntax(KclErrorDetails {
|
||||||
source_ranges: token.as_source_ranges(),
|
source_ranges: token.as_source_ranges(),
|
||||||
@ -378,7 +371,7 @@ fn whitespace(i: TokenSlice) -> PResult<Vec<Token>> {
|
|||||||
|
|
||||||
/// Parse the = operator.
|
/// Parse the = operator.
|
||||||
fn equals(i: TokenSlice) -> PResult<Token> {
|
fn equals(i: TokenSlice) -> PResult<Token> {
|
||||||
any.verify(|token: &Token| matches!(token.token_type, TokenType::Operator) && token.value == "=")
|
one_of((TokenType::Operator, "="))
|
||||||
.context(expected("the equals operator, ="))
|
.context(expected("the equals operator, ="))
|
||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
@ -404,10 +397,11 @@ fn integer_range(i: TokenSlice) -> PResult<Vec<Value>> {
|
|||||||
let (_token1, ceiling) = integer.parse_next(i)?;
|
let (_token1, ceiling) = integer.parse_next(i)?;
|
||||||
Ok((floor..=ceiling)
|
Ok((floor..=ceiling)
|
||||||
.map(|num| {
|
.map(|num| {
|
||||||
|
let num = num as i64;
|
||||||
Value::Literal(Box::new(Literal {
|
Value::Literal(Box::new(Literal {
|
||||||
start: token0.start,
|
start: token0.start,
|
||||||
end: token0.end,
|
end: token0.end,
|
||||||
value: JValue::Number(num.into()),
|
value: num.into(),
|
||||||
raw: num.to_string(),
|
raw: num.to_string(),
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
@ -456,7 +450,7 @@ fn object(i: TokenSlice) -> PResult<ObjectExpression> {
|
|||||||
/// Parse the % symbol, used to substitute a curried argument from a |> (pipe).
|
/// Parse the % symbol, used to substitute a curried argument from a |> (pipe).
|
||||||
fn pipe_sub(i: TokenSlice) -> PResult<PipeSubstitution> {
|
fn pipe_sub(i: TokenSlice) -> PResult<PipeSubstitution> {
|
||||||
any.try_map(|token: Token| {
|
any.try_map(|token: Token| {
|
||||||
if matches!(token.token_type, TokenType::Operator) && token.value == "%" {
|
if matches!(token.token_type, TokenType::Operator) && token.value == PIPE_SUBSTITUTION_OPERATOR {
|
||||||
Ok(PipeSubstitution {
|
Ok(PipeSubstitution {
|
||||||
start: token.start,
|
start: token.start,
|
||||||
end: token.end,
|
end: token.end,
|
||||||
@ -1026,35 +1020,33 @@ fn unary_expression(i: TokenSlice) -> PResult<UnaryExpression> {
|
|||||||
/// Consume tokens that make up a binary expression, but don't actually return them.
|
/// Consume tokens that make up a binary expression, but don't actually return them.
|
||||||
/// Why not?
|
/// Why not?
|
||||||
/// Because this is designed to be used with .recognize() within the `binary_expression` parser.
|
/// Because this is designed to be used with .recognize() within the `binary_expression` parser.
|
||||||
fn binary_expression_tokens(i: TokenSlice) -> PResult<()> {
|
fn binary_expression_tokens(i: TokenSlice) -> PResult<Vec<BinaryExpressionToken>> {
|
||||||
let _first = operand.parse_next(i)?;
|
let first = operand.parse_next(i).map(BinaryExpressionToken::from)?;
|
||||||
let _remaining: Vec<_> = repeat(
|
let remaining: Vec<_> = repeat(
|
||||||
1..,
|
1..,
|
||||||
(
|
(
|
||||||
preceded(opt(whitespace), binary_operator),
|
preceded(opt(whitespace), binary_operator).map(BinaryExpressionToken::from),
|
||||||
preceded(opt(whitespace), operand),
|
preceded(opt(whitespace), operand).map(BinaryExpressionToken::from),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.context(expected(
|
.context(expected(
|
||||||
"one or more binary operators (like + or -) and operands for them, e.g. 1 + 2 - 3",
|
"one or more binary operators (like + or -) and operands for them, e.g. 1 + 2 - 3",
|
||||||
))
|
))
|
||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
Ok(())
|
let mut out = Vec::with_capacity(1 + 2 * remaining.len());
|
||||||
|
out.push(first);
|
||||||
|
out.extend(remaining.into_iter().flat_map(|(a, b)| [a, b]));
|
||||||
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an infix binary expression.
|
/// Parse an infix binary expression.
|
||||||
fn binary_expression(i: TokenSlice) -> PResult<BinaryExpression> {
|
fn binary_expression(i: TokenSlice) -> PResult<BinaryExpression> {
|
||||||
// Find the slice of tokens which makes up the binary expression
|
// Find the slice of tokens which makes up the binary expression
|
||||||
let tokens = binary_expression_tokens.recognize().parse_next(i)?;
|
let tokens = binary_expression_tokens.parse_next(i)?;
|
||||||
|
|
||||||
// Pass the token slice into the specialized math parser, for things like
|
// Pass the token slice into the specialized math parser, for things like
|
||||||
// precedence and converting infix operations to an AST.
|
// precedence and converting infix operations to an AST.
|
||||||
let mut math_parser = MathParser::new(tokens);
|
Ok(super::math::parse(tokens))
|
||||||
let expr = math_parser
|
|
||||||
.parse()
|
|
||||||
.map_err(error::ContextError::from)
|
|
||||||
.map_err(ErrMode::Backtrack)?;
|
|
||||||
Ok(expr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binary_expr_in_parens(i: TokenSlice) -> PResult<BinaryExpression> {
|
fn binary_expr_in_parens(i: TokenSlice) -> PResult<BinaryExpression> {
|
||||||
@ -1103,8 +1095,7 @@ fn expression(i: TokenSlice) -> PResult<ExpressionStatement> {
|
|||||||
|
|
||||||
/// Parse a KCL integer, and the token that held it.
|
/// Parse a KCL integer, and the token that held it.
|
||||||
fn integer(i: TokenSlice) -> PResult<(Token, u64)> {
|
fn integer(i: TokenSlice) -> PResult<(Token, u64)> {
|
||||||
let num = any
|
let num = one_of(TokenType::Number)
|
||||||
.verify(|token: &Token| matches!(token.token_type, TokenType::Number))
|
|
||||||
.context(expected("a number token e.g. 3"))
|
.context(expected("a number token e.g. 3"))
|
||||||
.try_map(|token: Token| {
|
.try_map(|token: Token| {
|
||||||
let source_ranges = token.as_source_ranges();
|
let source_ranges = token.as_source_ranges();
|
||||||
@ -1123,20 +1114,20 @@ fn integer(i: TokenSlice) -> PResult<(Token, u64)> {
|
|||||||
|
|
||||||
/// Parse the given brace symbol.
|
/// Parse the given brace symbol.
|
||||||
fn some_brace(symbol: &'static str, i: TokenSlice) -> PResult<Token> {
|
fn some_brace(symbol: &'static str, i: TokenSlice) -> PResult<Token> {
|
||||||
any.verify(|token: &Token| matches!(token.token_type, TokenType::Brace) && token.value == symbol)
|
one_of((TokenType::Brace, symbol))
|
||||||
.context(expected(symbol))
|
.context(expected(symbol))
|
||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a => operator.
|
/// Parse a => operator.
|
||||||
fn big_arrow(i: TokenSlice) -> PResult<Token> {
|
fn big_arrow(i: TokenSlice) -> PResult<Token> {
|
||||||
any.verify(|token: &Token| matches!(token.token_type, TokenType::Operator) && token.value == "=>")
|
one_of((TokenType::Operator, "=>"))
|
||||||
.context(expected("the => symbol, used for declaring functions"))
|
.context(expected("the => symbol, used for declaring functions"))
|
||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
/// Parse a |> operator.
|
/// Parse a |> operator.
|
||||||
fn pipe_operator(i: TokenSlice) -> PResult<Token> {
|
fn pipe_operator(i: TokenSlice) -> PResult<Token> {
|
||||||
any.verify(|token: &Token| matches!(token.token_type, TokenType::Operator) && token.value == "|>")
|
one_of((TokenType::Operator, PIPE_OPERATOR))
|
||||||
.context(expected(
|
.context(expected(
|
||||||
"the |> operator, used for 'piping' one function's output into another function's input",
|
"the |> operator, used for 'piping' one function's output into another function's input",
|
||||||
))
|
))
|
||||||
@ -1144,7 +1135,8 @@ fn pipe_operator(i: TokenSlice) -> PResult<Token> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ws_with_newline(i: TokenSlice) -> PResult<Token> {
|
fn ws_with_newline(i: TokenSlice) -> PResult<Token> {
|
||||||
any.verify(|token: &Token| matches!(token.token_type, TokenType::Whitespace) && token.value.contains('\n'))
|
one_of(TokenType::Whitespace)
|
||||||
|
.verify(|token: &Token| token.value.contains('\n'))
|
||||||
.context(expected("a newline, possibly with whitespace"))
|
.context(expected("a newline, possibly with whitespace"))
|
||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
@ -1461,7 +1453,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
argument: Value::Literal(Box::new(Literal {
|
argument: Value::Literal(Box::new(Literal {
|
||||||
start: 32,
|
start: 32,
|
||||||
end: 33,
|
end: 33,
|
||||||
value: JValue::Number(JNumber::from(2)),
|
value: 2u32.into(),
|
||||||
raw: "2".to_owned(),
|
raw: "2".to_owned(),
|
||||||
})),
|
})),
|
||||||
})],
|
})],
|
||||||
@ -1616,7 +1608,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
BinaryPart::Literal(Box::new(Literal {
|
BinaryPart::Literal(Box::new(Literal {
|
||||||
start: 9,
|
start: 9,
|
||||||
end: 10,
|
end: 10,
|
||||||
value: JValue::Number(JNumber::from(3)),
|
value: 3u32.into(),
|
||||||
raw: "3".to_owned(),
|
raw: "3".to_owned(),
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
@ -1758,19 +1750,11 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
{
|
{
|
||||||
// Run the original parser
|
// Run the original parser
|
||||||
let tokens = crate::token::lexer(test_program);
|
let tokens = crate::token::lexer(test_program);
|
||||||
let expected = crate::parser::Parser::new(tokens.clone())
|
// TODO: get snapshots of what this outputs.
|
||||||
.ast_old()
|
let _actual = match program.parse(&tokens) {
|
||||||
.expect("Old parser failed");
|
|
||||||
|
|
||||||
// Run the second parser, check it matches the first parser.
|
|
||||||
let actual = match program.parse(&tokens) {
|
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(_e) => panic!("could not parse test {i}"),
|
Err(_e) => panic!("could not parse test {i}"),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
|
||||||
expected, actual,
|
|
||||||
"old parser (left) and new parser (right) disagree on test {i}"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1784,11 +1768,11 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
let BinaryPart::Literal(left) = actual.left else {
|
let BinaryPart::Literal(left) = actual.left else {
|
||||||
panic!("should be expression");
|
panic!("should be expression");
|
||||||
};
|
};
|
||||||
assert_eq!(left.value, serde_json::Value::Number(1.into()));
|
assert_eq!(left.value, 1u32.into());
|
||||||
let BinaryPart::Literal(right) = actual.right else {
|
let BinaryPart::Literal(right) = actual.right else {
|
||||||
panic!("should be expression");
|
panic!("should be expression");
|
||||||
};
|
};
|
||||||
assert_eq!(right.value, serde_json::Value::Number(2.into()));
|
assert_eq!(right.value, 2u32.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1967,12 +1951,10 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
let parsed_literal = literal.parse(&tokens).unwrap();
|
let parsed_literal = literal.parse(&tokens).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parsed_literal.value,
|
parsed_literal.value,
|
||||||
JValue::String(
|
|
||||||
"
|
"
|
||||||
// a comment
|
// a comment
|
||||||
"
|
"
|
||||||
.to_owned()
|
.into()
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2065,4 +2047,702 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
assert_eq!(value.raw, "5");
|
assert_eq!(value.raw, "5");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_math_parse() {
|
||||||
|
let tokens = crate::token::lexer(r#"5 + "a""#);
|
||||||
|
let actual = crate::parser::Parser::new(tokens).ast().unwrap().body;
|
||||||
|
let expr = BinaryExpression {
|
||||||
|
start: 0,
|
||||||
|
end: 7,
|
||||||
|
operator: BinaryOperator::Add,
|
||||||
|
left: BinaryPart::Literal(Box::new(Literal {
|
||||||
|
start: 0,
|
||||||
|
end: 1,
|
||||||
|
value: 5u32.into(),
|
||||||
|
raw: "5".to_owned(),
|
||||||
|
})),
|
||||||
|
right: BinaryPart::Literal(Box::new(Literal {
|
||||||
|
start: 4,
|
||||||
|
end: 7,
|
||||||
|
value: "a".into(),
|
||||||
|
raw: r#""a""#.to_owned(),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
let expected = vec![BodyItem::ExpressionStatement(ExpressionStatement {
|
||||||
|
start: 0,
|
||||||
|
end: 7,
|
||||||
|
expression: Value::BinaryExpression(Box::new(expr)),
|
||||||
|
})];
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_code_token() {
|
||||||
|
let tokens = [
|
||||||
|
Token {
|
||||||
|
token_type: TokenType::Word,
|
||||||
|
start: 0,
|
||||||
|
end: 3,
|
||||||
|
value: "log".to_string(),
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
token_type: TokenType::Brace,
|
||||||
|
start: 3,
|
||||||
|
end: 4,
|
||||||
|
value: "(".to_string(),
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
token_type: TokenType::Number,
|
||||||
|
start: 4,
|
||||||
|
end: 5,
|
||||||
|
value: "5".to_string(),
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
token_type: TokenType::Comma,
|
||||||
|
start: 5,
|
||||||
|
end: 6,
|
||||||
|
value: ",".to_string(),
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
token_type: TokenType::String,
|
||||||
|
start: 7,
|
||||||
|
end: 14,
|
||||||
|
value: "\"hello\"".to_string(),
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
token_type: TokenType::Word,
|
||||||
|
start: 16,
|
||||||
|
end: 27,
|
||||||
|
value: "aIdentifier".to_string(),
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
token_type: TokenType::Brace,
|
||||||
|
start: 27,
|
||||||
|
end: 28,
|
||||||
|
value: ")".to_string(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
for (i, token) in tokens.iter().enumerate() {
|
||||||
|
assert!(token.is_code_token(), "failed test {i}: {token:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_not_code_token() {
|
||||||
|
let tokens = [
|
||||||
|
Token {
|
||||||
|
token_type: TokenType::Whitespace,
|
||||||
|
start: 6,
|
||||||
|
end: 7,
|
||||||
|
value: " ".to_string(),
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
token_type: TokenType::BlockComment,
|
||||||
|
start: 28,
|
||||||
|
end: 30,
|
||||||
|
value: "/* abte */".to_string(),
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
token_type: TokenType::LineComment,
|
||||||
|
start: 30,
|
||||||
|
end: 33,
|
||||||
|
value: "// yoyo a line".to_string(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
for (i, token) in tokens.iter().enumerate() {
|
||||||
|
assert!(!token.is_code_token(), "failed test {i}: {token:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_abstract_syntax_tree() {
|
||||||
|
let code = "5 +6";
|
||||||
|
let parser = crate::parser::Parser::new(crate::token::lexer(code));
|
||||||
|
let result = parser.ast().unwrap();
|
||||||
|
let expected_result = Program {
|
||||||
|
start: 0,
|
||||||
|
end: 4,
|
||||||
|
body: vec![BodyItem::ExpressionStatement(ExpressionStatement {
|
||||||
|
start: 0,
|
||||||
|
end: 4,
|
||||||
|
expression: Value::BinaryExpression(Box::new(BinaryExpression {
|
||||||
|
start: 0,
|
||||||
|
end: 4,
|
||||||
|
left: BinaryPart::Literal(Box::new(Literal {
|
||||||
|
start: 0,
|
||||||
|
end: 1,
|
||||||
|
value: 5u32.into(),
|
||||||
|
raw: "5".to_string(),
|
||||||
|
})),
|
||||||
|
operator: BinaryOperator::Add,
|
||||||
|
right: BinaryPart::Literal(Box::new(Literal {
|
||||||
|
start: 3,
|
||||||
|
end: 4,
|
||||||
|
value: 6u32.into(),
|
||||||
|
raw: "6".to_string(),
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
})],
|
||||||
|
non_code_meta: NonCodeMeta::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(result, expected_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_file() {
|
||||||
|
let some_program_string = r#""#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result.err().unwrap().to_string().contains("file is empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_half_pipe_small() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
"const secondExtrude = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0,0], %)
|
||||||
|
|",
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result.err().unwrap().to_string().contains("Unexpected token"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_member_expression_double_nested_braces() {
|
||||||
|
let tokens = crate::token::lexer(r#"const prop = yo["one"][two]"#);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_member_expression_binary_expression_period_number_first() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"const obj = { a: 1, b: 2 }
|
||||||
|
const height = 1 - obj.a"#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_member_expression_binary_expression_brace_number_first() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"const obj = { a: 1, b: 2 }
|
||||||
|
const height = 1 - obj["a"]"#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_member_expression_binary_expression_brace_number_second() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"const obj = { a: 1, b: 2 }
|
||||||
|
const height = obj["a"] - 1"#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_member_expression_binary_expression_in_array_number_first() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"const obj = { a: 1, b: 2 }
|
||||||
|
const height = [1 - obj["a"], 0]"#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_member_expression_binary_expression_in_array_number_second() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"const obj = { a: 1, b: 2 }
|
||||||
|
const height = [obj["a"] - 1, 0]"#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_member_expression_binary_expression_in_array_number_second_missing_space() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"const obj = { a: 1, b: 2 }
|
||||||
|
const height = [obj["a"] -1, 0]"#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_half_pipe() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
"const height = 10
|
||||||
|
|
||||||
|
const firstExtrude = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0,0], %)
|
||||||
|
|> line([0, 8], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, -8], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(2, %)
|
||||||
|
|
||||||
|
show(firstExtrude)
|
||||||
|
|
||||||
|
const secondExtrude = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0,0], %)
|
||||||
|
|",
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result.err().unwrap().to_string().contains("Unexpected token"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_greater_bang() {
|
||||||
|
let tokens = crate::token::lexer(">!");
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let err = parser.ast().unwrap_err();
|
||||||
|
// TODO: Better errors when program cannot tokenize.
|
||||||
|
// https://github.com/KittyCAD/modeling-app/issues/696
|
||||||
|
assert!(err.to_string().contains("file is empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_z_percent_parens() {
|
||||||
|
let tokens = crate::token::lexer("z%)");
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result.err().unwrap().to_string().contains("Unexpected token"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_parens_unicode() {
|
||||||
|
let tokens = crate::token::lexer("(ޜ");
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
// TODO: Better errors when program cannot tokenize.
|
||||||
|
// https://github.com/KittyCAD/modeling-app/issues/696
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_negative_in_array_binary_expression() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"const leg1 = 5
|
||||||
|
const thickness = 0.56
|
||||||
|
|
||||||
|
const bracket = [-leg2 + thickness, 0]
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_nested_open_brackets() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"
|
||||||
|
z(-[["#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_weird_new_line_function() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"z
|
||||||
|
(--#"#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
// TODO: Better errors when program cannot tokenize.
|
||||||
|
// https://github.com/KittyCAD/modeling-app/issues/696
|
||||||
|
assert_eq!(
|
||||||
|
result.err().unwrap().to_string(),
|
||||||
|
r#"syntax: KclErrorDetails { source_ranges: [], message: "file is empty" }"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_weird_lots_of_fancy_brackets() {
|
||||||
|
let tokens = crate::token::lexer(r#"zz({{{{{{{{)iegAng{{{{{{{##"#);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
// TODO: Better errors when program cannot tokenize.
|
||||||
|
// https://github.com/KittyCAD/modeling-app/issues/696
|
||||||
|
assert_eq!(
|
||||||
|
result.err().unwrap().to_string(),
|
||||||
|
r#"syntax: KclErrorDetails { source_ranges: [], message: "file is empty" }"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_weird_close_before_open() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"fn)n
|
||||||
|
e
|
||||||
|
["#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
.contains("expected whitespace, found ')' which is brace"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_weird_close_before_nada() {
|
||||||
|
let tokens = crate::token::lexer(r#"fn)n-"#);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
.contains("expected whitespace, found ')' which is brace"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_weird_lots_of_slashes() {
|
||||||
|
let tokens = crate::token::lexer(
|
||||||
|
r#"J///////////o//+///////////P++++*++++++P///////˟
|
||||||
|
++4"#,
|
||||||
|
);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result.err().unwrap().to_string().contains("Unexpected token"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_expand_array() {
|
||||||
|
let code = "const myArray = [0..10]";
|
||||||
|
let parser = crate::parser::Parser::new(crate::token::lexer(code));
|
||||||
|
let result = parser.ast().unwrap();
|
||||||
|
let expected_result = Program {
|
||||||
|
start: 0,
|
||||||
|
end: 23,
|
||||||
|
body: vec![BodyItem::VariableDeclaration(VariableDeclaration {
|
||||||
|
start: 0,
|
||||||
|
end: 23,
|
||||||
|
declarations: vec![VariableDeclarator {
|
||||||
|
start: 6,
|
||||||
|
end: 23,
|
||||||
|
id: Identifier {
|
||||||
|
start: 6,
|
||||||
|
end: 13,
|
||||||
|
name: "myArray".to_string(),
|
||||||
|
},
|
||||||
|
init: Value::ArrayExpression(Box::new(ArrayExpression {
|
||||||
|
start: 16,
|
||||||
|
end: 23,
|
||||||
|
elements: vec![
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 0u32.into(),
|
||||||
|
raw: "0".to_string(),
|
||||||
|
})),
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 1u32.into(),
|
||||||
|
raw: "1".to_string(),
|
||||||
|
})),
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 2u32.into(),
|
||||||
|
raw: "2".to_string(),
|
||||||
|
})),
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 3u32.into(),
|
||||||
|
raw: "3".to_string(),
|
||||||
|
})),
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 4u32.into(),
|
||||||
|
raw: "4".to_string(),
|
||||||
|
})),
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 5u32.into(),
|
||||||
|
raw: "5".to_string(),
|
||||||
|
})),
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 6u32.into(),
|
||||||
|
raw: "6".to_string(),
|
||||||
|
})),
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 7u32.into(),
|
||||||
|
raw: "7".to_string(),
|
||||||
|
})),
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 8u32.into(),
|
||||||
|
raw: "8".to_string(),
|
||||||
|
})),
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 9u32.into(),
|
||||||
|
raw: "9".to_string(),
|
||||||
|
})),
|
||||||
|
Value::Literal(Box::new(Literal {
|
||||||
|
start: 17,
|
||||||
|
end: 18,
|
||||||
|
value: 10u32.into(),
|
||||||
|
raw: "10".to_string(),
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
})),
|
||||||
|
}],
|
||||||
|
kind: VariableKind::Const,
|
||||||
|
})],
|
||||||
|
non_code_meta: NonCodeMeta::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(result, expected_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_keyword_in_variable() {
|
||||||
|
let some_program_string = r#"const let = "thing""#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.err().unwrap().to_string(),
|
||||||
|
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([6, 9])], message: "Cannot assign a variable to a reserved keyword: let" }"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_keyword_in_fn_name() {
|
||||||
|
let some_program_string = r#"fn let = () {}"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.err().unwrap().to_string(),
|
||||||
|
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 6])], message: "Cannot assign a variable to a reserved keyword: let" }"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_stdlib_in_fn_name() {
|
||||||
|
let some_program_string = r#"fn cos = () => {
|
||||||
|
return 1
|
||||||
|
}"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.err().unwrap().to_string(),
|
||||||
|
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 6])], message: "Cannot assign a variable to a reserved keyword: cos" }"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_keyword_in_fn_args() {
|
||||||
|
let some_program_string = r#"fn thing = (let) => {
|
||||||
|
return 1
|
||||||
|
}"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.err().unwrap().to_string(),
|
||||||
|
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([12, 15])], message: "Cannot assign a variable to a reserved keyword: let" }"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_stdlib_in_fn_args() {
|
||||||
|
let some_program_string = r#"fn thing = (cos) => {
|
||||||
|
return 1
|
||||||
|
}"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.err().unwrap().to_string(),
|
||||||
|
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([12, 15])], message: "Cannot assign a variable to a reserved keyword: cos" }"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zero_param_function() {
|
||||||
|
let program = r#"
|
||||||
|
fn firstPrimeNumber = () => {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
firstPrimeNumber()
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(program);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let _ast = parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keyword_ok_in_fn_args_return() {
|
||||||
|
let some_program_string = r#"fn thing = (param) => {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
thing(false)
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_define_function_as_var() {
|
||||||
|
for name in ["var", "let", "const"] {
|
||||||
|
let some_program_string = format!(
|
||||||
|
r#"{} thing = (param) => {{
|
||||||
|
return true
|
||||||
|
}}
|
||||||
|
|
||||||
|
thing(false)
|
||||||
|
"#,
|
||||||
|
name
|
||||||
|
);
|
||||||
|
let tokens = crate::token::lexer(&some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.err().unwrap().to_string(),
|
||||||
|
format!(
|
||||||
|
r#"syntax: KclErrorDetails {{ source_ranges: [SourceRange([0, {}])], message: "Expected a `fn` variable kind, found: `{}`" }}"#,
|
||||||
|
name.len(),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_define_var_as_function() {
|
||||||
|
let some_program_string = r#"fn thing = "thing""#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let result = parser.ast();
|
||||||
|
assert!(result.is_err());
|
||||||
|
// TODO: https://github.com/KittyCAD/modeling-app/issues/784
|
||||||
|
// Improve this error message.
|
||||||
|
// It should say that the compiler is expecting a function expression on the RHS.
|
||||||
|
assert_eq!(
|
||||||
|
result.err().unwrap().to_string(),
|
||||||
|
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([11, 18])], message: "Unexpected token" }"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_member_expression_sketch_group() {
|
||||||
|
let some_program_string = r#"fn cube = (pos, scale) => {
|
||||||
|
const sg = startSketchOn('XY')
|
||||||
|
|> startProfileAt(pos, %)
|
||||||
|
|> line([0, scale], %)
|
||||||
|
|> line([scale, 0], %)
|
||||||
|
|> line([0, -scale], %)
|
||||||
|
|
||||||
|
return sg
|
||||||
|
}
|
||||||
|
|
||||||
|
const b1 = cube([0,0], 10)
|
||||||
|
const b2 = cube([3,3], 4)
|
||||||
|
|
||||||
|
const pt1 = b1[0]
|
||||||
|
const pt2 = b2[0]
|
||||||
|
|
||||||
|
show(b1)
|
||||||
|
show(b2)"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_math_with_stdlib() {
|
||||||
|
let some_program_string = r#"const d2r = pi() / 2
|
||||||
|
let other_thing = 2 * cos(3)"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_negative_arguments() {
|
||||||
|
let some_program_string = r#"fn box = (p, h, l, w) => {
|
||||||
|
const myBox = startSketchOn('XY')
|
||||||
|
|> startProfileAt(p, %)
|
||||||
|
|> line([0, l], %)
|
||||||
|
|> line([w, 0], %)
|
||||||
|
|> line([0, -l], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(h, %)
|
||||||
|
|
||||||
|
return myBox
|
||||||
|
}
|
||||||
|
let myBox = box([0,0], -3, -16, -10)
|
||||||
|
show(myBox)"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
parser.ast().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_math() {
|
||||||
|
for math_expression in [
|
||||||
|
"1 + 2",
|
||||||
|
"1+2",
|
||||||
|
"1 -2",
|
||||||
|
"1 + 2 * 3",
|
||||||
|
"1 * ( 2 + 3 )",
|
||||||
|
"1 * ( 2 + 3 ) / 4",
|
||||||
|
"1 + ( 2 + 3 ) / 4",
|
||||||
|
"1 * (( 2 + 3 ) / 4 + 5 )",
|
||||||
|
"1 * ((( 2 + 3 )))",
|
||||||
|
"distance * p * FOS * 6 / (sigmaAllow * width)",
|
||||||
|
"2 + (((3)))",
|
||||||
|
] {
|
||||||
|
let tokens = crate::token::lexer(math_expression);
|
||||||
|
let _expr = binary_expression.parse(&tokens).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,11 @@ use schemars::JsonSchema;
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{MemoryItem, SketchGroup},
|
executor::{MemoryItem, SketchGroup},
|
||||||
std::{utils::Angle, Args},
|
std::Args,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::utils::between;
|
||||||
|
|
||||||
/// Returns the segment end of x.
|
/// Returns the segment end of x.
|
||||||
pub async fn segment_end_x(args: Args) -> Result<MemoryItem, KclError> {
|
pub async fn segment_end_x(args: Args) -> Result<MemoryItem, KclError> {
|
||||||
let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
|
let (segment_name, sketch_group) = args.get_segment_name_sketch_group()?;
|
||||||
@ -174,7 +176,7 @@ fn inner_segment_angle(segment_name: &str, sketch_group: Box<SketchGroup>, args:
|
|||||||
})?;
|
})?;
|
||||||
let line = path.get_base();
|
let line = path.get_base();
|
||||||
|
|
||||||
let result = Angle::between(line.from.into(), line.to.into());
|
let result = between(line.from.into(), line.to.into());
|
||||||
|
|
||||||
Ok(result.degrees())
|
Ok(result.degrees())
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use derive_docs::stdlib;
|
use derive_docs::stdlib;
|
||||||
use kittycad::types::{ModelingCmd, Point3D};
|
use kittycad::types::{Angle, ModelingCmd, Point3D};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::utils::Angle;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{
|
executor::{
|
||||||
@ -334,6 +333,16 @@ pub enum AngledLineData {
|
|||||||
AngleAndLength([f64; 2]),
|
AngleAndLength([f64; 2]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AngledLineData {
|
||||||
|
pub fn into_inner_line(self, to: [f64; 2]) -> LineData {
|
||||||
|
if let AngledLineData::AngleWithTag { tag, .. } = self {
|
||||||
|
LineData::PointWithTag { to, tag }
|
||||||
|
} else {
|
||||||
|
LineData::Point(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw an angled line.
|
/// Draw an angled line.
|
||||||
pub async fn angled_line(args: Args) -> Result<MemoryItem, KclError> {
|
pub async fn angled_line(args: Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AngledLineData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AngledLineData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
|
||||||
@ -429,16 +438,7 @@ async fn inner_angled_line_of_x_length(
|
|||||||
|
|
||||||
let to = get_y_component(Angle::from_degrees(angle), length);
|
let to = get_y_component(Angle::from_degrees(angle), length);
|
||||||
|
|
||||||
let new_sketch_group = inner_line(
|
let new_sketch_group = inner_line(data.into_inner_line(to.into()), sketch_group, args).await?;
|
||||||
if let AngledLineData::AngleWithTag { tag, .. } = data {
|
|
||||||
LineData::PointWithTag { to: to.into(), tag }
|
|
||||||
} else {
|
|
||||||
LineData::Point(to.into())
|
|
||||||
},
|
|
||||||
sketch_group,
|
|
||||||
args,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(new_sketch_group)
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
@ -461,6 +461,16 @@ pub enum AngledLineToData {
|
|||||||
AngleAndPoint([f64; 2]),
|
AngleAndPoint([f64; 2]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AngledLineToData {
|
||||||
|
pub fn into_inner_line(self, x_to: f64, y_to: f64) -> LineToData {
|
||||||
|
if let AngledLineToData::AngleWithTag { tag, .. } = self {
|
||||||
|
LineToData::PointWithTag { to: [x_to, y_to], tag }
|
||||||
|
} else {
|
||||||
|
LineToData::Point([x_to, y_to])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw an angled line to a given x coordinate.
|
/// Draw an angled line to a given x coordinate.
|
||||||
pub async fn angled_line_to_x(args: Args) -> Result<MemoryItem, KclError> {
|
pub async fn angled_line_to_x(args: Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AngledLineToData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AngledLineToData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
|
||||||
@ -488,16 +498,7 @@ async fn inner_angled_line_to_x(
|
|||||||
let y_component = x_component * f64::tan(angle.to_radians());
|
let y_component = x_component * f64::tan(angle.to_radians());
|
||||||
let y_to = from.y + y_component;
|
let y_to = from.y + y_component;
|
||||||
|
|
||||||
let new_sketch_group = inner_line_to(
|
let new_sketch_group = inner_line_to(data.into_inner_line(x_to, y_to), sketch_group, args).await?;
|
||||||
if let AngledLineToData::AngleWithTag { tag, .. } = data {
|
|
||||||
LineToData::PointWithTag { to: [x_to, y_to], tag }
|
|
||||||
} else {
|
|
||||||
LineToData::Point([x_to, y_to])
|
|
||||||
},
|
|
||||||
sketch_group,
|
|
||||||
args,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(new_sketch_group)
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,16 +527,7 @@ async fn inner_angled_line_of_y_length(
|
|||||||
|
|
||||||
let to = get_x_component(Angle::from_degrees(angle), length);
|
let to = get_x_component(Angle::from_degrees(angle), length);
|
||||||
|
|
||||||
let new_sketch_group = inner_line(
|
let new_sketch_group = inner_line(data.into_inner_line(to.into()), sketch_group, args).await?;
|
||||||
if let AngledLineData::AngleWithTag { tag, .. } = data {
|
|
||||||
LineData::PointWithTag { to: to.into(), tag }
|
|
||||||
} else {
|
|
||||||
LineData::Point(to.into())
|
|
||||||
},
|
|
||||||
sketch_group,
|
|
||||||
args,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(new_sketch_group)
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
@ -567,16 +559,7 @@ async fn inner_angled_line_to_y(
|
|||||||
let x_component = y_component / f64::tan(angle.to_radians());
|
let x_component = y_component / f64::tan(angle.to_radians());
|
||||||
let x_to = from.x + x_component;
|
let x_to = from.x + x_component;
|
||||||
|
|
||||||
let new_sketch_group = inner_line_to(
|
let new_sketch_group = inner_line_to(data.into_inner_line(x_to, y_to), sketch_group, args).await?;
|
||||||
if let AngledLineToData::AngleWithTag { tag, .. } = data {
|
|
||||||
LineToData::PointWithTag { to: [x_to, y_to], tag }
|
|
||||||
} else {
|
|
||||||
LineToData::Point([x_to, y_to])
|
|
||||||
},
|
|
||||||
sketch_group,
|
|
||||||
args,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(new_sketch_group)
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,7 +568,7 @@ async fn inner_angled_line_to_y(
|
|||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
// TODO: make sure the docs on the args below are correct.
|
// TODO: make sure the docs on the args below are correct.
|
||||||
pub struct AngeledLineThatIntersectsData {
|
pub struct AngledLineThatIntersectsData {
|
||||||
/// The angle of the line.
|
/// The angle of the line.
|
||||||
pub angle: f64,
|
pub angle: f64,
|
||||||
/// The tag of the line to intersect with.
|
/// The tag of the line to intersect with.
|
||||||
@ -598,7 +581,7 @@ pub struct AngeledLineThatIntersectsData {
|
|||||||
|
|
||||||
/// Draw an angled line that intersects with a given line.
|
/// Draw an angled line that intersects with a given line.
|
||||||
pub async fn angled_line_that_intersects(args: Args) -> Result<MemoryItem, KclError> {
|
pub async fn angled_line_that_intersects(args: Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, sketch_group): (AngeledLineThatIntersectsData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
|
let (data, sketch_group): (AngledLineThatIntersectsData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
|
||||||
let new_sketch_group = inner_angled_line_that_intersects(data, sketch_group, args).await?;
|
let new_sketch_group = inner_angled_line_that_intersects(data, sketch_group, args).await?;
|
||||||
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
}
|
}
|
||||||
@ -608,7 +591,7 @@ pub async fn angled_line_that_intersects(args: Args) -> Result<MemoryItem, KclEr
|
|||||||
name = "angledLineThatIntersects",
|
name = "angledLineThatIntersects",
|
||||||
}]
|
}]
|
||||||
async fn inner_angled_line_that_intersects(
|
async fn inner_angled_line_that_intersects(
|
||||||
data: AngeledLineThatIntersectsData,
|
data: AngledLineThatIntersectsData,
|
||||||
sketch_group: Box<SketchGroup>,
|
sketch_group: Box<SketchGroup>,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Box<SketchGroup>, KclError> {
|
) -> Result<Box<SketchGroup>, KclError> {
|
||||||
@ -1049,6 +1032,8 @@ async fn inner_arc(data: ArcData, sketch_group: Box<SketchGroup>, args: Args) ->
|
|||||||
segment: kittycad::types::PathSegment::Arc {
|
segment: kittycad::types::PathSegment::Arc {
|
||||||
angle_start: angle_start.degrees(),
|
angle_start: angle_start.degrees(),
|
||||||
angle_end: angle_end.degrees(),
|
angle_end: angle_end.degrees(),
|
||||||
|
start: Some(angle_start),
|
||||||
|
end: Some(angle_end),
|
||||||
center: center.into(),
|
center: center.into(),
|
||||||
radius,
|
radius,
|
||||||
relative: false,
|
relative: false,
|
||||||
@ -1148,40 +1133,12 @@ async fn inner_tangential_arc(
|
|||||||
to.into()
|
to.into()
|
||||||
}
|
}
|
||||||
TangentialArcData::PointWithTag { to, .. } => {
|
TangentialArcData::PointWithTag { to, .. } => {
|
||||||
args.send_modeling_cmd(
|
args.send_modeling_cmd(id, tan_arc_to(&sketch_group, to)).await?;
|
||||||
id,
|
|
||||||
ModelingCmd::ExtendPath {
|
|
||||||
path: sketch_group.id,
|
|
||||||
segment: kittycad::types::PathSegment::TangentialArcTo {
|
|
||||||
angle_snap_increment: None,
|
|
||||||
to: kittycad::types::Point3D {
|
|
||||||
x: to[0],
|
|
||||||
y: to[1],
|
|
||||||
z: 0.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
*to
|
*to
|
||||||
}
|
}
|
||||||
TangentialArcData::Point(to) => {
|
TangentialArcData::Point(to) => {
|
||||||
args.send_modeling_cmd(
|
args.send_modeling_cmd(id, tan_arc_to(&sketch_group, to)).await?;
|
||||||
id,
|
|
||||||
ModelingCmd::ExtendPath {
|
|
||||||
path: sketch_group.id,
|
|
||||||
segment: kittycad::types::PathSegment::TangentialArcTo {
|
|
||||||
angle_snap_increment: None,
|
|
||||||
to: kittycad::types::Point3D {
|
|
||||||
x: to[0],
|
|
||||||
y: to[1],
|
|
||||||
z: 0.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
*to
|
*to
|
||||||
}
|
}
|
||||||
@ -1207,6 +1164,20 @@ async fn inner_tangential_arc(
|
|||||||
Ok(new_sketch_group)
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tan_arc_to(sketch_group: &SketchGroup, to: &[f64; 2]) -> ModelingCmd {
|
||||||
|
ModelingCmd::ExtendPath {
|
||||||
|
path: sketch_group.id,
|
||||||
|
segment: kittycad::types::PathSegment::TangentialArcTo {
|
||||||
|
angle_snap_increment: None,
|
||||||
|
to: Point3D {
|
||||||
|
x: to[0],
|
||||||
|
y: to[1],
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Data to draw a tangential arc to a specific point.
|
/// Data to draw a tangential arc to a specific point.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -1247,24 +1218,8 @@ async fn inner_tangential_arc_to(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let delta = [to[0] - from.x, to[1] - from.y];
|
let delta = [to[0] - from.x, to[1] - from.y];
|
||||||
|
|
||||||
let id = uuid::Uuid::new_v4();
|
let id = uuid::Uuid::new_v4();
|
||||||
|
args.send_modeling_cmd(id, tan_arc_to(&sketch_group, &delta)).await?;
|
||||||
args.send_modeling_cmd(
|
|
||||||
id,
|
|
||||||
ModelingCmd::ExtendPath {
|
|
||||||
path: sketch_group.id,
|
|
||||||
segment: kittycad::types::PathSegment::TangentialArcTo {
|
|
||||||
angle_snap_increment: None,
|
|
||||||
to: kittycad::types::Point3D {
|
|
||||||
x: delta[0],
|
|
||||||
y: delta[1],
|
|
||||||
z: 0.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let current_path = Path::ToPoint {
|
let current_path = Path::ToPoint {
|
||||||
base: BasePath {
|
base: BasePath {
|
||||||
|
@ -1,54 +1,26 @@
|
|||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
|
use kittycad::types::Angle;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{Point2d, SourceRange},
|
executor::{Point2d, SourceRange},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, PartialOrd, PartialEq, Debug)]
|
|
||||||
pub struct Angle {
|
|
||||||
degrees: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<kittycad::types::Angle> for Angle {
|
|
||||||
fn from(angle: kittycad::types::Angle) -> Self {
|
|
||||||
match angle.unit {
|
|
||||||
kittycad::types::UnitAngle::Degrees => Self::from_degrees(angle.value),
|
|
||||||
kittycad::types::UnitAngle::Radians => Self::from_radians(angle.value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Angle {
|
|
||||||
const ZERO: Self = Self { degrees: 0.0 };
|
|
||||||
/// Make an angle of the given degrees.
|
|
||||||
pub fn from_degrees(degrees: f64) -> Self {
|
|
||||||
Self { degrees }
|
|
||||||
}
|
|
||||||
/// Make an angle of the given radians.
|
|
||||||
pub fn from_radians(radians: f64) -> Self {
|
|
||||||
Self::from_degrees(radians.to_degrees())
|
|
||||||
}
|
|
||||||
/// Get the angle in degrees
|
|
||||||
pub fn degrees(&self) -> f64 {
|
|
||||||
self.degrees
|
|
||||||
}
|
|
||||||
/// Get the angle in radians
|
|
||||||
pub fn radians(&self) -> f64 {
|
|
||||||
self.degrees.to_radians()
|
|
||||||
}
|
|
||||||
/// Get the angle between these points
|
/// Get the angle between these points
|
||||||
pub fn between(a: Point2d, b: Point2d) -> Self {
|
pub fn between(a: Point2d, b: Point2d) -> Angle {
|
||||||
let x = b.x - a.x;
|
let x = b.x - a.x;
|
||||||
let y = b.y - a.y;
|
let y = b.y - a.y;
|
||||||
Self::from_radians(y.atan2(x)).normalize()
|
normalize(Angle::from_radians(y.atan2(x)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalize the angle
|
/// Normalize the angle
|
||||||
pub fn normalize(self) -> Self {
|
pub fn normalize(angle: Angle) -> Angle {
|
||||||
let angle = self.degrees();
|
let deg = angle.degrees();
|
||||||
let result = ((angle % 360.0) + 360.0) % 360.0;
|
let result = ((deg % 360.0) + 360.0) % 360.0;
|
||||||
Self::from_degrees(if result > 180.0 { result - 360.0 } else { result })
|
Angle::from_degrees(if result > 180.0 { result - 360.0 } else { result })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gives the ▲-angle between from and to angles (shortest path), use radians.
|
/// Gives the ▲-angle between from and to angles (shortest path), use radians.
|
||||||
///
|
///
|
||||||
/// Sign of the returned angle denotes direction, positive means counterClockwise 🔄
|
/// Sign of the returned angle denotes direction, positive means counterClockwise 🔄
|
||||||
@ -65,7 +37,7 @@ impl Angle {
|
|||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn delta(from_angle: Self, to_angle: Self) -> Self {
|
pub fn delta(from_angle: Angle, to_angle: Angle) -> Angle {
|
||||||
let norm_from_angle = normalize_rad(from_angle.radians());
|
let norm_from_angle = normalize_rad(from_angle.radians());
|
||||||
let norm_to_angle = normalize_rad(to_angle.radians());
|
let norm_to_angle = normalize_rad(to_angle.radians());
|
||||||
let provisional = norm_to_angle - norm_from_angle;
|
let provisional = norm_to_angle - norm_from_angle;
|
||||||
@ -81,7 +53,6 @@ impl Angle {
|
|||||||
}
|
}
|
||||||
Angle::ZERO
|
Angle::ZERO
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn clockwise_sign(points: &[Point2d]) -> i32 {
|
pub fn clockwise_sign(points: &[Point2d]) -> i32 {
|
||||||
|
@ -5,6 +5,7 @@ use parse_display::{Display, FromStr};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tower_lsp::lsp_types::SemanticTokenType;
|
use tower_lsp::lsp_types::SemanticTokenType;
|
||||||
|
use winnow::stream::ContainsToken;
|
||||||
|
|
||||||
use crate::{ast::types::VariableKind, executor::SourceRange};
|
use crate::{ast::types::VariableKind, executor::SourceRange};
|
||||||
|
|
||||||
@ -129,6 +130,18 @@ pub struct Token {
|
|||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ContainsToken<Token> for (TokenType, &str) {
|
||||||
|
fn contains_token(&self, token: Token) -> bool {
|
||||||
|
self.0 == token.token_type && self.1 == token.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContainsToken<Token> for TokenType {
|
||||||
|
fn contains_token(&self, token: Token) -> bool {
|
||||||
|
*self == token.token_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
pub fn from_range(range: std::ops::Range<usize>, token_type: TokenType, value: String) -> Self {
|
pub fn from_range(range: std::ops::Range<usize>, token_type: TokenType, value: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
20
src/wasm-lib/tests/executor/inputs/math.kcl
Normal file
20
src/wasm-lib/tests/executor/inputs/math.kcl
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const svg = startSketchOn('XY')
|
||||||
|
|> lineTo([0])
|
||||||
|
|> lineTo([0 + 1])
|
||||||
|
|> lineTo([0 + 1 + 2])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17])
|
||||||
|
|> lineTo([0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18])
|
40
wdio.conf.js
Normal file
40
wdio.conf.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
const os = require('os')
|
||||||
|
const path = require('path')
|
||||||
|
const { spawn } = require('child_process')
|
||||||
|
|
||||||
|
// keep track of the `tauri-driver` child process
|
||||||
|
let tauriDriver
|
||||||
|
|
||||||
|
const application =
|
||||||
|
process.env.E2E_APPLICATION || './src-tauri/target/release/kittycad-modeling'
|
||||||
|
|
||||||
|
exports.config = {
|
||||||
|
port: 4444,
|
||||||
|
specs: ['./e2e/tauri/specs/**/*.js'],
|
||||||
|
maxInstances: 1,
|
||||||
|
capabilities: [
|
||||||
|
{
|
||||||
|
maxInstances: 1,
|
||||||
|
'tauri:options': {
|
||||||
|
application,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
reporters: ['spec'],
|
||||||
|
framework: 'mocha',
|
||||||
|
mochaOpts: {
|
||||||
|
ui: 'bdd',
|
||||||
|
timeout: 600000,
|
||||||
|
},
|
||||||
|
|
||||||
|
// ensure we are running `tauri-driver` before the session starts so that we can proxy the webdriver requests
|
||||||
|
beforeSession: () =>
|
||||||
|
(tauriDriver = spawn(
|
||||||
|
path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'),
|
||||||
|
[],
|
||||||
|
{ stdio: [null, process.stdout, process.stderr] }
|
||||||
|
)),
|
||||||
|
|
||||||
|
// clean up the `tauri-driver` process we spawned at the start of the session
|
||||||
|
afterSession: () => tauriDriver.kill(),
|
||||||
|
}
|
Reference in New Issue
Block a user