Compare commits

...

67 Commits

Author SHA1 Message Date
589cd39eec Cut release v0.9.4 (#759)
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2023-10-02 20:09:53 -04:00
63feebef5c add report a bug button (#758)
* add report a bug button

* format
2023-10-02 16:33:33 -07:00
65037abd9a new sample code w tangental arc (#756)
* new sample code w tangental arc

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

* line 6

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

* make sure sample code works in test

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-10-02 16:14:09 -07:00
97bc339a62 Bump tauri-plugin-fs-extra from 9af4c37 to 9b96996 in /src-tauri (#751)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `9af4c37` to `9b96996`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](9af4c3727c...9b96996b5a)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-02 12:27:24 -07:00
4e9a6375a5 Bump tauri-build from 1.4.1 to 1.5.0 in /src-tauri (#752)
Bumps [tauri-build](https://github.com/tauri-apps/tauri) from 1.4.1 to 1.5.0.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-build-v1.4.1...tauri-build-v1.5)

---
updated-dependencies:
- dependency-name: tauri-build
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-02 12:27:16 -07:00
3d19dfb800 Cut release v0.9.3 (#753) 2023-10-02 14:14:00 -04:00
d2a7b84292 Request a feature should open externally (#737)
Fixes #659
2023-10-02 13:26:23 -04:00
9e02bab155 Explicitly close the Engine Connection when ICE fails (#732)
This will avoid letting the stack think its ready until it times out,
and will hopefully help with faster retry logic in the future

Signed-off-by: Paul Tagliamonte <paul@kittycad.io>
2023-10-02 09:50:49 -04:00
7352de5a70 Bump vitest from 0.34.1 to 0.34.6 (#747)
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 0.34.1 to 0.34.6.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v0.34.6/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-30 12:24:39 +10:00
9797d0cb81 Fix pipe expression start (#746) 2023-09-29 22:19:20 -04:00
83907fa9db adds tangentalArc and tangentalArcTo to the stdlib (#748)
* start of tangental arc

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

* updates

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

* update docs

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

* fixes

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

* updates

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-29 14:41:14 -07:00
a367be4e2b add gizmo (#738)
* add gizmo

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

* updates

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

* fixups

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

* fix force ast execution

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-29 12:41:58 -07:00
056fa00adc Bump tauri from 1.4.1 to 1.5.0 in /src-tauri (#742)
Bumps [tauri](https://github.com/tauri-apps/tauri) from 1.4.1 to 1.5.0.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-v1.4.1...tauri-v1.5.0)

---
updated-dependencies:
- dependency-name: tauri
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 12:03:35 -07:00
4759fb2e6f Bump postcss from 8.4.27 to 8.4.31 (#743)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.27 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.27...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 11:12:08 -07:00
45f497d9cd Bump tauri-build from 1.4.0 to 1.4.1 in /src-tauri (#741)
Bumps [tauri-build](https://github.com/tauri-apps/tauri) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-build-v1.4...tauri-build-v1.4.1)

---
updated-dependencies:
- dependency-name: tauri-build
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 11:11:57 -07:00
dc61bdebdf Bump tauri-plugin-fs-extra from 3d27909 to 9af4c37 in /src-tauri (#740)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `3d27909` to `9af4c37`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](3d279094d4...9af4c3727c)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 11:11:42 -07:00
61943055e5 refactor a bit before more rust port (#739)
* partially there

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

* cleanup

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

* fixes

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

* fixups

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-29 11:11:01 -07:00
416fe0f644 Bump clap from 4.4.5 to 4.4.6 in /src/wasm-lib (#744)
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.5 to 4.4.6.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.5...v4.4.6)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 12:41:22 -04:00
708465d818 Bump kittycad from 0.2.27 to 0.2.28 in /src-tauri (#734)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.27 to 0.2.28.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.27...v0.2.28)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-28 18:09:56 -07:00
e706fb02d6 Bump openapitor from d3e98c4 to fa0345c in /src/wasm-lib (#733)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `d3e98c4` to `fa0345c`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](d3e98c4ec0...fa0345c514)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-28 18:09:46 -07:00
1bf7daa474 Bump tauri-plugin-fs-extra from 6c7a4c0 to 3d27909 in /src-tauri (#735)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `6c7a4c0` to `3d27909`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](6c7a4c0984...3d279094d4)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-28 18:09:31 -07:00
ffc47f8f40 actually use the units the user sets (#727)
* actually use the units the user sets

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

* update kittycad lib

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

* next

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

* fix tests

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

* updates

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

* fix bug

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

* fix types

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-27 15:42:16 -07:00
768aaa84f6 Bump clap from 4.4.4 to 4.4.5 in /src/wasm-lib (#711)
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.4 to 4.4.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.4...v4.4.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 09:10:07 -07:00
f3a700eec8 Bump expectorate from 1.0.7 to 1.1.0 in /src/wasm-lib (#712)
Bumps [expectorate](https://github.com/oxidecomputer/expectorate) from 1.0.7 to 1.1.0.
- [Release notes](https://github.com/oxidecomputer/expectorate/releases)
- [Commits](https://github.com/oxidecomputer/expectorate/compare/v1.0.7...v1.1.0)

---
updated-dependencies:
- dependency-name: expectorate
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:39:16 -07:00
c853637a9a Bump thiserror from 1.0.48 to 1.0.49 in /src/wasm-lib (#721)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.48 to 1.0.49.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.48...1.0.49)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:39:03 -07:00
9af30d9ef6 Bump kittycad from 0.2.26 to 0.2.27 in /src/wasm-lib (#722)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.26 to 0.2.27.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.26...v0.2.27)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:38:49 -07:00
6164714a6b Bump openapitor from 61a1605 to d3e98c4 in /src/wasm-lib (#723)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `61a1605` to `d3e98c4`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](61a16059b3...d3e98c4ec0)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:38:37 -07:00
64ceb98eba Bump toml from 0.8.0 to 0.8.1 in /src-tauri (#724)
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.0 to 0.8.1.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.0...toml-v0.8.1)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:38:25 -07:00
2cbf260900 Bump tauri-plugin-fs-extra from b04bde3 to 6c7a4c0 in /src-tauri (#725)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `b04bde3` to `6c7a4c0`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](b04bde3461...6c7a4c0984)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:38:14 -07:00
cfaaedf602 Bump kittycad from 0.2.26 to 0.2.27 in /src-tauri (#726)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.26 to 0.2.27.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.26...v0.2.27)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:37:54 -07:00
12b3717eb5 Cut release v0.9.2 (#714) 2023-09-26 20:39:05 -04:00
0bc685b0c4 Bump tungstenite from 0.20.0 to 0.20.1 in /src/wasm-lib/kcl/fuzz (#709)
Bumps [tungstenite](https://github.com/snapview/tungstenite-rs) from 0.20.0 to 0.20.1.
- [Changelog](https://github.com/snapview/tungstenite-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/snapview/tungstenite-rs/compare/v0.20.0...v0.20.1)

---
updated-dependencies:
- dependency-name: tungstenite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 20:49:16 -07:00
9ee032771a unused dep (#710) 2023-09-26 03:22:05 +00:00
c307ddd1b1 resize (#706)
* start of resize

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

* refactor

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

* check if 0

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

* updates

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

* updates

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

* will work w new lib

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

* new types

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

* handle resize effect

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
2023-09-25 19:49:53 -07:00
a30818ff2b fixes negative args in function (#707)
* updates

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

* fixes

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

* updates

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

* fixes

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

* fixes

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-25 15:25:58 -07:00
53e763d938 fix close arc (#704)
* fix close arc

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

* much bigger radius

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-25 12:14:41 -07:00
8f74cd1d0c Bump tauri-plugin-fs-extra from 0190f68 to b04bde3 in /src-tauri (#702)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `0190f68` to `b04bde3`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](0190f68f1d...b04bde3461)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 09:33:53 -07:00
c271942897 remove errors (#703) 2023-09-25 07:28:03 +00:00
a03d09b41d Restructure tokenizer module (#700)
* Remove duplicated tests

These tests already were copied to tokeniser2.rs, so removing them doesn't affect code coverage.

* Move tokeniser to its own module

Now there's a module for tokens, and the tokenizer/lexer implementation is private within the token module.
2023-09-24 20:01:17 -05:00
2971b7752b Bump rust websocket libraries (#701)
Changelog: https://github.com/snapview/tokio-tungstenite/blob/master/CHANGELOG.md#0201
2023-09-24 21:34:31 +00:00
70e99eb00b Refactor is_code_token into a method (#699)
* Refactor is_code_token into a method

* Fix typos, use Parser as it was imported
2023-09-24 21:11:36 +00:00
5c66af59d2 New tokenizer based on winnow (#697)
* New tokenizer, using Winnow instead of regexes.

Between 1.3x and 4.4x speedup on lexer benchmarks :)

* Use dispatch instead of alt

Most of the time, if you know the first character of a token, you can narrow down its potential possible token types, instead of just trying each token type until one succeeds.

This further speeds up the lexer. Compared to main, this branch is now between 3x and 12x faster than main.
2023-09-22 21:57:39 -05:00
6dda6daeef Use separate benchmarks for lexing and parsing (#698) 2023-09-23 02:01:18 +00:00
b5387f1220 Cut release v0.9.1 (#693)
* bump to ️v0.9.1️

* update bump instructions

* readme update

* read me again

* change pr convention
2023-09-22 10:38:17 +10:00
fd5921b366 Convert the lexer to be iterative not recursive (#691)
This is often more memory-efficient (does not create a bunch of stack
frames)
2023-09-21 19:19:08 -05:00
716ad938fc stop gap for large files making editor slow (#690)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-21 16:13:22 -07:00
40136eb392 Bump kittycad from 0.2.25 to 0.2.26 in /src-tauri (#680)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.25 to 0.2.26.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.25...v0.2.26)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 14:05:55 -07:00
8d2b89fcd1 Bump openapitor from 0d121f6 to 61a1605 in /src/wasm-lib (#679)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `0d121f6` to `61a1605`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](0d121f6881...61a16059b3)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 15:22:04 -05:00
ad9fba3390 Bump tauri-plugin-fs-extra from 76832e6 to 0190f68 in /src-tauri (#681)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `76832e6` to `0190f68`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](76832e60bf...0190f68f1d)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 15:21:08 -05:00
911c43af50 Bump phonenumber from 0.3.2+8.13.9 to 0.3.3+8.13.9 in /src-tauri (#687)
Bumps [phonenumber](https://github.com/whisperfish/rust-phonenumber) from 0.3.2+8.13.9 to 0.3.3+8.13.9.
- [Commits](https://github.com/whisperfish/rust-phonenumber/commits)

---
updated-dependencies:
- dependency-name: phonenumber
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 15:02:37 -05:00
ab4e04f6c2 Bump phonenumber from 0.3.2+8.13.9 to 0.3.3+8.13.9 in /src/wasm-lib (#685)
Bumps [phonenumber](https://github.com/whisperfish/rust-phonenumber) from 0.3.2+8.13.9 to 0.3.3+8.13.9.
- [Commits](https://github.com/whisperfish/rust-phonenumber/commits)

---
updated-dependencies:
- dependency-name: phonenumber
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 15:02:23 -05:00
94aef05f74 Bump phonenumber from 0.3.2+8.13.9 to 0.3.3+8.13.9 in /src/wasm-lib/kcl/fuzz (#686)
Bump phonenumber in /src/wasm-lib/kcl/fuzz

Bumps [phonenumber](https://github.com/whisperfish/rust-phonenumber) from 0.3.2+8.13.9 to 0.3.3+8.13.9.
- [Commits](https://github.com/whisperfish/rust-phonenumber/commits)

---
updated-dependencies:
- dependency-name: phonenumber
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 15:02:10 -05:00
d820cf2446 Tokenizer is accidentally quadratic (#689)
* Add comments and rename a function

* Typo: paran -> paren

* Use bytes, not string, for the tokenizer

* Fix typo
2023-09-21 14:18:42 -05:00
0c724c4971 Start to restructure the Engine's connection to the backend (#674)
* Start to restructure the Engine's connectio to the backend

1) Add in a tearDown stub for when the Engine is torn down. This is now
   distinct from a 'close', which will not stop connect from trying
   again. Running tearDown will mark the connection to not be retried
   and close active connections.

2) Move the retry logic out of connect and into the constructor. It will
   attempt to reconnect at the same rate as we had previously.

3) The timeout will now only close the connection, not restart it.

Signed-off-by: Paul Tagliamonte <paul@kittycad.io>

* Don't continue on dead conn & setTimeout on init only

* Clean up extra setTimeout

* Keep track of connection timeouts and clear on close

* Fix tsc by defining Timeout

Signed-off-by: Paul Tagliamonte <paul@kittycad.io>

* appease the format gods

---------

Signed-off-by: Paul Tagliamonte <paul@kittycad.io>
Co-authored-by: Adam Sunderland <adam@kittycad.io>
2023-09-21 12:07:47 -04:00
b54ac4a694 improve getNodePathFromSourceRange and therefore the ast explorer aswell (#683)
improve getNodePathFromSourceRange and therefore the ast explorer as well
2023-09-21 05:40:41 +00:00
27227092b1 app stuck on blur when engine errors (#682)
* temp fix for when engine returns error

* don't add extrued to show function
2023-09-21 04:32:47 +00:00
04e1b92a5b Add a benchmark for parsing pipes-on-pipes (#678) 2023-09-21 03:13:07 +00:00
0553cd4621 tests for big files (#675)
* shit;

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

* updates

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

* cleanup

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

* updates

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

* u[dates;

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

* updates

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

* still ignore the big one

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

* Add big kitt SVG to benchmarks

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Adam Chalmers <adam.s.chalmers@gmail.com>
2023-09-20 19:35:37 -07:00
61a0c88af4 Add IDE dirs to .gitignore (#676) 2023-09-21 02:03:09 +00:00
d5b0544437 Bump tauri-plugin-fs-extra from 5b814f5 to 76832e6 in /src-tauri (#657)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `5b814f5` to `76832e6`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](5b814f56e6...76832e60bf)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-20 18:47:38 -07:00
6cc8af5c23 make stdlib functions async (#672)
* wip

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

updates

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

updates

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

fixes

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

closer

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

fixes

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

* fixes

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

* closer

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

* closer

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

* compiles

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

* connection

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

* fixes

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

* fix wasm

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

* timeout

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

* remove the drop

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

* drop handle

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

* updates

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

* fixes

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

* fix

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

* fixes

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-20 18:27:08 -07:00
888104080e bump v0.9.0 (#673) 2023-09-21 10:38:40 +10:00
b6769889e3 Handle relative paths at kcl level (#506)
* handle relative paths at kcl level

* fmt

* update kittycad

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

* updates

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

* bump

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

* fix tests

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2023-09-21 10:36:26 +10:00
a32258dac4 Engine manager can be cloned (#671) 2023-09-20 16:22:47 -07:00
18dbbad244 Use an actor to manage the Tokio engine connection (#669)
* Use an actor to manage the Tokio engine connection

This means EngineManager trait's methods take &self not &mut self, and the tokio implementation can be cloned.

* Clean up code
2023-09-20 16:59:03 -05:00
b67c16cc9d Benchmark for KCL parser (#664)
* KCL benchmarks

* CI for benchmarks

* More specific name for benchmark

* Benchmark the right directory

* Format
2023-09-20 13:15:28 -05:00
ad482641ef Unit test for zero-param programs (#663) 2023-09-20 10:51:49 -05:00
122 changed files with 7136 additions and 2423 deletions

View File

@ -54,4 +54,4 @@ jobs:
- name: Run clippy - name: Run clippy
run: | run: |
cd "${{ matrix.dir }}" cd "${{ matrix.dir }}"
cargo clippy --all --tests -- -D warnings cargo clippy --all --tests --benches -- -D warnings

37
.github/workflows/cargo-criterion.yml vendored Normal file
View File

@ -0,0 +1,37 @@
on:
push:
branches:
- main
paths:
- '**.rs'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-criterion.yml
pull_request:
paths:
- '**.rs'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-criterion.yml
workflow_dispatch:
permissions: read-all
name: cargo criterion
jobs:
cargocriterion:
name: cargo criterion
runs-on: ubuntu-latest-8-cores
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install dependencies
run: |
cargo install cargo-criterion
- name: Rust Cache
uses: Swatinem/rust-cache@v2.6.1
- name: Benchmark kcl library
shell: bash
run: |-
cd src/wasm-lib/kcl; cargo criterion

View File

@ -58,4 +58,5 @@ jobs:
cargo nextest run --workspace --no-fail-fast -P ci cargo nextest run --workspace --no-fail-fast -P ci
env: env:
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}} KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
RUST_MIN_STACK: 10485760000

5
.gitignore vendored
View File

@ -22,6 +22,11 @@ npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
.idea
.vscode
src/wasm-lib/.idea
src/wasm-lib/.vscode
# rust # rust
src/wasm-lib/target src/wasm-lib/target
src/wasm-lib/bindings src/wasm-lib/bindings

View File

@ -123,13 +123,24 @@ Before you submit a contribution PR to this repo, please ensure that:
## Release a new version ## Release a new version
1. Bump the versions in the .json files by creating a `Bump to v{x}.{y}.{z}` PR, committing the changes from 1. Bump the versions in the .json files by creating a `Cut release v{x}.{y}.{z}` PR, committing the changes from
```bash ```bash
VERSION=x.y.z yarn run bump-jsons VERSION=x.y.z yarn run bump-jsons
``` ```
The PR may serve as a place to discuss the human-readable changelog and extra QA. The PR may serve as a place to discuss the human-readable changelog and extra QA. A quick way of getting PR's merged since the last bump is to [use this PR filter](https://github.com/KittyCAD/modeling-app/pulls?q=is%3Apr+sort%3Aupdated-desc+is%3Amerged+), open up the browser console and past in the following
```typescript
console.log(
'- ' +
Array.from(
document.querySelectorAll('[data-hovercard-type="pull_request"]')
).map((a) => `[${a.innerText}](${a.href})`).join(`
- `)
)
```
grab the md list and delete any that are older than the last bump
2. Merge the PR 2. Merge the PR

File diff suppressed because it is too large Load Diff

View File

@ -50,6 +50,8 @@
* [`sqrt`](#sqrt) * [`sqrt`](#sqrt)
* [`startSketchAt`](#startSketchAt) * [`startSketchAt`](#startSketchAt)
* [`tan`](#tan) * [`tan`](#tan)
* [`tangentalArc`](#tangentalArc)
* [`tangentalArcTo`](#tangentalArcTo)
* [`tau`](#tau) * [`tau`](#tau)
* [`xLine`](#xLine) * [`xLine`](#xLine)
* [`xLineTo`](#xLineTo) * [`xLineTo`](#xLineTo)
@ -3234,6 +3236,320 @@ tan(num: number) -> number
### tangentalArc
Draw an arc.
```
tangentalArc(data: TangentalArcData, sketch_group: SketchGroup) -> SketchGroup
```
#### Arguments
* `data`: `TangentalArcData` - Data to draw a tangental arc.
```
{
// Offset of the arc, in degrees.
offset: number,
// Radius of the arc. Not to be confused with Raiders of the Lost Ark.
radius: number,
} |
{
// The tag.
tag: string,
// Where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.
to: [number],
} |
[number]
```
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
```
{
// The id of the sketch group.
id: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
rotation: [number],
// The starting path.
start: {
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
},
// The paths in the sketch group.
value: [{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
// The y coordinate.
y: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
}],
}
```
#### Returns
* `SketchGroup` - A sketch group is a collection of paths.
```
{
// The id of the sketch group.
id: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
rotation: [number],
// The starting path.
start: {
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
},
// The paths in the sketch group.
value: [{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
// The y coordinate.
y: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
}],
}
```
### tangentalArcTo
Draw an arc.
```
tangentalArcTo(data: TangentalArcToData, sketch_group: SketchGroup) -> SketchGroup
```
#### Arguments
* `data`: `TangentalArcToData` - Data to draw a tangental arc to a specific point.
```
{
// The tag.
tag: string,
// Where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.
to: [number],
} |
[number]
```
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
```
{
// The id of the sketch group.
id: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
rotation: [number],
// The starting path.
start: {
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
},
// The paths in the sketch group.
value: [{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
// The y coordinate.
y: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
}],
}
```
#### Returns
* `SketchGroup` - A sketch group is a collection of paths.
```
{
// The id of the sketch group.
id: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
rotation: [number],
// The starting path.
start: {
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
},
// The paths in the sketch group.
value: [{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
// The y coordinate.
y: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
}],
}
```
### tau ### tau
Return the value of `tau`. The full circle constant (τ). Equal to 2π. Return the value of `tau`. The full circle constant (τ). Equal to 2π.

View File

@ -1,6 +1,6 @@
{ {
"name": "untitled-app", "name": "untitled-app",
"version": "0.8.2", "version": "0.9.4",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.9.0", "@codemirror/autocomplete": "^6.9.0",
@ -10,7 +10,7 @@
"@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.37", "@kittycad/lib": "^0.0.40",
"@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",
@ -27,6 +27,7 @@
"@uiw/react-codemirror": "^4.21.13", "@uiw/react-codemirror": "^4.21.13",
"@xstate/react": "^3.2.2", "@xstate/react": "^3.2.2",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"debounce-promise": "^3.1.2",
"formik": "^2.4.3", "formik": "^2.4.3",
"fuse.js": "^6.6.2", "fuse.js": "^6.6.2",
"http-server": "^14.1.1", "http-server": "^14.1.1",
@ -47,7 +48,7 @@
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.4.2", "typescript": "^4.4.2",
"uuid": "^9.0.0", "uuid": "^9.0.0",
"vitest": "^0.34.1", "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",
@ -65,7 +66,7 @@
"pretest": "yarn remove-importmeta", "pretest": "yarn remove-importmeta",
"test": "vitest --mode development", "test": "vitest --mode development",
"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)", "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",
"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",
@ -101,7 +102,7 @@
"@babel/preset-env": "^7.22.9", "@babel/preset-env": "^7.22.9",
"@tauri-apps/cli": "^1.3.1", "@tauri-apps/cli": "^1.3.1",
"@types/crypto-js": "^4.1.1", "@types/crypto-js": "^4.1.1",
"@types/debounce": "^1.2.1", "@types/debounce-promise": "^3.1.6",
"@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.1",
@ -115,7 +116,7 @@
"eslint-plugin-css-modules": "^2.11.0", "eslint-plugin-css-modules": "^2.11.0",
"happy-dom": "^10.8.0", "happy-dom": "^10.8.0",
"husky": "^8.0.3", "husky": "^8.0.3",
"postcss": "^8.4.19", "postcss": "^8.4.31",
"prettier": "^2.8.0", "prettier": "^2.8.0",
"setimmediate": "^1.0.5", "setimmediate": "^1.0.5",
"tailwindcss": "^3.2.4", "tailwindcss": "^3.2.4",

194
src-tauri/Cargo.lock generated
View File

@ -84,7 +84,7 @@ dependencies = [
"tauri-build", "tauri-build",
"tauri-plugin-fs-extra", "tauri-plugin-fs-extra",
"tokio", "tokio",
"toml 0.8.0", "toml 0.8.1",
] ]
[[package]] [[package]]
@ -155,6 +155,20 @@ version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "bigdecimal"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "454bca3db10617b88b566f205ed190aedb0e0e6dd4cad61d3988a72e8c5594cb"
dependencies = [
"autocfg",
"libm",
"num-bigint",
"num-integer",
"num-traits",
"serde",
]
[[package]] [[package]]
name = "bincode" name = "bincode"
version = "1.3.3" version = "1.3.3"
@ -1295,7 +1309,21 @@ checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148"
dependencies = [ dependencies = [
"log", "log",
"mac", "mac",
"markup5ever", "markup5ever 0.10.1",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "html5ever"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
dependencies = [
"log",
"mac",
"markup5ever 0.11.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",
@ -1630,13 +1658,14 @@ dependencies = [
[[package]] [[package]]
name = "kittycad" name = "kittycad"
version = "0.2.25" version = "0.2.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9cf962b1e81a0b4eb923a727e761b40672cbacc7f5f0b75e13579d346352bc7" checksum = "35b2f9302648dbb06fd7121687f9505fc3179eba84111a06d76b246e3158f5dc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
"base64 0.21.2", "base64 0.21.2",
"bigdecimal",
"bytes", "bytes",
"chrono", "chrono",
"data-encoding", "data-encoding",
@ -1671,7 +1700,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358"
dependencies = [ dependencies = [
"cssparser", "cssparser",
"html5ever", "html5ever 0.25.2",
"matches",
"selectors",
]
[[package]]
name = "kuchikiki"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8"
dependencies = [
"cssparser",
"html5ever 0.26.0",
"indexmap 1.9.3",
"matches", "matches",
"selectors", "selectors",
] ]
@ -1688,6 +1730,12 @@ version = "0.2.148"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
[[package]]
name = "libm"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
[[package]] [[package]]
name = "line-wrap" name = "line-wrap"
version = "0.1.1" version = "0.1.1"
@ -1727,9 +1775,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.18" version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -1781,7 +1829,21 @@ checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
dependencies = [ dependencies = [
"log", "log",
"phf 0.8.0", "phf 0.8.0",
"phf_codegen", "phf_codegen 0.8.0",
"string_cache",
"string_cache_codegen",
"tendril",
]
[[package]]
name = "markup5ever"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
dependencies = [
"log",
"phf 0.10.1",
"phf_codegen 0.10.0",
"string_cache", "string_cache",
"string_cache_codegen", "string_cache_codegen",
"tendril", "tendril",
@ -1959,6 +2021,17 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.45" version = "0.1.45"
@ -1982,9 +2055,9 @@ dependencies = [
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.15" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
@ -2342,6 +2415,16 @@ dependencies = [
"phf_shared 0.8.0", "phf_shared 0.8.0",
] ]
[[package]]
name = "phf_codegen"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
]
[[package]] [[package]]
name = "phf_generator" name = "phf_generator"
version = "0.8.0" version = "0.8.0"
@ -2410,9 +2493,9 @@ dependencies = [
[[package]] [[package]]
name = "phonenumber" name = "phonenumber"
version = "0.3.2+8.13.9" version = "0.3.3+8.13.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34749f64ea9d76f10cdc8a859588b57775f59177c7dd91f744d620bd62982d6f" checksum = "635f3e6288e4f01c049d89332a031bd74f25d64b6fb94703ca966e819488cd06"
dependencies = [ dependencies = [
"bincode", "bincode",
"either", "either",
@ -2425,6 +2508,7 @@ dependencies = [
"regex-cache", "regex-cache",
"serde", "serde",
"serde_derive", "serde_derive",
"strum",
"thiserror", "thiserror",
] ]
@ -3021,10 +3105,11 @@ dependencies = [
[[package]] [[package]]
name = "schemars" name = "schemars"
version = "0.8.13" version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "763f8cd0d4c71ed8389c90cb8100cba87e763bd01a8e614d4f0af97bcd50a161" checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c"
dependencies = [ dependencies = [
"bigdecimal",
"bytes", "bytes",
"chrono", "chrono",
"dyn-clone", "dyn-clone",
@ -3037,9 +3122,9 @@ dependencies = [
[[package]] [[package]]
name = "schemars_derive" name = "schemars_derive"
version = "0.8.13" version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3105,7 +3190,7 @@ dependencies = [
"log", "log",
"matches", "matches",
"phf 0.8.0", "phf 0.8.0",
"phf_codegen", "phf_codegen 0.8.0",
"precomputed-hash", "precomputed-hash",
"servo_arc", "servo_arc",
"smallvec", "smallvec",
@ -3458,6 +3543,28 @@ dependencies = [
"syn 2.0.33", "syn 2.0.33",
] ]
[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"rustversion",
"syn 1.0.109",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -3605,9 +3712,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri" name = "tauri"
version = "1.4.1" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbe522898e35407a8e60dc3870f7579fea2fc262a6a6072eccdd37ae1e1d91e" checksum = "72aee3277d0a0df01472cc704ab5934a51a1f25348838df17bfb3c5cb727880c"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64 0.21.2", "base64 0.21.2",
@ -3661,12 +3768,13 @@ dependencies = [
[[package]] [[package]]
name = "tauri-build" name = "tauri-build"
version = "1.4.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d2edd6a259b5591c8efdeb9d5702cb53515b82a6affebd55c7fd6d3a27b7d1b" checksum = "defbfc551bd38ab997e5f8e458f87396d2559d05ce32095076ad6c30f7fc5f9c"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cargo_toml", "cargo_toml",
"dirs-next",
"heck 0.4.1", "heck 0.4.1",
"json-patch", "json-patch",
"semver", "semver",
@ -3674,13 +3782,14 @@ dependencies = [
"serde_json", "serde_json",
"tauri-utils", "tauri-utils",
"tauri-winres", "tauri-winres",
"walkdir",
] ]
[[package]] [[package]]
name = "tauri-codegen" name = "tauri-codegen"
version = "1.4.0" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54ad2d49fdeab4a08717f5b49a163bdc72efc3b1950b6758245fcde79b645e1a" checksum = "7b3475e55acec0b4a50fb96435f19631fb58cbcd31923e1a213de5c382536bbb"
dependencies = [ dependencies = [
"base64 0.21.2", "base64 0.21.2",
"brotli", "brotli",
@ -3704,9 +3813,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-macros" name = "tauri-macros"
version = "1.4.0" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eb12a2454e747896929338d93b0642144bb51e0dddbb36e579035731f0d76b7" checksum = "613740228de92d9196b795ac455091d3a5fbdac2654abb8bb07d010b62ab43af"
dependencies = [ dependencies = [
"heck 0.4.1", "heck 0.4.1",
"proc-macro2", "proc-macro2",
@ -3719,7 +3828,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#5b814f56e6368fdec46c4ddb04a07e0923ff995a" source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#9b96996b5a90a6a57d587ce4312975f13a4d8bc2"
dependencies = [ dependencies = [
"log", "log",
"serde", "serde",
@ -3730,9 +3839,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-runtime" name = "tauri-runtime"
version = "0.14.0" version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "108683199cb18f96d2d4134187bb789964143c845d2d154848dda209191fd769" checksum = "07f8e9e53e00e9f41212c115749e87d5cd2a9eebccafca77a19722eeecd56d43"
dependencies = [ dependencies = [
"gtk", "gtk",
"http", "http",
@ -3751,9 +3860,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-runtime-wry" name = "tauri-runtime-wry"
version = "0.14.0" version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7aa256a1407a3a091b5d843eccc1a5042289baf0a43d1179d9f0fcfea37c1b" checksum = "8141d72b6b65f2008911e9ef5b98a68d1e3413b7a1464e8f85eb3673bb19a895"
dependencies = [ dependencies = [
"cocoa", "cocoa",
"gtk", "gtk",
@ -3771,19 +3880,20 @@ dependencies = [
[[package]] [[package]]
name = "tauri-utils" name = "tauri-utils"
version = "1.4.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03fc02bb6072bb397e1d473c6f76c953cda48b4a2d0cce605df284aa74a12e84" checksum = "34d55e185904a84a419308d523c2c6891d5e2dbcee740c4997eb42e75a7b0f46"
dependencies = [ dependencies = [
"brotli", "brotli",
"ctor", "ctor",
"dunce", "dunce",
"glob", "glob",
"heck 0.4.1", "heck 0.4.1",
"html5ever", "html5ever 0.26.0",
"infer", "infer",
"json-patch", "json-patch",
"kuchiki", "kuchikiki",
"log",
"memchr", "memchr",
"phf 0.10.1", "phf 0.10.1",
"proc-macro2", "proc-macro2",
@ -3968,14 +4078,14 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e" checksum = "1bc1433177506450fe920e46a4f9812d0c211f5dd556da10e731a0a3dfa151f0"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"toml_edit 0.20.0", "toml_edit 0.20.1",
] ]
[[package]] [[package]]
@ -4002,9 +4112,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.20.0" version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95" checksum = "ca676d9ba1a322c1b64eb8045a5ec5c0cfb0c9d08e15e9ff622589ad5221c8fe"
dependencies = [ dependencies = [
"indexmap 2.0.0", "indexmap 2.0.0",
"serde", "serde",
@ -4799,9 +4909,9 @@ dependencies = [
[[package]] [[package]]
name = "wry" name = "wry"
version = "0.24.3" version = "0.24.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33748f35413c8a98d45f7a08832d848c0c5915501803d1faade5a4ebcd258cea" checksum = "88ef04bdad49eba2e01f06e53688c8413bd6a87b0bc14b72284465cf96e3578e"
dependencies = [ dependencies = [
"base64 0.13.1", "base64 0.13.1",
"block", "block",
@ -4813,7 +4923,7 @@ dependencies = [
"gio", "gio",
"glib", "glib",
"gtk", "gtk",
"html5ever", "html5ever 0.25.2",
"http", "http",
"kuchiki", "kuchiki",
"libc", "libc",

View File

@ -12,18 +12,18 @@ rust-version = "1.60"
# 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
[build-dependencies] [build-dependencies]
tauri-build = { version = "1.4.0", features = [] } tauri-build = { version = "1.5.0", features = [] }
[dependencies] [dependencies]
anyhow = "1" anyhow = "1"
kittycad = "0.2.25" kittycad = "0.2.28"
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"
tauri = { version = "1.4.1", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] } tauri = { version = "1.5.0", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] }
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tokio = { version = "1.32.0", features = ["time"] } tokio = { version = "1.32.0", features = ["time"] }
toml = "0.8.0" toml = "0.8.1"
[features] [features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.

View File

@ -8,7 +8,7 @@
}, },
"package": { "package": {
"productName": "kittycad-modeling", "productName": "kittycad-modeling",
"version": "0.8.2" "version": "0.9.4"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {

View File

@ -31,6 +31,7 @@ import { TextEditor } from 'components/TextEditor'
import { Themes, getSystemTheme } from 'lib/theme' import { Themes, getSystemTheme } from 'lib/theme'
import { useSetupEngineManager } from 'hooks/useSetupEngineManager' import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions' import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
import { engineCommandManager } from './lang/std/engineConnection'
export function App() { export function App() {
const { code: loadedCode, project } = useLoaderData() as IndexLoaderData const { code: loadedCode, project } = useLoaderData() as IndexLoaderData
@ -39,7 +40,6 @@ export function App() {
useHotKeyListener() useHotKeyListener()
const { const {
setCode, setCode,
engineCommandManager,
buttonDownInStream, buttonDownInStream,
openPanes, openPanes,
setOpenPanes, setOpenPanes,
@ -52,7 +52,6 @@ export function App() {
guiMode: s.guiMode, guiMode: s.guiMode,
setGuiMode: s.setGuiMode, setGuiMode: s.setGuiMode,
setCode: s.setCode, setCode: s.setCode,
engineCommandManager: s.engineCommandManager,
buttonDownInStream: s.buttonDownInStream, buttonDownInStream: s.buttonDownInStream,
openPanes: s.openPanes, openPanes: s.openPanes,
setOpenPanes: s.setOpenPanes, setOpenPanes: s.setOpenPanes,
@ -91,12 +90,12 @@ export function App() {
if (guiMode.sketchMode === 'sketchEdit') { if (guiMode.sketchMode === 'sketchEdit') {
// TODO: share this with Toolbar's "Exit sketch" button // TODO: share this with Toolbar's "Exit sketch" button
// exiting sketch should be done consistently across all exits // exiting sketch should be done consistently across all exits
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { type: 'edit_mode_exit' }, cmd: { type: 'edit_mode_exit' },
}) })
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { type: 'default_camera_disable_sketch_mode' }, cmd: { type: 'default_camera_disable_sketch_mode' },
@ -107,7 +106,7 @@ export function App() {
// when exiting sketch mode in the future // when exiting sketch mode in the future
executeAst() executeAst()
} else { } else {
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {
@ -156,7 +155,7 @@ export function App() {
useEngineConnectionSubscriptions() useEngineConnectionSubscriptions()
const debounceSocketSend = throttle<EngineCommand>((message) => { const debounceSocketSend = throttle<EngineCommand>((message) => {
engineCommandManager?.sendSceneCommand(message) engineCommandManager.sendSceneCommand(message)
}, 16) }, 16)
const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => { const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
e.nativeEvent.preventDefault() e.nativeEvent.preventDefault()
@ -216,7 +215,6 @@ export function App() {
} else if (interactionGuards.zoom.dragCallback(eWithButton)) { } else if (interactionGuards.zoom.dragCallback(eWithButton)) {
interaction = 'zoom' interaction = 'zoom'
} else { } else {
console.log('none')
return return
} }

View File

@ -18,6 +18,7 @@ import styles from './Toolbar.module.css'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { useAppMode } from 'hooks/useAppMode' import { useAppMode } from 'hooks/useAppMode'
import { ActionIcon } from 'components/ActionIcon' import { ActionIcon } from 'components/ActionIcon'
import { engineCommandManager } from './lang/std/engineConnection'
export const sketchButtonClassnames = { export const sketchButtonClassnames = {
background: background:
@ -50,7 +51,6 @@ export const Toolbar = () => {
ast, ast,
updateAst, updateAst,
programMemory, programMemory,
engineCommandManager,
executeAst, executeAst,
} = useStore((s) => ({ } = useStore((s) => ({
guiMode: s.guiMode, guiMode: s.guiMode,
@ -59,15 +59,10 @@ export const Toolbar = () => {
ast: s.ast, ast: s.ast,
updateAst: s.updateAst, updateAst: s.updateAst,
programMemory: s.programMemory, programMemory: s.programMemory,
engineCommandManager: s.engineCommandManager,
executeAst: s.executeAst, executeAst: s.executeAst,
})) }))
useAppMode() useAppMode()
useEffect(() => {
console.log('guiMode', guiMode)
}, [guiMode])
function ToolbarButtons({ className }: React.HTMLAttributes<HTMLElement>) { function ToolbarButtons({ className }: React.HTMLAttributes<HTMLElement>) {
return ( return (
<span className={styles.toolbarButtons + ' ' + className}> <span className={styles.toolbarButtons + ' ' + className}>
@ -173,12 +168,12 @@ export const Toolbar = () => {
{guiMode.mode === 'sketch' && ( {guiMode.mode === 'sketch' && (
<button <button
onClick={() => { onClick={() => {
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { type: 'edit_mode_exit' }, cmd: { type: 'edit_mode_exit' },
}) })
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { type: 'default_camera_disable_sketch_mode' }, cmd: { type: 'default_camera_disable_sketch_mode' },
@ -214,7 +209,7 @@ export const Toolbar = () => {
<button <button
key={sketchFnName} key={sketchFnName}
onClick={() => { onClick={() => {
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {

View File

@ -23,10 +23,7 @@ type ActionButtonAsLink = BaseActionButtonProps &
} }
type ActionButtonAsExternal = BaseActionButtonProps & type ActionButtonAsExternal = BaseActionButtonProps &
Omit< Omit<LinkProps, keyof BaseActionButtonProps> & {
React.AnchorHTMLAttributes<HTMLAnchorElement>,
keyof BaseActionButtonProps
> & {
Element: 'externalLink' Element: 'externalLink'
} }
@ -69,12 +66,17 @@ export const ActionButton = (props: ActionButtonProps) => {
) )
} }
case 'externalLink': { case 'externalLink': {
const { Element, icon, children, className, ...rest } = props const { Element, to, icon, children, className, ...rest } = props
return ( return (
<a className={classNames} {...rest}> <Link
to={to || paths.INDEX}
className={classNames}
{...rest}
target="_blank"
>
{icon && <ActionIcon {...icon} />} {icon && <ActionIcon {...icon} />}
{children} {children}
</a> </Link>
) )
} }
default: { default: {

View File

@ -1,7 +1,5 @@
import { useEffect, useState, useRef } from 'react' import { useEffect, useState, useRef } from 'react'
import { parser_wasm } from '../lang/abstractSyntaxTree' import { parse, BinaryPart, Value, executor } from '../lang/wasm'
import { BinaryPart, Value } from '../lang/abstractSyntaxTreeTypes'
import { executor } from '../lang/executor'
import { import {
createIdentifier, createIdentifier,
createLiteral, createLiteral,
@ -10,6 +8,7 @@ import {
} from '../lang/modifyAst' } from '../lang/modifyAst'
import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst' import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
import { useStore } from '../useStore' import { useStore } from '../useStore'
import { engineCommandManager } from '../lang/std/engineConnection'
export const AvailableVars = ({ export const AvailableVars = ({
onVarClick, onVarClick,
@ -92,14 +91,11 @@ export function useCalc({
newVariableInsertIndex: number newVariableInsertIndex: number
setNewVariableName: (a: string) => void setNewVariableName: (a: string) => void
} { } {
const { ast, programMemory, selectionRange, engineCommandManager } = useStore( const { ast, programMemory, selectionRange } = useStore((s) => ({
(s) => ({
ast: s.ast, ast: s.ast,
programMemory: s.programMemory, programMemory: s.programMemory,
selectionRange: s.selectionRanges.codeBasedSelections[0].range, selectionRange: s.selectionRanges.codeBasedSelections[0].range,
engineCommandManager: s.engineCommandManager, }))
})
)
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
const [availableVarInfo, setAvailableVarInfo] = useState< const [availableVarInfo, setAvailableVarInfo] = useState<
ReturnType<typeof findAllPreviousVariables> ReturnType<typeof findAllPreviousVariables>
@ -140,10 +136,9 @@ export function useCalc({
}, [ast, programMemory, selectionRange]) }, [ast, programMemory, selectionRange])
useEffect(() => { useEffect(() => {
if (!engineCommandManager) return
try { try {
const code = `const __result__ = ${value}\nshow(__result__)` const code = `const __result__ = ${value}\nshow(__result__)`
const ast = parser_wasm(code) const ast = parse(code)
const _programMem: any = { root: {}, return: null } const _programMem: any = { root: {}, return: null }
availableVarInfo.variables.forEach(({ key, value }) => { availableVarInfo.variables.forEach(({ key, value }) => {
_programMem.root[key] = { type: 'userVal', value, __meta: [] } _programMem.root[key] = { type: 'userVal', value, __meta: [] }

View File

@ -1,5 +1,4 @@
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel' import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { useStore } from '../useStore'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { EngineCommand } from '../lang/std/engineConnection' import { EngineCommand } from '../lang/std/engineConnection'
import { useState } from 'react' import { useState } from 'react'
@ -7,6 +6,7 @@ import { ActionButton } from '../components/ActionButton'
import { faCheck } from '@fortawesome/free-solid-svg-icons' import { faCheck } from '@fortawesome/free-solid-svg-icons'
import { isReducedMotion } from 'lang/util' import { isReducedMotion } from 'lang/util'
import { AstExplorer } from './AstExplorer' import { AstExplorer } from './AstExplorer'
import { engineCommandManager } from '../lang/std/engineConnection'
type SketchModeCmd = Extract< type SketchModeCmd = Extract<
Extract<EngineCommand, { type: 'modeling_cmd_req' }>['cmd'], Extract<EngineCommand, { type: 'modeling_cmd_req' }>['cmd'],
@ -14,9 +14,6 @@ type SketchModeCmd = Extract<
> >
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => { export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
const { engineCommandManager } = useStore((s) => ({
engineCommandManager: s.engineCommandManager,
}))
const [sketchModeCmd, setSketchModeCmd] = useState<SketchModeCmd>({ const [sketchModeCmd, setSketchModeCmd] = useState<SketchModeCmd>({
type: 'default_camera_enable_sketch_mode', type: 'default_camera_enable_sketch_mode',
origin: { x: 0, y: 0, z: 0 }, origin: { x: 0, y: 0, z: 0 },
@ -70,19 +67,18 @@ export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
className="w-16" className="w-16"
type="checkbox" type="checkbox"
checked={sketchModeCmd.ortho} checked={sketchModeCmd.ortho}
onChange={(a) => { onChange={(a) =>
console.log(a, (a as any).checked)
setSketchModeCmd({ setSketchModeCmd({
...sketchModeCmd, ...sketchModeCmd,
ortho: a.target.checked, ortho: a.target.checked,
}) })
}} }
/> />
</div> </div>
<ActionButton <ActionButton
Element="button" Element="button"
onClick={() => { onClick={() => {
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: sketchModeCmd, cmd: sketchModeCmd,
cmd_id: uuidv4(), cmd_id: uuidv4(),

View File

@ -1,13 +1,17 @@
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { useStore } from '../useStore'
import { faFileExport, faXmark } from '@fortawesome/free-solid-svg-icons' import { faFileExport, faXmark } from '@fortawesome/free-solid-svg-icons'
import { ActionButton } from './ActionButton' import { ActionButton } from './ActionButton'
import Modal from 'react-modal' import Modal from 'react-modal'
import React from 'react' import React from 'react'
import { useFormik } from 'formik' import { useFormik } from 'formik'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
import { engineCommandManager } from '../lang/std/engineConnection'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
type OutputFormat = Models['OutputFormat_type'] type OutputFormat = Models['OutputFormat_type']
type OutputTypeKey = OutputFormat['type']
type ExtractStorageTypes<T> = T extends { storage: infer U } ? U : never
type StorageUnion = ExtractStorageTypes<OutputFormat>
interface ExportButtonProps extends React.PropsWithChildren { interface ExportButtonProps extends React.PropsWithChildren {
className?: { className?: {
@ -18,14 +22,19 @@ interface ExportButtonProps extends React.PropsWithChildren {
} }
export const ExportButton = ({ children, className }: ExportButtonProps) => { export const ExportButton = ({ children, className }: ExportButtonProps) => {
const { engineCommandManager } = useStore((s) => ({
engineCommandManager: s.engineCommandManager,
}))
const [modalIsOpen, setIsOpen] = React.useState(false) const [modalIsOpen, setIsOpen] = React.useState(false)
const {
settings: {
state: {
context: { baseUnit },
},
},
} = useGlobalStateContext()
const defaultType = 'gltf' const defaultType = 'gltf'
const [type, setType] = React.useState(defaultType) const [type, setType] = React.useState<OutputTypeKey>(defaultType)
const defaultStorage = 'embedded'
const [storage, setStorage] = React.useState<StorageUnion>(defaultStorage)
function openModal() { function openModal() {
setIsOpen(true) setIsOpen(true)
@ -38,7 +47,7 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
// Default to gltf and embedded. // Default to gltf and embedded.
const initialValues: OutputFormat = { const initialValues: OutputFormat = {
type: defaultType, type: defaultType,
storage: 'embedded', storage: defaultStorage,
presentation: 'pretty', presentation: 'pretty',
} }
const formik = useFormik({ const formik = useFormik({
@ -66,7 +75,18 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
}, },
} }
} }
engineCommandManager?.sendSceneCommand({ if (values.type === 'obj' || values.type === 'stl') {
values.units = baseUnit
}
if (
values.type === 'ply' ||
values.type === 'stl' ||
values.type === 'gltf'
) {
// Set the storage type.
values.storage = storage
}
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'export', type: 'export',
@ -75,6 +95,7 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
// in the scene to export. In that case, you'd pass the IDs thru here. // in the scene to export. In that case, you'd pass the IDs thru here.
entity_ids: [], entity_ids: [],
format: values, format: values,
source_unit: baseUnit,
}, },
cmd_id: uuidv4(), cmd_id: uuidv4(),
}) })
@ -109,7 +130,17 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
id="type" id="type"
name="type" name="type"
onChange={(e) => { onChange={(e) => {
setType(e.target.value) setType(e.target.value as OutputTypeKey)
if (e.target.value === 'gltf') {
// Set default to embedded.
setStorage('embedded')
} else if (e.target.value === 'ply') {
// Set default to ascii.
setStorage('ascii')
} else if (e.target.value === 'stl') {
// Set default to ascii.
setStorage('ascii')
}
formik.handleChange(e) formik.handleChange(e)
}} }}
className="bg-chalkboard-20 dark:bg-chalkboard-90 w-full" className="bg-chalkboard-20 dark:bg-chalkboard-90 w-full"
@ -127,10 +158,10 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
<select <select
id="storage" id="storage"
name="storage" name="storage"
onChange={formik.handleChange} onChange={(e) => {
value={ setStorage(e.target.value as StorageUnion)
'storage' in formik.values ? formik.values.storage : '' formik.handleChange(e)
} }}
className="bg-chalkboard-20 dark:bg-chalkboard-90 w-full" className="bg-chalkboard-20 dark:bg-chalkboard-90 w-full"
> >
{type === 'gltf' && ( {type === 'gltf' && (

View File

@ -1,7 +1,6 @@
import { processMemory } from './MemoryPanel' import { processMemory } from './MemoryPanel'
import { parser_wasm } from '../lang/abstractSyntaxTree'
import { enginelessExecutor } from '../lib/testHelpers' import { enginelessExecutor } from '../lib/testHelpers'
import { initPromise } from '../lang/rust' import { initPromise, parse } from '../lang/wasm'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -26,7 +25,7 @@ describe('processMemory', () => {
|> lineTo([2.15, 4.32], %) |> lineTo([2.15, 4.32], %)
// |> rx(90, %) // |> rx(90, %)
show(theExtrude, theSketch)` show(theExtrude, theSketch)`
const ast = parser_wasm(code) const ast = parse(code)
const programMemory = await enginelessExecutor(ast, { const programMemory = await enginelessExecutor(ast, {
root: {}, root: {},
return: null, return: null,

View File

@ -2,7 +2,7 @@ import ReactJson from 'react-json-view'
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel' import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { useStore } from '../useStore' import { useStore } from '../useStore'
import { useMemo } from 'react' import { useMemo } from 'react'
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/executor' import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm'
import { Themes } from '../lib/theme' import { Themes } from '../lib/theme'
interface MemoryPanelProps extends CollapsiblePanelProps { interface MemoryPanelProps extends CollapsiblePanelProps {
@ -50,7 +50,7 @@ export const MemoryPanel = ({
export const processMemory = (programMemory: ProgramMemory) => { export const processMemory = (programMemory: ProgramMemory) => {
const processedMemory: any = {} const processedMemory: any = {}
Object.keys(programMemory.root).forEach((key) => { Object.keys(programMemory?.root || {}).forEach((key) => {
const val = programMemory.root[key] const val = programMemory.root[key]
if (typeof val.value !== 'function') { if (typeof val.value !== 'function') {
if (val.type === 'SketchGroup') { if (val.type === 'SketchGroup') {

View File

@ -2,6 +2,8 @@ import { fireEvent, render, screen } from '@testing-library/react'
import { BrowserRouter } from 'react-router-dom' import { BrowserRouter } from 'react-router-dom'
import ProjectSidebarMenu from './ProjectSidebarMenu' import ProjectSidebarMenu from './ProjectSidebarMenu'
import { ProjectWithEntryPointMetadata } from '../Router' import { ProjectWithEntryPointMetadata } from '../Router'
import { GlobalStateProvider } from './GlobalStateProvider'
import CommandBarProvider from './CommandBar'
const now = new Date() const now = new Date()
const projectWellFormed = { const projectWellFormed = {
@ -38,7 +40,11 @@ describe('ProjectSidebarMenu tests', () => {
test('Renders the project name', () => { test('Renders the project name', () => {
render( render(
<BrowserRouter> <BrowserRouter>
<CommandBarProvider>
<GlobalStateProvider>
<ProjectSidebarMenu project={projectWellFormed} /> <ProjectSidebarMenu project={projectWellFormed} />
</GlobalStateProvider>
</CommandBarProvider>
</BrowserRouter> </BrowserRouter>
) )
@ -55,7 +61,11 @@ describe('ProjectSidebarMenu tests', () => {
test('Renders app name if given no project', () => { test('Renders app name if given no project', () => {
render( render(
<BrowserRouter> <BrowserRouter>
<CommandBarProvider>
<GlobalStateProvider>
<ProjectSidebarMenu /> <ProjectSidebarMenu />
</GlobalStateProvider>
</CommandBarProvider>
</BrowserRouter> </BrowserRouter>
) )
@ -69,7 +79,14 @@ describe('ProjectSidebarMenu tests', () => {
test('Renders as a link if set to do so', () => { test('Renders as a link if set to do so', () => {
render( render(
<BrowserRouter> <BrowserRouter>
<ProjectSidebarMenu project={projectWellFormed} renderAsLink={true} /> <CommandBarProvider>
<GlobalStateProvider>
<ProjectSidebarMenu
project={projectWellFormed}
renderAsLink={true}
/>
</GlobalStateProvider>
</CommandBarProvider>
</BrowserRouter> </BrowserRouter>
) )

View File

@ -1,6 +1,6 @@
import { Dialog, Transition } from '@headlessui/react' import { Dialog, Transition } from '@headlessui/react'
import { Fragment, useState } from 'react' import { Fragment, useState } from 'react'
import { Value } from '../lang/abstractSyntaxTreeTypes' import { Value } from '../lang/wasm'
import { import {
AvailableVars, AvailableVars,
addToInputHelper, addToInputHelper,

View File

@ -1,6 +1,6 @@
import { Dialog, Transition } from '@headlessui/react' import { Dialog, Transition } from '@headlessui/react'
import { Fragment, useState } from 'react' import { Fragment, useState } from 'react'
import { Value } from '../lang/abstractSyntaxTreeTypes' import { Value } from '../lang/wasm'
import { import {
AvailableVars, AvailableVars,
addToInputHelper, addToInputHelper,

View File

@ -20,11 +20,15 @@ import {
compareVec2Epsilon, compareVec2Epsilon,
} from 'lang/std/sketch' } from 'lang/std/sketch'
import { getNodeFromPath } from 'lang/queryAst' import { getNodeFromPath } from 'lang/queryAst'
import { Program, VariableDeclarator } from 'lang/abstractSyntaxTreeTypes' import {
import { modify_ast_for_sketch } from '../wasm-lib/pkg/wasm_lib' Program,
VariableDeclarator,
rangeTypeFix,
modifyAstForSketch,
} from 'lang/wasm'
import { KCLError } from 'lang/errors' import { KCLError } from 'lang/errors'
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError' import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
import { rangeTypeFix } from 'lang/abstractSyntaxTree' import { engineCommandManager } from '../lang/std/engineConnection'
export const Stream = ({ className = '' }) => { export const Stream = ({ className = '' }) => {
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
@ -32,7 +36,6 @@ export const Stream = ({ className = '' }) => {
const videoRef = useRef<HTMLVideoElement>(null) const videoRef = useRef<HTMLVideoElement>(null)
const { const {
mediaStream, mediaStream,
engineCommandManager,
setButtonDownInStream, setButtonDownInStream,
didDragInStream, didDragInStream,
setDidDragInStream, setDidDragInStream,
@ -45,7 +48,6 @@ export const Stream = ({ className = '' }) => {
programMemory, programMemory,
} = useStore((s) => ({ } = useStore((s) => ({
mediaStream: s.mediaStream, mediaStream: s.mediaStream,
engineCommandManager: s.engineCommandManager,
setButtonDownInStream: s.setButtonDownInStream, setButtonDownInStream: s.setButtonDownInStream,
fileId: s.fileId, fileId: s.fileId,
didDragInStream: s.didDragInStream, didDragInStream: s.didDragInStream,
@ -73,7 +75,7 @@ export const Stream = ({ className = '' }) => {
if (!videoRef.current) return if (!videoRef.current) return
if (!mediaStream) return if (!mediaStream) return
videoRef.current.srcObject = mediaStream videoRef.current.srcObject = mediaStream
}, [mediaStream, engineCommandManager]) }, [mediaStream])
const handleMouseDown: MouseEventHandler<HTMLVideoElement> = (e) => { const handleMouseDown: MouseEventHandler<HTMLVideoElement> = (e) => {
if (!videoRef.current) return if (!videoRef.current) return
@ -107,7 +109,7 @@ export const Stream = ({ className = '' }) => {
} }
if (guiMode.mode === 'sketch' && guiMode.sketchMode === ('move' as any)) { if (guiMode.mode === 'sketch' && guiMode.sketchMode === ('move' as any)) {
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'handle_mouse_drag_start', type: 'handle_mouse_drag_start',
@ -121,7 +123,7 @@ export const Stream = ({ className = '' }) => {
guiMode.sketchMode === ('sketch_line' as any) guiMode.sketchMode === ('sketch_line' as any)
) )
) { ) {
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'camera_drag_start', type: 'camera_drag_start',
@ -139,7 +141,7 @@ export const Stream = ({ className = '' }) => {
const handleScroll: WheelEventHandler<HTMLVideoElement> = (e) => { const handleScroll: WheelEventHandler<HTMLVideoElement> = (e) => {
if (!cameraMouseDragGuards[cameraControls].zoom.scrollCallback(e)) return if (!cameraMouseDragGuards[cameraControls].zoom.scrollCallback(e)) return
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'default_camera_zoom', type: 'default_camera_zoom',
@ -177,7 +179,7 @@ export const Stream = ({ className = '' }) => {
} }
if (!didDragInStream) { if (!didDragInStream) {
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'select_with_point', type: 'select_with_point',
@ -214,7 +216,7 @@ export const Stream = ({ className = '' }) => {
window: { x, y }, window: { x, y },
} }
} }
engineCommandManager?.sendSceneCommand(command).then(async (resp) => { engineCommandManager.sendSceneCommand(command).then(async (resp) => {
if (!(guiMode.mode === 'sketch')) return if (!(guiMode.mode === 'sketch')) return
if (guiMode.sketchMode === 'selectFace') return if (guiMode.sketchMode === 'selectFace') return
@ -240,9 +242,6 @@ export const Stream = ({ className = '' }) => {
) { ) {
// Let's get the updated ast. // Let's get the updated ast.
if (sketchGroupId === '') return if (sketchGroupId === '') return
console.log('guiMode.pathId', guiMode.pathId)
// We have a problem if we do not have an id for the sketch group. // We have a problem if we do not have an id for the sketch group.
if ( if (
guiMode.pathId === undefined || guiMode.pathId === undefined ||
@ -253,26 +252,14 @@ export const Stream = ({ className = '' }) => {
let engineId = guiMode.pathId let engineId = guiMode.pathId
try { const updatedAst: Program = await modifyAstForSketch(
const updatedAst: Program = await modify_ast_for_sketch(
engineCommandManager, engineCommandManager,
JSON.stringify(ast), ast,
variableName, variableName,
engineId engineId
) )
updateAst(updatedAst, false) updateAst(updatedAst, false)
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
return return
} }
@ -285,7 +272,7 @@ export const Stream = ({ className = '' }) => {
guiMode.waitingFirstClick && guiMode.waitingFirstClick &&
!isEditingExistingSketch !isEditingExistingSketch
) { ) {
const curve = await engineCommandManager?.sendSceneCommand({ const curve = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {
@ -326,7 +313,7 @@ export const Stream = ({ className = '' }) => {
resp?.data?.data?.entities_modified?.length && resp?.data?.data?.entities_modified?.length &&
(!guiMode.waitingFirstClick || isEditingExistingSketch) (!guiMode.waitingFirstClick || isEditingExistingSketch)
) { ) {
const curve = await engineCommandManager?.sendSceneCommand({ const curve = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {
@ -371,12 +358,12 @@ export const Stream = ({ className = '' }) => {
setGuiMode({ setGuiMode({
mode: 'default', mode: 'default',
}) })
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { type: 'edit_mode_exit' }, cmd: { type: 'edit_mode_exit' },
}) })
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { type: 'default_camera_disable_sketch_mode' }, cmd: { type: 'default_camera_disable_sketch_mode' },

View File

@ -30,6 +30,7 @@ import { isOverlap, roundOff } from 'lib/utils'
import { kclErrToDiagnostic } from 'lang/errors' import { kclErrToDiagnostic } from 'lang/errors'
import { CSSRuleObject } from 'tailwindcss/types/config' import { CSSRuleObject } from 'tailwindcss/types/config'
import interact from '@replit/codemirror-interact' import interact from '@replit/codemirror-interact'
import { engineCommandManager } from '../lang/std/engineConnection'
export const editorShortcutMeta = { export const editorShortcutMeta = {
formatCode: { formatCode: {
@ -52,7 +53,6 @@ export const TextEditor = ({
code, code,
deferredSetCode, deferredSetCode,
editorView, editorView,
engineCommandManager,
formatCode, formatCode,
isLSPServerReady, isLSPServerReady,
selectionRanges, selectionRanges,
@ -64,7 +64,6 @@ export const TextEditor = ({
code: s.code, code: s.code,
deferredSetCode: s.deferredSetCode, deferredSetCode: s.deferredSetCode,
editorView: s.editorView, editorView: s.editorView,
engineCommandManager: s.engineCommandManager,
formatCode: s.formatCode, formatCode: s.formatCode,
isLSPServerReady: s.isLSPServerReady, isLSPServerReady: s.isLSPServerReady,
selectionRanges: s.selectionRanges, selectionRanges: s.selectionRanges,
@ -173,7 +172,7 @@ export const TextEditor = ({
const idBasedSelections = codeBasedSelections const idBasedSelections = codeBasedSelections
.map(({ type, range }) => { .map(({ type, range }) => {
const hasOverlap = Object.entries( const hasOverlap = Object.entries(
engineCommandManager?.sourceRangeMap || {} engineCommandManager.sourceRangeMap || {}
).filter(([_, sourceRange]) => { ).filter(([_, sourceRange]) => {
return isOverlap(sourceRange, range) return isOverlap(sourceRange, range)
}) })
@ -186,7 +185,7 @@ export const TextEditor = ({
}) })
.filter(Boolean) as any .filter(Boolean) as any
engineCommandManager?.cusorsSelected({ engineCommandManager.cusorsSelected({
otherSelections: [], otherSelections: [],
idBasedSelections, idBasedSelections,
}) })

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { toolTips, useStore } from '../../useStore' import { toolTips, useStore } from '../../useStore'
import { Value, VariableDeclarator } from '../../lang/abstractSyntaxTreeTypes' import { Value, VariableDeclarator } from '../../lang/wasm'
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,
getNodeFromPath, getNodeFromPath,

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { toolTips, useStore } from '../../useStore' import { toolTips, useStore } from '../../useStore'
import { Value, VariableDeclarator } from '../../lang/abstractSyntaxTreeTypes' import { Value, VariableDeclarator } from '../../lang/wasm'
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,
getNodeFromPath, getNodeFromPath,

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { toolTips, useStore } from '../../useStore' import { toolTips, useStore } from '../../useStore'
import { Value } from '../../lang/abstractSyntaxTreeTypes' import { Value } from '../../lang/wasm'
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,
getNodeFromPath, getNodeFromPath,

View File

@ -1,11 +1,7 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { create } from 'react-modal-promise' import { create } from 'react-modal-promise'
import { toolTips, useStore } from '../../useStore' import { toolTips, useStore } from '../../useStore'
import { import { BinaryPart, Value, VariableDeclarator } from '../../lang/wasm'
BinaryPart,
Value,
VariableDeclarator,
} from '../../lang/abstractSyntaxTreeTypes'
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,
getNodeFromPath, getNodeFromPath,

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { toolTips, useStore } from '../../useStore' import { toolTips, useStore } from '../../useStore'
import { Value } from '../../lang/abstractSyntaxTreeTypes' import { Value } from '../../lang/wasm'
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,
getNodeFromPath, getNodeFromPath,

View File

@ -1,7 +1,7 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { create } from 'react-modal-promise' import { create } from 'react-modal-promise'
import { toolTips, useStore } from '../../useStore' import { toolTips, useStore } from '../../useStore'
import { Value } from '../../lang/abstractSyntaxTreeTypes' import { Value } from '../../lang/wasm'
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,
getNodeFromPath, getNodeFromPath,
@ -133,7 +133,7 @@ export const SetAbsDistance = ({ buttonType }: { buttonType: ButtonType }) => {
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap), callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
}) })
} catch (e) { } catch (e) {
console.log('e', e) console.log('error', e)
} }
}} }}
disabled={!enableAngLen} disabled={!enableAngLen}

View File

@ -1,11 +1,7 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { create } from 'react-modal-promise' import { create } from 'react-modal-promise'
import { toolTips, useStore } from '../../useStore' import { toolTips, useStore } from '../../useStore'
import { import { BinaryPart, Value, VariableDeclarator } from '../../lang/wasm'
BinaryPart,
Value,
VariableDeclarator,
} from '../../lang/abstractSyntaxTreeTypes'
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,
getNodeFromPath, getNodeFromPath,

View File

@ -1,11 +1,7 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { create } from 'react-modal-promise' import { create } from 'react-modal-promise'
import { toolTips, useStore } from '../../useStore' import { toolTips, useStore } from '../../useStore'
import { import { BinaryPart, Value, VariableDeclarator } from '../../lang/wasm'
BinaryPart,
Value,
VariableDeclarator,
} from '../../lang/abstractSyntaxTreeTypes'
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,
getNodeFromPath, getNodeFromPath,
@ -21,8 +17,6 @@ import { GetInfoModal } from '../SetHorVertDistanceModal'
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst' import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { updateCursors } from '../../lang/util' import { updateCursors } from '../../lang/util'
import { ActionIcon } from 'components/ActionIcon'
import { sketchButtonClassnames } from 'Toolbar'
const getModalInfo = create(GetInfoModal as any) const getModalInfo = create(GetInfoModal as any)

View File

@ -1,7 +1,7 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { create } from 'react-modal-promise' import { create } from 'react-modal-promise'
import { toolTips, useStore } from '../../useStore' import { toolTips, useStore } from '../../useStore'
import { Value } from '../../lang/abstractSyntaxTreeTypes' import { Value } from '../../lang/wasm'
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,
getNodeFromPath, getNodeFromPath,
@ -147,7 +147,7 @@ export const SetAngleLength = ({
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap), callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
}) })
} catch (e) { } catch (e) {
console.log('e', e) console.log('erorr', e)
} }
}} }}
disabled={!enableAngLen} disabled={!enableAngLen}

View File

@ -1,6 +1,11 @@
import { Popover, Transition } from '@headlessui/react' import { Popover, Transition } from '@headlessui/react'
import { ActionButton } from './ActionButton' import { ActionButton } from './ActionButton'
import { faBars, faGear, faSignOutAlt } from '@fortawesome/free-solid-svg-icons' import {
faBars,
faBug,
faGear,
faSignOutAlt,
} from '@fortawesome/free-solid-svg-icons'
import { faGithub } from '@fortawesome/free-brands-svg-icons' import { faGithub } from '@fortawesome/free-brands-svg-icons'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { Fragment, useState } from 'react' import { Fragment, useState } from 'react'
@ -137,13 +142,21 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
Settings Settings
</ActionButton> </ActionButton>
<ActionButton <ActionButton
Element="link" Element="externalLink"
to="https://github.com/KittyCAD/modeling-app/discussions" to="https://github.com/KittyCAD/modeling-app/discussions"
icon={{ icon: faGithub }} icon={{ icon: faGithub }}
className="border-transparent dark:border-transparent dark:hover:border-liquid-60" className="border-transparent dark:border-transparent dark:hover:border-liquid-60"
> >
Request a feature Request a feature
</ActionButton> </ActionButton>
<ActionButton
Element="externalLink"
to="https://github.com/KittyCAD/modeling-app/issues/new"
icon={{ icon: faBug }}
className="border-transparent dark:border-transparent dark:hover:border-liquid-60"
>
Report a bug
</ActionButton>
<ActionButton <ActionButton
Element="button" Element="button"
onClick={() => send('Log out')} onClick={() => send('Log out')}

View File

@ -109,7 +109,6 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
} }
} }
messageString += message messageString += message
// console.log(messageString)
return return
}) })

View File

@ -13,6 +13,7 @@ import {
CompletionItemKind, CompletionItemKind,
CompletionTriggerKind, CompletionTriggerKind,
} from 'vscode-languageserver-protocol' } from 'vscode-languageserver-protocol'
import debounce from 'debounce-promise'
import type { import type {
Completion, Completion,
@ -53,14 +54,11 @@ export class LanguageServerPlugin implements PluginValue {
private languageId: string private languageId: string
private documentVersion: number private documentVersion: number
private changesTimeout: number
constructor(private view: EditorView, private allowHTMLContent: boolean) { constructor(private view: EditorView, private allowHTMLContent: boolean) {
this.client = this.view.state.facet(client) this.client = this.view.state.facet(client)
this.documentUri = this.view.state.facet(documentUri) this.documentUri = this.view.state.facet(documentUri)
this.languageId = this.view.state.facet(languageId) this.languageId = this.view.state.facet(languageId)
this.documentVersion = 0 this.documentVersion = 0
this.changesTimeout = 0
this.client.attachPlugin(this) this.client.attachPlugin(this)
@ -71,12 +69,10 @@ export class LanguageServerPlugin implements PluginValue {
update({ docChanged }: ViewUpdate) { update({ docChanged }: ViewUpdate) {
if (!docChanged) return if (!docChanged) return
if (this.changesTimeout) clearTimeout(this.changesTimeout)
this.changesTimeout = window.setTimeout(() => {
this.sendChange({ this.sendChange({
documentText: this.view.state.doc.toString(), documentText: this.view.state.doc.toString(),
}) })
}, changesDelay)
} }
destroy() { destroy() {
@ -99,14 +95,32 @@ export class LanguageServerPlugin implements PluginValue {
async sendChange({ documentText }: { documentText: string }) { async sendChange({ documentText }: { documentText: string }) {
if (!this.client.ready) return if (!this.client.ready) return
if (documentText.length > 5000) {
// Clear out the text it thinks we have, large documents will throw a stack error.
// This is obviously not a good fix but it works for now til we figure
// out the stack limits in wasm and also rewrite the parser.
// Since this is only for hover and completions it will be fine,
// completions will still work for stdlib but hover will not.
// That seems like a fine trade-off for a working editor for the time
// being.
documentText = ''
}
try { try {
await this.client.textDocumentDidChange({ debounce(
() => {
return this.client.textDocumentDidChange({
textDocument: { textDocument: {
uri: this.documentUri, uri: this.documentUri,
version: this.documentVersion++, version: this.documentVersion++,
}, },
contentChanges: [{ text: documentText }], contentChanges: [{ text: documentText }],
}) })
},
changesDelay,
{ leading: true }
)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }

View File

@ -8,6 +8,7 @@ import { ArtifactMap, EngineCommandManager } from 'lang/std/engineConnection'
import { Models } from '@kittycad/lib/dist/types/src' import { Models } from '@kittycad/lib/dist/types/src'
import { isReducedMotion } from 'lang/util' import { isReducedMotion } from 'lang/util'
import { isOverlap } from 'lib/utils' import { isOverlap } from 'lib/utils'
import { engineCommandManager } from '../lang/std/engineConnection'
interface DefaultPlanes { interface DefaultPlanes {
xy: string xy: string
@ -17,17 +18,11 @@ interface DefaultPlanes {
} }
export function useAppMode() { export function useAppMode() {
const { const { guiMode, setGuiMode, selectionRanges, selectionRangeTypeMap } =
guiMode, useStore((s) => ({
setGuiMode,
selectionRanges,
engineCommandManager,
selectionRangeTypeMap,
} = useStore((s) => ({
guiMode: s.guiMode, guiMode: s.guiMode,
setGuiMode: s.setGuiMode, setGuiMode: s.setGuiMode,
selectionRanges: s.selectionRanges, selectionRanges: s.selectionRanges,
engineCommandManager: s.engineCommandManager,
selectionRangeTypeMap: s.selectionRangeTypeMap, selectionRangeTypeMap: s.selectionRangeTypeMap,
})) }))
const [defaultPlanes, setDefaultPlanes] = useState<DefaultPlanes | null>(null) const [defaultPlanes, setDefaultPlanes] = useState<DefaultPlanes | null>(null)
@ -65,7 +60,7 @@ export function useAppMode() {
setDefaultPlanesHidden(engineCommandManager, localDefaultPlanes, true) setDefaultPlanesHidden(engineCommandManager, localDefaultPlanes, true)
// TODO figure out the plane to use based on the sketch // TODO figure out the plane to use based on the sketch
// maybe it's easier to make a new plane than rely on the defaults // maybe it's easier to make a new plane than rely on the defaults
await engineCommandManager?.sendSceneCommand({ await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {
@ -135,7 +130,7 @@ export function useAppMode() {
]) ])
useEffect(() => { useEffect(() => {
const unSub = engineCommandManager?.subscribeTo({ const unSub = engineCommandManager.subscribeTo({
event: 'select_with_point', event: 'select_with_point',
callback: async ({ data }) => { callback: async ({ data }) => {
if (!data.entity_id) return if (!data.entity_id) return
@ -144,8 +139,7 @@ export function useAppMode() {
// user clicked something else in the scene // user clicked something else in the scene
return return
} }
const sketchModeResponse = await engineCommandManager?.sendSceneCommand( const sketchModeResponse = await engineCommandManager.sendSceneCommand({
{
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {
@ -154,8 +148,7 @@ export function useAppMode() {
ortho: true, ortho: true,
animated: !isReducedMotion(), animated: !isReducedMotion(),
}, },
} })
)
setDefaultPlanesHidden(engineCommandManager, defaultPlanes, true) setDefaultPlanesHidden(engineCommandManager, defaultPlanes, true)
const sketchUuid = uuidv4() const sketchUuid = uuidv4()
const proms: any[] = [] const proms: any[] = []
@ -178,8 +171,7 @@ export function useAppMode() {
}, },
}) })
) )
const res = await Promise.all(proms) await Promise.all(proms)
console.log('res', res)
setGuiMode({ setGuiMode({
mode: 'sketch', mode: 'sketch',
sketchMode: 'sketchEdit', sketchMode: 'sketchEdit',
@ -209,7 +201,7 @@ async function createPlane(
} }
) { ) {
const planeId = uuidv4() const planeId = uuidv4()
await engineCommandManager?.sendSceneCommand({ await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'make_plane', type: 'make_plane',
@ -221,7 +213,7 @@ async function createPlane(
}, },
cmd_id: planeId, cmd_id: planeId,
}) })
await engineCommandManager?.sendSceneCommand({ await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'plane_set_color', type: 'plane_set_color',
@ -234,12 +226,12 @@ async function createPlane(
} }
function setDefaultPlanesHidden( function setDefaultPlanesHidden(
engineCommandManager: EngineCommandManager | undefined, engineCommandManager: EngineCommandManager,
defaultPlanes: DefaultPlanes, defaultPlanes: DefaultPlanes,
hidden: boolean hidden: boolean
) { ) {
Object.values(defaultPlanes).forEach((planeId) => { Object.values(defaultPlanes).forEach((planeId) => {
engineCommandManager?.sendSceneCommand({ engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {

View File

@ -1,14 +1,9 @@
import { useEffect } from 'react' import { useEffect } from 'react'
import { useStore } from 'useStore' import { useStore } from 'useStore'
import { engineCommandManager } from '../lang/std/engineConnection'
export function useEngineConnectionSubscriptions() { export function useEngineConnectionSubscriptions() {
const { const { setCursor2, setHighlightRange, highlightRange } = useStore((s) => ({
engineCommandManager,
setCursor2,
setHighlightRange,
highlightRange,
} = useStore((s) => ({
engineCommandManager: s.engineCommandManager,
setCursor2: s.setCursor2, setCursor2: s.setCursor2,
setHighlightRange: s.setHighlightRange, setHighlightRange: s.setHighlightRange,
highlightRange: s.highlightRange, highlightRange: s.highlightRange,

View File

@ -1,53 +1,89 @@
import { useLayoutEffect } from 'react' import { useLayoutEffect, useEffect, useRef } from 'react'
import { _executor } from '../lang/executor' import { _executor } 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 { v4 as uuidv4 } from 'uuid'
export function useSetupEngineManager( export function useSetupEngineManager(
streamRef: React.RefObject<HTMLDivElement>, streamRef: React.RefObject<HTMLDivElement>,
token?: string token?: string
) { ) {
const { const {
setEngineCommandManager,
setMediaStream, setMediaStream,
setIsStreamReady, setIsStreamReady,
setStreamDimensions, setStreamDimensions,
streamDimensions,
executeCode, executeCode,
} = useStore((s) => ({ } = useStore((s) => ({
setEngineCommandManager: s.setEngineCommandManager,
setMediaStream: s.setMediaStream, setMediaStream: s.setMediaStream,
setIsStreamReady: s.setIsStreamReady, setIsStreamReady: s.setIsStreamReady,
setStreamDimensions: s.setStreamDimensions, setStreamDimensions: s.setStreamDimensions,
streamDimensions: s.streamDimensions,
executeCode: s.executeCode, executeCode: s.executeCode,
})) }))
const streamWidth = streamRef?.current?.offsetWidth const streamWidth = streamRef?.current?.offsetWidth
const streamHeight = streamRef?.current?.offsetHeight const streamHeight = streamRef?.current?.offsetHeight
const width = streamWidth ? streamWidth : 0 const hasSetNonZeroDimensions = useRef<boolean>(false)
const quadWidth = Math.round(width / 4) * 4
const height = streamHeight ? streamHeight : 0
const quadHeight = Math.round(height / 4) * 4
useLayoutEffect(() => { useLayoutEffect(() => {
setStreamDimensions({ // Load the engine command manager once with the initial width and height,
streamWidth: quadWidth, // then we do not want to reload it.
streamHeight: quadHeight, const { width: quadWidth, height: quadHeight } = getDimensions(
}) streamWidth,
if (!width || !height) return streamHeight
const eng = new EngineCommandManager({ )
if (!hasSetNonZeroDimensions.current && quadHeight && quadWidth) {
engineCommandManager.start({
setMediaStream, setMediaStream,
setIsStreamReady, setIsStreamReady,
width: quadWidth, width: quadWidth,
height: quadHeight, height: quadHeight,
executeCode,
token, token,
}) })
setEngineCommandManager(eng) setStreamDimensions({
eng.waitForReady.then(() => { streamWidth: quadWidth,
executeCode() streamHeight: quadHeight,
}) })
return () => { hasSetNonZeroDimensions.current = true
eng?.tearDown()
} }
}, [quadWidth, quadHeight]) }, [streamRef?.current?.offsetWidth, streamRef?.current?.offsetHeight])
useEffect(() => {
const handleResize = deferExecution(() => {
const { width, height } = getDimensions(
streamRef?.current?.offsetWidth,
streamRef?.current?.offsetHeight
)
if (
streamDimensions.streamWidth !== width ||
streamDimensions.streamHeight !== height
) {
engineCommandManager.handleResize({
streamWidth: width,
streamHeight: height,
})
setStreamDimensions({
streamWidth: width,
streamHeight: height,
})
}
}, 500)
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
}
function getDimensions(streamWidth?: number, streamHeight?: number) {
const width = streamWidth ? streamWidth : 0
const quadWidth = Math.round(width / 4) * 4
const height = streamHeight ? streamHeight : 0
const quadHeight = Math.round(height / 4) * 4
return { width: quadWidth, height: quadHeight }
} }

View File

@ -48,7 +48,7 @@ export function useConvertToVariable() {
updateAst(_modifiedAst, true) updateAst(_modifiedAst, true)
} catch (e) { } catch (e) {
console.log('e', e) console.log('error', e)
} }
} }

View File

@ -1,12 +1,11 @@
import { parser_wasm } from './abstractSyntaxTree'
import { KCLError } from './errors' import { KCLError } from './errors'
import { initPromise } from './rust' import { initPromise, parse } from './wasm'
beforeAll(() => initPromise) beforeAll(() => initPromise)
describe('testing AST', () => { describe('testing AST', () => {
test('5 + 6', () => { test('5 + 6', () => {
const result = parser_wasm('5 +6') const result = parse('5 +6')
delete (result as any).nonCodeMeta delete (result as any).nonCodeMeta
expect(result.body).toEqual([ expect(result.body).toEqual([
{ {
@ -37,7 +36,7 @@ describe('testing AST', () => {
]) ])
}) })
test('const myVar = 5', () => { test('const myVar = 5', () => {
const { body } = parser_wasm('const myVar = 5') const { body } = parse('const myVar = 5')
expect(body).toEqual([ expect(body).toEqual([
{ {
type: 'VariableDeclaration', type: 'VariableDeclaration',
@ -71,7 +70,7 @@ describe('testing AST', () => {
const code = `const myVar = 5 const code = `const myVar = 5
const newVar = myVar + 1 const newVar = myVar + 1
` `
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body).toEqual([ expect(body).toEqual([
{ {
type: 'VariableDeclaration', type: 'VariableDeclaration',
@ -143,7 +142,7 @@ const newVar = myVar + 1
describe('testing function declaration', () => { describe('testing function declaration', () => {
test('fn funcN = () => {}', () => { test('fn funcN = () => {}', () => {
const { body } = parser_wasm('fn funcN = () => {}') const { body } = parse('fn funcN = () => {}')
delete (body[0] as any).declarations[0].init.body.nonCodeMeta delete (body[0] as any).declarations[0].init.body.nonCodeMeta
expect(body).toEqual([ expect(body).toEqual([
{ {
@ -179,7 +178,7 @@ describe('testing function declaration', () => {
]) ])
}) })
test('fn funcN = (a, b) => {return a + b}', () => { test('fn funcN = (a, b) => {return a + b}', () => {
const { body } = parser_wasm( const { body } = parse(
['fn funcN = (a, b) => {', ' return a + b', '}'].join('\n') ['fn funcN = (a, b) => {', ' return a + b', '}'].join('\n')
) )
delete (body[0] as any).declarations[0].init.body.nonCodeMeta delete (body[0] as any).declarations[0].init.body.nonCodeMeta
@ -256,7 +255,7 @@ describe('testing function declaration', () => {
test('call expression assignment', () => { test('call expression assignment', () => {
const code = `fn funcN = (a, b) => { return a + b } const code = `fn funcN = (a, b) => { return a + b }
const myVar = funcN(1, 2)` const myVar = funcN(1, 2)`
const { body } = parser_wasm(code) const { body } = parse(code)
delete (body[0] as any).declarations[0].init.body.nonCodeMeta delete (body[0] as any).declarations[0].init.body.nonCodeMeta
expect(body).toEqual([ expect(body).toEqual([
{ {
@ -388,7 +387,7 @@ describe('testing pipe operator special', () => {
|> lineTo([1, 1], %) |> lineTo([1, 1], %)
|> rx(45, %) |> rx(45, %)
` `
const { body } = parser_wasm(code) const { body } = parse(code)
delete (body[0] as any).declarations[0].init.nonCodeMeta delete (body[0] as any).declarations[0].init.nonCodeMeta
expect(body).toEqual([ expect(body).toEqual([
{ {
@ -404,7 +403,7 @@ describe('testing pipe operator special', () => {
id: { type: 'Identifier', start: 6, end: 14, name: 'mySketch' }, id: { type: 'Identifier', start: 6, end: 14, name: 'mySketch' },
init: { init: {
type: 'PipeExpression', type: 'PipeExpression',
start: 15, start: 17,
end: 145, end: 145,
body: [ body: [
{ {
@ -624,7 +623,7 @@ describe('testing pipe operator special', () => {
}) })
test('pipe operator with binary expression', () => { test('pipe operator with binary expression', () => {
let code = `const myVar = 5 + 6 |> myFunc(45, %)` let code = `const myVar = 5 + 6 |> myFunc(45, %)`
const { body } = parser_wasm(code) const { body } = parse(code)
delete (body as any)[0].declarations[0].init.nonCodeMeta delete (body as any)[0].declarations[0].init.nonCodeMeta
expect(body).toEqual([ expect(body).toEqual([
{ {
@ -645,7 +644,7 @@ describe('testing pipe operator special', () => {
}, },
init: { init: {
type: 'PipeExpression', type: 'PipeExpression',
start: 12, start: 14,
end: 36, end: 36,
body: [ body: [
{ {
@ -706,7 +705,7 @@ describe('testing pipe operator special', () => {
}) })
test('array expression', () => { test('array expression', () => {
let code = `const yo = [1, '2', three, 4 + 5]` let code = `const yo = [1, '2', three, 4 + 5]`
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body).toEqual([ expect(body).toEqual([
{ {
type: 'VariableDeclaration', type: 'VariableDeclaration',
@ -781,7 +780,7 @@ describe('testing pipe operator special', () => {
'const three = 3', 'const three = 3',
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}", "const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
].join('\n') ].join('\n')
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body).toEqual([ expect(body).toEqual([
{ {
type: 'VariableDeclaration', type: 'VariableDeclaration',
@ -925,7 +924,7 @@ describe('testing pipe operator special', () => {
const code = `const yo = {key: { const code = `const yo = {key: {
key2: 'value' key2: 'value'
}}` }}`
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body).toEqual([ expect(body).toEqual([
{ {
type: 'VariableDeclaration', type: 'VariableDeclaration',
@ -993,7 +992,7 @@ describe('testing pipe operator special', () => {
}) })
test('object expression with array ast', () => { test('object expression with array ast', () => {
const code = `const yo = {key: [1, '2']}` const code = `const yo = {key: [1, '2']}`
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body).toEqual([ expect(body).toEqual([
{ {
type: 'VariableDeclaration', type: 'VariableDeclaration',
@ -1057,7 +1056,7 @@ describe('testing pipe operator special', () => {
}) })
test('object memberExpression simple', () => { test('object memberExpression simple', () => {
const code = `const prop = yo.one.two` const code = `const prop = yo.one.two`
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body).toEqual([ expect(body).toEqual([
{ {
type: 'VariableDeclaration', type: 'VariableDeclaration',
@ -1112,7 +1111,7 @@ describe('testing pipe operator special', () => {
}) })
test('object memberExpression with square braces', () => { test('object memberExpression with square braces', () => {
const code = `const prop = yo.one["two"]` const code = `const prop = yo.one["two"]`
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body).toEqual([ expect(body).toEqual([
{ {
type: 'VariableDeclaration', type: 'VariableDeclaration',
@ -1168,7 +1167,7 @@ describe('testing pipe operator special', () => {
}) })
test('object memberExpression with two square braces literal and identifier', () => { test('object memberExpression with two square braces literal and identifier', () => {
const code = `const prop = yo["one"][two]` const code = `const prop = yo["one"][two]`
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body).toEqual([ expect(body).toEqual([
{ {
type: 'VariableDeclaration', type: 'VariableDeclaration',
@ -1227,7 +1226,7 @@ describe('testing pipe operator special', () => {
describe('nests binary expressions correctly', () => { describe('nests binary expressions correctly', () => {
it('works with the simple case', () => { it('works with the simple case', () => {
const code = `const yo = 1 + 2` const code = `const yo = 1 + 2`
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body[0]).toEqual({ expect(body[0]).toEqual({
type: 'VariableDeclaration', type: 'VariableDeclaration',
start: 0, start: 0,
@ -1271,7 +1270,7 @@ describe('nests binary expressions correctly', () => {
it('should nest according to precedence with multiply first', () => { it('should nest according to precedence with multiply first', () => {
// should be binExp { binExp { lit-1 * lit-2 } + lit} // should be binExp { binExp { lit-1 * lit-2 } + lit}
const code = `const yo = 1 * 2 + 3` const code = `const yo = 1 * 2 + 3`
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body[0]).toEqual({ expect(body[0]).toEqual({
type: 'VariableDeclaration', type: 'VariableDeclaration',
start: 0, start: 0,
@ -1328,7 +1327,7 @@ describe('nests binary expressions correctly', () => {
it('should nest according to precedence with sum first', () => { it('should nest according to precedence with sum first', () => {
// should be binExp { lit-1 + binExp { lit-2 * lit-3 } } // should be binExp { lit-1 + binExp { lit-2 * lit-3 } }
const code = `const yo = 1 + 2 * 3` const code = `const yo = 1 + 2 * 3`
const { body } = parser_wasm(code) const { body } = parse(code)
expect(body[0]).toEqual({ expect(body[0]).toEqual({
type: 'VariableDeclaration', type: 'VariableDeclaration',
start: 0, start: 0,
@ -1384,7 +1383,7 @@ describe('nests binary expressions correctly', () => {
}) })
it('should nest properly with two opperators of equal precedence', () => { it('should nest properly with two opperators of equal precedence', () => {
const code = `const yo = 1 + 2 - 3` const code = `const yo = 1 + 2 - 3`
const { body } = parser_wasm(code) const { body } = parse(code)
expect((body[0] as any).declarations[0].init).toEqual({ expect((body[0] as any).declarations[0].init).toEqual({
type: 'BinaryExpression', type: 'BinaryExpression',
start: 11, start: 11,
@ -1421,7 +1420,7 @@ describe('nests binary expressions correctly', () => {
}) })
it('should nest properly with two opperators of equal (but higher) precedence', () => { it('should nest properly with two opperators of equal (but higher) precedence', () => {
const code = `const yo = 1 * 2 / 3` const code = `const yo = 1 * 2 / 3`
const { body } = parser_wasm(code) const { body } = parse(code)
expect((body[0] as any).declarations[0].init).toEqual({ expect((body[0] as any).declarations[0].init).toEqual({
type: 'BinaryExpression', type: 'BinaryExpression',
start: 11, start: 11,
@ -1458,7 +1457,7 @@ describe('nests binary expressions correctly', () => {
}) })
it('should nest properly with longer example', () => { it('should nest properly with longer example', () => {
const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6` const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6`
const { body } = parser_wasm(code) const { body } = parse(code)
const init = (body[0] as any).declarations[0].init const init = (body[0] as any).declarations[0].init
expect(init).toEqual({ expect(init).toEqual({
type: 'BinaryExpression', type: 'BinaryExpression',
@ -1520,14 +1519,12 @@ const key = 'c'`
value: 'this is a comment', value: 'this is a comment',
}, },
} }
const { nonCodeMeta } = parser_wasm(code) const { nonCodeMeta } = parse(code)
expect(nonCodeMeta.nonCodeNodes[0]).toEqual(nonCodeMetaInstance) expect(nonCodeMeta.nonCodeNodes[0]).toEqual(nonCodeMetaInstance)
// extra whitespace won't change it's position (0) or value (NB the start end would have changed though) // extra whitespace won't change it's position (0) or value (NB the start end would have changed though)
const codeWithExtraStartWhitespace = '\n\n\n' + code const codeWithExtraStartWhitespace = '\n\n\n' + code
const { nonCodeMeta: nonCodeMeta2 } = parser_wasm( const { nonCodeMeta: nonCodeMeta2 } = parse(codeWithExtraStartWhitespace)
codeWithExtraStartWhitespace
)
expect(nonCodeMeta2.nonCodeNodes[0].value).toStrictEqual( expect(nonCodeMeta2.nonCodeNodes[0].value).toStrictEqual(
nonCodeMetaInstance.value nonCodeMetaInstance.value
) )
@ -1545,7 +1542,7 @@ const key = 'c'`
|> close(%) |> close(%)
` `
const { body } = parser_wasm(code) const { body } = parse(code)
const indexOfSecondLineToExpression = 2 const indexOfSecondLineToExpression = 2
const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta
.nonCodeNodes .nonCodeNodes
@ -1569,7 +1566,7 @@ const key = 'c'`
' |> rx(90, %)', ' |> rx(90, %)',
].join('\n') ].join('\n')
const { body } = parser_wasm(code) const { body } = parse(code)
const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta
.nonCodeNodes .nonCodeNodes
expect(sketchNonCodeMeta[3]).toEqual({ expect(sketchNonCodeMeta[3]).toEqual({
@ -1587,7 +1584,7 @@ const key = 'c'`
describe('test UnaryExpression', () => { describe('test UnaryExpression', () => {
it('should parse a unary expression in simple var dec situation', () => { it('should parse a unary expression in simple var dec situation', () => {
const code = `const myVar = -min(4, 100)` const code = `const myVar = -min(4, 100)`
const { body } = parser_wasm(code) const { body } = parse(code)
const myVarInit = (body?.[0] as any).declarations[0]?.init const myVarInit = (body?.[0] as any).declarations[0]?.init
expect(myVarInit).toEqual({ expect(myVarInit).toEqual({
type: 'UnaryExpression', type: 'UnaryExpression',
@ -1613,7 +1610,7 @@ describe('test UnaryExpression', () => {
describe('testing nested call expressions', () => { describe('testing nested call expressions', () => {
it('callExp in a binExp in a callExp', () => { it('callExp in a binExp in a callExp', () => {
const code = 'const myVar = min(100, 1 + legLen(5, 3))' const code = 'const myVar = min(100, 1 + legLen(5, 3))'
const { body } = parser_wasm(code) const { body } = parse(code)
const myVarInit = (body?.[0] as any).declarations[0]?.init const myVarInit = (body?.[0] as any).declarations[0]?.init
expect(myVarInit).toEqual({ expect(myVarInit).toEqual({
type: 'CallExpression', type: 'CallExpression',
@ -1651,7 +1648,7 @@ describe('testing nested call expressions', () => {
describe('should recognise callExpresions in binaryExpressions', () => { describe('should recognise callExpresions in binaryExpressions', () => {
const code = "xLineTo(segEndX('seg02', %) + 1, %)" const code = "xLineTo(segEndX('seg02', %) + 1, %)"
it('should recognise the callExp', () => { it('should recognise the callExp', () => {
const { body } = parser_wasm(code) const { body } = parse(code)
const callExpArgs = (body?.[0] as any).expression?.arguments const callExpArgs = (body?.[0] as any).expression?.arguments
expect(callExpArgs).toEqual([ expect(callExpArgs).toEqual([
{ {
@ -1690,8 +1687,7 @@ describe('parsing errors', () => {
let _theError let _theError
try { try {
const result = expect(parser_wasm(code)) const result = expect(parse(code))
console.log('result', result)
} catch (e) { } catch (e) {
_theError = e _theError = e
} }

View File

@ -1,48 +0,0 @@
import { Program } from './abstractSyntaxTreeTypes'
import { parse_js } from '../wasm-lib/pkg/wasm_lib'
import { initPromise } from './rust'
import { Token } from './tokeniser'
import { KCLError } from './errors'
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
ranges.map(([start, end]) => [start, end])
export const parser_wasm = (code: string): Program => {
try {
const program: Program = parse_js(code)
return program
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}
export async function asyncParser(code: string): Promise<Program> {
await initPromise
try {
const program: Program = parse_js(code)
return program
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}
export function rangeOfToken(token: Token | undefined): [number, number][] {
return token === undefined ? [] : [[token.start, token.end]]
}

View File

@ -1,37 +0,0 @@
export type { Program } from '../wasm-lib/kcl/bindings/Program'
export type { Value } from '../wasm-lib/kcl/bindings/Value'
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
export type { MemberExpression } from '../wasm-lib/kcl/bindings/MemberExpression'
export type { PipeExpression } from '../wasm-lib/kcl/bindings/PipeExpression'
export type { VariableDeclaration } from '../wasm-lib/kcl/bindings/VariableDeclaration'
export type { PipeSubstitution } from '../wasm-lib/kcl/bindings/PipeSubstitution'
export type { Identifier } from '../wasm-lib/kcl/bindings/Identifier'
export type { UnaryExpression } from '../wasm-lib/kcl/bindings/UnaryExpression'
export type { BinaryExpression } from '../wasm-lib/kcl/bindings/BinaryExpression'
export type { ReturnStatement } from '../wasm-lib/kcl/bindings/ReturnStatement'
export type { ExpressionStatement } from '../wasm-lib/kcl/bindings/ExpressionStatement'
export type { CallExpression } from '../wasm-lib/kcl/bindings/CallExpression'
export type { VariableDeclarator } from '../wasm-lib/kcl/bindings/VariableDeclarator'
export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart'
export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
export type SyntaxType =
| 'Program'
| 'ExpressionStatement'
| 'BinaryExpression'
| 'CallExpression'
| 'Identifier'
| 'ReturnStatement'
| 'VariableDeclaration'
| 'VariableDeclarator'
| 'MemberExpression'
| 'ArrayExpression'
| 'ObjectExpression'
| 'ObjectProperty'
| 'FunctionExpression'
| 'PipeExpression'
| 'PipeSubstitution'
| 'Literal'
| 'NonCodeNode'
| 'UnaryExpression'

View File

@ -1,5 +1,4 @@
import { parser_wasm } from './abstractSyntaxTree' import { parse, initPromise } from './wasm'
import { initPromise } from './rust'
import { enginelessExecutor } from '../lib/testHelpers' import { enginelessExecutor } from '../lib/testHelpers'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -13,7 +12,7 @@ const mySketch001 = startSketchAt([0, 0])
|> lineTo([0.46, -5.82], %) |> lineTo([0.46, -5.82], %)
// |> rx(45, %) // |> rx(45, %)
show(mySketch001)` show(mySketch001)`
const programMemory = await enginelessExecutor(parser_wasm(code)) const programMemory = await enginelessExecutor(parse(code))
// @ts-ignore // @ts-ignore
const shown = programMemory?.return?.map( const shown = programMemory?.return?.map(
// @ts-ignore // @ts-ignore
@ -69,7 +68,7 @@ const mySketch001 = startSketchAt([0, 0])
// |> rx(45, %) // |> rx(45, %)
|> extrude(2, %) |> extrude(2, %)
show(mySketch001)` show(mySketch001)`
const programMemory = await enginelessExecutor(parser_wasm(code)) const programMemory = await enginelessExecutor(parse(code))
// @ts-ignore // @ts-ignore
const shown = programMemory?.return?.map( const shown = programMemory?.return?.map(
// @ts-ignore // @ts-ignore
@ -109,7 +108,7 @@ const sk2 = startSketchAt([0, 0])
show(theExtrude, sk2)` show(theExtrude, sk2)`
const programMemory = await enginelessExecutor(parser_wasm(code)) const programMemory = await enginelessExecutor(parse(code))
// @ts-ignore // @ts-ignore
const geos = programMemory?.return?.map( const geos = programMemory?.return?.map(
// @ts-ignore // @ts-ignore

View File

@ -1,10 +1,7 @@
import fs from 'node:fs' import fs from 'node:fs'
import { parser_wasm } from './abstractSyntaxTree' import { parse, ProgramMemory, SketchGroup, initPromise } from './wasm'
import { ProgramMemory, SketchGroup } from './executor'
import { initPromise } from './rust'
import { enginelessExecutor } from '../lib/testHelpers' import { enginelessExecutor } from '../lib/testHelpers'
import { vi } from 'vitest'
import { KCLError } from './errors' import { KCLError } from './errors'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -403,7 +400,7 @@ async function exe(
code: string, code: string,
programMemory: ProgramMemory = { root: {}, return: null } programMemory: ProgramMemory = { root: {}, return: null }
) { ) {
const ast = parser_wasm(code) const ast = parse(code)
const result = await enginelessExecutor(ast, programMemory) const result = await enginelessExecutor(ast, programMemory)
return result return result

View File

@ -1,81 +0,0 @@
import { Program } from './abstractSyntaxTreeTypes'
import {
EngineCommandManager,
ArtifactMap,
SourceRangeMap,
} from './std/engineConnection'
import { ProgramReturn } from '../wasm-lib/kcl/bindings/ProgramReturn'
import { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
import { execute_wasm } from '../wasm-lib/pkg/wasm_lib'
import { KCLError } from './errors'
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
import { rangeTypeFix } from './abstractSyntaxTree'
export type { SourceRange } from '../wasm-lib/kcl/bindings/SourceRange'
export type { Position } from '../wasm-lib/kcl/bindings/Position'
export type { Rotation } from '../wasm-lib/kcl/bindings/Rotation'
export type { Path } from '../wasm-lib/kcl/bindings/Path'
export type { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup'
export type { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
export type PathToNode = [string | number, string][]
interface Memory {
[key: string]: MemoryItem
}
export interface ProgramMemory {
root: Memory
return: ProgramReturn | null
}
export const executor = async (
node: Program,
programMemory: ProgramMemory = { root: {}, return: null },
engineCommandManager: EngineCommandManager,
// work around while the gemotry is still be stored on the frontend
// will be removed when the stream UI is added.
tempMapCallback: (a: {
artifactMap: ArtifactMap
sourceRangeMap: SourceRangeMap
}) => void = () => {}
): Promise<ProgramMemory> => {
engineCommandManager.startNewSession()
const _programMemory = await _executor(
node,
programMemory,
engineCommandManager
)
const { artifactMap, sourceRangeMap } =
await engineCommandManager.waitForAllCommands(node, _programMemory)
tempMapCallback({ artifactMap, sourceRangeMap })
engineCommandManager.endSession()
return _programMemory
}
export const _executor = async (
node: Program,
programMemory: ProgramMemory = { root: {}, return: null },
engineCommandManager: EngineCommandManager
): Promise<ProgramMemory> => {
try {
const memory: ProgramMemory = await execute_wasm(
JSON.stringify(node),
JSON.stringify(programMemory),
engineCommandManager
)
return memory
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}

View File

@ -1,6 +1,5 @@
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst' import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
import { parser_wasm } from './abstractSyntaxTree' import { Identifier, parse, initPromise } from './wasm'
import { initPromise } from './rust'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -20,11 +19,88 @@ const sk3 = startSketchAt([0, 0])
lineToSubstringIndex + subStr.length, lineToSubstringIndex + subStr.length,
] ]
const ast = parser_wasm(code) const ast = parse(code)
const nodePath = getNodePathFromSourceRange(ast, sourceRange) const nodePath = getNodePathFromSourceRange(ast, sourceRange)
const { node } = getNodeFromPath<any>(ast, nodePath) const { node } = getNodeFromPath<any>(ast, nodePath)
expect([node.start, node.end]).toEqual(sourceRange) expect([node.start, node.end]).toEqual(sourceRange)
expect(node.type).toBe('CallExpression') expect(node.type).toBe('CallExpression')
}) })
it('gets path right for function definition params', () => {
const code = `fn cube = (pos, scale) => {
const sg = startSketchAt(pos)
|> line([0, scale], %)
|> line([scale, 0], %)
|> line([0, -scale], %)
return sg
}
const b1 = cube([0,0], 10)`
const subStr = 'pos, scale'
const subStrIndex = code.indexOf(subStr)
const sourceRange: [number, number] = [
subStrIndex,
subStrIndex + 'pos'.length,
]
const ast = parse(code)
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
const node = getNodeFromPath<Identifier>(ast, nodePath).node
expect(nodePath).toEqual([
['body', ''],
[0, 'index'],
['declarations', 'VariableDeclaration'],
[0, 'index'],
['init', ''],
['params', 'FunctionExpression'],
[0, 'index'],
])
expect(node.type).toBe('Identifier')
expect(node.name).toBe('pos')
})
it('gets path right for deep within function definition body', () => {
const code = `fn cube = (pos, scale) => {
const sg = startSketchAt(pos)
|> line([0, scale], %)
|> line([scale, 0], %)
|> line([0, -scale], %)
return sg
}
const b1 = cube([0,0], 10)`
const subStr = 'scale, 0'
const subStrIndex = code.indexOf(subStr)
const sourceRange: [number, number] = [
subStrIndex,
subStrIndex + 'scale'.length,
]
const ast = parse(code)
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
const node = getNodeFromPath<Identifier>(ast, nodePath).node
expect(nodePath).toEqual([
['body', ''],
[0, 'index'],
['declarations', 'VariableDeclaration'],
[0, 'index'],
['init', ''],
['body', 'FunctionExpression'],
['body', 'FunctionExpression'],
[0, 'index'],
['declarations', 'VariableDeclaration'],
[0, 'index'],
['init', ''],
['body', 'PipeExpression'],
[2, 'index'],
['arguments', 'CallExpression'],
[0, 'index'],
['elements', 'ArrayExpression'],
[0, 'index'],
])
expect(node.type).toBe('Identifier')
expect(node.name).toBe('scale')
})
}) })

View File

@ -1,4 +1,4 @@
import { parser_wasm } from './abstractSyntaxTree' import { parse, recast, initPromise } from './wasm'
import { import {
createLiteral, createLiteral,
createIdentifier, createIdentifier,
@ -13,8 +13,6 @@ import {
giveSketchFnCallTag, giveSketchFnCallTag,
moveValueIntoNewVariable, moveValueIntoNewVariable,
} from './modifyAst' } from './modifyAst'
import { recast } from './recast'
import { initPromise } from './rust'
import { enginelessExecutor } from '../lib/testHelpers' import { enginelessExecutor } from '../lib/testHelpers'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -126,7 +124,7 @@ function giveSketchFnCallTagTestHelper(
// giveSketchFnCallTag inputs and outputs an ast, which is very verbose for testing // giveSketchFnCallTag inputs and outputs an ast, which is very verbose for testing
// this wrapper changes the input and output to code // this wrapper changes the input and output to code
// making it more of an integration test, but easier to read the test intention is the goal // making it more of an integration test, but easier to read the test intention is the goal
const ast = parser_wasm(code) const ast = parse(code)
const start = code.indexOf(searchStr) const start = code.indexOf(searchStr)
const range: [number, number] = [start, start + searchStr.length] const range: [number, number] = [start, start + searchStr.length]
const { modifiedAst, tag, isTagExisting } = giveSketchFnCallTag(ast, range) const { modifiedAst, tag, isTagExisting } = giveSketchFnCallTag(ast, range)
@ -197,7 +195,7 @@ const part001 = startSketchAt([-1.2, 4.83])
const yo2 = hmm([identifierGuy + 5]) const yo2 = hmm([identifierGuy + 5])
show(part001)` show(part001)`
it('should move a binary expression into a new variable', async () => { it('should move a binary expression into a new variable', async () => {
const ast = parser_wasm(code) const ast = parse(code)
const programMemory = await enginelessExecutor(ast) const programMemory = await enginelessExecutor(ast)
const startIndex = code.indexOf('100 + 100') + 1 const startIndex = code.indexOf('100 + 100') + 1
const { modifiedAst } = moveValueIntoNewVariable( const { modifiedAst } = moveValueIntoNewVariable(
@ -211,7 +209,7 @@ show(part001)`
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`) expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
}) })
it('should move a value into a new variable', async () => { it('should move a value into a new variable', async () => {
const ast = parser_wasm(code) const ast = parse(code)
const programMemory = await enginelessExecutor(ast) const programMemory = await enginelessExecutor(ast)
const startIndex = code.indexOf('2.8') + 1 const startIndex = code.indexOf('2.8') + 1
const { modifiedAst } = moveValueIntoNewVariable( const { modifiedAst } = moveValueIntoNewVariable(
@ -225,7 +223,7 @@ show(part001)`
expect(newCode).toContain(`line([newVar, 0], %)`) expect(newCode).toContain(`line([newVar, 0], %)`)
}) })
it('should move a callExpression into a new variable', async () => { it('should move a callExpression into a new variable', async () => {
const ast = parser_wasm(code) const ast = parse(code)
const programMemory = await enginelessExecutor(ast) const programMemory = await enginelessExecutor(ast)
const startIndex = code.indexOf('def(') const startIndex = code.indexOf('def(')
const { modifiedAst } = moveValueIntoNewVariable( const { modifiedAst } = moveValueIntoNewVariable(
@ -239,7 +237,7 @@ show(part001)`
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`) expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
}) })
it('should move a binary expression with call expression into a new variable', async () => { it('should move a binary expression with call expression into a new variable', async () => {
const ast = parser_wasm(code) const ast = parse(code)
const programMemory = await enginelessExecutor(ast) const programMemory = await enginelessExecutor(ast)
const startIndex = code.indexOf('jkl(') + 1 const startIndex = code.indexOf('jkl(') + 1
const { modifiedAst } = moveValueIntoNewVariable( const { modifiedAst } = moveValueIntoNewVariable(
@ -253,7 +251,7 @@ show(part001)`
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`) expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
}) })
it('should move a identifier into a new variable', async () => { it('should move a identifier into a new variable', async () => {
const ast = parser_wasm(code) const ast = parse(code)
const programMemory = await enginelessExecutor(ast) const programMemory = await enginelessExecutor(ast)
const startIndex = code.indexOf('identifierGuy +') + 1 const startIndex = code.indexOf('identifierGuy +') + 1
const { modifiedAst } = moveValueIntoNewVariable( const { modifiedAst } = moveValueIntoNewVariable(

View File

@ -14,14 +14,15 @@ import {
ObjectExpression, ObjectExpression,
UnaryExpression, UnaryExpression,
BinaryExpression, BinaryExpression,
} from './abstractSyntaxTreeTypes' PathToNode,
ProgramMemory,
} from './wasm'
import { import {
findAllPreviousVariables, findAllPreviousVariables,
getNodeFromPath, getNodeFromPath,
getNodePathFromSourceRange, getNodePathFromSourceRange,
isNodeSafeToReplace, isNodeSafeToReplace,
} from './queryAst' } from './queryAst'
import { PathToNode, ProgramMemory } from './executor'
import { import {
addTagForSketchOnFace, addTagForSketchOnFace,
getFirstArg, getFirstArg,
@ -321,7 +322,7 @@ export function extrudeSketch(
[0, 'index'], [0, 'index'],
] ]
return { return {
modifiedAst: addToShow(_node, name), modifiedAst: node,
pathToNode: [...pathToNode.slice(0, -1), [showCallIndex, 'index']], pathToNode: [...pathToNode.slice(0, -1), [showCallIndex, 'index']],
pathToExtrudeArg, pathToExtrudeArg,
} }

View File

@ -1,11 +1,10 @@
import { parser_wasm } from './abstractSyntaxTree' import { parse, recast, initPromise } from './wasm'
import { import {
findAllPreviousVariables, findAllPreviousVariables,
isNodeSafeToReplace, isNodeSafeToReplace,
isTypeInValue, isTypeInValue,
getNodePathFromSourceRange, getNodePathFromSourceRange,
} from './queryAst' } from './queryAst'
import { initPromise } from './rust'
import { enginelessExecutor } from '../lib/testHelpers' import { enginelessExecutor } from '../lib/testHelpers'
import { import {
createArrayExpression, createArrayExpression,
@ -13,7 +12,6 @@ import {
createLiteral, createLiteral,
createPipeSubstitution, createPipeSubstitution,
} from './modifyAst' } from './modifyAst'
import { recast } from './recast'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -36,7 +34,7 @@ const variableBelowShouldNotBeIncluded = 3
show(part001)` show(part001)`
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7 const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
const ast = parser_wasm(code) const ast = parse(code)
const programMemory = await enginelessExecutor(ast) const programMemory = await enginelessExecutor(ast)
const { variables, bodyPath, insertIndex } = findAllPreviousVariables( const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
@ -70,7 +68,7 @@ const yo = 5 + 6
const yo2 = hmm([identifierGuy + 5]) const yo2 = hmm([identifierGuy + 5])
show(part001)` show(part001)`
it('find a safe binaryExpression', () => { it('find a safe binaryExpression', () => {
const ast = parser_wasm(code) const ast = parse(code)
const rangeStart = code.indexOf('100 + 100') + 2 const rangeStart = code.indexOf('100 + 100') + 2
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart]) const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true) expect(result.isSafe).toBe(true)
@ -84,7 +82,7 @@ show(part001)`
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`) expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
}) })
it('find a safe Identifier', () => { it('find a safe Identifier', () => {
const ast = parser_wasm(code) const ast = parse(code)
const rangeStart = code.indexOf('abc') const rangeStart = code.indexOf('abc')
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart]) const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true) expect(result.isSafe).toBe(true)
@ -92,7 +90,7 @@ show(part001)`
expect(code.slice(result.value.start, result.value.end)).toBe('abc') expect(code.slice(result.value.start, result.value.end)).toBe('abc')
}) })
it('find a safe CallExpression', () => { it('find a safe CallExpression', () => {
const ast = parser_wasm(code) const ast = parse(code)
const rangeStart = code.indexOf('def') const rangeStart = code.indexOf('def')
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart]) const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true) expect(result.isSafe).toBe(true)
@ -106,7 +104,7 @@ show(part001)`
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`) expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
}) })
it('find an UNsafe CallExpression, as it has a PipeSubstitution', () => { it('find an UNsafe CallExpression, as it has a PipeSubstitution', () => {
const ast = parser_wasm(code) const ast = parse(code)
const rangeStart = code.indexOf('ghi') const rangeStart = code.indexOf('ghi')
const range: [number, number] = [rangeStart, rangeStart] const range: [number, number] = [rangeStart, rangeStart]
const result = isNodeSafeToReplace(ast, range) const result = isNodeSafeToReplace(ast, range)
@ -115,7 +113,7 @@ show(part001)`
expect(code.slice(result.value.start, result.value.end)).toBe('ghi(%)') expect(code.slice(result.value.start, result.value.end)).toBe('ghi(%)')
}) })
it('find an UNsafe Identifier, as it is a callee', () => { it('find an UNsafe Identifier, as it is a callee', () => {
const ast = parser_wasm(code) const ast = parse(code)
const rangeStart = code.indexOf('ine([2.8,') const rangeStart = code.indexOf('ine([2.8,')
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart]) const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(false) expect(result.isSafe).toBe(false)
@ -125,7 +123,7 @@ show(part001)`
) )
}) })
it("find a safe BinaryExpression that's assigned to a variable", () => { it("find a safe BinaryExpression that's assigned to a variable", () => {
const ast = parser_wasm(code) const ast = parse(code)
const rangeStart = code.indexOf('5 + 6') + 1 const rangeStart = code.indexOf('5 + 6') + 1
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart]) const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true) expect(result.isSafe).toBe(true)
@ -139,7 +137,7 @@ show(part001)`
expect(outCode).toContain(`const yo = replaceName`) expect(outCode).toContain(`const yo = replaceName`)
}) })
it('find a safe BinaryExpression that has a CallExpression within', () => { it('find a safe BinaryExpression that has a CallExpression within', () => {
const ast = parser_wasm(code) const ast = parse(code)
const rangeStart = code.indexOf('jkl') + 1 const rangeStart = code.indexOf('jkl') + 1
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart]) const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true) expect(result.isSafe).toBe(true)
@ -155,7 +153,7 @@ show(part001)`
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`) expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
}) })
it('find a safe BinaryExpression within a CallExpression', () => { it('find a safe BinaryExpression within a CallExpression', () => {
const ast = parser_wasm(code) const ast = parse(code)
const rangeStart = code.indexOf('identifierGuy') + 1 const rangeStart = code.indexOf('identifierGuy') + 1
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart]) const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true) expect(result.isSafe).toBe(true)
@ -203,7 +201,7 @@ show(part001)`
it('finds the second line when cursor is put at the end', () => { it('finds the second line when cursor is put at the end', () => {
const searchLn = `line([0.94, 2.61], %)` const searchLn = `line([0.94, 2.61], %)`
const sourceIndex = code.indexOf(searchLn) + searchLn.length const sourceIndex = code.indexOf(searchLn) + searchLn.length
const ast = parser_wasm(code) const ast = parse(code)
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex]) const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
expect(result).toEqual([ expect(result).toEqual([
['body', ''], ['body', ''],
@ -218,7 +216,7 @@ show(part001)`
it('finds the last line when cursor is put at the end', () => { it('finds the last line when cursor is put at the end', () => {
const searchLn = `line([-0.21, -1.4], %)` const searchLn = `line([-0.21, -1.4], %)`
const sourceIndex = code.indexOf(searchLn) + searchLn.length const sourceIndex = code.indexOf(searchLn) + searchLn.length
const ast = parser_wasm(code) const ast = parse(code)
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex]) const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
const expected = [ const expected = [
['body', ''], ['body', ''],

View File

@ -1,4 +1,3 @@
import { PathToNode, ProgramMemory, SketchGroup, SourceRange } from './executor'
import { Selection, ToolTip } from '../useStore' import { Selection, ToolTip } from '../useStore'
import { import {
BinaryExpression, BinaryExpression,
@ -10,7 +9,11 @@ import {
VariableDeclaration, VariableDeclaration,
ReturnStatement, ReturnStatement,
ArrayExpression, ArrayExpression,
} from './abstractSyntaxTreeTypes' PathToNode,
ProgramMemory,
SketchGroup,
SourceRange,
} from './wasm'
import { createIdentifier, splitPathAtLastIndex } from './modifyAst' import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints' import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
import { getAngle } from '../lib/utils' import { getAngle } from '../lib/utils'
@ -239,7 +242,29 @@ function moreNodePathFromSourceRange(
} }
return path return path
} }
console.error('not implemented') if (_node.type === 'FunctionExpression' && isInRange) {
for (let i = 0; i < _node.params.length; i++) {
const param = _node.params[i]
if (param.start <= start && param.end >= end) {
path.push(['params', 'FunctionExpression'])
path.push([i, 'index'])
return moreNodePathFromSourceRange(param, sourceRange, path)
}
}
if (_node.body.start <= start && _node.body.end >= end) {
path.push(['body', 'FunctionExpression'])
const fnBody = _node.body.body
for (let i = 0; i < fnBody.length; i++) {
const statement = fnBody[i]
if (statement.start <= start && statement.end >= end) {
path.push(['body', 'FunctionExpression'])
path.push([i, 'index'])
return moreNodePathFromSourceRange(statement, sourceRange, path)
}
}
}
}
console.error('not implemented: ' + node.type)
return path return path
} }

View File

@ -1,8 +1,5 @@
import { recast } from './recast' import { parse, Program, recast, initPromise } from './wasm'
import { parser_wasm } from './abstractSyntaxTree'
import { Program } from './abstractSyntaxTreeTypes'
import fs from 'node:fs' import fs from 'node:fs'
import { initPromise } from './rust'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -366,6 +363,6 @@ describe('it recasts binary expression using brackets where needed', () => {
// helpers // helpers
function code2ast(code: string): { ast: Program } { function code2ast(code: string): { ast: Program } {
const ast = parser_wasm(code) const ast = parse(code)
return { ast } return { ast }
} }

View File

@ -1,13 +0,0 @@
import { Program } from './abstractSyntaxTreeTypes'
import { recast_wasm } from '../wasm-lib/pkg/wasm_lib'
export const recast = (ast: Program): string => {
try {
const s: string = recast_wasm(JSON.stringify(ast))
return s
} catch (e) {
// TODO: do something real with the error.
console.log('recast', e)
throw e
}
}

View File

@ -1,20 +0,0 @@
import init from '../wasm-lib/pkg/wasm_lib'
const initialise = async () => {
const baseUrl =
typeof window === 'undefined'
? 'http://127.0.0.1:3000'
: window.location.origin.includes('tauri://localhost')
? 'tauri://localhost'
: window.location.origin.includes('localhost')
? 'http://localhost:3000'
: window.location.origin && window.location.origin !== 'null'
? window.location.origin
: 'http://localhost:3000'
const fullUrl = baseUrl + '/wasm_lib_bg.wasm'
const input = await fetch(fullUrl)
const buffer = await input.arrayBuffer()
return init(buffer)
}
export const initPromise = initialise()

View File

@ -1,4 +1,9 @@
import { ProgramMemory, SourceRange } from 'lang/executor' import {
ProgramMemory,
SourceRange,
Program,
VariableDeclarator,
} from 'lang/wasm'
import { Selections } from 'useStore' import { Selections } from 'useStore'
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_CONNECTION_TIMEOUT_MS } from 'env' import { VITE_KC_API_WS_MODELING_URL, VITE_KC_CONNECTION_TIMEOUT_MS } from 'env'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
@ -6,7 +11,6 @@ import { exportSave } from 'lib/exportSave'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import * as Sentry from '@sentry/react' import * as Sentry from '@sentry/react'
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst' import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { Program, VariableDeclarator } from 'lang/abstractSyntaxTreeTypes'
let lastMessage = '' let lastMessage = ''
@ -23,6 +27,10 @@ interface ResultCommand extends CommandInfo {
data: any data: any
raw: WebSocketResponse raw: WebSocketResponse
} }
interface FailedCommand extends CommandInfo {
type: 'failed'
errors: Models['FailureWebSocketResponse_type']['errors']
}
interface PendingCommand extends CommandInfo { interface PendingCommand extends CommandInfo {
type: 'pending' type: 'pending'
promise: Promise<any> promise: Promise<any>
@ -30,7 +38,7 @@ interface PendingCommand extends CommandInfo {
} }
export interface ArtifactMap { export interface ArtifactMap {
[key: string]: ResultCommand | PendingCommand [key: string]: ResultCommand | PendingCommand | FailedCommand
} }
export interface SourceRangeMap { export interface SourceRangeMap {
[key: string]: SourceRange [key: string]: SourceRange
@ -41,6 +49,11 @@ interface NewTrackArgs {
mediaStream: MediaStream mediaStream: MediaStream
} }
// This looks funny, I know. This is needed because node and the browser
// disagree as to the type. In a browser it's a number, but in node it's a
// "Timeout".
type Timeout = ReturnType<typeof setTimeout>
type ClientMetrics = Models['ClientMetrics_type'] type ClientMetrics = Models['ClientMetrics_type']
// EngineConnection encapsulates the connection(s) to the Engine // EngineConnection encapsulates the connection(s) to the Engine
@ -52,6 +65,9 @@ export class EngineConnection {
unreliableDataChannel?: RTCDataChannel unreliableDataChannel?: RTCDataChannel
private ready: boolean private ready: boolean
private connecting: boolean
private dead: boolean
private failedConnTimeout: Timeout | null
readonly url: string readonly url: string
private readonly token?: string private readonly token?: string
@ -87,6 +103,9 @@ export class EngineConnection {
this.url = url this.url = url
this.token = token this.token = token
this.ready = false this.ready = false
this.connecting = false
this.dead = false
this.failedConnTimeout = null
this.onWebsocketOpen = onWebsocketOpen this.onWebsocketOpen = onWebsocketOpen
this.onDataChannelOpen = onDataChannelOpen this.onDataChannelOpen = onDataChannelOpen
this.onEngineConnectionOpen = onEngineConnectionOpen this.onEngineConnectionOpen = onEngineConnectionOpen
@ -97,7 +116,10 @@ export class EngineConnection {
// TODO(paultag): This ought to be tweakable. // TODO(paultag): This ought to be tweakable.
const pingIntervalMs = 10000 const pingIntervalMs = 10000
setInterval(() => { let pingInterval = setInterval(() => {
if (this.dead) {
clearInterval(pingInterval)
}
if (this.isReady()) { if (this.isReady()) {
// When we're online, every 10 seconds, we'll attempt to put a 'ping' // When we're online, every 10 seconds, we'll attempt to put a 'ping'
// command through the WebSocket connection. This will help both ends // command through the WebSocket connection. This will help both ends
@ -106,6 +128,24 @@ export class EngineConnection {
this.send({ type: 'ping' }) this.send({ type: 'ping' })
} }
}, pingIntervalMs) }, pingIntervalMs)
const connectionTimeoutMs = VITE_KC_CONNECTION_TIMEOUT_MS
let connectInterval = setInterval(() => {
if (this.dead) {
clearInterval(connectInterval)
return
}
if (this.isReady()) {
return
}
console.log('connecting via retry')
this.connect()
}, connectionTimeoutMs)
}
// isConnecting will return true when connect has been called, but the full
// WebRTC is not online.
isConnecting() {
return this.connecting
} }
// isReady will return true only when the WebRTC *and* WebSocket connection // isReady will return true only when the WebRTC *and* WebSocket connection
// are connected. During setup, the WebSocket connection comes online first, // are connected. During setup, the WebSocket connection comes online first,
@ -114,6 +154,10 @@ export class EngineConnection {
isReady() { isReady() {
return this.ready return this.ready
} }
tearDown() {
this.dead = true
this.close()
}
// shouldTrace will return true when Sentry should be used to instrument // shouldTrace will return true when Sentry should be used to instrument
// the Engine. // the Engine.
shouldTrace() { shouldTrace() {
@ -125,8 +169,10 @@ export class EngineConnection {
// This will attempt the full handshake, and retry if the connection // This will attempt the full handshake, and retry if the connection
// did not establish. // did not establish.
connect() { connect() {
// TODO(paultag): make this safe to call multiple times, and figure out console.log('connect was called')
// when a connection is in progress (state: connecting or something). if (this.isConnecting() || this.isReady()) {
return
}
// Information on the connect transaction // Information on the connect transaction
@ -314,6 +360,11 @@ export class EngineConnection {
if (this.shouldTrace()) { if (this.shouldTrace()) {
iceSpan.resolve?.() iceSpan.resolve?.()
} }
} else if (this.pc?.iceConnectionState === 'failed') {
// failed is a terminal state; let's explicitly kill the
// connection to the server at this point.
console.log('failed to negotiate ice connection; restarting')
this.close()
} }
}) })
@ -358,20 +409,6 @@ export class EngineConnection {
}) })
}) })
} }
// TODO(paultag): This ought to be both controllable, as well as something
// like exponential backoff to have some grace on the backend, as well as
// fix responsiveness for clients that had a weird network hiccup.
const connectionTimeoutMs = VITE_KC_CONNECTION_TIMEOUT_MS
setTimeout(() => {
if (this.isReady()) {
return
}
console.log('engine connection timeout on connection, retrying')
this.close()
this.connect()
}, connectionTimeoutMs)
}) })
this.pc.addEventListener('track', (event) => { this.pc.addEventListener('track', (event) => {
@ -459,8 +496,11 @@ export class EngineConnection {
this.onDataChannelOpen(this) this.onDataChannelOpen(this)
this.onEngineConnectionOpen(this)
this.ready = true this.ready = true
this.connecting = false
// Do this after we set the connection is ready to avoid errors when
// we try to send messages before the connection is ready.
this.onEngineConnectionOpen(this)
}) })
this.unreliableDataChannel.addEventListener('close', (event) => { this.unreliableDataChannel.addEventListener('close', (event) => {
@ -474,6 +514,22 @@ export class EngineConnection {
}) })
}) })
const connectionTimeoutMs = VITE_KC_CONNECTION_TIMEOUT_MS
if (this.failedConnTimeout) {
console.log('clearing timeout before set')
clearTimeout(this.failedConnTimeout)
this.failedConnTimeout = null
}
console.log('timeout set')
this.failedConnTimeout = setTimeout(() => {
if (this.isReady()) {
return
}
console.log('engine connection timeout on connection, closing')
this.close()
}, connectionTimeoutMs)
this.onConnectionStarted(this) this.onConnectionStarted(this)
} }
unreliableSend(message: object | string) { unreliableSend(message: object | string) {
@ -498,9 +554,15 @@ export class EngineConnection {
this.pc = undefined this.pc = undefined
this.unreliableDataChannel = undefined this.unreliableDataChannel = undefined
this.webrtcStatsCollector = undefined this.webrtcStatsCollector = undefined
if (this.failedConnTimeout) {
console.log('closed timeout in close')
clearTimeout(this.failedConnTimeout)
this.failedConnTimeout = null
}
this.onClose(this) this.onClose(this)
this.ready = false this.ready = false
this.connecting = false
} }
} }
@ -531,6 +593,9 @@ export class EngineCommandManager {
outSequence = 1 outSequence = 1
inSequence = 1 inSequence = 1
engineConnection?: EngineConnection engineConnection?: EngineConnection
// Folks should realize that wait for ready does not get called _everytime_
// the connection resets and restarts, it only gets called the first time.
// Be careful what you put here.
waitForReady: Promise<void> = new Promise(() => {}) waitForReady: Promise<void> = new Promise(() => {})
private resolveReady = () => {} private resolveReady = () => {}
@ -544,19 +609,36 @@ export class EngineCommandManager {
[localUnsubscribeId: string]: (a: any) => void [localUnsubscribeId: string]: (a: any) => void
} }
} = {} as any } = {} as any
constructor({
constructor() {
this.engineConnection = undefined
}
start({
setMediaStream, setMediaStream,
setIsStreamReady, setIsStreamReady,
width, width,
height, height,
executeCode,
token, token,
}: { }: {
setMediaStream: (stream: MediaStream) => void setMediaStream: (stream: MediaStream) => void
setIsStreamReady: (isStreamReady: boolean) => void setIsStreamReady: (isStreamReady: boolean) => void
width: number width: number
height: number height: number
executeCode: (code?: string, force?: boolean) => void
token?: string token?: string
}) { }) {
if (width === 0 || height === 0) {
return
}
// If we already have an engine connection, just need to resize the stream.
if (this.engineConnection) {
this.handleResize({ streamWidth: width, streamHeight: height })
return
}
this.waitForReady = new Promise((resolve) => { this.waitForReady = new Promise((resolve) => {
this.resolveReady = resolve this.resolveReady = resolve
}) })
@ -567,6 +649,32 @@ export class EngineCommandManager {
onEngineConnectionOpen: () => { onEngineConnectionOpen: () => {
this.resolveReady() this.resolveReady()
setIsStreamReady(true) setIsStreamReady(true)
// Make the axis gizmo.
// We do this after the connection opened to avoid a race condition.
// Connected opened is the last thing that happens when the stream
// is ready.
// We also do this here because we want to ensure we create the gizmo
// and execute the code everytime the stream is restarted.
const gizmoId = uuidv4()
this.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: gizmoId,
cmd: {
type: 'make_axes_gizmo',
clobber: false,
// If true, axes gizmo will be placed in the corner of the screen.
// If false, it will be placed at the origin of the scene.
gizmo_mode: true,
},
})
// We execute the code here to make sure if the stream was to
// restart in a session, we want to make sure to execute the code.
// We force it to re-execute the code because we want to make sure
// the code is executed everytime the stream is restarted.
// We pass undefined for the code so it reads from the current state.
executeCode(undefined, true)
}, },
onClose: () => { onClose: () => {
setIsStreamReady(false) setIsStreamReady(false)
@ -617,6 +725,8 @@ export class EngineCommandManager {
message.request_id message.request_id
) { ) {
this.handleModelingCommand(message.resp, message.request_id) this.handleModelingCommand(message.resp, message.request_id)
} else if (!message.success && message.request_id) {
this.handleFailedModelingCommand(message)
} }
} }
}) })
@ -636,6 +746,30 @@ export class EngineCommandManager {
this.engineConnection?.connect() this.engineConnection?.connect()
} }
handleResize({
streamWidth,
streamHeight,
}: {
streamWidth: number
streamHeight: number
}) {
console.log('handleResize', streamWidth, streamHeight)
if (!this.engineConnection?.isReady()) {
return
}
const resizeCmd: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'reconfigure_stream',
width: streamWidth,
height: streamHeight,
fps: 60,
},
}
this.engineConnection?.send(resizeCmd)
}
handleModelingCommand(message: WebSocketResponse, id: string) { handleModelingCommand(message: WebSocketResponse, id: string) {
if (message.type !== 'modeling') { if (message.type !== 'modeling') {
return return
@ -673,8 +807,40 @@ export class EngineCommandManager {
} }
} }
} }
handleFailedModelingCommand({
request_id,
errors,
}: Models['FailureWebSocketResponse_type']) {
const id = request_id
if (!id) return
const command = this.artifactMap[id]
if (command && command.type === 'pending') {
const resolve = command.resolve
this.artifactMap[id] = {
type: 'failed',
range: command.range,
commandType: command.commandType,
parentId: command.parentId ? command.parentId : undefined,
errors,
}
resolve({
id,
commandType: command.commandType,
range: command.range,
errors,
})
} else {
this.artifactMap[id] = {
type: 'failed',
range: command.range,
commandType: command.commandType,
parentId: command.parentId ? command.parentId : undefined,
errors,
}
}
}
tearDown() { tearDown() {
this.engineConnection?.close() this.engineConnection?.tearDown()
} }
startNewSession() { startNewSession() {
this.artifactMap = {} this.artifactMap = {}
@ -769,6 +935,14 @@ export class EngineCommandManager {
}) })
} }
sendSceneCommand(command: EngineCommand): Promise<any> { sendSceneCommand(command: EngineCommand): Promise<any> {
if (this.engineConnection === undefined) {
return Promise.resolve()
}
if (!this.engineConnection?.isReady()) {
return Promise.resolve()
}
if ( if (
command.type === 'modeling_cmd_req' && command.type === 'modeling_cmd_req' &&
command.cmd.type !== lastMessage command.cmd.type !== lastMessage
@ -776,10 +950,6 @@ export class EngineCommandManager {
console.log('sending command', command.cmd.type) console.log('sending command', command.cmd.type)
lastMessage = command.cmd.type lastMessage = command.cmd.type
} }
if (!this.engineConnection?.isReady()) {
console.log('socket not ready')
return Promise.resolve()
}
if (command.type !== 'modeling_cmd_req') return Promise.resolve() if (command.type !== 'modeling_cmd_req') return Promise.resolve()
const cmd = command.cmd const cmd = command.cmd
if ( if (
@ -821,10 +991,12 @@ export class EngineCommandManager {
range: SourceRange range: SourceRange
command: EngineCommand | string command: EngineCommand | string
}): Promise<any> { }): Promise<any> {
if (this.engineConnection === undefined) {
return Promise.resolve()
}
this.sourceRangeMap[id] = range this.sourceRangeMap[id] = range
if (!this.engineConnection?.isReady()) { if (!this.engineConnection?.isReady()) {
console.log('socket not ready')
return Promise.resolve() return Promise.resolve()
} }
this.engineConnection?.send(command) this.engineConnection?.send(command)
@ -867,6 +1039,9 @@ export class EngineCommandManager {
rangeStr: string, rangeStr: string,
commandStr: string commandStr: string
): Promise<any> { ): Promise<any> {
if (this.engineConnection === undefined) {
return Promise.resolve()
}
if (id === undefined) { if (id === undefined) {
throw new Error('id is undefined') throw new Error('id is undefined')
} }
@ -890,6 +1065,8 @@ export class EngineCommandManager {
} }
if (command.type === 'result') { if (command.type === 'result') {
return command.data return command.data
} else if (command.type === 'failed') {
return Promise.resolve(command.errors)
} }
return command.promise return command.promise
} }
@ -915,6 +1092,9 @@ export class EngineCommandManager {
} }
} }
private async fixIdMappings(ast: Program, programMemory: ProgramMemory) { private async fixIdMappings(ast: Program, programMemory: ProgramMemory) {
if (this.engineConnection === undefined) {
return
}
/* This is a temporary solution since the cmd_ids that are sent through when /* This is a temporary solution since the cmd_ids that are sent through when
sending 'extend_path' ids are not used as the segment ids. sending 'extend_path' ids are not used as the segment ids.
@ -994,3 +1174,5 @@ export class EngineCommandManager {
}) })
} }
} }
export const engineCommandManager = new EngineCommandManager()

View File

@ -6,13 +6,9 @@ import {
getXComponent, getXComponent,
addCloseToPipe, addCloseToPipe,
} from './sketch' } from './sketch'
import { parser_wasm } from '../abstractSyntaxTree' import { parse, recast, initPromise } from '../wasm'
import { getNodePathFromSourceRange } from '../queryAst' import { getNodePathFromSourceRange } from '../queryAst'
import { recast } from '../recast'
import { enginelessExecutor } from '../../lib/testHelpers' import { enginelessExecutor } from '../../lib/testHelpers'
import { initPromise } from '../rust'
beforeAll(() => initPromise)
const eachQuad: [number, [number, number]][] = [ const eachQuad: [number, [number, number]][] = [
[-315, [1, 1]], [-315, [1, 1]],
@ -29,6 +25,8 @@ const eachQuad: [number, [number, number]][] = [
[675, [1, -1]], [675, [1, -1]],
] ]
beforeAll(() => initPromise)
describe('testing getYComponent', () => { describe('testing getYComponent', () => {
it('should return the vertical component of a vector correctly when given angles in each quadrant (and with angles < 0, or > 360)', () => { it('should return the vertical component of a vector correctly when given angles in each quadrant (and with angles < 0, or > 360)', () => {
const expected: [number, number][] = [] const expected: [number, number][] = []
@ -106,7 +104,7 @@ show(mySketch001)
` `
const code = genCode(lineToChange) const code = genCode(lineToChange)
const expectedCode = genCode(lineAfterChange) const expectedCode = genCode(lineAfterChange)
const ast = parser_wasm(code) const ast = parse(code)
const programMemory = await enginelessExecutor(ast) const programMemory = await enginelessExecutor(ast)
const sourceStart = code.indexOf(lineToChange) const sourceStart = code.indexOf(lineToChange)
const { modifiedAst } = changeSketchArguments( const { modifiedAst } = changeSketchArguments(
@ -144,7 +142,7 @@ const mySketch001 = startSketchAt([0, 0])
|> lineTo([-1.59, -1.54], %) |> lineTo([-1.59, -1.54], %)
|> lineTo([0.46, -5.82], %) |> lineTo([0.46, -5.82], %)
show(mySketch001)` show(mySketch001)`
const ast = parser_wasm(code) const ast = parse(code)
const programMemory = await enginelessExecutor(ast) const programMemory = await enginelessExecutor(ast)
const sourceStart = code.indexOf(lineToChange) const sourceStart = code.indexOf(lineToChange)
expect(sourceStart).toBe(66) expect(sourceStart).toBe(66)
@ -205,7 +203,7 @@ describe('testing addTagForSketchOnFace', () => {
show(mySketch001) show(mySketch001)
` `
const code = genCode(originalLine) const code = genCode(originalLine)
const ast = parser_wasm(code) const ast = parse(code)
const programMemory = await enginelessExecutor(ast) const programMemory = await enginelessExecutor(ast)
const sourceStart = code.indexOf(originalLine) const sourceStart = code.indexOf(originalLine)
const sourceRange: [number, number] = [ const sourceRange: [number, number] = [

View File

@ -4,9 +4,6 @@ import {
SketchGroup, SketchGroup,
SourceRange, SourceRange,
PathToNode, PathToNode,
MemoryItem,
} from '../executor'
import {
Program, Program,
PipeExpression, PipeExpression,
CallExpression, CallExpression,
@ -14,7 +11,7 @@ import {
Value, Value,
Literal, Literal,
VariableDeclaration, VariableDeclaration,
} from '../abstractSyntaxTreeTypes' } from '../wasm'
import { import {
getNodeFromPath, getNodeFromPath,
getNodeFromPathCurry, getNodeFromPathCurry,
@ -38,7 +35,6 @@ import {
findUniqueName, findUniqueName,
} from '../modifyAst' } from '../modifyAst'
import { roundOff, getLength, getAngle } from '../../lib/utils' import { roundOff, getLength, getAngle } from '../../lib/utils'
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
import { perpendicularDistance } from 'sketch-helpers' import { perpendicularDistance } from 'sketch-helpers'
export type Coords2d = [number, number] export type Coords2d = [number, number]

View File

@ -1,12 +1,9 @@
import { parser_wasm } from '../abstractSyntaxTree' import { parse, SketchGroup, recast, initPromise } from '../wasm'
import { SketchGroup } from '../executor'
import { import {
ConstraintType, ConstraintType,
getTransformInfos, getTransformInfos,
transformAstSketchLines, transformAstSketchLines,
} from './sketchcombos' } from './sketchcombos'
import { recast } from '../recast'
import { initPromise } from '../rust'
import { getSketchSegmentFromSourceRange } from './sketchConstraints' import { getSketchSegmentFromSourceRange } from './sketchConstraints'
import { Selection } from '../../useStore' import { Selection } from '../../useStore'
import { enginelessExecutor } from '../../lib/testHelpers' import { enginelessExecutor } from '../../lib/testHelpers'
@ -31,7 +28,7 @@ async function testingSwapSketchFnCall({
type: 'default', type: 'default',
range: [startIndex, startIndex + callToSwap.length], range: [startIndex, startIndex + callToSwap.length],
} }
const ast = parser_wasm(inputCode) const ast = parse(inputCode)
const programMemory = await enginelessExecutor(ast) const programMemory = await enginelessExecutor(ast)
const selections = { const selections = {
codeBasedSelections: [range], codeBasedSelections: [range],
@ -381,7 +378,7 @@ const part001 = startSketchAt([0, 0.04]) // segment-in-start
|> xLine(3.54, %) |> xLine(3.54, %)
show(part001)` show(part001)`
it('normal case works', async () => { it('normal case works', async () => {
const programMemory = await enginelessExecutor(parser_wasm(code)) const programMemory = await enginelessExecutor(parse(code))
const index = code.indexOf('// normal-segment') - 7 const index = code.indexOf('// normal-segment') - 7
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange( const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
programMemory.root['part001'] as SketchGroup, programMemory.root['part001'] as SketchGroup,
@ -395,7 +392,7 @@ show(part001)`
}) })
}) })
it('verify it works when the segment is in the `start` property', async () => { it('verify it works when the segment is in the `start` property', async () => {
const programMemory = await enginelessExecutor(parser_wasm(code)) const programMemory = await enginelessExecutor(parse(code))
const index = code.indexOf('// segment-in-start') - 7 const index = code.indexOf('// segment-in-start') - 7
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange( const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
programMemory.root['part001'] as SketchGroup, programMemory.root['part001'] as SketchGroup,

View File

@ -3,8 +3,10 @@ import {
Program, Program,
VariableDeclarator, VariableDeclarator,
CallExpression, CallExpression,
} from '../abstractSyntaxTreeTypes' SketchGroup,
import { SketchGroup, SourceRange, Path } from '../executor' SourceRange,
Path,
} from '../wasm'
export function getSketchSegmentFromSourceRange( export function getSketchSegmentFromSourceRange(
sketchGroup: SketchGroup, sketchGroup: SketchGroup,

View File

@ -1,5 +1,4 @@
import { parser_wasm } from '../abstractSyntaxTree' import { parse, Value, recast, initPromise } from '../wasm'
import { Value } from '../abstractSyntaxTreeTypes'
import { import {
getConstraintType, getConstraintType,
getTransformInfos, getTransformInfos,
@ -8,10 +7,8 @@ import {
ConstraintType, ConstraintType,
getConstraintLevelFromSourceRange, getConstraintLevelFromSourceRange,
} from './sketchcombos' } from './sketchcombos'
import { initPromise } from '../rust'
import { Selections, ToolTip } from '../../useStore' import { Selections, ToolTip } from '../../useStore'
import { enginelessExecutor } from '../../lib/testHelpers' import { enginelessExecutor } from '../../lib/testHelpers'
import { recast } from '../../lang/recast'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -63,7 +60,7 @@ describe('testing getConstraintType', () => {
function getConstraintTypeFromSourceHelper( function getConstraintTypeFromSourceHelper(
code: string code: string
): ReturnType<typeof getConstraintType> { ): ReturnType<typeof getConstraintType> {
const ast = parser_wasm(code) const ast = parse(code)
const args = (ast.body[0] as any).expression.arguments[0].elements as [ const args = (ast.body[0] as any).expression.arguments[0].elements as [
Value, Value,
Value Value
@ -74,7 +71,7 @@ function getConstraintTypeFromSourceHelper(
function getConstraintTypeFromSourceHelper2( function getConstraintTypeFromSourceHelper2(
code: string code: string
): ReturnType<typeof getConstraintType> { ): ReturnType<typeof getConstraintType> {
const ast = parser_wasm(code) const ast = parse(code)
const arg = (ast.body[0] as any).expression.arguments[0] as Value const arg = (ast.body[0] as any).expression.arguments[0] as Value
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
return getConstraintType(arg, fnName) return getConstraintType(arg, fnName)
@ -199,7 +196,7 @@ const part001 = startSketchAt([0, 0])
show(part001) show(part001)
` `
it('should transform the ast', async () => { it('should transform the ast', async () => {
const ast = parser_wasm(inputScript) const ast = parse(inputScript)
const selectionRanges: Selections['codeBasedSelections'] = inputScript const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n') .split('\n')
.filter((ln) => ln.includes('//')) .filter((ln) => ln.includes('//'))
@ -286,7 +283,7 @@ const part001 = startSketchAt([0, 0])
|> angledLineToY([301, myVar], %) // select for vertical constraint 10 |> angledLineToY([301, myVar], %) // select for vertical constraint 10
show(part001) show(part001)
` `
const ast = parser_wasm(inputScript) const ast = parse(inputScript)
const selectionRanges: Selections['codeBasedSelections'] = inputScript const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n') .split('\n')
.filter((ln) => ln.includes('// select for horizontal constraint')) .filter((ln) => ln.includes('// select for horizontal constraint'))
@ -344,7 +341,7 @@ const part001 = startSketchAt([0, 0])
|> yLineTo(myVar, %) // select for vertical constraint 10 |> yLineTo(myVar, %) // select for vertical constraint 10
show(part001) show(part001)
` `
const ast = parser_wasm(inputScript) const ast = parse(inputScript)
const selectionRanges: Selections['codeBasedSelections'] = inputScript const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n') .split('\n')
.filter((ln) => ln.includes('// select for vertical constraint')) .filter((ln) => ln.includes('// select for vertical constraint'))
@ -435,7 +432,7 @@ async function helperThing(
linesOfInterest: string[], linesOfInterest: string[],
constraint: ConstraintType constraint: ConstraintType
): Promise<string> { ): Promise<string> {
const ast = parser_wasm(inputScript) const ast = parse(inputScript)
const selectionRanges: Selections['codeBasedSelections'] = inputScript const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n') .split('\n')
.filter((ln) => .filter((ln) =>
@ -498,7 +495,7 @@ const part001 = startSketchAt([-0.01, -0.05])
|> xLine(-3.43 + 0, %) // full |> xLine(-3.43 + 0, %) // full
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full |> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full
show(part001)` show(part001)`
const ast = parser_wasm(code) const ast = parse(code)
const constraintLevels: ReturnType< const constraintLevels: ReturnType<
typeof getConstraintLevelFromSourceRange typeof getConstraintLevelFromSourceRange
>[] = ['full', 'partial', 'free'] >[] = ['full', 'partial', 'free']

View File

@ -6,7 +6,9 @@ import {
Value, Value,
BinaryPart, BinaryPart,
VariableDeclarator, VariableDeclarator,
} from '../abstractSyntaxTreeTypes' PathToNode,
ProgramMemory,
} from '../wasm'
import { import {
getNodeFromPath, getNodeFromPath,
getNodeFromPathCurry, getNodeFromPathCurry,
@ -25,10 +27,8 @@ import {
giveSketchFnCallTag, giveSketchFnCallTag,
} from '../modifyAst' } from '../modifyAst'
import { createFirstArg, getFirstArg, replaceSketchLine } from './sketch' import { createFirstArg, getFirstArg, replaceSketchLine } from './sketch'
import { PathToNode, ProgramMemory } from '../executor'
import { getSketchSegmentFromSourceRange } from './sketchConstraints' import { getSketchSegmentFromSourceRange } from './sketchConstraints'
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils' import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
import { MemoryItem } from 'wasm-lib/kcl/bindings/MemoryItem'
type LineInputsType = type LineInputsType =
| 'xAbsolute' | 'xAbsolute'
@ -1279,7 +1279,7 @@ export function getTransformInfos(
}) as TransformInfo[] }) as TransformInfo[]
return theTransforms return theTransforms
} catch (error) { } catch (error) {
console.log(error) console.log('error', error)
return [] return []
} }
} }

View File

@ -1,6 +1,5 @@
import { parser_wasm } from '../abstractSyntaxTree' import { parse, initPromise } from '../wasm'
import { enginelessExecutor } from '../../lib/testHelpers' import { enginelessExecutor } from '../../lib/testHelpers'
import { initPromise } from '../rust'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -17,9 +16,9 @@ describe('testing angledLineThatIntersects', () => {
}, %) }, %)
const intersect = segEndX('yo2', part001) const intersect = segEndX('yo2', part001)
show(part001)` show(part001)`
const { root } = await enginelessExecutor(parser_wasm(code('-1'))) const { root } = await enginelessExecutor(parse(code('-1')))
expect(root.intersect.value).toBe(1 + Math.sqrt(2)) expect(root.intersect.value).toBe(1 + Math.sqrt(2))
const { root: noOffset } = await enginelessExecutor(parser_wasm(code('0'))) const { root: noOffset } = await enginelessExecutor(parse(code('0')))
expect(noOffset.intersect.value).toBeCloseTo(1) expect(noOffset.intersect.value).toBeCloseTo(1)
}) })
}) })

View File

@ -1,7 +1,12 @@
import { ProgramMemory, Path, SourceRange } from '../executor' import {
import { Program, Value } from '../abstractSyntaxTreeTypes' ProgramMemory,
Path,
SourceRange,
Program,
Value,
PathToNode,
} from '../wasm'
import { ToolTip } from '../../useStore' import { ToolTip } from '../../useStore'
import { PathToNode } from '../executor'
import { EngineCommandManager } from './engineConnection' import { EngineCommandManager } from './engineConnection'
export interface InternalFirstArg { export interface InternalFirstArg {

View File

@ -1,5 +1,4 @@
import { lexer, asyncLexer } from './tokeniser' import { lexer, initPromise } from './wasm'
import { initPromise } from './rust'
beforeAll(() => initPromise) beforeAll(() => initPromise)
@ -10,9 +9,9 @@ describe('testing lexer', () => {
const code3 = `const yo = 45 /* this is a comment const code3 = `const yo = 45 /* this is a comment
const ya = 6 */ const ya = 6 */
const yi=45` const yi=45`
expect(await asyncLexer(code)).toEqual(lexer(code)) expect(lexer(code)).toEqual(lexer(code))
expect(await asyncLexer(code2)).toEqual(lexer(code2)) expect(lexer(code2)).toEqual(lexer(code2))
expect(await asyncLexer(code3)).toEqual(lexer(code3)) expect(lexer(code3)).toEqual(lexer(code3))
}) })
it('test lexer', () => { it('test lexer', () => {
expect(stringSummaryLexer('1 + 2')).toEqual([ expect(stringSummaryLexer('1 + 2')).toEqual([

View File

@ -1,28 +0,0 @@
import { lexer_js } from '../wasm-lib/pkg/wasm_lib'
import { initPromise } from './rust'
import { Token } from '../wasm-lib/kcl/bindings/Token'
export type { Token } from '../wasm-lib/kcl/bindings/Token'
export async function asyncLexer(str: string): Promise<Token[]> {
await initPromise
try {
const tokens: Token[] = lexer_js(str)
return tokens
} catch (e) {
// TODO: do something real with the error.
console.log('lexer', e)
throw e
}
}
export function lexer(str: string): Token[] {
try {
const tokens: Token[] = lexer_js(str)
return tokens
} catch (e) {
// TODO: do something real with the error.
console.log('lexer', e)
throw e
}
}

View File

@ -1,6 +1,5 @@
import { Selections, StoreState } from '../useStore' import { Selections, StoreState } from '../useStore'
import { Program } from './abstractSyntaxTreeTypes' import { Program, PathToNode } from './wasm'
import { PathToNode } from './executor'
import { getNodeFromPath } from './queryAst' import { getNodeFromPath } from './queryAst'
export function updateCursors( export function updateCursors(

215
src/lang/wasm.ts Normal file
View File

@ -0,0 +1,215 @@
import init, {
parse_wasm,
recast_wasm,
execute_wasm,
lexer_wasm,
modify_ast_for_sketch_wasm,
} from '../wasm-lib/pkg/wasm_lib'
import { KCLError } from './errors'
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
import {
EngineCommandManager,
ArtifactMap,
SourceRangeMap,
} from './std/engineConnection'
import { ProgramReturn } from '../wasm-lib/kcl/bindings/ProgramReturn'
import { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
import type { Program } from '../wasm-lib/kcl/bindings/Program'
import type { Token } from '../wasm-lib/kcl/bindings/Token'
export type { Program } from '../wasm-lib/kcl/bindings/Program'
export type { Value } from '../wasm-lib/kcl/bindings/Value'
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
export type { MemberExpression } from '../wasm-lib/kcl/bindings/MemberExpression'
export type { PipeExpression } from '../wasm-lib/kcl/bindings/PipeExpression'
export type { VariableDeclaration } from '../wasm-lib/kcl/bindings/VariableDeclaration'
export type { PipeSubstitution } from '../wasm-lib/kcl/bindings/PipeSubstitution'
export type { Identifier } from '../wasm-lib/kcl/bindings/Identifier'
export type { UnaryExpression } from '../wasm-lib/kcl/bindings/UnaryExpression'
export type { BinaryExpression } from '../wasm-lib/kcl/bindings/BinaryExpression'
export type { ReturnStatement } from '../wasm-lib/kcl/bindings/ReturnStatement'
export type { ExpressionStatement } from '../wasm-lib/kcl/bindings/ExpressionStatement'
export type { CallExpression } from '../wasm-lib/kcl/bindings/CallExpression'
export type { VariableDeclarator } from '../wasm-lib/kcl/bindings/VariableDeclarator'
export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart'
export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
export type SyntaxType =
| 'Program'
| 'ExpressionStatement'
| 'BinaryExpression'
| 'CallExpression'
| 'Identifier'
| 'ReturnStatement'
| 'VariableDeclaration'
| 'VariableDeclarator'
| 'MemberExpression'
| 'ArrayExpression'
| 'ObjectExpression'
| 'ObjectProperty'
| 'FunctionExpression'
| 'PipeExpression'
| 'PipeSubstitution'
| 'Literal'
| 'NonCodeNode'
| 'UnaryExpression'
export type { SourceRange } from '../wasm-lib/kcl/bindings/SourceRange'
export type { Position } from '../wasm-lib/kcl/bindings/Position'
export type { Rotation } from '../wasm-lib/kcl/bindings/Rotation'
export type { Path } from '../wasm-lib/kcl/bindings/Path'
export type { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup'
export type { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
// Initialise the wasm module.
const initialise = async () => {
const baseUrl =
typeof window === 'undefined'
? 'http://127.0.0.1:3000'
: window.location.origin.includes('tauri://localhost')
? 'tauri://localhost'
: window.location.origin.includes('localhost')
? 'http://localhost:3000'
: window.location.origin && window.location.origin !== 'null'
? window.location.origin
: 'http://localhost:3000'
const fullUrl = baseUrl + '/wasm_lib_bg.wasm'
const input = await fetch(fullUrl)
const buffer = await input.arrayBuffer()
return init(buffer)
}
export const initPromise = initialise()
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
ranges.map(([start, end]) => [start, end])
export const parse = (code: string): Program => {
try {
const program: Program = parse_wasm(code)
return program
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}
export type PathToNode = [string | number, string][]
interface Memory {
[key: string]: MemoryItem
}
export interface ProgramMemory {
root: Memory
return: ProgramReturn | null
}
export const executor = async (
node: Program,
programMemory: ProgramMemory = { root: {}, return: null },
engineCommandManager: EngineCommandManager,
// work around while the gemotry is still be stored on the frontend
// will be removed when the stream UI is added.
tempMapCallback: (a: {
artifactMap: ArtifactMap
sourceRangeMap: SourceRangeMap
}) => void = () => {}
): Promise<ProgramMemory> => {
engineCommandManager.startNewSession()
const _programMemory = await _executor(
node,
programMemory,
engineCommandManager
)
const { artifactMap, sourceRangeMap } =
await engineCommandManager.waitForAllCommands(node, _programMemory)
tempMapCallback({ artifactMap, sourceRangeMap })
engineCommandManager.endSession()
return _programMemory
}
export const _executor = async (
node: Program,
programMemory: ProgramMemory = { root: {}, return: null },
engineCommandManager: EngineCommandManager
): Promise<ProgramMemory> => {
try {
const memory: ProgramMemory = await execute_wasm(
JSON.stringify(node),
JSON.stringify(programMemory),
engineCommandManager
)
return memory
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}
export const recast = (ast: Program): string => {
try {
const s: string = recast_wasm(JSON.stringify(ast))
return s
} catch (e) {
// TODO: do something real with the error.
console.log('recast error', e)
throw e
}
}
export function lexer(str: string): Token[] {
try {
const tokens: Token[] = lexer_wasm(str)
return tokens
} catch (e) {
// TODO: do something real with the error.
console.log('lexer error', e)
throw e
}
}
export const modifyAstForSketch = async (
engineCommandManager: EngineCommandManager,
ast: Program,
variableName: string,
engineId: string
): Promise<Program> => {
try {
const updatedAst: Program = await modify_ast_for_sketch_wasm(
engineCommandManager,
JSON.stringify(ast),
variableName,
engineId
)
return updatedAst
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}

View File

@ -1,19 +1,27 @@
export const bracket = `// Material: 6061-T6 Aluminum export const bracket = `const sigmaAllow = 15000 // psi
const sigmaAllow = 35000 // psi const width = 11 // inch
const width = 9 // inch
const p = 150 // Force on shelf - lbs const p = 150 // Force on shelf - lbs
const distance = 6 // inches const distance = 12 // inches
const FOS = 2 const FOS = 2
const thickness = sqrt(distance * p * FOS * 6 / ( sigmaAllow * width ))
const filletR = thickness * 2
const shelfMountL = 9
const wallMountL = 8
const leg1 = 5 // inches
const leg2 = 8 // inches
const thickness = sqrt(distance * p * FOS * 6 / sigmaAllow / width) // inches
const bracket = startSketchAt([0, 0]) const bracket = startSketchAt([0, 0])
|> line([0, leg1], %) |> line([0, wallMountL], %)
|> line([leg2, 0], %) |> tangentalArc({
radius: filletR,
offset: 90
}, %)
|> line([-shelfMountL, 0], %)
|> line([0, -thickness], %) |> line([0, -thickness], %)
|> line([-leg2 + thickness, 0], %) |> line([shelfMountL, 0], %)
|> line([0, -leg1 + thickness], %) |> tangentalArc({
radius: filletR - thickness,
offset: -90
}, %)
|> line([0, -wallMountL], %)
|> close(%) |> close(%)
|> extrude(width, %) |> extrude(width, %)

View File

@ -39,6 +39,6 @@ export async function exportSave(data: ArrayBuffer) {
} }
} catch (e) { } catch (e) {
// TODO: do something real with the error. // TODO: do something real with the error.
console.log('export', e) console.log('export error', e)
} }
} }

View File

@ -36,7 +36,7 @@ export async function initializeProjectDirectory(directory: string) {
try { try {
docDirectory = await documentDir() docDirectory = await documentDir()
} catch (e) { } catch (e) {
console.log(e) console.log('error', e)
docDirectory = await homeDir() // seems to work better on Linux docDirectory = await homeDir() // seems to work better on Linux
} }

View File

@ -1,10 +1,11 @@
import { Program } from '../lang/abstractSyntaxTreeTypes' import { Program, ProgramMemory, _executor, SourceRange } from '../lang/wasm'
import { ProgramMemory, _executor } from '../lang/executor'
import { import {
EngineCommandManager, EngineCommandManager,
EngineCommand, EngineCommand,
} from '../lang/std/engineConnection' } from '../lang/std/engineConnection'
import { SourceRange } from 'lang/executor' import { Models } from '@kittycad/lib'
type WebSocketResponse = Models['OkWebSocketResponseData_type']
class MockEngineCommandManager { class MockEngineCommandManager {
constructor(mockParams: { constructor(mockParams: {
@ -23,7 +24,13 @@ class MockEngineCommandManager {
range: SourceRange range: SourceRange
command: EngineCommand command: EngineCommand
}): Promise<any> { }): Promise<any> {
return Promise.resolve() const response: WebSocketResponse = {
type: 'modeling',
data: {
modeling_response: { type: 'empty' },
},
}
return Promise.resolve(JSON.stringify(response))
} }
sendModelingCommandFromWasm( sendModelingCommandFromWasm(
id: string, id: string,
@ -66,11 +73,13 @@ export async function executor(
ast: Program, ast: Program,
pm: ProgramMemory = { root: {}, return: null } pm: ProgramMemory = { root: {}, return: null }
): Promise<ProgramMemory> { ): Promise<ProgramMemory> {
const engineCommandManager = new EngineCommandManager({ const engineCommandManager = new EngineCommandManager()
engineCommandManager.start({
setIsStreamReady: () => {}, setIsStreamReady: () => {},
setMediaStream: () => {}, setMediaStream: () => {},
width: 100, width: 0,
height: 100, height: 0,
executeCode: () => {},
}) })
await engineCommandManager.waitForReady await engineCommandManager.waitForReady
engineCommandManager.startNewSession() engineCommandManager.startNewSession()

View File

@ -1,5 +1,5 @@
import { isOverlap, roundOff } from './utils' import { isOverlap, roundOff } from './utils'
import { SourceRange } from '../lang/executor' import { SourceRange } from '../lang/wasm'
describe('testing isOverlapping', () => { describe('testing isOverlapping', () => {
testBothOrders([0, 3], [3, 10]) testBothOrders([0, 3], [3, 10])

View File

@ -1,4 +1,4 @@
import { SourceRange } from '../lang/executor' import { SourceRange } from '../lang/wasm'
export function isOverlap(a: SourceRange, b: SourceRange) { export function isOverlap(a: SourceRange, b: SourceRange) {
const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a] const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a]

View File

@ -2,6 +2,7 @@ import { assign, createMachine } from 'xstate'
import { CommandBarMeta } from '../lib/commands' import { CommandBarMeta } from '../lib/commands'
import { Themes, getSystemTheme, setThemeClass } from '../lib/theme' import { Themes, getSystemTheme, setThemeClass } from '../lib/theme'
import { CameraSystem, cameraSystems } from 'lib/cameraControls' import { CameraSystem, cameraSystems } from 'lib/cameraControls'
import { Models } from '@kittycad/lib'
export const DEFAULT_PROJECT_NAME = 'project-$nnn' export const DEFAULT_PROJECT_NAME = 'project-$nnn'
@ -11,11 +12,11 @@ export enum UnitSystem {
} }
export const baseUnits = { export const baseUnits = {
imperial: ['in', 'ft'], imperial: ['in', 'ft', 'yd'],
metric: ['mm', 'cm', 'm'], metric: ['mm', 'cm', 'm'],
} as const } as const
export type BaseUnit = 'in' | 'ft' | 'mm' | 'cm' | 'm' export type BaseUnit = Models['UnitLength_type']
export const baseUnitsUnion = Object.values(baseUnits).flatMap((v) => v) export const baseUnitsUnion = Object.values(baseUnits).flatMap((v) => v)

View File

@ -1,24 +1,25 @@
import { create } from 'zustand' import { create } from 'zustand'
import { persist } from 'zustand/middleware' import { persist } from 'zustand/middleware'
import { addLineHighlight, EditorView } from './editor/highlightextension' import { addLineHighlight, EditorView } from './editor/highlightextension'
import { parser_wasm } from './lang/abstractSyntaxTree'
import { Program } from './lang/abstractSyntaxTreeTypes'
import { getNodeFromPath } from './lang/queryAst'
import { enginelessExecutor } from './lib/testHelpers'
import { import {
parse,
Program,
_executor,
recast,
ProgramMemory, ProgramMemory,
Position, Position,
PathToNode, PathToNode,
Rotation, Rotation,
SourceRange, SourceRange,
} from './lang/executor' } from './lang/wasm'
import { recast } from './lang/recast' import { getNodeFromPath } from './lang/queryAst'
import { enginelessExecutor } from './lib/testHelpers'
import { EditorSelection } from '@codemirror/state' import { EditorSelection } from '@codemirror/state'
import { EngineCommandManager } from './lang/std/engineConnection' import { EngineCommandManager } from './lang/std/engineConnection'
import { KCLError } from './lang/errors' import { KCLError } from './lang/errors'
import { deferExecution } from 'lib/utils' import { deferExecution } from 'lib/utils'
import { _executor } from './lang/executor'
import { bracket } from 'lib/exampleKcl' import { bracket } from 'lib/exampleKcl'
import { engineCommandManager } from './lang/std/engineConnection'
export type Selection = { export type Selection = {
type: 'default' | 'line-end' | 'line-mid' type: 'default' | 'line-end' | 'line-mid'
@ -156,14 +157,12 @@ export interface StoreState {
code: string code: string
setCode: (code: string) => void setCode: (code: string) => void
deferredSetCode: (code: string) => void deferredSetCode: (code: string) => void
executeCode: (code?: string) => void executeCode: (code?: string, force?: boolean) => void
formatCode: () => void formatCode: () => void
programMemory: ProgramMemory programMemory: ProgramMemory
setProgramMemory: (programMemory: ProgramMemory) => void setProgramMemory: (programMemory: ProgramMemory) => void
isShiftDown: boolean isShiftDown: boolean
setIsShiftDown: (isShiftDown: boolean) => void setIsShiftDown: (isShiftDown: boolean) => void
engineCommandManager?: EngineCommandManager
setEngineCommandManager: (engineCommandManager: EngineCommandManager) => void
mediaStream?: MediaStream mediaStream?: MediaStream
setMediaStream: (mediaStream: MediaStream) => void setMediaStream: (mediaStream: MediaStream) => void
isStreamReady: boolean isStreamReady: boolean
@ -222,11 +221,12 @@ export const useStore = create<StoreState>()(
editorView.dispatch({ effects: addLineHighlight.of(selection) }) editorView.dispatch({ effects: addLineHighlight.of(selection) })
} }
}, },
executeCode: async (code) => { executeCode: async (code, force) => {
const result = await executeCode({ const result = await executeCode({
code: code || get().code, code: code || get().code,
lastAst: get().ast, lastAst: get().ast,
engineCommandManager: get().engineCommandManager, engineCommandManager: engineCommandManager,
force,
}) })
if (!result.isChange) { if (!result.isChange) {
return return
@ -332,8 +332,6 @@ export const useStore = create<StoreState>()(
executeAst: async (ast) => { executeAst: async (ast) => {
const _ast = ast || get().ast const _ast = ast || get().ast
if (!get().isStreamReady) return if (!get().isStreamReady) return
const engineCommandManager = get().engineCommandManager!
if (!engineCommandManager) return
set({ isExecuting: true }) set({ isExecuting: true })
const { logs, errors, programMemory } = await executeAst({ const { logs, errors, programMemory } = await executeAst({
@ -350,8 +348,6 @@ export const useStore = create<StoreState>()(
executeAstMock: async (ast) => { executeAstMock: async (ast) => {
const _ast = ast || get().ast const _ast = ast || get().ast
if (!get().isStreamReady) return if (!get().isStreamReady) return
const engineCommandManager = get().engineCommandManager!
if (!engineCommandManager) return
const { logs, errors, programMemory } = await executeAst({ const { logs, errors, programMemory } = await executeAst({
ast: _ast, ast: _ast,
@ -371,7 +367,7 @@ export const useStore = create<StoreState>()(
{ focusPath, callBack = () => {} } = {} { focusPath, callBack = () => {} } = {}
) => { ) => {
const newCode = recast(ast) const newCode = recast(ast)
const astWithUpdatedSource = parser_wasm(newCode) const astWithUpdatedSource = parse(newCode)
callBack(astWithUpdatedSource) callBack(astWithUpdatedSource)
set({ set({
@ -427,7 +423,7 @@ export const useStore = create<StoreState>()(
}, },
formatCode: async () => { formatCode: async () => {
const code = get().code const code = get().code
const ast = parser_wasm(code) const ast = parse(code)
const newCode = recast(ast) const newCode = recast(ast)
set({ code: newCode, ast }) set({ code: newCode, ast })
}, },
@ -435,8 +431,6 @@ export const useStore = create<StoreState>()(
setProgramMemory: (programMemory) => set({ programMemory }), setProgramMemory: (programMemory) => set({ programMemory }),
isShiftDown: false, isShiftDown: false,
setIsShiftDown: (isShiftDown) => set({ isShiftDown }), setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
setEngineCommandManager: (engineCommandManager) =>
set({ engineCommandManager }),
setMediaStream: (mediaStream) => set({ mediaStream }), setMediaStream: (mediaStream) => set({ mediaStream }),
isStreamReady: false, isStreamReady: false,
setIsStreamReady: (isStreamReady) => set({ isStreamReady }), setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
@ -454,7 +448,9 @@ export const useStore = create<StoreState>()(
fileId: '', fileId: '',
setFileId: (fileId) => set({ fileId }), setFileId: (fileId) => set({ fileId }),
streamDimensions: { streamWidth: 1280, streamHeight: 720 }, streamDimensions: { streamWidth: 1280, streamHeight: 720 },
setStreamDimensions: (streamDimensions) => set({ streamDimensions }), setStreamDimensions: (streamDimensions) => {
set({ streamDimensions })
},
isExecuting: false, isExecuting: false,
setIsExecuting: (isExecuting) => set({ isExecuting }), setIsExecuting: (isExecuting) => set({ isExecuting }),
@ -516,10 +512,12 @@ async function executeCode({
engineCommandManager, engineCommandManager,
code, code,
lastAst, lastAst,
force,
}: { }: {
code: string code: string
lastAst: Program lastAst: Program
engineCommandManager?: EngineCommandManager engineCommandManager: EngineCommandManager
force?: boolean
}): Promise< }): Promise<
| { | {
logs: string[] logs: string[]
@ -532,14 +530,14 @@ async function executeCode({
> { > {
let ast: Program let ast: Program
try { try {
ast = parser_wasm(code) ast = parse(code)
} catch (e) { } catch (e) {
let errors: KCLError[] = [] let errors: KCLError[] = []
let logs: string[] = [JSON.stringify(e)] let logs: string[] = [JSON.stringify(e)]
if (e instanceof KCLError) { if (e instanceof KCLError) {
errors = [e] errors = [e]
logs = [] logs = []
if (e.msg === 'file is empty') engineCommandManager?.endSession() if (e.msg === 'file is empty') engineCommandManager.endSession()
} }
return { return {
isChange: true, isChange: true,
@ -562,7 +560,7 @@ async function executeCode({
} }
// Check if the ast we have is equal to the ast in the storage. // Check if the ast we have is equal to the ast in the storage.
// If it is, we don't need to update the ast. // If it is, we don't need to update the ast.
if (!engineCommandManager || JSON.stringify(ast) === JSON.stringify(lastAst)) if (JSON.stringify(ast) === JSON.stringify(lastAst) && !force)
return { isChange: false } return { isChange: false }
const { logs, errors, programMemory } = await executeAst({ const { logs, errors, programMemory } = await executeAst({

View File

@ -6,10 +6,10 @@
serial-integration = { max-threads = 4 } serial-integration = { max-threads = 4 }
[profile.default] [profile.default]
slow-timeout = { period = "10s", terminate-after = 1 } slow-timeout = { period = "60s", terminate-after = 1 }
[profile.ci] [profile.ci]
slow-timeout = { period = "60s", terminate-after = 10 } slow-timeout = { period = "120s", terminate-after = 10 }
[[profile.default.overrides]] [[profile.default.overrides]]
filter = "test(serial_test_)" filter = "test(serial_test_)"

256
src/wasm-lib/Cargo.lock generated
View File

@ -64,10 +64,16 @@ dependencies = [
] ]
[[package]] [[package]]
name = "anstream" name = "anes"
version = "0.5.0" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6cd65a4b849ace0b7f6daeebcc1a1d111282227ca745458c61dbf670e52a597"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
@ -103,9 +109,9 @@ dependencies = [
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "2.1.0" version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys 0.48.0", "windows-sys 0.48.0",
@ -142,6 +148,17 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "async-recursion"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.73" version = "0.1.73"
@ -211,13 +228,16 @@ checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
[[package]] [[package]]
name = "bigdecimal" name = "bigdecimal"
version = "0.3.1" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" checksum = "454bca3db10617b88b566f205ed190aedb0e0e6dd4cad61d3988a72e8c5594cb"
dependencies = [ dependencies = [
"autocfg",
"libm",
"num-bigint", "num-bigint",
"num-integer", "num-integer",
"num-traits", "num-traits",
"serde",
] ]
[[package]] [[package]]
@ -337,6 +357,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.83" version = "1.0.83"
@ -374,6 +400,33 @@ dependencies = [
"windows-targets 0.48.5", "windows-targets 0.48.5",
] ]
[[package]]
name = "ciborium"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
[[package]]
name = "ciborium-ll"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
dependencies = [
"ciborium-io",
"half 1.8.2",
]
[[package]] [[package]]
name = "clang-sys" name = "clang-sys"
version = "1.6.1" version = "1.6.1"
@ -387,9 +440,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.4" version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -397,9 +450,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.4.4" version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -507,6 +560,42 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "criterion"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
"anes",
"cast",
"ciborium",
"clap",
"criterion-plot",
"is-terminal",
"itertools 0.10.5",
"num-traits",
"once_cell",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools 0.10.5",
]
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.8" version = "0.5.8"
@ -606,6 +695,20 @@ dependencies = [
"syn 2.0.37", "syn 2.0.37",
] ]
[[package]]
name = "derive-docs"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c357dec14992ba88803535217ed83d6f6cd80efcb8fa8e3f8a30a9b84fadc1c7"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"serde",
"serde_tokenstream",
"syn 2.0.37",
]
[[package]] [[package]]
name = "diff" name = "diff"
version = "0.1.13" version = "0.1.13"
@ -699,9 +802,9 @@ dependencies = [
[[package]] [[package]]
name = "expectorate" name = "expectorate"
version = "1.0.7" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "710ab6a2d57038a835d66f78d5af3fa5d27c1ec4682f823b9203c48826cb0591" checksum = "de6f19b25bdfa2747ae775f37cd109c31f1272d4e4c83095be0727840aa1d75f"
dependencies = [ dependencies = [
"console", "console",
"newline-converter", "newline-converter",
@ -716,7 +819,7 @@ checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"flume", "flume",
"half", "half 2.2.1",
"lebe", "lebe",
"miniz_oxide", "miniz_oxide",
"rayon-core", "rayon-core",
@ -983,6 +1086,12 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]] [[package]]
name = "half" name = "half"
version = "2.2.1" version = "2.2.1"
@ -1281,14 +1390,16 @@ dependencies = [
[[package]] [[package]]
name = "kcl-lib" name = "kcl-lib"
version = "0.1.30" version = "0.1.32"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-recursion",
"async-trait", "async-trait",
"bson", "bson",
"clap", "clap",
"criterion",
"dashmap", "dashmap",
"derive-docs", "derive-docs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"expectorate", "expectorate",
"futures", "futures",
"itertools 0.11.0", "itertools 0.11.0",
@ -1297,7 +1408,6 @@ dependencies = [
"lazy_static", "lazy_static",
"parse-display", "parse-display",
"pretty_assertions", "pretty_assertions",
"regex",
"reqwest", "reqwest",
"schemars", "schemars",
"serde", "serde",
@ -1311,17 +1421,19 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
"winnow",
] ]
[[package]] [[package]]
name = "kittycad" name = "kittycad"
version = "0.2.25" version = "0.2.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9cf962b1e81a0b4eb923a727e761b40672cbacc7f5f0b75e13579d346352bc7" checksum = "35b2f9302648dbb06fd7121687f9505fc3179eba84111a06d76b246e3158f5dc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
"base64 0.21.4", "base64 0.21.4",
"bigdecimal",
"bytes", "bytes",
"chrono", "chrono",
"data-encoding", "data-encoding",
@ -1383,6 +1495,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "libm"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.6" version = "0.5.6"
@ -1606,10 +1724,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d11de466f4a3006fe8a5e7ec84e93b79c70cb992ae0aa0eb631ad2df8abfe2" checksum = "44d11de466f4a3006fe8a5e7ec84e93b79c70cb992ae0aa0eb631ad2df8abfe2"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]] [[package]]
name = "openapitor" name = "openapitor"
version = "0.0.9" version = "0.0.9"
source = "git+https://github.com/KittyCAD/kittycad.rs?branch=main#0d121f6881da91b4a30bee18bbfe50e4a2096073" source = "git+https://github.com/KittyCAD/kittycad.rs?branch=main#fa0345c514fcc9ae6cd74ae35c8e5c2800fec34f"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"anyhow", "anyhow",
@ -1783,14 +1907,14 @@ dependencies = [
[[package]] [[package]]
name = "phonenumber" name = "phonenumber"
version = "0.3.2+8.13.9" version = "0.3.3+8.13.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34749f64ea9d76f10cdc8a859588b57775f59177c7dd91f744d620bd62982d6f" checksum = "635f3e6288e4f01c049d89332a031bd74f25d64b6fb94703ca966e819488cd06"
dependencies = [ dependencies = [
"bincode", "bincode",
"either", "either",
"fnv", "fnv",
"itertools 0.10.5", "itertools 0.11.0",
"lazy_static", "lazy_static",
"nom", "nom",
"quick-xml", "quick-xml",
@ -1798,6 +1922,7 @@ dependencies = [
"regex-cache", "regex-cache",
"serde", "serde",
"serde_derive", "serde_derive",
"strum",
"thiserror", "thiserror",
] ]
@ -1839,6 +1964,34 @@ version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "plotters"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-svg"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]] [[package]]
name = "png" name = "png"
version = "0.17.10" version = "0.17.10"
@ -2696,6 +2849,28 @@ dependencies = [
"syn 2.0.37", "syn 2.0.37",
] ]
[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 1.0.109",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -2784,18 +2959,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.48" version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.48" version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2853,6 +3028,16 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.6.0" version = "1.6.0"
@ -2910,9 +3095,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-tungstenite" name = "tokio-tungstenite"
version = "0.20.0" version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2" checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"log", "log",
@ -3132,9 +3317,9 @@ dependencies = [
[[package]] [[package]]
name = "tungstenite" name = "tungstenite"
version = "0.20.0" version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649" checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"bytes", "bytes",
@ -3621,6 +3806,15 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "winreg" name = "winreg"
version = "0.50.0" version = "0.50.0"

View File

@ -11,7 +11,7 @@ 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.25", default-features = false, features = ["js"] } kittycad = { version = "0.2.27", default-features = false, features = ["js"] }
serde_json = "1.0.107" serde_json = "1.0.107"
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] } uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
wasm-bindgen = "0.2.87" wasm-bindgen = "0.2.87"
@ -20,7 +20,7 @@ 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.25" kittycad = "0.2.27"
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"
reqwest = { version = "0.11.20", default-features = false } reqwest = { version = "0.11.20", default-features = false }
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] } tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }

View File

@ -19,6 +19,6 @@ serde_tokenstream = "0.2"
syn = { version = "2.0.37", features = ["full"] } syn = { version = "2.0.37", features = ["full"] }
[dev-dependencies] [dev-dependencies]
expectorate = "1.0.7" expectorate = "1.1.0"
openapitor = { git = "https://github.com/KittyCAD/kittycad.rs", branch = "main" } openapitor = { git = "https://github.com/KittyCAD/kittycad.rs", branch = "main" }
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"

View File

@ -79,13 +79,6 @@ fn do_stdlib_inner(
)); ));
} }
if ast.sig.asyncness.is_some() {
errors.push(Error::new_spanned(
&ast.sig.fn_token,
"stdlib functions must not be async",
));
}
if ast.sig.unsafety.is_some() { if ast.sig.unsafety.is_some() {
errors.push(Error::new_spanned( errors.push(Error::new_spanned(
&ast.sig.unsafety, &ast.sig.unsafety,
@ -118,6 +111,7 @@ fn do_stdlib_inner(
let fn_name = &ast.sig.ident; let fn_name = &ast.sig.ident;
let fn_name_str = fn_name.to_string().replace("inner_", ""); let fn_name_str = fn_name.to_string().replace("inner_", "");
let fn_name_ident = format_ident!("{}", fn_name_str); let fn_name_ident = format_ident!("{}", fn_name_str);
let boxed_fn_name_ident = format_ident!("boxed_{}", fn_name_str);
let _visibility = &ast.vis; let _visibility = &ast.vis;
let (summary_text, description_text) = extract_doc_from_attrs(&ast.attrs); let (summary_text, description_text) = extract_doc_from_attrs(&ast.attrs);
@ -204,7 +198,10 @@ fn do_stdlib_inner(
syn::FnArg::Typed(pat) => pat.ty.as_ref().into_token_stream(), syn::FnArg::Typed(pat) => pat.ty.as_ref().into_token_stream(),
}; };
let ty_string = ty.to_string().replace('&', "").replace("mut", "").replace(' ', ""); let mut ty_string = ty.to_string().replace('&', "").replace("mut", "").replace(' ', "");
if ty_string.starts_with("Args") {
ty_string = "Args".to_string();
}
let ty_string = ty_string.trim().to_string(); let ty_string = ty_string.trim().to_string();
let ty_ident = if ty_string.starts_with("Vec<") { let ty_ident = if ty_string.starts_with("Vec<") {
let ty_string = ty_string.trim_start_matches("Vec<").trim_end_matches('>'); let ty_string = ty_string.trim_start_matches("Vec<").trim_end_matches('>');
@ -305,6 +302,14 @@ fn do_stdlib_inner(
#description_doc_comment #description_doc_comment
#const_struct #const_struct
fn #boxed_fn_name_ident(
args: crate::std::Args,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>>>,
> {
Box::pin(#fn_name_ident(args))
}
impl #docs_crate::StdLibFn for #name_ident impl #docs_crate::StdLibFn for #name_ident
{ {
fn name(&self) -> String { fn name(&self) -> String {
@ -348,7 +353,7 @@ fn do_stdlib_inner(
} }
fn std_lib_fn(&self) -> crate::std::StdFn { fn std_lib_fn(&self) -> crate::std::StdFn {
#fn_name_ident #boxed_fn_name_ident
} }
fn clone_box(&self) -> Box<dyn #docs_crate::StdLibFn> { fn clone_box(&self) -> Box<dyn #docs_crate::StdLibFn> {

View File

@ -7,6 +7,18 @@ pub(crate) struct Show {}
#[allow(non_upper_case_globals, missing_docs)] #[allow(non_upper_case_globals, missing_docs)]
#[doc = "Std lib function: show"] #[doc = "Std lib function: show"]
pub(crate) const Show: Show = Show {}; pub(crate) const Show: Show = Show {};
fn boxed_show(
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>,
>,
> {
Box::pin(show(args))
}
impl crate::docs::StdLibFn for Show { impl crate::docs::StdLibFn for Show {
fn name(&self) -> String { fn name(&self) -> String {
"show".to_string() "show".to_string()
@ -57,7 +69,7 @@ impl crate::docs::StdLibFn for Show {
} }
fn std_lib_fn(&self) -> crate::std::StdFn { fn std_lib_fn(&self) -> crate::std::StdFn {
show boxed_show
} }
fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> { fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> {

View File

@ -7,6 +7,18 @@ pub(crate) struct LineTo {}
#[allow(non_upper_case_globals, missing_docs)] #[allow(non_upper_case_globals, missing_docs)]
#[doc = "Std lib function: lineTo"] #[doc = "Std lib function: lineTo"]
pub(crate) const LineTo: LineTo = LineTo {}; pub(crate) const LineTo: LineTo = LineTo {};
fn boxed_line_to(
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>,
>,
> {
Box::pin(line_to(args))
}
impl crate::docs::StdLibFn for LineTo { impl crate::docs::StdLibFn for LineTo {
fn name(&self) -> String { fn name(&self) -> String {
"lineTo".to_string() "lineTo".to_string()
@ -65,7 +77,7 @@ impl crate::docs::StdLibFn for LineTo {
} }
fn std_lib_fn(&self) -> crate::std::StdFn { fn std_lib_fn(&self) -> crate::std::StdFn {
line_to boxed_line_to
} }
fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> { fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> {

View File

@ -7,6 +7,18 @@ pub(crate) struct Min {}
#[allow(non_upper_case_globals, missing_docs)] #[allow(non_upper_case_globals, missing_docs)]
#[doc = "Std lib function: min"] #[doc = "Std lib function: min"]
pub(crate) const Min: Min = Min {}; pub(crate) const Min: Min = Min {};
fn boxed_min(
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>,
>,
> {
Box::pin(min(args))
}
impl crate::docs::StdLibFn for Min { impl crate::docs::StdLibFn for Min {
fn name(&self) -> String { fn name(&self) -> String {
"min".to_string() "min".to_string()
@ -57,7 +69,7 @@ impl crate::docs::StdLibFn for Min {
} }
fn std_lib_fn(&self) -> crate::std::StdFn { fn std_lib_fn(&self) -> crate::std::StdFn {
min boxed_min
} }
fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> { fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> {

View File

@ -7,6 +7,18 @@ pub(crate) struct Show {}
#[allow(non_upper_case_globals, missing_docs)] #[allow(non_upper_case_globals, missing_docs)]
#[doc = "Std lib function: show"] #[doc = "Std lib function: show"]
pub(crate) const Show: Show = Show {}; pub(crate) const Show: Show = Show {};
fn boxed_show(
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>,
>,
> {
Box::pin(show(args))
}
impl crate::docs::StdLibFn for Show { impl crate::docs::StdLibFn for Show {
fn name(&self) -> String { fn name(&self) -> String {
"show".to_string() "show".to_string()
@ -52,7 +64,7 @@ impl crate::docs::StdLibFn for Show {
} }
fn std_lib_fn(&self) -> crate::std::StdFn { fn std_lib_fn(&self) -> crate::std::StdFn {
show boxed_show
} }
fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> { fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> {

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-lib" name = "kcl-lib"
description = "KittyCAD Language" description = "KittyCAD Language"
version = "0.1.30" version = "0.1.32"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
@ -9,21 +9,22 @@ license = "MIT"
[dependencies] [dependencies]
anyhow = { version = "1.0.75", features = ["backtrace"] } anyhow = { version = "1.0.75", features = ["backtrace"] }
async-recursion = "1.0.5"
async-trait = "0.1.73" async-trait = "0.1.73"
clap = { version = "4.4.3", features = ["cargo", "derive", "env", "unicode"], optional = true } clap = { version = "4.4.6", 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.25", default-features = false, features = ["js"] } kittycad = { version = "0.2.27", default-features = false, features = ["js"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
parse-display = "0.8.2" parse-display = "0.8.2"
regex = "1.7.1"
schemars = { version = "0.8", features = ["impl_json_schema", "url", "uuid1"] } schemars = { version = "0.8", features = ["impl_json_schema", "url", "uuid1"] }
serde = { version = "1.0.188", features = ["derive"] } serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.107" serde_json = "1.0.107"
thiserror = "1.0.48" thiserror = "1.0.49"
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.4.1", features = ["v4", "js", "serde"] }
winnow = "0.5.15"
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = { version = "0.3.64" } js-sys = { version = "0.3.64" }
@ -50,7 +51,12 @@ panic = "abort"
debug = true debug = true
[dev-dependencies] [dev-dependencies]
expectorate = "1.0.7" criterion = "0.5.1"
expectorate = "1.1.0"
itertools = "0.11.0" itertools = "0.11.0"
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] } tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
[[bench]]
name = "compiler_benchmark"
harness = false

View File

@ -0,0 +1,41 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
pub fn bench_lex(c: &mut Criterion) {
c.bench_function("lex_cube", |b| b.iter(|| lex(CUBE_PROGRAM)));
c.bench_function("lex_big_kitt", |b| b.iter(|| lex(KITT_PROGRAM)));
c.bench_function("lex_pipes_on_pipes", |b| b.iter(|| lex(PIPES_PROGRAM)));
}
pub fn bench_lex_parse(c: &mut Criterion) {
c.bench_function("parse_lex_cube", |b| b.iter(|| lex_and_parse(CUBE_PROGRAM)));
c.bench_function("parse_lex_big_kitt", |b| b.iter(|| lex_and_parse(KITT_PROGRAM)));
c.bench_function("parse_lex_pipes_on_pipes", |b| b.iter(|| lex_and_parse(PIPES_PROGRAM)));
}
fn lex(program: &str) {
black_box(kcl_lib::token::lexer(program));
}
fn lex_and_parse(program: &str) {
let tokens = kcl_lib::token::lexer(program);
let parser = kcl_lib::parser::Parser::new(tokens);
black_box(parser.ast().unwrap());
}
criterion_group!(benches, bench_lex, bench_lex_parse);
criterion_main!(benches);
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl");
const CUBE_PROGRAM: &str = r#"fn cube = (pos, scale) => {
const sg = startSketchAt(pos)
|> line([0, scale], %)
|> line([scale, 0], %)
|> line([0, -scale], %)
return sg
}
const b1 = cube([0,0], 10)
const pt1 = b1[0]
show(b1)"#;

View File

@ -0,0 +1 @@
enum-variant-size-threshold = 24

View File

@ -44,54 +44,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "anstream"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea"
[[package]]
name = "anstyle-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.75" version = "1.0.75"
@ -123,6 +75,17 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "async-recursion"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.73" version = "0.1.73"
@ -131,7 +94,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -179,6 +142,20 @@ version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
[[package]]
name = "bigdecimal"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "454bca3db10617b88b566f205ed190aedb0e0e6dd4cad61d3988a72e8c5594cb"
dependencies = [
"autocfg",
"libm",
"num-bigint",
"num-integer",
"num-traits",
"serde",
]
[[package]] [[package]]
name = "bincode" name = "bincode"
version = "1.3.3" version = "1.3.3"
@ -284,54 +261,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "clap"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
"unicase",
"unicode-width",
]
[[package]]
name = "clap_derive"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.31",
]
[[package]]
name = "clap_lex"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]] [[package]]
name = "convert_case" name = "convert_case"
version = "0.6.0" version = "0.6.0"
@ -403,16 +332,14 @@ checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
[[package]] [[package]]
name = "derive-docs" name = "derive-docs"
version = "0.1.3" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fe5c5ea065cfabc5a7c5e8ed616e369fbf108c4be01e0e5609bc9846a732664"
dependencies = [ dependencies = [
"convert_case", "convert_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"serde_tokenstream", "serde_tokenstream",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -529,7 +456,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -769,11 +696,12 @@ dependencies = [
[[package]] [[package]]
name = "kcl-lib" name = "kcl-lib"
version = "0.1.24" version = "0.1.31"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-recursion",
"async-trait",
"bson", "bson",
"clap",
"dashmap", "dashmap",
"derive-docs", "derive-docs",
"futures", "futures",
@ -781,7 +709,6 @@ dependencies = [
"kittycad", "kittycad",
"lazy_static", "lazy_static",
"parse-display", "parse-display",
"regex",
"reqwest", "reqwest",
"schemars", "schemars",
"serde", "serde",
@ -794,6 +721,8 @@ dependencies = [
"uuid", "uuid",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys",
"winnow",
] ]
[[package]] [[package]]
@ -806,12 +735,13 @@ dependencies = [
[[package]] [[package]]
name = "kittycad" name = "kittycad"
version = "0.2.24" version = "0.2.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd2f78e95054c83ab77059fe0237b128e2b241b4b8e9466e452d701c2599f3d3" checksum = "e2623ee601ce203476229df3f9d3a14664cb43e3f7455e9ac8ed91aacaa6163d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64 0.21.3", "base64 0.21.3",
"bigdecimal",
"bytes", "bytes",
"chrono", "chrono",
"data-encoding", "data-encoding",
@ -850,6 +780,12 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "libm"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.6" version = "0.5.6"
@ -942,6 +878,27 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.16" version = "0.2.16"
@ -1034,7 +991,7 @@ dependencies = [
"regex", "regex",
"regex-syntax 0.7.5", "regex-syntax 0.7.5",
"structmeta", "structmeta",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -1045,9 +1002,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]] [[package]]
name = "phonenumber" name = "phonenumber"
version = "0.3.2+8.13.9" version = "0.3.3+8.13.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34749f64ea9d76f10cdc8a859588b57775f59177c7dd91f744d620bd62982d6f" checksum = "635f3e6288e4f01c049d89332a031bd74f25d64b6fb94703ca966e819488cd06"
dependencies = [ dependencies = [
"bincode", "bincode",
"either", "either",
@ -1060,6 +1017,7 @@ dependencies = [
"regex-cache", "regex-cache",
"serde", "serde",
"serde_derive", "serde_derive",
"strum",
"thiserror", "thiserror",
] ]
@ -1080,7 +1038,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -1127,9 +1085,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.66" version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -1342,6 +1300,12 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "rustversion"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.15" version = "1.0.15"
@ -1359,10 +1323,11 @@ dependencies = [
[[package]] [[package]]
name = "schemars" name = "schemars"
version = "0.8.13" version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "763f8cd0d4c71ed8389c90cb8100cba87e763bd01a8e614d4f0af97bcd50a161" checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c"
dependencies = [ dependencies = [
"bigdecimal",
"bytes", "bytes",
"chrono", "chrono",
"dyn-clone", "dyn-clone",
@ -1375,9 +1340,9 @@ dependencies = [
[[package]] [[package]]
name = "schemars_derive" name = "schemars_derive"
version = "0.8.13" version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1450,7 +1415,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -1466,9 +1431,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.105" version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
dependencies = [ dependencies = [
"indexmap 2.0.0", "indexmap 2.0.0",
"itoa", "itoa",
@ -1484,7 +1449,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -1496,7 +1461,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -1572,12 +1537,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "structmeta" name = "structmeta"
version = "0.2.0" version = "0.2.0"
@ -1587,7 +1546,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta-derive", "structmeta-derive",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -1598,7 +1557,29 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
]
[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 1.0.109",
] ]
[[package]] [[package]]
@ -1614,9 +1595,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.31" version = "2.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1655,7 +1636,7 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -1728,7 +1709,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -1822,7 +1803,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -1851,7 +1832,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
] ]
[[package]] [[package]]
@ -1891,15 +1872,15 @@ dependencies = [
"Inflector", "Inflector",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
"termcolor", "termcolor",
] ]
[[package]] [[package]]
name = "tungstenite" name = "tungstenite"
version = "0.20.0" version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649" checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"bytes", "bytes",
@ -1921,15 +1902,6 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unicase"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [
"version_check",
]
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.13" version = "0.3.13"
@ -1957,12 +1929,6 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.7.1" version = "0.7.1"
@ -1987,12 +1953,6 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.4.1" version = "1.4.1"
@ -2046,7 +2006,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2080,7 +2040,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.37",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2198,6 +2158,15 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "winreg" name = "winreg"
version = "0.50.0" version = "0.50.0"

View File

@ -71,7 +71,7 @@ pub async fn modify_ast_for_sketch(
// Let's get the path info. // Let's get the path info.
let resp = engine let resp = engine
.send_modeling_cmd_get_response( .send_modeling_cmd(
uuid::Uuid::new_v4(), uuid::Uuid::new_v4(),
SourceRange::default(), SourceRange::default(),
ModelingCmd::PathGetInfo { path_id: sketch_id }, ModelingCmd::PathGetInfo { path_id: sketch_id },
@ -88,47 +88,6 @@ pub async fn modify_ast_for_sketch(
})); }));
}; };
/* // Let's try to get the children of the sketch.
let resp = engine
.send_modeling_cmd_get_response(
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::EntityGetAllChildUuids { entity_id: sketch_id },
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::EntityGetAllChildUuids { data: children_info },
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {
message: format!("Get child info response was not as expected: {:?}", resp),
source_ranges: vec![SourceRange::default()],
}));
};
println!("children_info: {:#?}", children_info);
// Let's try to get the parent id.
let resp = engine
.send_modeling_cmd_get_response(
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::EntityGetParentId { entity_id: sketch_id },
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::EntityGetParentId { data: parent_info },
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {
message: format!("Get parent id response was not as expected: {:?}", resp),
source_ranges: vec![SourceRange::default()],
}));
};
println!("parent_info: {:#?}", parent_info);*/
// Now let's get the control points for all the segments. // Now let's get the control points for all the segments.
// TODO: We should probably await all these at once so we aren't going one by one. // TODO: We should probably await all these at once so we aren't going one by one.
// But I guess this is fine for now. // But I guess this is fine for now.
@ -136,7 +95,7 @@ pub async fn modify_ast_for_sketch(
let mut control_points = Vec::new(); let mut control_points = Vec::new();
for segment in &path_info.segments { for segment in &path_info.segments {
if let Some(command_id) = &segment.command_id { if let Some(command_id) = &segment.command_id {
let h = engine.send_modeling_cmd_get_response( let h = engine.send_modeling_cmd(
uuid::Uuid::new_v4(), uuid::Uuid::new_v4(),
SourceRange::default(), SourceRange::default(),
ModelingCmd::CurveGetControlPoints { curve_id: *command_id }, ModelingCmd::CurveGetControlPoints { curve_id: *command_id },
@ -207,7 +166,7 @@ pub async fn modify_ast_for_sketch(
let recasted = program.recast(&FormatOptions::default(), 0); let recasted = program.recast(&FormatOptions::default(), 0);
// Re-parse the ast so we get the correct source ranges. // Re-parse the ast so we get the correct source ranges.
let tokens = crate::tokeniser::lexer(&recasted); let tokens = crate::token::lexer(&recasted);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
*program = parser.ast()?; *program = parser.ast()?;

View File

@ -258,6 +258,23 @@ impl Program {
} }
} }
/// Replace a value with the new value, use the source range for matching the exact value.
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for item in &mut self.body {
match item {
BodyItem::ExpressionStatement(ref mut expression_statement) => expression_statement
.expression
.replace_value(source_range, new_value.clone()),
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
variable_declaration.replace_value(source_range, new_value.clone())
}
BodyItem::ReturnStatement(ref mut return_statement) => {
return_statement.argument.replace_value(source_range, new_value.clone())
}
}
}
}
/// Get the variable declaration with the given name. /// Get the variable declaration with the given name.
pub fn get_variable(&self, name: &str) -> Option<&VariableDeclarator> { pub fn get_variable(&self, name: &str) -> Option<&VariableDeclarator> {
for item in &self.body { for item in &self.body {
@ -393,6 +410,27 @@ impl Value {
} }
} }
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
if source_range == self.clone().into() {
*self = new_value;
return;
}
match self {
Value::BinaryExpression(ref mut bin_exp) => bin_exp.replace_value(source_range, new_value),
Value::ArrayExpression(ref mut array_exp) => array_exp.replace_value(source_range, new_value),
Value::ObjectExpression(ref mut obj_exp) => obj_exp.replace_value(source_range, new_value),
Value::MemberExpression(_) => {}
Value::Literal(_) => {}
Value::FunctionExpression(ref mut func_exp) => func_exp.replace_value(source_range, new_value),
Value::CallExpression(ref mut call_exp) => call_exp.replace_value(source_range, new_value),
Value::Identifier(_) => {}
Value::PipeExpression(ref mut pipe_exp) => pipe_exp.replace_value(source_range, new_value),
Value::UnaryExpression(ref mut unary_exp) => unary_exp.replace_value(source_range, new_value),
Value::PipeSubstitution(_) => {}
}
}
pub fn start(&self) -> usize { pub fn start(&self) -> usize {
match self { match self {
Value::Literal(literal) => literal.start(), Value::Literal(literal) => literal.start(),
@ -538,6 +576,23 @@ impl BinaryPart {
} }
} }
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
match self {
BinaryPart::Literal(_) => {}
BinaryPart::Identifier(_) => {}
BinaryPart::BinaryExpression(ref mut binary_expression) => {
binary_expression.replace_value(source_range, new_value)
}
BinaryPart::CallExpression(ref mut call_expression) => {
call_expression.replace_value(source_range, new_value)
}
BinaryPart::UnaryExpression(ref mut unary_expression) => {
unary_expression.replace_value(source_range, new_value)
}
BinaryPart::MemberExpression(_) => {}
}
}
fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String { fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
match &self { match &self {
BinaryPart::Literal(literal) => literal.recast(), BinaryPart::Literal(literal) => literal.recast(),
@ -571,11 +626,12 @@ impl BinaryPart {
} }
} }
pub fn get_result( #[async_recursion::async_recursion(?Send)]
pub async fn get_result(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo, pipe_info: &mut PipeInfo,
engine: &mut EngineConnection, engine: &EngineConnection,
) -> 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 gloablly 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.
@ -590,11 +646,13 @@ impl BinaryPart {
Ok(value.clone()) Ok(value.clone())
} }
BinaryPart::BinaryExpression(binary_expression) => { BinaryPart::BinaryExpression(binary_expression) => {
binary_expression.get_result(memory, &mut new_pipe_info, engine) binary_expression.get_result(memory, &mut new_pipe_info, engine).await
}
BinaryPart::CallExpression(call_expression) => {
call_expression.execute(memory, &mut new_pipe_info, engine).await
} }
BinaryPart::CallExpression(call_expression) => call_expression.execute(memory, &mut new_pipe_info, engine),
BinaryPart::UnaryExpression(unary_expression) => { BinaryPart::UnaryExpression(unary_expression) => {
unary_expression.get_result(memory, &mut new_pipe_info, engine) unary_expression.get_result(memory, &mut new_pipe_info, engine).await
} }
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(memory), BinaryPart::MemberExpression(member_expression) => member_expression.get_result(memory),
} }
@ -798,6 +856,12 @@ impl CallExpression {
}) })
} }
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for arg in &mut self.arguments {
arg.replace_value(source_range, new_value.clone());
}
}
fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String { fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
format!( format!(
"{}({})", "{}({})",
@ -810,11 +874,12 @@ impl CallExpression {
) )
} }
pub fn execute( #[async_recursion::async_recursion(?Send)]
pub async fn execute(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo, pipe_info: &mut PipeInfo,
engine: &mut EngineConnection, engine: &EngineConnection,
) -> Result<MemoryItem, KclError> { ) -> Result<MemoryItem, KclError> {
let fn_name = self.callee.name.clone(); let fn_name = self.callee.name.clone();
@ -828,7 +893,7 @@ impl CallExpression {
value.clone() value.clone()
} }
Value::BinaryExpression(binary_expression) => { Value::BinaryExpression(binary_expression) => {
binary_expression.get_result(memory, pipe_info, engine)? binary_expression.get_result(memory, pipe_info, engine).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 gloablly because if we did and this was called inside a pipe it would
@ -836,11 +901,15 @@ impl CallExpression {
// THIS IS IMPORTANT. // THIS IS IMPORTANT.
let mut new_pipe_info = pipe_info.clone(); let mut new_pipe_info = pipe_info.clone();
new_pipe_info.is_in_pipe = false; new_pipe_info.is_in_pipe = false;
call_expression.execute(memory, &mut new_pipe_info, engine)? call_expression.execute(memory, &mut new_pipe_info, engine).await?
} }
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, engine)?, Value::UnaryExpression(unary_expression) => {
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, engine)?, unary_expression.get_result(memory, pipe_info, engine).await?
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine)?, }
Value::ObjectExpression(object_expression) => {
object_expression.execute(memory, pipe_info, engine).await?
}
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
Value::PipeExpression(pipe_expression) => { Value::PipeExpression(pipe_expression) => {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: format!("PipeExpression not implemented here: {:?}", pipe_expression), message: format!("PipeExpression not implemented here: {:?}", pipe_expression),
@ -871,26 +940,23 @@ impl CallExpression {
match &self.function { match &self.function {
Function::StdLib { func } => { Function::StdLib { func } => {
/* let source_range: SourceRange = self.into();
println!(
"Calling stdlib function: {}, source_range: {:?}, args: {:?}",
fn_name, source_range, fn_args
);*/
// Attempt to call the function. // Attempt to call the function.
let mut args = crate::std::Args::new(fn_args, self.into(), engine); let args = crate::std::Args::new(fn_args, self.into(), engine.clone());
let result = func.std_lib_fn()(&mut args)?; let result = func.std_lib_fn()(args).await?;
if pipe_info.is_in_pipe { if pipe_info.is_in_pipe {
pipe_info.index += 1; pipe_info.index += 1;
pipe_info.previous_results.push(result); pipe_info.previous_results.push(result);
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), engine) execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), engine).await
} else { } else {
Ok(result) Ok(result)
} }
} }
Function::InMemory => { Function::InMemory => {
let mem = memory.clone(); let func = memory.get(&fn_name, self.into())?;
let func = mem.get(&fn_name, self.into())?; let result = func
let result = func.call_fn(&fn_args, &mem, engine)?.ok_or_else(|| { .call_fn(fn_args, memory.clone(), engine.clone())
.await?
.ok_or_else(|| {
KclError::UndefinedValue(KclErrorDetails { KclError::UndefinedValue(KclErrorDetails {
message: format!("Result of function {} is undefined", fn_name), message: format!("Result of function {} is undefined", fn_name),
source_ranges: vec![self.into()], source_ranges: vec![self.into()],
@ -903,7 +969,7 @@ impl CallExpression {
pipe_info.index += 1; pipe_info.index += 1;
pipe_info.previous_results.push(result); pipe_info.previous_results.push(result);
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), engine) execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), engine).await
} else { } else {
Ok(result) Ok(result)
} }
@ -1009,6 +1075,12 @@ impl VariableDeclaration {
} }
} }
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for declaration in &mut self.declarations {
declaration.init.replace_value(source_range, new_value.clone());
}
}
/// Returns a value that includes the given character position. /// Returns a value that includes the given character position.
pub fn get_value_for_position(&self, pos: usize) -> Option<&Value> { pub fn get_value_for_position(&self, pos: usize) -> Option<&Value> {
for declaration in &self.declarations { for declaration in &self.declarations {
@ -1362,6 +1434,12 @@ impl ArrayExpression {
} }
} }
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for element in &mut self.elements {
element.replace_value(source_range, new_value.clone());
}
}
pub fn get_constraint_level(&self) -> ConstraintLevel { pub fn get_constraint_level(&self) -> ConstraintLevel {
if self.elements.is_empty() { if self.elements.is_empty() {
return ConstraintLevel::Ignore { return ConstraintLevel::Ignore {
@ -1424,11 +1502,12 @@ impl ArrayExpression {
None None
} }
pub fn execute( #[async_recursion::async_recursion(?Send)]
pub async fn execute(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo, pipe_info: &mut PipeInfo,
engine: &mut EngineConnection, engine: &EngineConnection,
) -> Result<MemoryItem, KclError> { ) -> Result<MemoryItem, KclError> {
let mut results = Vec::with_capacity(self.elements.len()); let mut results = Vec::with_capacity(self.elements.len());
@ -1440,7 +1519,7 @@ impl ArrayExpression {
value.clone() value.clone()
} }
Value::BinaryExpression(binary_expression) => { Value::BinaryExpression(binary_expression) => {
binary_expression.get_result(memory, pipe_info, engine)? binary_expression.get_result(memory, pipe_info, engine).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 gloablly because if we did and this was called inside a pipe it would
@ -1448,12 +1527,16 @@ impl ArrayExpression {
// THIS IS IMPORTANT. // THIS IS IMPORTANT.
let mut new_pipe_info = pipe_info.clone(); let mut new_pipe_info = pipe_info.clone();
new_pipe_info.is_in_pipe = false; new_pipe_info.is_in_pipe = false;
call_expression.execute(memory, &mut new_pipe_info, engine)? call_expression.execute(memory, &mut new_pipe_info, engine).await?
} }
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, engine)?, Value::UnaryExpression(unary_expression) => {
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, engine)?, unary_expression.get_result(memory, pipe_info, engine).await?
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine)?, }
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, engine)?, Value::ObjectExpression(object_expression) => {
object_expression.execute(memory, pipe_info, engine).await?
}
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, engine).await?,
Value::PipeSubstitution(pipe_substitution) => { Value::PipeSubstitution(pipe_substitution) => {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution), message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
@ -1507,6 +1590,12 @@ impl ObjectExpression {
} }
} }
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for property in &mut self.properties {
property.value.replace_value(source_range, new_value.clone());
}
}
pub fn get_constraint_level(&self) -> ConstraintLevel { pub fn get_constraint_level(&self) -> ConstraintLevel {
if self.properties.is_empty() { if self.properties.is_empty() {
return ConstraintLevel::Ignore { return ConstraintLevel::Ignore {
@ -1569,11 +1658,12 @@ impl ObjectExpression {
None None
} }
pub fn execute( #[async_recursion::async_recursion(?Send)]
pub async fn execute(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo, pipe_info: &mut PipeInfo,
engine: &mut EngineConnection, engine: &EngineConnection,
) -> Result<MemoryItem, KclError> { ) -> Result<MemoryItem, KclError> {
let mut object = Map::new(); let mut object = Map::new();
for property in &self.properties { for property in &self.properties {
@ -1584,7 +1674,7 @@ impl ObjectExpression {
value.clone() value.clone()
} }
Value::BinaryExpression(binary_expression) => { Value::BinaryExpression(binary_expression) => {
binary_expression.get_result(memory, pipe_info, engine)? binary_expression.get_result(memory, pipe_info, engine).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 gloablly because if we did and this was called inside a pipe it would
@ -1592,12 +1682,16 @@ impl ObjectExpression {
// THIS IS IMPORTANT. // THIS IS IMPORTANT.
let mut new_pipe_info = pipe_info.clone(); let mut new_pipe_info = pipe_info.clone();
new_pipe_info.is_in_pipe = false; new_pipe_info.is_in_pipe = false;
call_expression.execute(memory, &mut new_pipe_info, engine)? call_expression.execute(memory, &mut new_pipe_info, engine).await?
} }
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, engine)?, Value::UnaryExpression(unary_expression) => {
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, engine)?, unary_expression.get_result(memory, pipe_info, engine).await?
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine)?, }
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, engine)?, Value::ObjectExpression(object_expression) => {
object_expression.execute(memory, pipe_info, engine).await?
}
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, engine).await?,
Value::PipeSubstitution(pipe_substitution) => { Value::PipeSubstitution(pipe_substitution) => {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution), message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
@ -1946,6 +2040,11 @@ impl BinaryExpression {
} }
} }
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
self.left.replace_value(source_range, new_value.clone());
self.right.replace_value(source_range, new_value);
}
pub fn get_constraint_level(&self) -> ConstraintLevel { pub fn get_constraint_level(&self) -> ConstraintLevel {
let left_constraint_level = self.left.get_constraint_level(); let left_constraint_level = self.left.get_constraint_level();
let right_constraint_level = self.right.get_constraint_level(); let right_constraint_level = self.right.get_constraint_level();
@ -2005,11 +2104,12 @@ impl BinaryExpression {
None None
} }
pub fn get_result( #[async_recursion::async_recursion(?Send)]
pub async fn get_result(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo, pipe_info: &mut PipeInfo,
engine: &mut EngineConnection, engine: &EngineConnection,
) -> 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 gloablly 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.
@ -2019,11 +2119,13 @@ impl BinaryExpression {
let left_json_value = self let left_json_value = self
.left .left
.get_result(memory, &mut new_pipe_info, engine)? .get_result(memory, &mut new_pipe_info, engine)
.await?
.get_json_value()?; .get_json_value()?;
let right_json_value = self let right_json_value = self
.right .right
.get_result(memory, &mut new_pipe_info, engine)? .get_result(memory, &mut new_pipe_info, engine)
.await?
.get_json_value()?; .get_json_value()?;
// First check if we are doing string concatenation. // First check if we are doing string concatenation.
@ -2165,6 +2267,10 @@ impl UnaryExpression {
} }
} }
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
self.argument.replace_value(source_range, new_value);
}
pub fn get_constraint_level(&self) -> ConstraintLevel { pub fn get_constraint_level(&self) -> ConstraintLevel {
self.argument.get_constraint_level() self.argument.get_constraint_level()
} }
@ -2173,11 +2279,11 @@ impl UnaryExpression {
format!("{}{}", &self.operator, self.argument.recast(options, 0)) format!("{}{}", &self.operator, self.argument.recast(options, 0))
} }
pub fn get_result( pub async fn get_result(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo, pipe_info: &mut PipeInfo,
engine: &mut EngineConnection, engine: &EngineConnection,
) -> 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 gloablly 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.
@ -2188,7 +2294,8 @@ impl UnaryExpression {
let num = parse_json_number_as_f64( let num = parse_json_number_as_f64(
&self &self
.argument .argument
.get_result(memory, &mut new_pipe_info, engine)? .get_result(memory, &mut new_pipe_info, engine)
.await?
.get_json_value()?, .get_json_value()?,
self.into(), self.into(),
)?; )?;
@ -2259,6 +2366,12 @@ impl PipeExpression {
} }
} }
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for value in &mut self.body {
value.replace_value(source_range, new_value.clone());
}
}
pub fn get_constraint_level(&self) -> ConstraintLevel { pub fn get_constraint_level(&self) -> ConstraintLevel {
if self.body.is_empty() { if self.body.is_empty() {
return ConstraintLevel::Ignore { return ConstraintLevel::Ignore {
@ -2310,16 +2423,16 @@ impl PipeExpression {
None None
} }
pub fn get_result( pub async fn get_result(
&self, &self,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo, pipe_info: &mut PipeInfo,
engine: &mut EngineConnection, engine: &EngineConnection,
) -> Result<MemoryItem, KclError> { ) -> Result<MemoryItem, KclError> {
// Reset the previous results. // Reset the previous results.
pipe_info.previous_results = vec![]; pipe_info.previous_results = vec![];
pipe_info.index = 0; pipe_info.index = 0;
execute_pipe_body(memory, &self.body, pipe_info, self.into(), engine) execute_pipe_body(memory, &self.body, pipe_info, self.into(), engine).await
} }
/// Rename all identifiers that have the old name to the new given name. /// Rename all identifiers that have the old name to the new given name.
@ -2330,12 +2443,13 @@ impl PipeExpression {
} }
} }
fn execute_pipe_body( #[async_recursion::async_recursion(?Send)]
async fn execute_pipe_body(
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
body: &[Value], body: &[Value],
pipe_info: &mut PipeInfo, pipe_info: &mut PipeInfo,
source_range: SourceRange, source_range: SourceRange,
engine: &mut EngineConnection, engine: &EngineConnection,
) -> Result<MemoryItem, KclError> { ) -> Result<MemoryItem, KclError> {
if pipe_info.index == body.len() { if pipe_info.index == body.len() {
pipe_info.is_in_pipe = false; pipe_info.is_in_pipe = false;
@ -2360,15 +2474,15 @@ fn execute_pipe_body(
match expression { match expression {
Value::BinaryExpression(binary_expression) => { Value::BinaryExpression(binary_expression) => {
let result = binary_expression.get_result(memory, pipe_info, engine)?; let result = binary_expression.get_result(memory, pipe_info, engine).await?;
pipe_info.previous_results.push(result); pipe_info.previous_results.push(result);
pipe_info.index += 1; pipe_info.index += 1;
execute_pipe_body(memory, body, pipe_info, source_range, engine) execute_pipe_body(memory, body, pipe_info, source_range, engine).await
} }
Value::CallExpression(call_expression) => { Value::CallExpression(call_expression) => {
pipe_info.is_in_pipe = true; pipe_info.is_in_pipe = true;
pipe_info.body = body.to_vec(); pipe_info.body = body.to_vec();
call_expression.execute(memory, pipe_info, engine) call_expression.execute(memory, pipe_info, engine).await
} }
_ => { _ => {
// Return an error this should not happen. // Return an error this should not happen.
@ -2400,6 +2514,10 @@ impl FunctionExpression {
} }
} }
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
self.body.replace_value(source_range, new_value);
}
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String { pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
// We don't want to end with a new line inside nested functions. // We don't want to end with a new line inside nested functions.
let mut new_options = options.clone(); let mut new_options = options.clone();
@ -2671,7 +2789,7 @@ fn ghi = (x) => {
} }
show(part001)"#; show(part001)"#;
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
let symbols = program.get_lsp_symbols(code); let symbols = program.get_lsp_symbols(code);
@ -2699,7 +2817,7 @@ show(part001)
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0]) let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
|> line([0.4900857016, -0.0240763666], %) |> line([0.4900857016, -0.0240763666], %)
|> line([0.6804562304, 0.9087880491], %)"#; |> line([0.6804562304, 0.9087880491], %)"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2718,7 +2836,7 @@ show(part001)
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0]) let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
|> line([0.4900857016, -0.0240763666], %) // hello world |> line([0.4900857016, -0.0240763666], %) // hello world
|> line([0.6804562304, 0.9087880491], %)"#; |> line([0.6804562304, 0.9087880491], %)"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2737,7 +2855,7 @@ show(part001)
|> line([0.4900857016, -0.0240763666], %) |> line([0.4900857016, -0.0240763666], %)
// hello world // hello world
|> line([0.6804562304, 0.9087880491], %)"#; |> line([0.6804562304, 0.9087880491], %)"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2763,7 +2881,7 @@ show(part001)
// this is also a comment // this is also a comment
return things return things
}"#; }"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2800,7 +2918,7 @@ const mySk1 = startSketchAt([0, 0])
|> ry(45, %) |> ry(45, %)
|> rx(45, %) |> rx(45, %)
// one more for good measure"#; // one more for good measure"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2839,7 +2957,7 @@ a comment between pipe expression statements */
|> line([-0.42, -1.72], %) |> line([-0.42, -1.72], %)
show(part001)"#; show(part001)"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2865,7 +2983,7 @@ const yo = [
" hey oooooo really long long long" " hey oooooo really long long long"
] ]
"#; "#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2883,7 +3001,7 @@ const key = 'c'
const things = "things" const things = "things"
// this is also a comment"#; // this is also a comment"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2901,7 +3019,7 @@ const things = "things"
// a comment // a comment
" "
}"#; }"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2926,7 +3044,7 @@ const part001 = startSketchAt([0, 0])
-angleToMatchLengthY('seg01', myVar, %), -angleToMatchLengthY('seg01', myVar, %),
myVar myVar
], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper"#; ], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2952,7 +3070,7 @@ const part001 = startSketchAt([0, 0])
myVar myVar
], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper ], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper
"#; "#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -2983,7 +3101,7 @@ fn ghi = (part001) => {
} }
show(part001)"#; show(part001)"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let mut program = parser.ast().unwrap(); let mut program = parser.ast().unwrap();
program.rename_symbol("mySuperCoolPart", 6); program.rename_symbol("mySuperCoolPart", 6);
@ -3014,7 +3132,7 @@ show(mySuperCoolPart)
let some_program_string = r#"fn ghi = (x, y, z) => { let some_program_string = r#"fn ghi = (x, y, z) => {
return x return x
}"#; }"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let mut program = parser.ast().unwrap(); let mut program = parser.ast().unwrap();
program.rename_symbol("newName", 10); program.rename_symbol("newName", 10);
@ -3043,7 +3161,7 @@ const firstExtrude = startSketchAt([0,0])
|> extrude(h, %) |> extrude(h, %)
show(firstExtrude)"#; show(firstExtrude)"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -3069,7 +3187,7 @@ show(firstExtrude)
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_recast_math_start_negative() { async fn test_recast_math_start_negative() {
let some_program_string = r#"const myVar = -5 + 6"#; let some_program_string = r#"const myVar = -5 + 6"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();
@ -3085,7 +3203,7 @@ const FOS = 2
const sigmaAllow = 8 const sigmaAllow = 8
const width = 20 const width = 20
const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#; const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap(); let program = parser.ast().unwrap();

View File

@ -3,10 +3,11 @@
use std::sync::Arc; use std::sync::Arc;
use anyhow::Result; use anyhow::{anyhow, Result};
use dashmap::DashMap; use dashmap::DashMap;
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use kittycad::types::{OkWebSocketResponseData, WebSocketRequest, WebSocketResponse}; use kittycad::types::{OkWebSocketResponseData, WebSocketRequest, WebSocketResponse};
use tokio::sync::{mpsc, oneshot};
use tokio_tungstenite::tungstenite::Message as WsMsg; use tokio_tungstenite::tungstenite::Message as WsMsg;
use crate::{ use crate::{
@ -14,18 +15,13 @@ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
}; };
#[derive(Debug)] type WebSocketTcpWrite = futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<reqwest::Upgraded>, WsMsg>;
#[derive(Debug, Clone)]
#[allow(dead_code)] // for the TcpReadHandle
pub struct EngineConnection { pub struct EngineConnection {
tcp_write: futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<reqwest::Upgraded>, WsMsg>, engine_req_tx: mpsc::Sender<ToEngineReq>,
tcp_read_handle: tokio::task::JoinHandle<Result<()>>,
responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>, responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>,
} tcp_read_handle: Arc<TcpReadHandle>,
impl Drop for EngineConnection {
fn drop(&mut self) {
// Drop the read handle.
self.tcp_read_handle.abort();
}
} }
pub struct TcpRead { pub struct TcpRead {
@ -46,16 +42,63 @@ impl TcpRead {
} }
} }
#[derive(Debug)]
pub struct TcpReadHandle {
handle: Arc<tokio::task::JoinHandle<Result<()>>>,
}
impl Drop for TcpReadHandle {
fn drop(&mut self) {
// Drop the read handle.
self.handle.abort();
}
}
/// Requests to send to the engine, and a way to await a response.
struct ToEngineReq {
/// The request to send
req: WebSocketRequest,
/// If this resolves to Ok, the request was sent.
/// If this resolves to Err, the request could not be sent.
/// If this has not yet resolved, the request has not been sent yet.
request_sent: oneshot::Sender<Result<()>>,
}
impl EngineConnection { impl EngineConnection {
/// Start waiting for incoming engine requests, and send each one over the WebSocket to the engine.
async fn start_write_actor(mut tcp_write: WebSocketTcpWrite, mut engine_req_rx: mpsc::Receiver<ToEngineReq>) {
while let Some(req) = engine_req_rx.recv().await {
let ToEngineReq { req, request_sent } = req;
let res = Self::inner_send_to_engine(req, &mut tcp_write).await;
let _ = request_sent.send(res);
}
}
/// Send the given `request` to the engine via the WebSocket connection `tcp_write`.
async fn inner_send_to_engine(request: WebSocketRequest, tcp_write: &mut WebSocketTcpWrite) -> Result<()> {
let msg = serde_json::to_string(&request).map_err(|e| anyhow!("could not serialize json: {e}"))?;
tcp_write
.send(WsMsg::Text(msg))
.await
.map_err(|e| anyhow!("could not send json over websocket: {e}"))?;
Ok(())
}
pub async fn new(ws: reqwest::Upgraded) -> Result<EngineConnection> { pub async fn new(ws: reqwest::Upgraded) -> Result<EngineConnection> {
let ws_stream = tokio_tungstenite::WebSocketStream::from_raw_socket( let ws_stream = tokio_tungstenite::WebSocketStream::from_raw_socket(
ws, ws,
tokio_tungstenite::tungstenite::protocol::Role::Client, tokio_tungstenite::tungstenite::protocol::Role::Client,
None, Some(tokio_tungstenite::tungstenite::protocol::WebSocketConfig {
write_buffer_size: 1024 * 128,
max_write_buffer_size: 1024 * 256,
..Default::default()
}),
) )
.await; .await;
let (tcp_write, tcp_read) = ws_stream.split(); let (tcp_write, tcp_read) = ws_stream.split();
let (engine_req_tx, engine_req_rx) = mpsc::channel(10);
tokio::task::spawn(Self::start_write_actor(tcp_write, engine_req_rx));
let mut tcp_read = TcpRead { stream: tcp_read }; let mut tcp_read = TcpRead { stream: tcp_read };
@ -80,42 +123,34 @@ impl EngineConnection {
}); });
Ok(EngineConnection { Ok(EngineConnection {
tcp_write, engine_req_tx,
tcp_read_handle, tcp_read_handle: Arc::new(TcpReadHandle {
handle: Arc::new(tcp_read_handle),
}),
responses, responses,
}) })
} }
pub async fn tcp_send(&mut self, msg: WebSocketRequest) -> Result<()> {
let msg = serde_json::to_string(&msg)?;
self.tcp_write.send(WsMsg::Text(msg)).await?;
Ok(())
}
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl EngineManager for EngineConnection { impl EngineManager for EngineConnection {
/// Send a modeling command. async fn send_modeling_cmd(
/// Do not wait for the response message. &self,
fn send_modeling_cmd(
&mut self,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd,
) -> Result<(), KclError> {
futures::executor::block_on(self.send_modeling_cmd_get_response(id, source_range, cmd))?;
Ok(())
}
/// Send a modeling command and wait for the response message.
async fn send_modeling_cmd_get_response(
&mut self,
id: uuid::Uuid, id: uuid::Uuid,
source_range: crate::executor::SourceRange, source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd, cmd: kittycad::types::ModelingCmd,
) -> Result<OkWebSocketResponseData, KclError> { ) -> Result<OkWebSocketResponseData, KclError> {
self.tcp_send(WebSocketRequest::ModelingCmdReq { cmd, cmd_id: id }) let (tx, rx) = oneshot::channel();
// Send the request to the engine, via the actor.
self.engine_req_tx
.send(ToEngineReq {
req: WebSocketRequest::ModelingCmdReq {
cmd: cmd.clone(),
cmd_id: id,
},
request_sent: tx,
})
.await .await
.map_err(|e| { .map_err(|e| {
KclError::Engine(KclErrorDetails { KclError::Engine(KclErrorDetails {
@ -124,18 +159,40 @@ impl EngineManager for EngineConnection {
}) })
})?; })?;
// Wait for the request to be sent.
rx.await
.map_err(|e| {
KclError::Engine(KclErrorDetails {
message: format!("could not send request to the engine actor: {e}"),
source_ranges: vec![source_range],
})
})?
.map_err(|e| {
KclError::Engine(KclErrorDetails {
message: format!("could not send request to the engine: {e}"),
source_ranges: vec![source_range],
})
})?;
// Wait for the response. // Wait for the response.
loop { let current_time = std::time::Instant::now();
if let Some(resp) = self.responses.get(&id) { while current_time.elapsed().as_secs() < 60 {
if let Some(data) = &resp.resp { // We pop off the responses to cleanup our mappings.
return Ok(data.clone()); if let Some((_, resp)) = self.responses.remove(&id) {
return if let Some(data) = &resp.resp {
Ok(data.clone())
} else { } else {
return Err(KclError::Engine(KclErrorDetails { Err(KclError::Engine(KclErrorDetails {
message: format!("Modeling command failed: {:?}", resp.errors), message: format!("Modeling command failed: {:?}", resp.errors),
source_ranges: vec![source_range], source_ranges: vec![source_range],
})); }))
} };
} }
} }
Err(KclError::Engine(KclErrorDetails {
message: format!("Modeling command timed out `{}`: {:?}", id, cmd),
source_ranges: vec![source_range],
}))
} }
} }

View File

@ -6,7 +6,7 @@ use kittycad::types::OkWebSocketResponseData;
use crate::errors::KclError; use crate::errors::KclError;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct EngineConnection {} pub struct EngineConnection {}
impl EngineConnection { impl EngineConnection {
@ -17,21 +17,14 @@ impl EngineConnection {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl crate::engine::EngineManager for EngineConnection { impl crate::engine::EngineManager for EngineConnection {
fn send_modeling_cmd( async fn send_modeling_cmd(
&mut self, &self,
_id: uuid::Uuid,
_source_range: crate::executor::SourceRange,
_cmd: kittycad::types::ModelingCmd,
) -> Result<(), KclError> {
Ok(())
}
async fn send_modeling_cmd_get_response(
&mut self,
_id: uuid::Uuid, _id: uuid::Uuid,
_source_range: crate::executor::SourceRange, _source_range: crate::executor::SourceRange,
_cmd: kittycad::types::ModelingCmd, _cmd: kittycad::types::ModelingCmd,
) -> Result<OkWebSocketResponseData, KclError> { ) -> Result<OkWebSocketResponseData, KclError> {
todo!() Ok(OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::Empty {},
})
} }
} }

View File

@ -1,5 +1,6 @@
//! Functions for setting up our WebSocket and WebRTC connections for communications with the //! Functions for setting up our WebSocket and WebRTC connections for communications with the
//! engine. //! engine.
use std::sync::Arc;
use anyhow::Result; use anyhow::Result;
use kittycad::types::WebSocketRequest; use kittycad::types::WebSocketRequest;
@ -23,44 +24,21 @@ extern "C" {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EngineConnection { pub struct EngineConnection {
manager: EngineCommandManager, manager: Arc<EngineCommandManager>,
} }
impl EngineConnection { impl EngineConnection {
pub async fn new(manager: EngineCommandManager) -> Result<EngineConnection, JsValue> { pub async fn new(manager: EngineCommandManager) -> Result<EngineConnection, JsValue> {
Ok(EngineConnection { manager }) Ok(EngineConnection {
manager: Arc::new(manager),
})
} }
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl crate::engine::EngineManager for EngineConnection { impl crate::engine::EngineManager for EngineConnection {
fn send_modeling_cmd( async fn send_modeling_cmd(
&mut self, &self,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd,
) -> Result<(), KclError> {
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
KclError::Engine(KclErrorDetails {
message: format!("Failed to serialize source range: {:?}", e),
source_ranges: vec![source_range],
})
})?;
let ws_msg = WebSocketRequest::ModelingCmdReq { cmd, cmd_id: id };
let cmd_str = serde_json::to_string(&ws_msg).map_err(|e| {
KclError::Engine(KclErrorDetails {
message: format!("Failed to serialize modeling command: {:?}", e),
source_ranges: vec![source_range],
})
})?;
let _ = self
.manager
.sendModelingCommandFromWasm(id.to_string(), source_range_str, cmd_str);
Ok(())
}
async fn send_modeling_cmd_get_response(
&mut self,
id: uuid::Uuid, id: uuid::Uuid,
source_range: crate::executor::SourceRange, source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd, cmd: kittycad::types::ModelingCmd,

View File

@ -32,19 +32,10 @@ use anyhow::Result;
pub use conn_mock::EngineConnection; pub use conn_mock::EngineConnection;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
pub trait EngineManager { pub trait EngineManager: Clone {
/// Send a modeling command.
/// Do not wait for the response message.
fn send_modeling_cmd(
&mut self,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd,
) -> Result<(), crate::errors::KclError>;
/// Send a modeling command and wait for the response message. /// Send a modeling command and wait for the response message.
async fn send_modeling_cmd_get_response( async fn send_modeling_cmd(
&mut self, &self,
id: uuid::Uuid, id: uuid::Uuid,
source_range: crate::executor::SourceRange, source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd, cmd: kittycad::types::ModelingCmd,

View File

@ -104,7 +104,7 @@ pub enum MemoryItem {
SketchGroup(Box<SketchGroup>), SketchGroup(Box<SketchGroup>),
ExtrudeGroup(Box<ExtrudeGroup>), ExtrudeGroup(Box<ExtrudeGroup>),
#[ts(skip)] #[ts(skip)]
ExtrudeTransform(ExtrudeTransform), ExtrudeTransform(Box<ExtrudeTransform>),
#[ts(skip)] #[ts(skip)]
Function { Function {
#[serde(skip)] #[serde(skip)]
@ -134,13 +134,28 @@ pub struct ExtrudeTransform {
pub meta: Vec<Metadata>, pub meta: Vec<Metadata>,
} }
pub type MemoryFunction = fn( pub type MemoryFunction =
s: &[MemoryItem], fn(
memory: &ProgramMemory, s: Vec<MemoryItem>,
expression: &FunctionExpression, memory: ProgramMemory,
metadata: &[Metadata], expression: Box<FunctionExpression>,
engine: &mut EngineConnection, metadata: Vec<Metadata>,
) -> Result<Option<ProgramReturn>, KclError>; engine: EngineConnection,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>>>>;
fn force_memory_function<
F: Fn(
Vec<MemoryItem>,
ProgramMemory,
Box<FunctionExpression>,
Vec<Metadata>,
EngineConnection,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>>>>,
>(
f: F,
) -> F {
f
}
impl From<MemoryItem> for Vec<SourceRange> { impl From<MemoryItem> for Vec<SourceRange> {
fn from(item: MemoryItem) -> Self { fn from(item: MemoryItem) -> Self {
@ -168,24 +183,24 @@ impl MemoryItem {
} }
} }
pub fn call_fn( pub async fn call_fn(
&self, &self,
args: &[MemoryItem], args: Vec<MemoryItem>,
memory: &ProgramMemory, memory: ProgramMemory,
engine: &mut EngineConnection, engine: EngineConnection,
) -> Result<Option<ProgramReturn>, KclError> { ) -> Result<Option<ProgramReturn>, KclError> {
if let MemoryItem::Function { func, expression, meta } = self { if let MemoryItem::Function { func, expression, meta } = &self {
if let Some(func) = func { if let Some(func) = func {
func(args, memory, expression, meta, engine) func(args, memory, expression.clone(), meta.clone(), engine).await
} else { } else {
Err(KclError::Semantic(KclErrorDetails { Err(KclError::Semantic(KclErrorDetails {
message: format!("Not a function: {:?}", self), message: format!("Not a function: {:?}", expression),
source_ranges: vec![], source_ranges: vec![],
})) }))
} }
} else { } else {
Err(KclError::Semantic(KclErrorDetails { Err(KclError::Semantic(KclErrorDetails {
message: format!("not a function: {:?}", self), message: "not a in memory function".to_string(),
source_ranges: vec![], source_ranges: vec![],
})) }))
} }
@ -579,11 +594,11 @@ impl Default for PipeInfo {
} }
/// Execute a AST's program. /// Execute a AST's program.
pub fn execute( pub async fn execute(
program: crate::ast::types::Program, program: crate::ast::types::Program,
memory: &mut ProgramMemory, memory: &mut ProgramMemory,
options: BodyType, options: BodyType,
engine: &mut EngineConnection, engine: &EngineConnection,
) -> Result<ProgramMemory, KclError> { ) -> Result<ProgramMemory, KclError> {
let mut pipe_info = PipeInfo::default(); let mut pipe_info = PipeInfo::default();
@ -602,7 +617,23 @@ pub fn execute(
args.push(memory_item.clone()); args.push(memory_item.clone());
} }
Value::CallExpression(call_expr) => { Value::CallExpression(call_expr) => {
let result = call_expr.execute(memory, &mut pipe_info, engine)?; let result = call_expr.execute(memory, &mut pipe_info, engine).await?;
args.push(result);
}
Value::BinaryExpression(binary_expression) => {
let result = binary_expression.get_result(memory, &mut pipe_info, engine).await?;
args.push(result);
}
Value::UnaryExpression(unary_expression) => {
let result = unary_expression.get_result(memory, &mut pipe_info, engine).await?;
args.push(result);
}
Value::ObjectExpression(object_expression) => {
let result = object_expression.execute(memory, &mut pipe_info, engine).await?;
args.push(result);
}
Value::ArrayExpression(array_expression) => {
let result = array_expression.execute(memory, &mut pipe_info, engine).await?;
args.push(result); args.push(result);
} }
// We do nothing for the rest. // We do nothing for the rest.
@ -620,7 +651,7 @@ pub fn execute(
memory.return_ = Some(ProgramReturn::Arguments(call_expr.arguments.clone())); memory.return_ = Some(ProgramReturn::Arguments(call_expr.arguments.clone()));
} else if let Some(func) = memory.clone().root.get(&fn_name) { } else if let Some(func) = memory.clone().root.get(&fn_name) {
let result = func.call_fn(&args, memory, engine)?; let result = func.call_fn(args.clone(), memory.clone(), engine.clone()).await?;
memory.return_ = result; memory.return_ = result;
} else { } else {
@ -646,22 +677,27 @@ pub fn execute(
memory.add(&var_name, value.clone(), source_range)?; memory.add(&var_name, value.clone(), source_range)?;
} }
Value::BinaryExpression(binary_expression) => { Value::BinaryExpression(binary_expression) => {
let result = binary_expression.get_result(memory, &mut pipe_info, engine)?; let result = binary_expression.get_result(memory, &mut pipe_info, engine).await?;
memory.add(&var_name, result, source_range)?; memory.add(&var_name, result, source_range)?;
} }
Value::FunctionExpression(function_expression) => { Value::FunctionExpression(function_expression) => {
memory.add( let mem_func = force_memory_function(
&var_name, |args: Vec<MemoryItem>,
MemoryItem::Function{ memory: ProgramMemory,
expression: function_expression.clone(), function_expression: Box<FunctionExpression>,
meta: vec![metadata], _metadata: Vec<Metadata>,
func: Some(|args: &[MemoryItem], memory: &ProgramMemory, function_expression: &FunctionExpression, _metadata: &[Metadata], engine: &mut EngineConnection| -> Result<Option<ProgramReturn>, KclError> { engine: EngineConnection| {
Box::pin(async move {
let mut fn_memory = memory.clone(); let mut fn_memory = memory.clone();
if args.len() != function_expression.params.len() { if args.len() != function_expression.params.len() {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: format!("Expected {} arguments, got {}", function_expression.params.len(), args.len()), message: format!(
source_ranges: vec![function_expression.into()], "Expected {} arguments, got {}",
function_expression.params.len(),
args.len(),
),
source_ranges: vec![(&function_expression).into()],
})); }));
} }
@ -674,20 +710,34 @@ pub fn execute(
)?; )?;
} }
let result = execute(function_expression.body.clone(), &mut fn_memory, BodyType::Block, engine)?; let result = execute(
function_expression.body.clone(),
&mut fn_memory,
BodyType::Block,
&engine,
)
.await?;
Ok(result.return_) Ok(result.return_)
}) })
}, },
);
memory.add(
&var_name,
MemoryItem::Function {
expression: function_expression.clone(),
meta: vec![metadata],
func: Some(mem_func),
},
source_range, source_range,
)?; )?;
} }
Value::CallExpression(call_expression) => { Value::CallExpression(call_expression) => {
let result = call_expression.execute(memory, &mut pipe_info, engine)?; let result = call_expression.execute(memory, &mut pipe_info, engine).await?;
memory.add(&var_name, result, source_range)?; memory.add(&var_name, result, source_range)?;
} }
Value::PipeExpression(pipe_expression) => { Value::PipeExpression(pipe_expression) => {
let result = pipe_expression.get_result(memory, &mut pipe_info, engine)?; let result = pipe_expression.get_result(memory, &mut pipe_info, engine).await?;
memory.add(&var_name, result, source_range)?; memory.add(&var_name, result, source_range)?;
} }
Value::PipeSubstitution(pipe_substitution) => { Value::PipeSubstitution(pipe_substitution) => {
@ -700,11 +750,11 @@ pub fn execute(
})); }));
} }
Value::ArrayExpression(array_expression) => { Value::ArrayExpression(array_expression) => {
let result = array_expression.execute(memory, &mut pipe_info, engine)?; let result = array_expression.execute(memory, &mut pipe_info, engine).await?;
memory.add(&var_name, result, source_range)?; memory.add(&var_name, result, source_range)?;
} }
Value::ObjectExpression(object_expression) => { Value::ObjectExpression(object_expression) => {
let result = object_expression.execute(memory, &mut pipe_info, engine)?; let result = object_expression.execute(memory, &mut pipe_info, engine).await?;
memory.add(&var_name, result, source_range)?; memory.add(&var_name, result, source_range)?;
} }
Value::MemberExpression(member_expression) => { Value::MemberExpression(member_expression) => {
@ -712,7 +762,7 @@ pub fn execute(
memory.add(&var_name, result, source_range)?; memory.add(&var_name, result, source_range)?;
} }
Value::UnaryExpression(unary_expression) => { Value::UnaryExpression(unary_expression) => {
let result = unary_expression.get_result(memory, &mut pipe_info, engine)?; let result = unary_expression.get_result(memory, &mut pipe_info, engine).await?;
memory.add(&var_name, result, source_range)?; memory.add(&var_name, result, source_range)?;
} }
} }
@ -720,11 +770,11 @@ pub fn execute(
} }
BodyItem::ReturnStatement(return_statement) => match &return_statement.argument { BodyItem::ReturnStatement(return_statement) => match &return_statement.argument {
Value::BinaryExpression(bin_expr) => { Value::BinaryExpression(bin_expr) => {
let result = bin_expr.get_result(memory, &mut pipe_info, engine)?; let result = bin_expr.get_result(memory, &mut pipe_info, engine).await?;
memory.return_ = Some(ProgramReturn::Value(result)); memory.return_ = Some(ProgramReturn::Value(result));
} }
Value::UnaryExpression(unary_expr) => { Value::UnaryExpression(unary_expr) => {
let result = unary_expr.get_result(memory, &mut pipe_info, engine)?; let result = unary_expr.get_result(memory, &mut pipe_info, engine).await?;
memory.return_ = Some(ProgramReturn::Value(result)); memory.return_ = Some(ProgramReturn::Value(result));
} }
Value::Identifier(identifier) => { Value::Identifier(identifier) => {
@ -735,15 +785,15 @@ pub fn execute(
memory.return_ = Some(ProgramReturn::Value(literal.into())); memory.return_ = Some(ProgramReturn::Value(literal.into()));
} }
Value::ArrayExpression(array_expr) => { Value::ArrayExpression(array_expr) => {
let result = array_expr.execute(memory, &mut pipe_info, engine)?; let result = array_expr.execute(memory, &mut pipe_info, engine).await?;
memory.return_ = Some(ProgramReturn::Value(result)); memory.return_ = Some(ProgramReturn::Value(result));
} }
Value::ObjectExpression(obj_expr) => { Value::ObjectExpression(obj_expr) => {
let result = obj_expr.execute(memory, &mut pipe_info, engine)?; let result = obj_expr.execute(memory, &mut pipe_info, engine).await?;
memory.return_ = Some(ProgramReturn::Value(result)); memory.return_ = Some(ProgramReturn::Value(result));
} }
Value::CallExpression(call_expr) => { Value::CallExpression(call_expr) => {
let result = call_expr.execute(memory, &mut pipe_info, engine)?; let result = call_expr.execute(memory, &mut pipe_info, engine).await?;
memory.return_ = Some(ProgramReturn::Value(result)); memory.return_ = Some(ProgramReturn::Value(result));
} }
Value::MemberExpression(member_expr) => { Value::MemberExpression(member_expr) => {
@ -751,7 +801,7 @@ pub fn execute(
memory.return_ = Some(ProgramReturn::Value(result)); memory.return_ = Some(ProgramReturn::Value(result));
} }
Value::PipeExpression(pipe_expr) => { Value::PipeExpression(pipe_expr) => {
let result = pipe_expr.get_result(memory, &mut pipe_info, engine)?; let result = pipe_expr.get_result(memory, &mut pipe_info, engine).await?;
memory.return_ = Some(ProgramReturn::Value(result)); memory.return_ = Some(ProgramReturn::Value(result));
} }
Value::PipeSubstitution(_) => {} Value::PipeSubstitution(_) => {}
@ -770,12 +820,12 @@ mod tests {
use super::*; use super::*;
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> { pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let program = parser.ast()?; let program = parser.ast()?;
let mut mem: ProgramMemory = Default::default(); let mut mem: ProgramMemory = Default::default();
let mut engine = EngineConnection::new().await?; let engine = EngineConnection::new().await?;
let memory = execute(program, &mut mem, BodyType::Root, &mut engine)?; let memory = execute(program, &mut mem, BodyType::Root, &engine).await?;
Ok(memory) Ok(memory)
} }
@ -1199,6 +1249,22 @@ show(thisBox)
); );
} }
#[tokio::test(flavor = "multi_thread")]
async fn test_zero_param_fn() {
let ast = r#"const sigmaAllow = 35000 // psi
const leg1 = 5 // inches
const leg2 = 8 // inches
fn thickness = () => { return 0.56 }
const bracket = startSketchAt([0,0])
|> line([0, leg1], %)
|> line([leg2, 0], %)
|> line([0, -thickness()], %)
|> line([-leg2 + thickness(), 0], %)
"#;
parse_execute(ast).await.unwrap();
}
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_math_negative_variable_in_binary_expression() { async fn test_math_negative_variable_in_binary_expression() {
let ast = r#"const sigmaAllow = 35000 // psi let ast = r#"const sigmaAllow = 35000 // psi

View File

@ -9,4 +9,4 @@ pub mod math_parser;
pub mod parser; pub mod parser;
pub mod server; pub mod server;
pub mod std; pub mod std;
pub mod tokeniser; pub mod token;

View File

@ -10,8 +10,8 @@ use crate::{
}, },
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
executor::SourceRange, executor::SourceRange,
parser::{is_not_code_token, Parser}, parser::Parser,
tokeniser::{Token, TokenType}, token::{Token, TokenType},
}; };
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone, ts_rs::TS)] #[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone, ts_rs::TS)]
@ -334,7 +334,7 @@ impl ReversePolishNotation {
return rpn.parse(); return rpn.parse();
} }
if is_not_code_token(current_token) { if !current_token.is_code_token() {
let rpn = ReversePolishNotation::new(&self.parser.tokens[1..], &self.previous_postfix, &self.operators); let rpn = ReversePolishNotation::new(&self.parser.tokens[1..], &self.previous_postfix, &self.operators);
return rpn.parse(); return rpn.parse();
} }
@ -704,7 +704,7 @@ mod test {
#[test] #[test]
fn test_parse_expression() { fn test_parse_expression() {
let tokens = crate::tokeniser::lexer("1 + 2"); let tokens = crate::token::lexer("1 + 2");
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
@ -731,7 +731,7 @@ mod test {
#[test] #[test]
fn test_parse_expression_add_no_spaces() { fn test_parse_expression_add_no_spaces() {
let tokens = crate::tokeniser::lexer("1+2"); let tokens = crate::token::lexer("1+2");
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
@ -758,7 +758,7 @@ mod test {
#[test] #[test]
fn test_parse_expression_sub_no_spaces() { fn test_parse_expression_sub_no_spaces() {
let tokens = crate::tokeniser::lexer("1 -2"); let tokens = crate::token::lexer("1 -2");
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
@ -785,7 +785,7 @@ mod test {
#[test] #[test]
fn test_parse_expression_plus_followed_by_star() { fn test_parse_expression_plus_followed_by_star() {
let tokens = crate::tokeniser::lexer("1 + 2 * 3"); let tokens = crate::token::lexer("1 + 2 * 3");
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
@ -823,7 +823,7 @@ mod test {
#[test] #[test]
fn test_parse_expression_with_parentheses() { fn test_parse_expression_with_parentheses() {
let tokens = crate::tokeniser::lexer("1 * ( 2 + 3 )"); let tokens = crate::token::lexer("1 * ( 2 + 3 )");
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
@ -861,7 +861,7 @@ mod test {
#[test] #[test]
fn test_parse_expression_parens_in_middle() { fn test_parse_expression_parens_in_middle() {
let tokens = crate::tokeniser::lexer("1 * ( 2 + 3 ) / 4"); let tokens = crate::token::lexer("1 * ( 2 + 3 ) / 4");
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
@ -910,7 +910,7 @@ mod test {
#[test] #[test]
fn test_parse_expression_parans_and_predence() { fn test_parse_expression_parans_and_predence() {
let tokens = crate::tokeniser::lexer("1 + ( 2 + 3 ) / 4"); let tokens = crate::token::lexer("1 + ( 2 + 3 ) / 4");
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
@ -958,7 +958,7 @@ mod test {
} }
#[test] #[test]
fn test_parse_expression_nested() { fn test_parse_expression_nested() {
let tokens = crate::tokeniser::lexer("1 * (( 2 + 3 ) / 4 + 5 )"); let tokens = crate::token::lexer("1 * (( 2 + 3 ) / 4 + 5 )");
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
@ -1017,7 +1017,7 @@ mod test {
} }
#[test] #[test]
fn test_parse_expression_redundant_braces() { fn test_parse_expression_redundant_braces() {
let tokens = crate::tokeniser::lexer("1 * ((( 2 + 3 )))"); let tokens = crate::token::lexer("1 * ((( 2 + 3 )))");
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
@ -1055,7 +1055,7 @@ mod test {
#[test] #[test]
fn test_reverse_polish_notation_simple() { fn test_reverse_polish_notation_simple() {
let parser = ReversePolishNotation::new(&crate::tokeniser::lexer("1 + 2"), &[], &[]); let parser = ReversePolishNotation::new(&crate::token::lexer("1 + 2"), &[], &[]);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
result, result,
@ -1084,7 +1084,7 @@ mod test {
#[test] #[test]
fn test_reverse_polish_notation_complex() { fn test_reverse_polish_notation_complex() {
let parser = ReversePolishNotation::new(&crate::tokeniser::lexer("1 + 2 * 3"), &[], &[]); let parser = ReversePolishNotation::new(&crate::token::lexer("1 + 2 * 3"), &[], &[]);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
result, result,
@ -1125,7 +1125,7 @@ mod test {
#[test] #[test]
fn test_reverse_polish_notation_complex_with_parentheses() { fn test_reverse_polish_notation_complex_with_parentheses() {
let parser = ReversePolishNotation::new(&crate::tokeniser::lexer("1 * ( 2 + 3 )"), &[], &[]); let parser = ReversePolishNotation::new(&crate::token::lexer("1 * ( 2 + 3 )"), &[], &[]);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
result, result,
@ -1179,7 +1179,7 @@ mod test {
#[test] #[test]
fn test_parse_expression_redundant_braces_around_literal() { fn test_parse_expression_redundant_braces_around_literal() {
let code = "2 + (((3)))"; let code = "2 + (((3)))";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap(); let result = parser.parse().unwrap();
assert_eq!( assert_eq!(
@ -1274,7 +1274,7 @@ mod test {
#[test] #[test]
fn test_parse_expression_braces_around_lots_of_math() { fn test_parse_expression_braces_around_lots_of_math() {
let code = "(distance * p * FOS * 6 / (sigmaAllow * width))"; let code = "(distance * p * FOS * 6 / (sigmaAllow * width))";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse(); let result = parser.parse();
assert!(result.is_ok()); assert!(result.is_ok());
@ -1283,7 +1283,7 @@ mod test {
#[test] #[test]
fn test_parse_expression_braces_around_internals_lots_of_math() { fn test_parse_expression_braces_around_internals_lots_of_math() {
let code = "distance * p * FOS * 6 / (sigmaAllow * width)"; let code = "distance * p * FOS * 6 / (sigmaAllow * width)";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let mut parser = MathParser::new(&tokens); let mut parser = MathParser::new(&tokens);
let result = parser.parse(); let result = parser.parse();
assert!(result.is_ok()); assert!(result.is_ok());

View File

@ -10,7 +10,7 @@ use crate::{
}, },
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
math_parser::MathParser, math_parser::MathParser,
tokeniser::{Token, TokenType}, token::{Token, TokenType},
}; };
pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%"; pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
@ -249,7 +249,7 @@ impl Parser {
} }
let current_token = self.get_token(index)?; let current_token = self.get_token(index)?;
if is_not_code_token(current_token) { if !current_token.is_code_token() {
return self.find_end_of_non_code_node(index + 1); return self.find_end_of_non_code_node(index + 1);
} }
@ -262,7 +262,7 @@ impl Parser {
} }
let current_token = self.get_token(index)?; let current_token = self.get_token(index)?;
if is_not_code_token(current_token) { if !current_token.is_code_token() {
return self.find_start_of_non_code_node(index - 1); return self.find_start_of_non_code_node(index - 1);
} }
@ -365,7 +365,7 @@ impl Parser {
}); });
}; };
if is_not_code_token(token) { if !token.is_code_token() {
let non_code_node = self.make_non_code_node(new_index)?; let non_code_node = self.make_non_code_node(new_index)?;
let new_new_index = non_code_node.1 + 1; let new_new_index = non_code_node.1 + 1;
let bonus_non_code_node = non_code_node.0; let bonus_non_code_node = non_code_node.0;
@ -1283,7 +1283,11 @@ impl Parser {
let end_token = self.get_token(pipe_body_result.last_index)?; let end_token = self.get_token(pipe_body_result.last_index)?;
Ok(PipeExpressionResult { Ok(PipeExpressionResult {
expression: PipeExpression { expression: PipeExpression {
start: current_token.start, start: pipe_body_result
.body
.first()
.map(|v| v.start())
.unwrap_or(current_token.start),
end: end_token.end, end: end_token.end,
body: pipe_body_result.body, body: pipe_body_result.body,
non_code_meta: pipe_body_result.non_code_meta, non_code_meta: pipe_body_result.non_code_meta,
@ -1623,7 +1627,7 @@ impl Parser {
}); });
} }
if is_not_code_token(token) { if !token.is_code_token() {
let next_token = self.next_meaningful_token(token_index, Some(0))?; let next_token = self.next_meaningful_token(token_index, Some(0))?;
if let Some(node) = &next_token.non_code_node { if let Some(node) = &next_token.non_code_node {
if previous_body.is_empty() { if previous_body.is_empty() {
@ -1788,12 +1792,6 @@ impl Parser {
} }
} }
pub fn is_not_code_token(token: &Token) -> bool {
token.token_type == TokenType::Whitespace
|| token.token_type == TokenType::LineComment
|| token.token_type == TokenType::BlockComment
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@ -1803,7 +1801,7 @@ mod tests {
#[test] #[test]
fn test_make_identifier() { fn test_make_identifier() {
let tokens = crate::tokeniser::lexer("a"); let tokens = crate::token::lexer("a");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let identifier = parser.make_identifier(0).unwrap(); let identifier = parser.make_identifier(0).unwrap();
assert_eq!( assert_eq!(
@ -1818,7 +1816,7 @@ mod tests {
#[test] #[test]
fn test_make_identifier_with_const_myvar_equals_5_and_index_2() { fn test_make_identifier_with_const_myvar_equals_5_and_index_2() {
let tokens = crate::tokeniser::lexer("const myVar = 5"); let tokens = crate::token::lexer("const myVar = 5");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let identifier = parser.make_identifier(2).unwrap(); let identifier = parser.make_identifier(2).unwrap();
assert_eq!( assert_eq!(
@ -1833,7 +1831,7 @@ mod tests {
#[test] #[test]
fn test_make_identifier_multiline() { fn test_make_identifier_multiline() {
let tokens = crate::tokeniser::lexer("const myVar = 5\nconst newVar = myVar + 1"); let tokens = crate::token::lexer("const myVar = 5\nconst newVar = myVar + 1");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let identifier = parser.make_identifier(2).unwrap(); let identifier = parser.make_identifier(2).unwrap();
assert_eq!( assert_eq!(
@ -1857,7 +1855,7 @@ mod tests {
#[test] #[test]
fn test_make_identifier_call_expression() { fn test_make_identifier_call_expression() {
let tokens = crate::tokeniser::lexer("log(5, \"hello\", aIdentifier)"); let tokens = crate::token::lexer("log(5, \"hello\", aIdentifier)");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let identifier = parser.make_identifier(0).unwrap(); let identifier = parser.make_identifier(0).unwrap();
assert_eq!( assert_eq!(
@ -1880,7 +1878,7 @@ mod tests {
} }
#[test] #[test]
fn test_make_non_code_node() { fn test_make_non_code_node() {
let tokens = crate::tokeniser::lexer("log(5, \"hello\", aIdentifier)"); let tokens = crate::token::lexer("log(5, \"hello\", aIdentifier)");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let index = 4; let index = 4;
let expected_output = (None, 4); let expected_output = (None, 4);
@ -1889,7 +1887,7 @@ mod tests {
let index = 7; let index = 7;
let expected_output = (None, 7); let expected_output = (None, 7);
assert_eq!(parser.make_non_code_node(index).unwrap(), expected_output); assert_eq!(parser.make_non_code_node(index).unwrap(), expected_output);
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#" r#"
const yo = { a: { b: { c: '123' } } } const yo = { a: { b: { c: '123' } } }
// this is a comment // this is a comment
@ -1920,7 +1918,7 @@ const key = 'c'"#,
31, 31,
); );
assert_eq!(parser.make_non_code_node(index).unwrap(), expected_output); assert_eq!(parser.make_non_code_node(index).unwrap(), expected_output);
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const mySketch = startSketchAt([0,0]) r#"const mySketch = startSketchAt([0,0])
|> lineTo({ to: [0, 1], tag: 'myPath' }, %) |> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|> lineTo([1, 1], %) /* this is |> lineTo([1, 1], %) /* this is
@ -1946,7 +1944,7 @@ const key = 'c'"#,
#[test] #[test]
fn test_collect_object_keys() { fn test_collect_object_keys() {
let tokens = crate::tokeniser::lexer("const prop = yo.one[\"two\"]"); let tokens = crate::token::lexer("const prop = yo.one[\"two\"]");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let keys_info = parser.collect_object_keys(6, None, false).unwrap(); let keys_info = parser.collect_object_keys(6, None, false).unwrap();
assert_eq!(keys_info.len(), 2); assert_eq!(keys_info.len(), 2);
@ -1966,7 +1964,7 @@ const key = 'c'"#,
#[test] #[test]
fn test_make_literal_call_expression() { fn test_make_literal_call_expression() {
let tokens = crate::tokeniser::lexer("log(5, \"hello\", aIdentifier)"); let tokens = crate::token::lexer("log(5, \"hello\", aIdentifier)");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let literal = parser.make_literal(2).unwrap(); let literal = parser.make_literal(2).unwrap();
assert_eq!( assert_eq!(
@ -1991,73 +1989,87 @@ const key = 'c'"#,
} }
#[test] #[test]
fn test_is_not_code_token() { fn test_is_code_token() {
assert!(!is_not_code_token(&Token { let tokens = [
Token {
token_type: TokenType::Word, token_type: TokenType::Word,
start: 0, start: 0,
end: 3, end: 3,
value: "log".to_string(), value: "log".to_string(),
})); },
assert!(!is_not_code_token(&Token { Token {
token_type: TokenType::Brace, token_type: TokenType::Brace,
start: 3, start: 3,
end: 4, end: 4,
value: "(".to_string(), value: "(".to_string(),
})); },
assert!(!is_not_code_token(&Token { Token {
token_type: TokenType::Number, token_type: TokenType::Number,
start: 4, start: 4,
end: 5, end: 5,
value: "5".to_string(), value: "5".to_string(),
})); },
assert!(!is_not_code_token(&Token { Token {
token_type: TokenType::Comma, token_type: TokenType::Comma,
start: 5, start: 5,
end: 6, end: 6,
value: ",".to_string(), value: ",".to_string(),
})); },
assert!(is_not_code_token(&Token { Token {
token_type: TokenType::Whitespace,
start: 6,
end: 7,
value: " ".to_string(),
}));
assert!(!is_not_code_token(&Token {
token_type: TokenType::String, token_type: TokenType::String,
start: 7, start: 7,
end: 14, end: 14,
value: "\"hello\"".to_string(), value: "\"hello\"".to_string(),
})); },
assert!(!is_not_code_token(&Token { Token {
token_type: TokenType::Word, token_type: TokenType::Word,
start: 16, start: 16,
end: 27, end: 27,
value: "aIdentifier".to_string(), value: "aIdentifier".to_string(),
})); },
assert!(!is_not_code_token(&Token { Token {
token_type: TokenType::Brace, token_type: TokenType::Brace,
start: 27, start: 27,
end: 28, end: 28,
value: ")".to_string(), value: ")".to_string(),
})); },
assert!(is_not_code_token(&Token { ];
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, token_type: TokenType::BlockComment,
start: 28, start: 28,
end: 30, end: 30,
value: "/* abte */".to_string(), value: "/* abte */".to_string(),
})); },
assert!(is_not_code_token(&Token { Token {
token_type: TokenType::LineComment, token_type: TokenType::LineComment,
start: 30, start: 30,
end: 33, end: 33,
value: "// yoyo a line".to_string(), value: "// yoyo a line".to_string(),
})); },
];
for (i, token) in tokens.iter().enumerate() {
assert!(!token.is_code_token(), "failed test {i}: {token:?}")
}
} }
#[test] #[test]
fn test_next_meaningful_token() { fn test_next_meaningful_token() {
let _offset = 1; let _offset = 1;
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const mySketch = startSketchAt([0,0]) r#"const mySketch = startSketchAt([0,0])
|> lineTo({ to: [0, 1], tag: 'myPath' }, %) |> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|> lineTo([1, 1], %) /* this is |> lineTo([1, 1], %) /* this is
@ -2443,7 +2455,7 @@ const key = 'c'"#,
#[test] #[test]
fn test_find_closing_brace() { fn test_find_closing_brace() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const mySketch = startSketchAt([0,0]) r#"const mySketch = startSketchAt([0,0])
|> lineTo({ to: [0, 1], tag: 'myPath' }, %) |> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|> lineTo([1, 1], %) /* this is |> lineTo([1, 1], %) /* this is
@ -2460,16 +2472,16 @@ const key = 'c'"#,
assert_eq!(parser.find_closing_brace(90, 0, "").unwrap(), 92); assert_eq!(parser.find_closing_brace(90, 0, "").unwrap(), 92);
let basic = "( hey )"; let basic = "( hey )";
let parser = Parser::new(crate::tokeniser::lexer(basic)); let parser = Parser::new(crate::token::lexer(basic));
assert_eq!(parser.find_closing_brace(0, 0, "").unwrap(), 4); assert_eq!(parser.find_closing_brace(0, 0, "").unwrap(), 4);
let handles_non_zero_index = "(indexForBracketToRightOfThisIsTwo(shouldBeFour)AndNotThisSix)"; let handles_non_zero_index = "(indexForBracketToRightOfThisIsTwo(shouldBeFour)AndNotThisSix)";
let parser = Parser::new(crate::tokeniser::lexer(handles_non_zero_index)); let parser = Parser::new(crate::token::lexer(handles_non_zero_index));
assert_eq!(parser.find_closing_brace(2, 0, "").unwrap(), 4); assert_eq!(parser.find_closing_brace(2, 0, "").unwrap(), 4);
assert_eq!(parser.find_closing_brace(0, 0, "").unwrap(), 6); assert_eq!(parser.find_closing_brace(0, 0, "").unwrap(), 6);
let handles_nested = "{a{b{c(}d]}eathou athoeu tah u} thatOneToTheLeftIsLast }"; let handles_nested = "{a{b{c(}d]}eathou athoeu tah u} thatOneToTheLeftIsLast }";
let parser = Parser::new(crate::tokeniser::lexer(handles_nested)); let parser = Parser::new(crate::token::lexer(handles_nested));
assert_eq!(parser.find_closing_brace(0, 0, "").unwrap(), 18); assert_eq!(parser.find_closing_brace(0, 0, "").unwrap(), 18);
// TODO expect error when not started on a brace // TODO expect error when not started on a brace
@ -2477,7 +2489,7 @@ const key = 'c'"#,
#[test] #[test]
fn test_is_call_expression() { fn test_is_call_expression() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const mySketch = startSketchAt([0,0]) r#"const mySketch = startSketchAt([0,0])
|> lineTo({ to: [0, 1], tag: 'myPath' }, %) |> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|> lineTo([1, 1], %) /* this is |> lineTo([1, 1], %) /* this is
@ -2498,7 +2510,7 @@ const key = 'c'"#,
#[test] #[test]
fn test_find_next_declaration_keyword() { fn test_find_next_declaration_keyword() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const mySketch = startSketchAt([0,0]) r#"const mySketch = startSketchAt([0,0])
|> lineTo({ to: [0, 1], tag: 'myPath' }, %) |> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|> lineTo([1, 1], %) /* this is |> lineTo([1, 1], %) /* this is
@ -2513,7 +2525,7 @@ const key = 'c'"#,
TokenReturn { token: None, index: 92 } TokenReturn { token: None, index: 92 }
); );
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const myVar = 5 r#"const myVar = 5
const newVar = myVar + 1 const newVar = myVar + 1
"#, "#,
@ -2543,7 +2555,7 @@ const newVar = myVar + 1
lineTo(2, 3) lineTo(2, 3)
} |> rx(45, %) } |> rx(45, %)
"#; "#;
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
assert_eq!( assert_eq!(
parser.has_pipe_operator(0, None).unwrap(), parser.has_pipe_operator(0, None).unwrap(),
@ -2562,7 +2574,7 @@ const newVar = myVar + 1
lineTo(2, 3) lineTo(2, 3)
} |> rx(45, %) |> rx(45, %) } |> rx(45, %) |> rx(45, %)
"#; "#;
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
assert_eq!( assert_eq!(
parser.has_pipe_operator(0, None).unwrap(), parser.has_pipe_operator(0, None).unwrap(),
@ -2584,7 +2596,7 @@ const newVar = myVar + 1
const yo = myFunc(9() const yo = myFunc(9()
|> rx(45, %) |> rx(45, %)
"#; "#;
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
assert_eq!( assert_eq!(
parser.has_pipe_operator(0, None).unwrap(), parser.has_pipe_operator(0, None).unwrap(),
@ -2596,7 +2608,7 @@ const yo = myFunc(9()
); );
let code = "const myVar2 = 5 + 1 |> myFn(%)"; let code = "const myVar2 = 5 + 1 |> myFn(%)";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
assert_eq!( assert_eq!(
parser.has_pipe_operator(1, None).unwrap(), parser.has_pipe_operator(1, None).unwrap(),
@ -2618,7 +2630,7 @@ const yo = myFunc(9()
lineTo(1,1) lineTo(1,1)
} |> rx(90, %) } |> rx(90, %)
show(mySk1)"#; show(mySk1)"#;
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens.clone()); let parser = Parser::new(tokens.clone());
let token_with_my_path_index = tokens.iter().position(|token| token.value == "myPath").unwrap(); let token_with_my_path_index = tokens.iter().position(|token| token.value == "myPath").unwrap();
// loop through getting the token and it's index // loop through getting the token and it's index
@ -2658,7 +2670,7 @@ show(mySk1)"#;
#[test] #[test]
fn test_make_member_expression() { fn test_make_member_expression() {
let tokens = crate::tokeniser::lexer("const prop = yo.one[\"two\"]"); let tokens = crate::token::lexer("const prop = yo.one[\"two\"]");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let member_expression_return = parser.make_member_expression(6).unwrap(); let member_expression_return = parser.make_member_expression(6).unwrap();
let member_expression = member_expression_return.expression; let member_expression = member_expression_return.expression;
@ -2700,63 +2712,63 @@ show(mySk1)"#;
#[test] #[test]
fn test_find_end_of_binary_expression() { fn test_find_end_of_binary_expression() {
let code = "1 + 2 * 3\nconst yo = 5"; let code = "1 + 2 * 3\nconst yo = 5";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens.clone()); let parser = Parser::new(tokens.clone());
let end = parser.find_end_of_binary_expression(0).unwrap(); let end = parser.find_end_of_binary_expression(0).unwrap();
assert_eq!(tokens[end].value, "3"); assert_eq!(tokens[end].value, "3");
let code = "(1 + 25) / 5 - 3\nconst yo = 5"; let code = "(1 + 25) / 5 - 3\nconst yo = 5";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens.clone()); let parser = Parser::new(tokens.clone());
let end = parser.find_end_of_binary_expression(0).unwrap(); let end = parser.find_end_of_binary_expression(0).unwrap();
assert_eq!(tokens[end].value, "3"); assert_eq!(tokens[end].value, "3");
let index_of_5 = code.find('5').unwrap(); let index_of_5 = code.find('5').unwrap();
let end_starting_at_the_5 = parser.find_end_of_binary_expression(index_of_5).unwrap(); let end_starting_at_the_5 = parser.find_end_of_binary_expression(index_of_5).unwrap();
assert_eq!(end_starting_at_the_5, end); assert_eq!(end_starting_at_the_5, end);
// whole thing wraped // whole thing wrapped
let code = "((1 + 2) / 5 - 3)\nconst yo = 5"; let code = "((1 + 2) / 5 - 3)\nconst yo = 5";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens.clone()); let parser = Parser::new(tokens.clone());
let end = parser.find_end_of_binary_expression(0).unwrap(); let end = parser.find_end_of_binary_expression(0).unwrap();
assert_eq!(tokens[end].end, code.find("3)").unwrap() + 2); assert_eq!(tokens[end].end, code.find("3)").unwrap() + 2);
// whole thing wraped but given index after the first brace // whole thing wrapped but given index after the first brace
let code = "((1 + 2) / 5 - 3)\nconst yo = 5"; let code = "((1 + 2) / 5 - 3)\nconst yo = 5";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens.clone()); let parser = Parser::new(tokens.clone());
let end = parser.find_end_of_binary_expression(1).unwrap(); let end = parser.find_end_of_binary_expression(1).unwrap();
assert_eq!(tokens[end].value, "3"); assert_eq!(tokens[end].value, "3");
// given the index of a small wrapped section i.e. `1 + 2` in ((1 + 2) / 5 - 3)' // given the index of a small wrapped section i.e. `1 + 2` in ((1 + 2) / 5 - 3)'
let code = "((1 + 2) / 5 - 3)\nconst yo = 5"; let code = "((1 + 2) / 5 - 3)\nconst yo = 5";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens.clone()); let parser = Parser::new(tokens.clone());
let end = parser.find_end_of_binary_expression(2).unwrap(); let end = parser.find_end_of_binary_expression(2).unwrap();
assert_eq!(tokens[end].value, "2"); assert_eq!(tokens[end].value, "2");
// lots of silly nesting // lots of silly nesting
let code = "(1 + 2) / (5 - (3))\nconst yo = 5"; let code = "(1 + 2) / (5 - (3))\nconst yo = 5";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens.clone()); let parser = Parser::new(tokens.clone());
let end = parser.find_end_of_binary_expression(0).unwrap(); let end = parser.find_end_of_binary_expression(0).unwrap();
assert_eq!(tokens[end].end, code.find("))").unwrap() + 2); assert_eq!(tokens[end].end, code.find("))").unwrap() + 2);
// with pipe operator at the end // with pipe operator at the end
let code = "(1 + 2) / (5 - (3))\n |> fn(%)"; let code = "(1 + 2) / (5 - (3))\n |> fn(%)";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens.clone()); let parser = Parser::new(tokens.clone());
let end = parser.find_end_of_binary_expression(0).unwrap(); let end = parser.find_end_of_binary_expression(0).unwrap();
assert_eq!(tokens[end].end, code.find("))").unwrap() + 2); assert_eq!(tokens[end].end, code.find("))").unwrap() + 2);
// with call expression at the start of binary expression // with call expression at the start of binary expression
let code = "yo(2) + 3\n |> fn(%)"; let code = "yo(2) + 3\n |> fn(%)";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens.clone()); let parser = Parser::new(tokens.clone());
let end = parser.find_end_of_binary_expression(0).unwrap(); let end = parser.find_end_of_binary_expression(0).unwrap();
assert_eq!(tokens[end].value, "3"); assert_eq!(tokens[end].value, "3");
// with call expression at the end of binary expression // with call expression at the end of binary expression
let code = "3 + yo(2)\n |> fn(%)"; let code = "3 + yo(2)\n |> fn(%)";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let _end = parser.find_end_of_binary_expression(0).unwrap(); let _end = parser.find_end_of_binary_expression(0).unwrap();
// with call expression at the end of binary expression // with call expression at the end of binary expression
let code = "-legX + 2, "; let code = "-legX + 2, ";
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = Parser::new(tokens.clone()); let parser = Parser::new(tokens.clone());
let end = parser.find_end_of_binary_expression(0).unwrap(); let end = parser.find_end_of_binary_expression(0).unwrap();
assert_eq!(tokens[end].value, "2"); assert_eq!(tokens[end].value, "2");
@ -2765,7 +2777,7 @@ show(mySk1)"#;
#[test] #[test]
fn test_make_array_expression() { fn test_make_array_expression() {
// input_index: 6, output_index: 14, output: {"type":"ArrayExpression","start":11,"end":26,"elements":[{"type":"Literal","start":12,"end":15,"value":"1","raw":"\"1\""},{"type":"Literal","start":17,"end":18,"value":2,"raw":"2"},{"type":"Identifier","start":20,"end":25,"name":"three"}]} // input_index: 6, output_index: 14, output: {"type":"ArrayExpression","start":11,"end":26,"elements":[{"type":"Literal","start":12,"end":15,"value":"1","raw":"\"1\""},{"type":"Literal","start":17,"end":18,"value":2,"raw":"2"},{"type":"Identifier","start":20,"end":25,"name":"three"}]}
let tokens = crate::tokeniser::lexer("const yo = [\"1\", 2, three]"); let tokens = crate::token::lexer("const yo = [\"1\", 2, three]");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let array_expression = parser.make_array_expression(6).unwrap(); let array_expression = parser.make_array_expression(6).unwrap();
let expression = array_expression.expression; let expression = array_expression.expression;
@ -2804,7 +2816,7 @@ show(mySk1)"#;
#[test] #[test]
fn test_make_call_expression() { fn test_make_call_expression() {
let tokens = crate::tokeniser::lexer("foo(\"a\", a, 3)"); let tokens = crate::token::lexer("foo(\"a\", a, 3)");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.make_call_expression(0).unwrap(); let result = parser.make_call_expression(0).unwrap();
assert_eq!(result.last_index, 9); assert_eq!(result.last_index, 9);
@ -2838,7 +2850,7 @@ show(mySk1)"#;
#[test] #[test]
fn test_make_variable_declaration() { fn test_make_variable_declaration() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const yo = startSketch([0, 0]) r#"const yo = startSketch([0, 0])
|> lineTo([1, myVar], %) |> lineTo([1, myVar], %)
|> foo(myVar2, %) |> foo(myVar2, %)
@ -2908,7 +2920,7 @@ show(mySk1)"#;
#[test] #[test]
fn test_make_body() { fn test_make_body() {
let tokens = crate::tokeniser::lexer("const myVar = 5"); let tokens = crate::token::lexer("const myVar = 5");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let body = parser let body = parser
.make_body( .make_body(
@ -2926,7 +2938,7 @@ show(mySk1)"#;
#[test] #[test]
fn test_abstract_syntax_tree() { fn test_abstract_syntax_tree() {
let code = "5 +6"; let code = "5 +6";
let parser = Parser::new(crate::tokeniser::lexer(code)); let parser = Parser::new(crate::token::lexer(code));
let result = parser.ast().unwrap(); let result = parser.ast().unwrap();
let expected_result = Program { let expected_result = Program {
start: 0, start: 0,
@ -2964,8 +2976,8 @@ show(mySk1)"#;
#[test] #[test]
fn test_empty_file() { fn test_empty_file() {
let some_program_string = r#""#; let some_program_string = r#""#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
assert!(result.err().unwrap().to_string().contains("file is empty")); assert!(result.err().unwrap().to_string().contains("file is empty"));
@ -2973,7 +2985,7 @@ show(mySk1)"#;
#[test] #[test]
fn test_parse_half_pipe_small() { fn test_parse_half_pipe_small() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
"const secondExtrude = startSketchAt([0,0]) "const secondExtrude = startSketchAt([0,0])
|", |",
); );
@ -2985,14 +2997,14 @@ show(mySk1)"#;
#[test] #[test]
fn test_parse_member_expression_double_nested_braces() { fn test_parse_member_expression_double_nested_braces() {
let tokens = crate::tokeniser::lexer(r#"const prop = yo["one"][two]"#); let tokens = crate::token::lexer(r#"const prop = yo["one"][two]"#);
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
parser.ast().unwrap(); parser.ast().unwrap();
} }
#[test] #[test]
fn test_parse_member_expression_binary_expression_period_number_first() { fn test_parse_member_expression_binary_expression_period_number_first() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const obj = { a: 1, b: 2 } r#"const obj = { a: 1, b: 2 }
const height = 1 - obj.a"#, const height = 1 - obj.a"#,
); );
@ -3002,7 +3014,7 @@ const height = 1 - obj.a"#,
#[test] #[test]
fn test_parse_member_expression_binary_expression_brace_number_first() { fn test_parse_member_expression_binary_expression_brace_number_first() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const obj = { a: 1, b: 2 } r#"const obj = { a: 1, b: 2 }
const height = 1 - obj["a"]"#, const height = 1 - obj["a"]"#,
); );
@ -3012,7 +3024,7 @@ const height = 1 - obj["a"]"#,
#[test] #[test]
fn test_parse_member_expression_binary_expression_brace_number_second() { fn test_parse_member_expression_binary_expression_brace_number_second() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const obj = { a: 1, b: 2 } r#"const obj = { a: 1, b: 2 }
const height = obj["a"] - 1"#, const height = obj["a"] - 1"#,
); );
@ -3022,7 +3034,7 @@ const height = obj["a"] - 1"#,
#[test] #[test]
fn test_parse_member_expression_binary_expression_in_array_number_first() { fn test_parse_member_expression_binary_expression_in_array_number_first() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const obj = { a: 1, b: 2 } r#"const obj = { a: 1, b: 2 }
const height = [1 - obj["a"], 0]"#, const height = [1 - obj["a"], 0]"#,
); );
@ -3032,7 +3044,7 @@ const height = [1 - obj["a"], 0]"#,
#[test] #[test]
fn test_parse_member_expression_binary_expression_in_array_number_second() { fn test_parse_member_expression_binary_expression_in_array_number_second() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const obj = { a: 1, b: 2 } r#"const obj = { a: 1, b: 2 }
const height = [obj["a"] - 1, 0]"#, const height = [obj["a"] - 1, 0]"#,
); );
@ -3042,7 +3054,7 @@ const height = [obj["a"] - 1, 0]"#,
#[test] #[test]
fn test_parse_member_expression_binary_expression_in_array_number_second_missing_space() { fn test_parse_member_expression_binary_expression_in_array_number_second_missing_space() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const obj = { a: 1, b: 2 } r#"const obj = { a: 1, b: 2 }
const height = [obj["a"] -1, 0]"#, const height = [obj["a"] -1, 0]"#,
); );
@ -3052,7 +3064,7 @@ const height = [obj["a"] -1, 0]"#,
#[test] #[test]
fn test_parse_half_pipe() { fn test_parse_half_pipe() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
"const height = 10 "const height = 10
const firstExtrude = startSketchAt([0,0]) const firstExtrude = startSketchAt([0,0])
@ -3075,15 +3087,17 @@ const secondExtrude = startSketchAt([0,0])
#[test] #[test]
fn test_parse_greater_bang() { fn test_parse_greater_bang() {
let tokens = crate::tokeniser::lexer(">!"); let tokens = crate::token::lexer(">!");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let err = parser.ast().unwrap_err();
assert!(result.is_ok()); // TODO: Better errors when program cannot tokenize.
// https://github.com/KittyCAD/modeling-app/issues/696
assert!(err.to_string().contains("file is empty"));
} }
#[test] #[test]
fn test_parse_z_percent_parens() { fn test_parse_z_percent_parens() {
let tokens = crate::tokeniser::lexer("z%)"); let tokens = crate::token::lexer("z%)");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
@ -3092,15 +3106,17 @@ const secondExtrude = startSketchAt([0,0])
#[test] #[test]
fn test_parse_parens_unicode() { fn test_parse_parens_unicode() {
let tokens = crate::tokeniser::lexer(""); let tokens = crate::token::lexer("");
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_ok()); // TODO: Better errors when program cannot tokenize.
// https://github.com/KittyCAD/modeling-app/issues/696
assert!(result.is_err());
} }
#[test] #[test]
fn test_parse_negative_in_array_binary_expression() { fn test_parse_negative_in_array_binary_expression() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"const leg1 = 5 r#"const leg1 = 5
const thickness = 0.56 const thickness = 0.56
@ -3114,7 +3130,7 @@ const bracket = [-leg2 + thickness, 0]
#[test] #[test]
fn test_parse_nested_open_brackets() { fn test_parse_nested_open_brackets() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#" r#"
z(-[["#, z(-[["#,
); );
@ -3129,31 +3145,38 @@ z(-[["#,
#[test] #[test]
fn test_parse_weird_new_line_function() { fn test_parse_weird_new_line_function() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"z r#"z
(--#"#, (--#"#,
); );
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
// TODO: Better errors when program cannot tokenize.
// https://github.com/KittyCAD/modeling-app/issues/696
assert_eq!( assert_eq!(
result.err().unwrap().to_string(), result.err().unwrap().to_string(),
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([0, 1])], message: "missing a closing brace for the function call" }"# r#"semantic: KclErrorDetails { source_ranges: [], message: "file is empty" }"#
); );
} }
#[test] #[test]
fn test_parse_weird_lots_of_fancy_brackets() { fn test_parse_weird_lots_of_fancy_brackets() {
let tokens = crate::tokeniser::lexer(r#"zz({{{{{{{{)iegAng{{{{{{{##"#); let tokens = crate::token::lexer(r#"zz({{{{{{{{)iegAng{{{{{{{##"#);
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
assert!(result.err().unwrap().to_string().contains("unexpected end")); // TODO: Better errors when program cannot tokenize.
// https://github.com/KittyCAD/modeling-app/issues/696
assert_eq!(
result.err().unwrap().to_string(),
r#"semantic: KclErrorDetails { source_ranges: [], message: "file is empty" }"#
);
} }
#[test] #[test]
fn test_parse_weird_close_before_open() { fn test_parse_weird_close_before_open() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"fn)n r#"fn)n
e e
["#, ["#,
@ -3170,7 +3193,7 @@ e
#[test] #[test]
fn test_parse_weird_close_before_nada() { fn test_parse_weird_close_before_nada() {
let tokens = crate::tokeniser::lexer(r#"fn)n-"#); let tokens = crate::token::lexer(r#"fn)n-"#);
let parser = Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
@ -3179,7 +3202,7 @@ e
#[test] #[test]
fn test_parse_weird_lots_of_slashes() { fn test_parse_weird_lots_of_slashes() {
let tokens = crate::tokeniser::lexer( let tokens = crate::token::lexer(
r#"J///////////o//+///////////P++++*++++++P///////˟ r#"J///////////o//+///////////P++++*++++++P///////˟
++4"#, ++4"#,
); );
@ -3196,7 +3219,7 @@ e
#[test] #[test]
fn test_parse_expand_array() { fn test_parse_expand_array() {
let code = "const myArray = [0..10]"; let code = "const myArray = [0..10]";
let parser = Parser::new(crate::tokeniser::lexer(code)); let parser = Parser::new(crate::token::lexer(code));
let result = parser.ast().unwrap(); let result = parser.ast().unwrap();
let expected_result = Program { let expected_result = Program {
start: 0, start: 0,
@ -3299,8 +3322,8 @@ e
#[test] #[test]
fn test_error_keyword_in_variable() { fn test_error_keyword_in_variable() {
let some_program_string = r#"const let = "thing""#; let some_program_string = r#"const let = "thing""#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
@ -3312,8 +3335,8 @@ e
#[test] #[test]
fn test_error_keyword_in_fn_name() { fn test_error_keyword_in_fn_name() {
let some_program_string = r#"fn let = () {}"#; let some_program_string = r#"fn let = () {}"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
@ -3325,8 +3348,8 @@ e
#[test] #[test]
fn test_error_stdlib_in_fn_name() { fn test_error_stdlib_in_fn_name() {
let some_program_string = r#"fn cos = () {}"#; let some_program_string = r#"fn cos = () {}"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
@ -3340,8 +3363,8 @@ e
let some_program_string = r#"fn thing = (let) => { let some_program_string = r#"fn thing = (let) => {
return 1 return 1
}"#; }"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
@ -3355,8 +3378,8 @@ e
let some_program_string = r#"fn thing = (cos) => { let some_program_string = r#"fn thing = (cos) => {
return 1 return 1
}"#; }"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
@ -3365,6 +3388,19 @@ e
); );
} }
#[test]
fn zero_param_function() {
let program = r#"
fn firstPrimeNumber = () => {
return 2
}
firstPrimeNumber()
"#;
let tokens = crate::token::lexer(program);
let parser = Parser::new(tokens);
let _ast = parser.ast().unwrap();
}
#[test] #[test]
fn test_keyword_ok_in_fn_args_return() { fn test_keyword_ok_in_fn_args_return() {
let some_program_string = r#"fn thing = (param) => { let some_program_string = r#"fn thing = (param) => {
@ -3373,8 +3409,8 @@ e
thing(false) thing(false)
"#; "#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
parser.ast().unwrap(); parser.ast().unwrap();
} }
@ -3390,8 +3426,8 @@ thing(false)
"#, "#,
name name
); );
let tokens = crate::tokeniser::lexer(&some_program_string); let tokens = crate::token::lexer(&some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
@ -3408,8 +3444,8 @@ thing(false)
#[test] #[test]
fn test_error_define_var_as_function() { fn test_error_define_var_as_function() {
let some_program_string = r#"fn thing = "thing""#; let some_program_string = r#"fn thing = "thing""#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
let result = parser.ast(); let result = parser.ast();
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
@ -3437,8 +3473,8 @@ const pt2 = b2[0]
show(b1) show(b1)
show(b2)"#; show(b2)"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
parser.ast().unwrap(); parser.ast().unwrap();
} }
@ -3446,18 +3482,36 @@ show(b2)"#;
fn test_math_with_stdlib() { fn test_math_with_stdlib() {
let some_program_string = r#"const d2r = pi() / 2 let some_program_string = r#"const d2r = pi() / 2
let other_thing = 2 * cos(3)"#; let other_thing = 2 * cos(3)"#;
let tokens = crate::tokeniser::lexer(some_program_string); let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens); let parser = Parser::new(tokens);
parser.ast().unwrap(); parser.ast().unwrap();
} }
#[test] #[test]
#[ignore] // ignore until more stack fixes
fn test_parse_pipes_on_pipes() { fn test_parse_pipes_on_pipes() {
let code = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl"); let code = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl");
let tokens = crate::tokeniser::lexer(code); let tokens = crate::token::lexer(code);
let parser = crate::parser::Parser::new(tokens); let 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 = startSketchAt(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 = Parser::new(tokens);
parser.ast().unwrap(); parser.ast().unwrap();
} }
} }

View File

@ -34,7 +34,7 @@ pub struct Backend {
/// The types of tokens the server supports. /// The types of tokens the server supports.
pub token_types: Vec<SemanticTokenType>, pub token_types: Vec<SemanticTokenType>,
/// Token maps. /// Token maps.
pub token_map: DashMap<String, Vec<crate::tokeniser::Token>>, pub token_map: DashMap<String, Vec<crate::token::Token>>,
/// AST maps. /// AST maps.
pub ast_map: DashMap<String, crate::ast::types::Program>, pub ast_map: DashMap<String, crate::ast::types::Program>,
/// Current code. /// Current code.
@ -56,7 +56,7 @@ impl Backend {
// Lets update the tokens. // Lets update the tokens.
self.current_code_map self.current_code_map
.insert(params.uri.to_string(), params.text.clone()); .insert(params.uri.to_string(), params.text.clone());
let tokens = crate::tokeniser::lexer(&params.text); let tokens = crate::token::lexer(&params.text);
self.token_map.insert(params.uri.to_string(), tokens.clone()); self.token_map.insert(params.uri.to_string(), tokens.clone());
// Update the semantic tokens map. // Update the semantic tokens map.
@ -69,9 +69,7 @@ impl Backend {
continue; continue;
}; };
if token.token_type == crate::tokeniser::TokenType::Word if token.token_type == crate::token::TokenType::Word && self.stdlib_completions.contains_key(&token.value) {
&& self.stdlib_completions.contains_key(&token.value)
{
// This is a stdlib function. // This is a stdlib function.
token_type = SemanticTokenType::FUNCTION; token_type = SemanticTokenType::FUNCTION;
} }
@ -549,7 +547,7 @@ impl LanguageServer for Backend {
// Parse the ast. // Parse the ast.
// I don't know if we need to do this again since it should be updated in the context. // I don't know if we need to do this again since it should be updated in the context.
// But I figure better safe than sorry since this will write back out to the file. // But I figure better safe than sorry since this will write back out to the file.
let tokens = crate::tokeniser::lexer(&current_code); let tokens = crate::token::lexer(&current_code);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let Ok(ast) = parser.ast() else { let Ok(ast) = parser.ast() else {
return Ok(None); return Ok(None);
@ -581,7 +579,7 @@ impl LanguageServer for Backend {
// Parse the ast. // Parse the ast.
// I don't know if we need to do this again since it should be updated in the context. // I don't know if we need to do this again since it should be updated in the context.
// But I figure better safe than sorry since this will write back out to the file. // But I figure better safe than sorry since this will write back out to the file.
let tokens = crate::tokeniser::lexer(&current_code); let tokens = crate::token::lexer(&current_code);
let parser = crate::parser::Parser::new(tokens); let parser = crate::parser::Parser::new(tokens);
let Ok(mut ast) = parser.ast() else { let Ok(mut ast) = parser.ast() else {
return Ok(None); return Ok(None);

View File

@ -11,10 +11,10 @@ use crate::{
}; };
/// Extrudes by a given amount. /// Extrudes by a given amount.
pub fn extrude(args: &mut Args) -> Result<MemoryItem, KclError> { pub async fn extrude(args: Args) -> Result<MemoryItem, KclError> {
let (length, sketch_group) = args.get_number_sketch_group()?; let (length, sketch_group) = args.get_number_sketch_group()?;
let result = inner_extrude(length, sketch_group, args)?; let result = inner_extrude(length, sketch_group, args).await?;
Ok(MemoryItem::ExtrudeGroup(result)) Ok(MemoryItem::ExtrudeGroup(result))
} }
@ -23,7 +23,7 @@ pub fn extrude(args: &mut Args) -> Result<MemoryItem, KclError> {
#[stdlib { #[stdlib {
name = "extrude" name = "extrude"
}] }]
fn inner_extrude(length: f64, sketch_group: Box<SketchGroup>, args: &mut Args) -> Result<Box<ExtrudeGroup>, KclError> { async fn inner_extrude(length: f64, sketch_group: Box<SketchGroup>, args: Args) -> Result<Box<ExtrudeGroup>, KclError> {
let id = uuid::Uuid::new_v4(); let id = uuid::Uuid::new_v4();
let cmd = kittycad::types::ModelingCmd::Extrude { let cmd = kittycad::types::ModelingCmd::Extrude {
@ -31,7 +31,7 @@ fn inner_extrude(length: f64, sketch_group: Box<SketchGroup>, args: &mut Args) -
distance: length, distance: length,
cap: true, cap: true,
}; };
args.send_modeling_cmd(id, cmd)?; args.send_modeling_cmd(id, cmd).await?;
Ok(Box::new(ExtrudeGroup { Ok(Box::new(ExtrudeGroup {
id, id,
@ -46,7 +46,7 @@ fn inner_extrude(length: f64, sketch_group: Box<SketchGroup>, args: &mut Args) -
} }
/// Returns the extrude wall transform. /// Returns the extrude wall transform.
pub fn get_extrude_wall_transform(args: &mut Args) -> Result<MemoryItem, KclError> { pub async fn get_extrude_wall_transform(args: Args) -> Result<MemoryItem, KclError> {
let (surface_name, extrude_group) = args.get_path_name_extrude_group()?; let (surface_name, extrude_group) = args.get_path_name_extrude_group()?;
let result = inner_get_extrude_wall_transform(&surface_name, *extrude_group, args)?; let result = inner_get_extrude_wall_transform(&surface_name, *extrude_group, args)?;
Ok(MemoryItem::ExtrudeTransform(result)) Ok(MemoryItem::ExtrudeTransform(result))
@ -59,8 +59,8 @@ pub fn get_extrude_wall_transform(args: &mut Args) -> Result<MemoryItem, KclErro
fn inner_get_extrude_wall_transform( fn inner_get_extrude_wall_transform(
surface_name: &str, surface_name: &str,
extrude_group: ExtrudeGroup, extrude_group: ExtrudeGroup,
args: &mut Args, args: Args,
) -> Result<ExtrudeTransform, KclError> { ) -> Result<Box<ExtrudeTransform>, KclError> {
let surface = extrude_group.get_path_by_name(surface_name).ok_or_else(|| { let surface = extrude_group.get_path_by_name(surface_name).ok_or_else(|| {
KclError::Type(KclErrorDetails { KclError::Type(KclErrorDetails {
message: format!( message: format!(
@ -71,9 +71,9 @@ fn inner_get_extrude_wall_transform(
}) })
})?; })?;
Ok(ExtrudeTransform { Ok(Box::new(ExtrudeTransform {
position: surface.get_position(), position: surface.get_position(),
rotation: surface.get_rotation(), rotation: surface.get_rotation(),
meta: extrude_group.meta, meta: extrude_group.meta,
}) }))
} }

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