From 8ce921e39c3d4e947063f206347b2932cee456ec Mon Sep 17 00:00:00 2001 From: Dan Rose Date: Sun, 15 Sep 2024 03:35:47 -0500 Subject: [PATCH 1/9] Move wasm-pack to js dependency (#206) --- .github/workflows/publish.yml | 5 - .github/workflows/rust.yml | 4 - src/js-cli-and-library/package-lock.json | 247 ++++++++++++++++++++++- src/js-cli-and-library/package.json | 3 +- 4 files changed, 248 insertions(+), 11 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2d71d68a..193a1505 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -101,11 +101,6 @@ jobs: if: ${{ inputs.ezno-version != 'none' }} run: rustup target add wasm32-unknown-unknown - - uses: brndnmtthws/rust-action-cargo-binstall@v1 - if: ${{ inputs.ezno-version != 'none' }} - with: - packages: wasm-pack@0.13.0 - - name: Set NPM package version & build id: set-npm-version if: ${{ inputs.ezno-version != 'none' }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4d6882bb..9a2efc66 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -138,10 +138,6 @@ jobs: checker: - 'checker/**' - - uses: brndnmtthws/rust-action-cargo-binstall@v1 - if: steps.changes.outputs.src == 'true' || github.ref_name == 'main' - with: - packages: wasm-pack@0.13.0 - uses: denoland/setup-deno@v1 if: steps.changes.outputs.src == 'true' || github.ref_name == 'main' with: diff --git a/src/js-cli-and-library/package-lock.json b/src/js-cli-and-library/package-lock.json index 30053943..5c68a52a 100644 --- a/src/js-cli-and-library/package-lock.json +++ b/src/js-cli-and-library/package-lock.json @@ -12,7 +12,8 @@ "ezno": "dist/cli.mjs" }, "devDependencies": { - "unbuild": "^2.0.0" + "unbuild": "^2.0.0", + "wasm-pack": "^0.13.0" }, "funding": { "type": "individual", @@ -1023,12 +1024,35 @@ "postcss": "^8.1.0" } }, + "node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/binary-install": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-1.1.0.tgz", + "integrity": "sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==", + "dev": true, + "dependencies": { + "axios": "^0.26.1", + "rimraf": "^3.0.2", + "tar": "^6.1.11" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -1144,6 +1168,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/citty": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.5.tgz", @@ -1189,6 +1222,12 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, "node_modules/consola": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", @@ -1593,6 +1632,26 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -1620,6 +1679,36 @@ "node": ">=14.14" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1986,6 +2075,58 @@ "node": ">=10" } }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mkdist": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/mkdist/-/mkdist-1.4.0.tgz", @@ -2103,6 +2244,15 @@ "wrappy": "1" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -2685,6 +2835,65 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/rollup": { "version": "3.29.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", @@ -2847,6 +3056,29 @@ "url": "https://opencollective.com/svgo" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -2994,6 +3226,19 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/wasm-pack": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.13.0.tgz", + "integrity": "sha512-AmboGZEnZoIcVCzSlkLEmNFEqJN+IwgshJ5S7pi30uNUTce4LvWkifQzsQRxnWj47G8gkqZxlyGlyQplsnIS7w==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "binary-install": "^1.0.1" + }, + "bin": { + "wasm-pack": "run.js" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/src/js-cli-and-library/package.json b/src/js-cli-and-library/package.json index 7bf1426a..7a6f1067 100644 --- a/src/js-cli-and-library/package.json +++ b/src/js-cli-and-library/package.json @@ -74,6 +74,7 @@ } }, "devDependencies": { - "unbuild": "^2.0.0" + "unbuild": "^2.0.0", + "wasm-pack": "^0.13.0" } } From d67cd1b0b9f5c308a8d3f8ea04c5ccb8baa470c5 Mon Sep 17 00:00:00 2001 From: Sor4chi <80559385+sor4chi@users.noreply.github.com> Date: Tue, 5 Nov 2024 04:41:06 +0900 Subject: [PATCH 2/9] feat: support `Object.seal` and `Object.preventExtensions` (#213) * chore: add `Object.seal` and `Object.preventExtensions`'s type * chore: update the checker definition * feat: extends frozen of LocalInformation for `Object.seal` and `Object.preventExtensions` * test: add staging cases * chore: update the checker definition * feat: support `Object.isExtensible` * Update function names and slight fixes to better mirror JS behaviour --------- Co-authored-by: Ben --- checker/definitions/internal.ts.d.bin | Bin 35199 -> 35522 bytes checker/definitions/overrides.d.ts | 12 +++ checker/definitions/simple.d.ts | 12 +++ checker/specification/specification.md | 97 +++++++++++++++++---- checker/src/context/information.rs | 15 +++- checker/src/context/mod.rs | 7 +- checker/src/features/constant_functions.rs | 75 ++++++++++++++-- checker/src/types/properties/assignment.rs | 18 +++- checker/src/types/subtyping.rs | 4 +- 9 files changed, 208 insertions(+), 32 deletions(-) diff --git a/checker/definitions/internal.ts.d.bin b/checker/definitions/internal.ts.d.bin index 720f630996b27a26f323280d9680158b32adbe5f..c4f51fa0ba1d3479fd8d68f331a04a8444f46035 100644 GIT binary patch literal 35522 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 literal 35199 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 diff --git a/checker/definitions/overrides.d.ts b/checker/definitions/overrides.d.ts index 03781523..ad67900c 100644 --- a/checker/definitions/overrides.d.ts +++ b/checker/definitions/overrides.d.ts @@ -368,9 +368,21 @@ declare class Object { @Constant static freeze(on: object): object; + @Constant + static seal(on: object): object; + + @Constant + static preventExtensions(on: object): object; + @Constant static isFrozen(on: object): boolean; + @Constant + static isSealed(on: object): boolean; + + @Constant + static isExtensible(on: object): boolean; + // TODO defineProperties via body (not constant) @Constant static defineProperty(on: object, property: string, discriminator: PropertyDescriptor): boolean; diff --git a/checker/definitions/simple.d.ts b/checker/definitions/simple.d.ts index c1b03c9d..aba85ea2 100644 --- a/checker/definitions/simple.d.ts +++ b/checker/definitions/simple.d.ts @@ -371,9 +371,21 @@ declare class Object { @Constant static freeze(on: object): object; + @Constant + static seal(on: object): object; + + @Constant + static preventExtensions(on: object): object; + @Constant static isFrozen(on: object): boolean; + @Constant + static isSealed(on: object): boolean; + + @Constant + static isExtensible(on: object): boolean; + // TODO defineProperties via body (not constant) @Constant static defineProperty(on: object, property: string, discriminator: PropertyDescriptor): boolean; diff --git a/checker/specification/specification.md b/checker/specification/specification.md index f218a2b4..c571ba41 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -529,22 +529,6 @@ keys satisfies boolean - Expected boolean, found "nbd" -#### `Object.freeze` - -> TODO seal & preventExtensions - -```ts -const obj = {} -let result = Object.freeze(obj); -(obj === result) satisfies true; -obj.property = 2; -Object.isFrozen(obj) satisfies true; -``` - -> TODO maybe error should say that whole object is frozen - -- Cannot write to property 'property' - #### `Object.defineProperty` writable > TODO defineProperties @@ -634,7 +618,86 @@ obj satisfies string; ``` - Expected string, found { a: 1, b: 2, c: 3 } -s + +#### `Object.freeze` + +> When `Object.freeze` is called, the object's `isSealed` is inferred as `true` + +```ts +const obj = {} +let result = Object.freeze(obj); +(obj === result) satisfies true; +obj.property = 2; +Object.isSealed(obj) satisfies true; +``` + +- Cannot write to property 'property' + +#### `Object.seal` + +> When `Object.seal` is called, the object's `isFrozen` and `isSealed` are inferred as `true` + +```ts +const obj = { a: 2 } +let result = Object.seal(obj); +(obj === result) satisfies true; + +// Allowed +obj.a = 4; +// Not allowed +obj.property = 2; + +Object.isSealed(obj) satisfies true; +Object.isFrozen(obj) satisfies false; +``` + +- Cannot write to property 'property' + +#### `Object.preventExtensions` + +> When `Object.preventExtensions` is called, the object's `isFrozen` and `isSealed` are inferred as `true` + +```ts +const obj = { a: 2 } +let result = Object.preventExtensions(obj); +(obj === result) satisfies true; + +// Allowed +obj.a = 4; +// Not allowed +obj.property = 2; + +Object.isFrozen(obj) satisfies false; +Object.isSealed(obj) satisfies false; +``` + +- Cannot write to property 'property' + +#### `Object.isExtensible` + +> The object that has been applied `Object.seal`, `Object.freeze` and `Object.preventExtensions` returns `false` by `Object.isExtensible`, otherwise returns `true` + +```ts +{ + const obj = {} + Object.isExtensible(obj) satisfies true; + Object.preventExtensions(obj); + Object.isExtensible(obj) satisfies false; +} +{ + const obj = {} + Object.seal(obj); + Object.isExtensible(obj) satisfies false; +} +{ + const obj = {} + Object.freeze(obj); + Object.isExtensible(obj) satisfies 5; +} +``` + +- Expected 5, found false + ### Excess properties > The following work through the same mechanism as forward inference diff --git a/checker/src/context/information.rs b/checker/src/context/information.rs index e84835be..486587a4 100644 --- a/checker/src/context/information.rs +++ b/checker/src/context/information.rs @@ -1,5 +1,5 @@ use source_map::SpanWithSource; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use crate::{ events::{Event, RootReference}, @@ -32,8 +32,8 @@ pub struct LocalInformation { /// `ContextId` is a mini context pub(crate) closure_current_values: HashMap<(ClosureId, RootReference), TypeId>, - /// Not writeable, `TypeError: Cannot add property t, object is not extensible`. TODO conditional ? - pub(crate) frozen: HashSet, + /// Not writeable, `TypeError: Cannot add property, object is not extensible`. TODO conditional ? + pub(crate) frozen: HashMap, /// Object type (LHS), must always be RHS /// @@ -52,6 +52,13 @@ pub struct LocalInformation { pub(crate) value_of_this: ThisValue, } +#[derive(Debug, Clone, Copy, binary_serialize_derive::BinarySerializable)] +pub enum ObjectProtectionState { + Frozen, + Sealed, + NoExtensions, +} + #[derive(Debug, Default, binary_serialize_derive::BinarySerializable, Clone)] pub(crate) enum ReturnState { #[default] @@ -228,7 +235,7 @@ impl LocalInformation { .extend(other.current_properties.iter().map(|(l, r)| (*l, r.clone()))); self.closure_current_values .extend(other.closure_current_values.iter().map(|(l, r)| (l.clone(), *r))); - self.frozen.extend(other.frozen.iter().clone()); + self.frozen.extend(other.frozen.clone()); self.narrowed_values.extend(other.narrowed_values.iter().copied()); self.state = other.state.clone(); } diff --git a/checker/src/context/mod.rs b/checker/src/context/mod.rs index e8b11f28..25126536 100644 --- a/checker/src/context/mod.rs +++ b/checker/src/context/mod.rs @@ -7,6 +7,7 @@ pub mod information; pub mod invocation; mod root; +use information::ObjectProtectionState; pub(crate) use invocation::CallCheckingBehavior; pub use root::RootContext; @@ -518,7 +519,7 @@ impl Context { } /// TODO doesn't look at aliases using `get_type_fact`! - pub fn is_frozen(&self, value: TypeId) -> Option { + pub fn get_object_protection(&self, value: TypeId) -> Option { self.parents_iter().find_map(|ctx| get_on_ctx!(ctx.info.frozen.get(&value))).copied() } @@ -526,9 +527,9 @@ impl Context { // TODO should check the TypeId::is_primitive... via aliases + open_poly pub(crate) fn _is_immutable(&self, _value: TypeId) -> bool { todo!() - // let is_frozen = self.is_frozen(value); + // let get_object_protection = self.get_object_protection(value); - // if is_frozen == Some(TypeId::TRUE) { + // if get_object_protection == Some(TypeId::TRUE) { // true // } else if let Some( // Constant::Boolean(..) diff --git a/checker/src/features/constant_functions.rs b/checker/src/features/constant_functions.rs index 630f5a51..a7b18ccf 100644 --- a/checker/src/features/constant_functions.rs +++ b/checker/src/features/constant_functions.rs @@ -2,7 +2,11 @@ use iterator_endiate::EndiateIteratorExt; use source_map::SpanWithSource; use crate::{ - context::{get_on_ctx, information::InformationChain, invocation::CheckThings}, + context::{ + get_on_ctx, + information::{InformationChain, ObjectProtectionState}, + invocation::CheckThings, + }, events::printing::debug_effects, features::objects::{ObjectBuilder, Proxy}, types::{ @@ -310,7 +314,27 @@ pub(crate) fn call_constant_function( if let Some(on) = (arguments.len() == 1).then(|| arguments[0].non_spread_type().ok()).flatten() { - environment.info.frozen.insert(on); + environment.info.frozen.insert(on, ObjectProtectionState::Frozen); + Ok(ConstantOutput::Value(on)) + } else { + Err(ConstantFunctionError::CannotComputeConstant) + } + } + "seal" => { + if let Some(on) = + (arguments.len() == 1).then(|| arguments[0].non_spread_type().ok()).flatten() + { + environment.info.frozen.insert(on, ObjectProtectionState::Sealed); + Ok(ConstantOutput::Value(on)) + } else { + Err(ConstantFunctionError::CannotComputeConstant) + } + } + "preventExtensions" => { + if let Some(on) = + (arguments.len() == 1).then(|| arguments[0].non_spread_type().ok()).flatten() + { + environment.info.frozen.insert(on, ObjectProtectionState::NoExtensions); Ok(ConstantOutput::Value(on)) } else { Err(ConstantFunctionError::CannotComputeConstant) @@ -320,9 +344,50 @@ pub(crate) fn call_constant_function( if let Some(on) = (arguments.len() == 1).then(|| arguments[0].non_spread_type().ok()).flatten() { - let is_frozen = - environment.get_chain_of_info().any(|info| info.frozen.contains(&on)); - Ok(ConstantOutput::Value(if is_frozen { TypeId::TRUE } else { TypeId::FALSE })) + let object_protection = environment.get_object_protection(on); + let result = if matches!(object_protection, Some(ObjectProtectionState::Frozen)) { + TypeId::TRUE + } else { + // TODO test properties here + TypeId::FALSE + }; + Ok(ConstantOutput::Value(result)) + } else { + Err(ConstantFunctionError::CannotComputeConstant) + } + } + "isSealed" => { + if let Some(on) = + (arguments.len() == 1).then(|| arguments[0].non_spread_type().ok()).flatten() + { + let object_protection = environment.get_object_protection(on); + let result = if matches!( + object_protection, + Some(ObjectProtectionState::Frozen | ObjectProtectionState::Sealed) + ) { + TypeId::TRUE + } else { + // TODO test properties here + TypeId::FALSE + }; + Ok(ConstantOutput::Value(result)) + } else { + Err(ConstantFunctionError::CannotComputeConstant) + } + } + "isExtensible" => { + if let Some(on) = + (arguments.len() == 1).then(|| arguments[0].non_spread_type().ok()).flatten() + { + // Not this method returns an inverse result + let object_protection = environment.get_object_protection(on); + let result = if object_protection.is_some() { + TypeId::FALSE + } else { + TypeId::TRUE + // TODO test properties here + }; + Ok(ConstantOutput::Value(result)) } else { Err(ConstantFunctionError::CannotComputeConstant) } diff --git a/checker/src/types/properties/assignment.rs b/checker/src/types/properties/assignment.rs index c28315a1..31538e68 100644 --- a/checker/src/types/properties/assignment.rs +++ b/checker/src/types/properties/assignment.rs @@ -1,7 +1,7 @@ use super::{get_property_unbound, Descriptor, PropertyKey, PropertyValue, Publicity}; use crate::{ - context::CallCheckingBehavior, + context::{information::ObjectProtectionState, CallCheckingBehavior}, diagnostics::{PropertyKeyRepresentation, TypeStringRepresentation}, events::Event, features::objects::Proxy, @@ -59,8 +59,11 @@ pub fn set_property( types: &mut TypeStore, ) -> SetPropertyResult { // Frozen checks + let object_protection = environment.get_object_protection(on); + { - if environment.info.frozen.contains(&on) { + if let Some(ObjectProtectionState::Frozen) = object_protection { + // FUTURE this could have a separate error? return Err(SetPropertyError::NotWriteable { property: PropertyKeyRepresentation::new(under, environment, types), position, @@ -266,6 +269,17 @@ pub fn set_property( position, }) } else { + // Sealed & no extensions check for NEW property (frozen case covered above) + { + if object_protection.is_some() { + // FUTURE this could have a separate error? + return Err(SetPropertyError::NotWriteable { + property: PropertyKeyRepresentation::new(under, environment, types), + position, + }); + } + } + crate::utilities::notify!("No property on object, assigning anyway"); let info = behavior.get_latest_info(environment); info.register_property( diff --git a/checker/src/types/subtyping.rs b/checker/src/types/subtyping.rs index eb274d06..b1ae45e0 100644 --- a/checker/src/types/subtyping.rs +++ b/checker/src/types/subtyping.rs @@ -764,7 +764,9 @@ pub(crate) fn type_is_subtype_with_generics( information, types, ) - } else if information.get_chain_of_info().any(|info| info.frozen.contains(&ty)) + } else if information + .get_chain_of_info() + .any(|info| info.frozen.contains_key(&ty)) || matches!(subtype, Type::Constant(_)) || matches!( ty, From ae5de5dd617ab9717469ccea00d1814a6bb96566 Mon Sep 17 00:00:00 2001 From: Thomas Beer <71586988+Tommypop2@users.noreply.github.com> Date: Sun, 10 Nov 2024 16:07:22 +0000 Subject: [PATCH 3/9] Add watch mode for check command (#202) * feat: add watch mode support * ignore WASM for now --------- Co-authored-by: Ben --- Cargo.lock | 69 ++++++++++++++++ Cargo.toml | 3 +- checker/src/options.rs | 1 + src/cli.rs | 175 +++++++++++++++++++++++++++++------------ src/lib.rs | 18 +++++ src/utilities.rs | 2 +- 6 files changed, 216 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b461f75b..fcfcbdfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -296,6 +296,7 @@ dependencies = [ "multiline-term-input", "native-tls", "notify", + "notify-debouncer-full", "pretty_assertions", "self-replace", "serde", @@ -398,6 +399,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +[[package]] +name = "file-id" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6584280525fb2059cba3db2c04abf947a1a29a45ddae89f3870f8281704fafc9" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "filetime" version = "0.2.25" @@ -586,6 +596,16 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.22" @@ -682,6 +702,20 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "notify-debouncer-full" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154" +dependencies = [ + "crossbeam-channel", + "file-id", + "log", + "notify", + "parking_lot", + "walkdir", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -750,6 +784,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + [[package]] name = "paste" version = "1.0.15" @@ -873,6 +930,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "security-framework" version = "2.11.1" @@ -993,6 +1056,12 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "837c5f8866aeabd3ac8addcf50ef1e2779fe8c2a8d74bdd97102bdfe5605c629" +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "source-map" version = "0.15.0" diff --git a/Cargo.toml b/Cargo.toml index e3a30122..8e1d21e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,6 @@ base64 = "0.21" console = "0.15" codespan-reporting = "0.11" enum-variants-strings = "0.3" -glob = "0.3" # For `StrComparison` for string comparison pretty_assertions = "1.3.0" serde = { version = "1.0", features = ["derive"] } @@ -59,6 +58,8 @@ native-tls = "0.2.11" multiline-term-input = "0.1.0" # For watching files notify = "6.1.0" +notify-debouncer-full = "0.3.1" +glob = "0.3" [dependencies.checker] path = "./checker" diff --git a/checker/src/options.rs b/checker/src/options.rs index 00a39872..5fd2919c 100644 --- a/checker/src/options.rs +++ b/checker/src/options.rs @@ -3,6 +3,7 @@ #[cfg_attr(feature = "serde-serialize", derive(serde::Deserialize), serde(default))] #[cfg_attr(target_family = "wasm", derive(tsify::Tsify))] #[allow(clippy::struct_excessive_bools)] +#[derive(Clone)] pub struct TypeCheckOptions { /// Parameters cannot be reassigned pub constant_parameters: bool, diff --git a/src/cli.rs b/src/cli.rs index d0118798..a86bc4db 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,7 +5,7 @@ use std::{ path::{Path, PathBuf}, process::Command, process::ExitCode, - time::Instant, + time::{Duration, Instant}, }; use crate::{ @@ -112,9 +112,9 @@ pub(crate) struct CheckArguments { /// paths to definition files #[argh(option, short = 'd')] pub definition_file: Option, - /// whether to re-check on file changes TODO #164 + /// whether to re-check on file changes #[argh(switch)] - pub _watch: bool, + pub watch: bool, /// whether to display check time #[argh(switch)] pub timings: bool, @@ -173,6 +173,68 @@ fn file_system_resolver(path: &Path) -> Option { } } +fn run_checker( + entry_points: Vec, + read_file: &T, + timings: bool, + definition_file: Option, + max_diagnostics: MaxDiagnostics, + type_check_options: TypeCheckOptions, + compact_diagnostics: bool, +) -> ExitCode { + let CheckOutput { diagnostics, module_contents, chronometer, types, .. } = + check(entry_points, read_file, definition_file.as_deref(), type_check_options); + + let diagnostics_count = diagnostics.count(); + let current = timings.then(std::time::Instant::now); + + let result = if diagnostics.has_error() { + if let MaxDiagnostics::FixedTo(0) = max_diagnostics { + let count = diagnostics.into_iter().count(); + print_to_cli(format_args!( + "Found {count} type errors and warnings {}", + console::Emoji(" 😬", ":/") + )) + } else { + report_diagnostics_to_cli( + diagnostics, + &module_contents, + compact_diagnostics, + max_diagnostics, + ) + .unwrap(); + } + ExitCode::FAILURE + } else { + // May be warnings or information here + report_diagnostics_to_cli( + diagnostics, + &module_contents, + compact_diagnostics, + max_diagnostics, + ) + .unwrap(); + print_to_cli(format_args!("No type errors found {}", console::Emoji("🎉", ":)"))); + ExitCode::SUCCESS + }; + + #[cfg(not(target_family = "wasm"))] + if timings { + let reporting = current.unwrap().elapsed(); + eprintln!("---\n"); + eprintln!("Diagnostics:\t{}", diagnostics_count); + eprintln!("Types: \t{}", types.count_of_types()); + eprintln!("Lines: \t{}", chronometer.lines); + eprintln!("Cache read: \t{:?}", chronometer.cached); + eprintln!("FS read: \t{:?}", chronometer.fs); + eprintln!("Parsed in: \t{:?}", chronometer.parse); + eprintln!("Checked in: \t{:?}", chronometer.check); + eprintln!("Reporting: \t{:?}", reporting); + } + + result +} + pub fn run_cli( cli_arguments: &[&str], read_file: &T, @@ -195,8 +257,7 @@ pub fn run_cli { let CheckArguments { input, - // TODO #164 - _watch, + watch, definition_file, timings, compact_diagnostics, @@ -215,57 +276,64 @@ pub fn run_cli return ExitCode::FAILURE, }; - let CheckOutput { diagnostics, module_contents, chronometer, types, .. } = - check(entry_points, read_file, definition_file.as_deref(), type_check_options); + if watch { + #[cfg(target_family = "wasm")] + panic!("'watch' mode not supported on WASM"); - let diagnostics_count = diagnostics.count(); - let current = timings.then(std::time::Instant::now); + #[cfg(not(target_family = "wasm"))] + { + use notify::Watcher; + use notify_debouncer_full::new_debouncer; - let result = if diagnostics.has_error() { - if let MaxDiagnostics::FixedTo(0) = max_diagnostics { - let count = diagnostics.into_iter().count(); - print_to_cli(format_args!( - "Found {count} type errors and warnings {}", - console::Emoji(" 😬", ":/") - )) - } else { - report_diagnostics_to_cli( - diagnostics, - &module_contents, + let (tx, rx) = std::sync::mpsc::channel(); + let mut debouncer = + new_debouncer(Duration::from_millis(200), None, tx).unwrap(); + + for e in &entry_points { + _ = debouncer.watcher().watch(e, notify::RecursiveMode::Recursive).unwrap(); + } + + // Run once + run_checker( + entry_points.clone(), + read_file, + timings, + definition_file.clone(), + max_diagnostics.clone(), + type_check_options.clone(), compact_diagnostics, - max_diagnostics, - ) - .unwrap(); + ); + + for res in rx { + match res { + Ok(_e) => { + run_checker( + entry_points.clone(), + read_file, + timings, + definition_file.clone(), + max_diagnostics.clone(), + type_check_options.clone(), + compact_diagnostics, + ); + } + Err(error) => eprintln!("Error: {error:?}"), + } + } + // Infinite loop here so the compiler is satisfied that this never returns + loop {} } - ExitCode::FAILURE - } else { - // May be warnings or information here - report_diagnostics_to_cli( - diagnostics, - &module_contents, - compact_diagnostics, - max_diagnostics, - ) - .unwrap(); - print_to_cli(format_args!("No type errors found {}", console::Emoji("🎉", ":)"))); - ExitCode::SUCCESS - }; - - #[cfg(not(target_family = "wasm"))] - if timings { - let reporting = current.unwrap().elapsed(); - eprintln!("---\n"); - eprintln!("Diagnostics:\t{}", diagnostics_count); - eprintln!("Types: \t{}", types.count_of_types()); - eprintln!("Lines: \t{}", chronometer.lines); - eprintln!("Cache read: \t{:?}", chronometer.cached); - eprintln!("FS read: \t{:?}", chronometer.fs); - eprintln!("Parsed in: \t{:?}", chronometer.parse); - eprintln!("Checked in: \t{:?}", chronometer.check); - eprintln!("Reporting: \t{:?}", reporting); } - result + run_checker( + entry_points, + read_file, + timings, + definition_file, + max_diagnostics, + type_check_options, + compact_diagnostics, + ) } CompilerSubCommand::Experimental(ExperimentalArguments { nested: ExperimentalSubcommand::Build(build_config), @@ -454,6 +522,13 @@ pub fn run_cli Result, ()> { + Ok(vec![input.into()]) +} + +#[cfg(not(target_family = "wasm"))] fn get_entry_points(input: String) -> Result, ()> { match glob::glob(&input) { Ok(files) => { diff --git a/src/lib.rs b/src/lib.rs index a81c0908..e5b7a08e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,10 +21,18 @@ pub fn prettifier(input: String) -> Result { Ok(module.to_string(&ToStringOptions::default())) } +#[cfg(target_family = "wasm")] pub trait ReadFromFS { fn get_content_at_path(&self, path: &std::path::Path) -> Option; } +// Not WASM require `Sync + Send` for watch mode +#[cfg(not(target_family = "wasm"))] +pub trait ReadFromFS: Sync + Send { + fn get_content_at_path(&self, path: &std::path::Path) -> Option; +} + +#[cfg(target_family = "wasm")] impl ReadFromFS for T where T: Fn(&std::path::Path) -> Option, @@ -34,6 +42,16 @@ where } } +#[cfg(not(target_family = "wasm"))] +impl ReadFromFS for T +where + T: Fn(&std::path::Path) -> Option + Sync + Send, +{ + fn get_content_at_path(&self, path: &std::path::Path) -> Option { + (self)(path) + } +} + /// prompt -> response pub trait CLIInputResolver: Fn(&str) -> Option {} diff --git a/src/utilities.rs b/src/utilities.rs index c99aa8e5..5a3165bc 100644 --- a/src/utilities.rs +++ b/src/utilities.rs @@ -89,7 +89,7 @@ pub(crate) fn print_to_cli_without_newline(arguments: Arguments) { io::Write::flush(&mut io::stdout()).unwrap(); } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub(crate) enum MaxDiagnostics { All, FixedTo(u16), From d0a21ba6b4ceb10299c9642c49ae2afc34e29faf Mon Sep 17 00:00:00 2001 From: Sor4chi <80559385+sor4chi@users.noreply.github.com> Date: Mon, 11 Nov 2024 01:28:25 +0900 Subject: [PATCH 4/9] fix: synthesize shape in sub-environment scope (#216) Fixes #196 --------- Co-authored-by: Ben --- checker/specification/specification.md | 5 + checker/src/synthesis/functions.rs | 168 ++++++++++++++----------- 2 files changed, 98 insertions(+), 75 deletions(-) diff --git a/checker/specification/specification.md b/checker/specification/specification.md index c571ba41..b1c6abb7 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -3363,8 +3363,13 @@ type X = string; type X = number; const a: X = "hello world"; } + +function func() {} + +type B = YEA; ``` +- Could not find type 'YEA' - Type "hello world" is not assignable to type X #### Type has no generics diff --git a/checker/src/synthesis/functions.rs b/checker/src/synthesis/functions.rs index b0e47b11..9f628135 100644 --- a/checker/src/synthesis/functions.rs +++ b/checker/src/synthesis/functions.rs @@ -636,86 +636,104 @@ pub(super) fn synthesise_shape( environment: &mut Environment, checking_data: &mut CheckingData, ) -> crate::features::functions::PartialFunction { - let type_parameters = function.type_parameters.as_ref().map(|type_parameters| { - super::functions::synthesise_type_parameters(type_parameters, environment, checking_data) - }); - - let parameters = function - .parameters - .parameters - .iter() - .map(|parameter| { - let parameter_constraint = - parameter.type_annotation.as_ref().map_or(TypeId::ANY_TYPE, |ta| { - synthesise_type_annotation(ta, environment, checking_data) + environment + .new_lexical_environment_fold_into_parent( + Scope::FunctionAnnotation {}, + checking_data, + |environment, checking_data| { + let type_parameters = function.type_parameters.as_ref().map(|type_parameters| { + super::functions::synthesise_type_parameters( + type_parameters, + environment, + checking_data, + ) }); - // TODO I think this is correct - let is_optional = parameter.additionally.is_some(); - let ty = if is_optional { - checking_data.types.new_or_type(parameter_constraint, TypeId::UNDEFINED_TYPE) - } else { - parameter_constraint - }; - - SynthesisedParameter { - name: variable_field_to_string(parameter.name.get_ast_ref()), - is_optional, - ty, - position: parameter.position.with_source(environment.get_source()), - } - }) - .collect(); - - let rest_parameter = function.parameters.rest_parameter.as_ref().map(|rest_parameter| { - let parameter_constraint = - rest_parameter.type_annotation.as_ref().map_or(TypeId::ANY_TYPE, |annotation| { - synthesise_type_annotation(annotation, environment, checking_data) - }); - - let item_type = if let TypeId::ERROR_TYPE = parameter_constraint { - TypeId::ERROR_TYPE - } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::ARRAY_TYPE, - arguments, - }) = checking_data.types.get_type_by_id(parameter_constraint) - { - if let Some(item) = arguments.get_structure_restriction(TypeId::T_TYPE) { - item - } else { - unreachable!() - } - } else { - crate::utilities::notify!("rest parameter should be array error"); - // checking_data.diagnostics_container.add_error( - // TypeCheckError::RestParameterAnnotationShouldBeArrayType(rest_parameter.get), - // ); - TypeId::ERROR_TYPE - }; - - let name = variable_field_to_string(&rest_parameter.name); + let parameters = function + .parameters + .parameters + .iter() + .map(|parameter| { + let parameter_constraint = + parameter.type_annotation.as_ref().map_or(TypeId::ANY_TYPE, |ta| { + synthesise_type_annotation(ta, environment, checking_data) + }); + + // TODO I think this is correct + let is_optional = parameter.additionally.is_some(); + let ty = if is_optional { + checking_data + .types + .new_or_type(parameter_constraint, TypeId::UNDEFINED_TYPE) + } else { + parameter_constraint + }; + + SynthesisedParameter { + name: variable_field_to_string(parameter.name.get_ast_ref()), + is_optional, + ty, + position: parameter.position.with_source(environment.get_source()), + } + }) + .collect(); + + let rest_parameter = + function.parameters.rest_parameter.as_ref().map(|rest_parameter| { + let parameter_constraint = rest_parameter.type_annotation.as_ref().map_or( + TypeId::ANY_TYPE, + |annotation| { + synthesise_type_annotation(annotation, environment, checking_data) + }, + ); + + let item_type = if let TypeId::ERROR_TYPE = parameter_constraint { + TypeId::ERROR_TYPE + } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::ARRAY_TYPE, + arguments, + }) = checking_data.types.get_type_by_id(parameter_constraint) + { + if let Some(item) = arguments.get_structure_restriction(TypeId::T_TYPE) + { + item + } else { + unreachable!() + } + } else { + crate::utilities::notify!("rest parameter should be array error"); + // checking_data.diagnostics_container.add_error( + // TypeCheckError::RestParameterAnnotationShouldBeArrayType(rest_parameter.get), + // ); + TypeId::ERROR_TYPE + }; + + let name = variable_field_to_string(&rest_parameter.name); + + SynthesisedRestParameter { + item_type, + // This will be overridden when actual synthesis + ty: parameter_constraint, + name, + position: rest_parameter.position.with_source(environment.get_source()), + } + }); - SynthesisedRestParameter { - item_type, - // This will be overridden when actual synthesis - ty: parameter_constraint, - name, - position: rest_parameter.position.with_source(environment.get_source()), - } - }); + let return_type = function.return_type.as_ref().map(|annotation| { + ReturnType( + synthesise_type_annotation(annotation, environment, checking_data), + annotation.get_position().with_source(environment.get_source()), + ) + }); - let return_type = function.return_type.as_ref().map(|annotation| { - ReturnType( - synthesise_type_annotation(annotation, environment, checking_data), - annotation.get_position().with_source(environment.get_source()), + crate::features::functions::PartialFunction( + type_parameters, + SynthesisedParameters { parameters, rest_parameter }, + return_type, + ) + }, ) - }); - - crate::features::functions::PartialFunction( - type_parameters, - SynthesisedParameters { parameters, rest_parameter }, - return_type, - ) + .0 } /// TODO WIP From 9088496e28796fa0a4a8c938ee261c6611e695bb Mon Sep 17 00:00:00 2001 From: Sor4chi <80559385+sor4chi@users.noreply.github.com> Date: Mon, 11 Nov 2024 01:44:33 +0900 Subject: [PATCH 5/9] feat: enhance template literal type printing (#218) * feat: implement template literal type printing --------- Co-authored-by: Ben --- checker/specification/specification.md | 10 +++++-- checker/src/types/printing.rs | 38 +++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/checker/specification/specification.md b/checker/specification/specification.md index b1c6abb7..60dbc932 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -2441,7 +2441,7 @@ fakeRead(array1) #### Always known math ```ts -// True regardless of +// True regardless of function func(a: number) { return a ** 0 } func satisfies string; @@ -3694,7 +3694,7 @@ type Name = "Ben" > Should be `Expected "Hello Ben", found "test"`. See #188 -- Expected string, found "test" +- Expected `Hello ${Name}`, found \"test\" #### Template literal type specialisation @@ -3751,14 +3751,18 @@ interface X { #### Template literal types +> Last one tests printing + ```ts type Introduction = `Hello ${string}`; const first: Introduction = "Hello Ben"; const second: Introduction = "Hi Ben"; +const third: `Hiya ${string}` = "Hello Ben"; ``` - Type "Hi Ben" is not assignable to type Introduction +- Type "Hello Ben" is not assignable to type `Hiya ${string}` #### Assigning to types as keys @@ -4095,7 +4099,7 @@ x.property_a satisfies number; x.property_b ``` -- No property 'property_b' on { [string]: X[keyof X & string] } +- No property 'property_b' on { [`property_${keyof X & string}`]: X[keyof X & string] } ### Readonly and `as const` diff --git a/checker/src/types/printing.rs b/checker/src/types/printing.rs index a9688428..63cf693a 100644 --- a/checker/src/types/printing.rs +++ b/checker/src/types/printing.rs @@ -57,6 +57,24 @@ pub fn print_type_with_type_arguments( buf } +pub fn print_inner_template_literal_type_into_buf( + ty: TypeId, + buf: &mut String, + cycles: &mut HashSet, + args: GenericChain, + types: &TypeStore, + info: &C, + debug: bool, +) { + if let Type::Constant(cst) = types.get_type_by_id(ty) { + buf.push_str(&cst.as_js_string()); + } else { + buf.push_str("${"); + print_type_into_buf(ty, buf, cycles, args, types, info, debug); + buf.push('}'); + } +} + /// Recursion safe + reuses buffer pub fn print_type_into_buf( ty: TypeId, @@ -469,7 +487,25 @@ pub fn print_type_into_buf( unreachable!() } }, - _constructor => { + constructor => { + if let Constructor::BinaryOperator { result: result_ty, lhs, rhs, .. } = constructor + { + if *result_ty != TypeId::NUMBER_TYPE + && !matches!( + types.get_type_by_id(*result_ty), + Type::PartiallyAppliedGenerics(_) | Type::RootPolyType(_) + ) { + buf.push('`'); + print_inner_template_literal_type_into_buf( + *lhs, buf, cycles, args, types, info, debug, + ); + print_inner_template_literal_type_into_buf( + *rhs, buf, cycles, args, types, info, debug, + ); + buf.push('`'); + return; + } + } let base = get_constraint(ty, types).unwrap(); print_type_into_buf(base, buf, cycles, args, types, info, debug); } From bba226c346c250ca3649486e778dd39a76825280 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 13 Nov 2024 11:44:09 +0000 Subject: [PATCH 6/9] Improve CI setup + other fixes (#214) * CI Improvements * Parser formatting * Update crates & library * Improvements to CLI and build command * Extract REPL code * Add JS tests * Update description of project * Add options to specification * Add `duplicate_block` example * Improvements * Fuzzing only on final or labelled * Sponsors in info fix --- .github/workflows/clippy-rustfmt-fix.yml | 18 +- .github/workflows/github-release.yml | 69 +- .github/workflows/performance-and-size.yml | 123 +- .github/workflows/publish.yml | 43 +- .github/workflows/rust.yml | 42 +- Cargo.lock | 154 +- Cargo.toml | 3 +- README.md | 3 +- checker/Cargo.toml | 2 +- checker/examples/run_checker.rs | 2 +- .../fuzz/fuzz_targets/check_project_naive.rs | 2 +- checker/specification/build.rs | 94 +- checker/specification/specification.md | 12 - checker/specification/test.rs | 17 +- checker/src/context/invocation.rs | 22 +- checker/src/diagnostics.rs | 12 +- checker/src/features/regexp.rs | 9 +- checker/src/lib.rs | 8 +- checker/src/synthesis/interactive.rs | 90 + checker/src/synthesis/interfaces.rs | 10 +- checker/src/synthesis/mod.rs | 94 +- checker/tests/partial_source.rs | 2 +- checker/tests/suggestions.rs | 2 +- checker/tests/type_mappings.rs | 2 +- parser/examples/duplicate_block.rs | 135 ++ parser/examples/parse.rs | 108 +- parser/examples/simple.rs | 9 + parser/src/block.rs | 8 +- parser/src/declarations/mod.rs | 9 +- parser/src/extensions/jsx.rs | 4 +- parser/src/functions/mod.rs | 33 +- parser/src/lexer.rs | 20 +- parser/src/lib.rs | 6 +- parser/src/modules.rs | 3 +- parser/src/tokens.rs | 15 +- parser/src/types/type_annotations.rs | 3 +- src/ast_explorer.rs | 72 +- src/build.rs | 193 +- src/check.rs | 12 +- src/cli.rs | 133 +- src/js-based-plugin/index.mjs | 7 +- src/js-cli-and-library/package-lock.json | 1867 ++++++++++------- src/js-cli-and-library/package.json | 14 +- src/js-cli-and-library/src/cli.js | 40 +- src/js-cli-and-library/test.mjs | 38 - src/js-cli-and-library/tests/cli.test.mjs | 64 + src/js-cli-and-library/tests/library.test.mjs | 66 + src/lib.rs | 39 +- src/main.rs | 24 +- src/repl.rs | 247 ++- src/reporting.rs | 18 +- src/utilities.rs | 120 +- src/wasm_bindings.rs | 173 +- 53 files changed, 2626 insertions(+), 1689 deletions(-) create mode 100644 checker/src/synthesis/interactive.rs create mode 100644 parser/examples/duplicate_block.rs create mode 100644 parser/examples/simple.rs delete mode 100644 src/js-cli-and-library/test.mjs create mode 100644 src/js-cli-and-library/tests/cli.test.mjs create mode 100644 src/js-cli-and-library/tests/library.test.mjs diff --git a/.github/workflows/clippy-rustfmt-fix.yml b/.github/workflows/clippy-rustfmt-fix.yml index 620863af..fc2cfd76 100644 --- a/.github/workflows/clippy-rustfmt-fix.yml +++ b/.github/workflows/clippy-rustfmt-fix.yml @@ -12,7 +12,7 @@ env: target/ jobs: - publish: + run-and-commit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -23,15 +23,19 @@ jobs: - name: Run automated fixes run: | - cargo clippy --fix + # Run clippy on projects + cargo clippy --fix --manifest-path ./parser/Cargo.toml --allow-dirty + cargo clippy --fix --manifest-path ./checker/Cargo.toml --allow-dirty + cargo clippy --fix --allow-dirty + + # Format cargo fmt - - name: Commit + - name: Commit changes run: | - git add . - git commit -m "Run clippy --fix & formatting" - git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - + + git add . + git commit -m "Run clippy --fix & cargo fmt" git push diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 86484f2c..f2fd325a 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -21,7 +21,8 @@ jobs: runs-on: ubuntu-latest outputs: - new-ezno-version: ${{ steps.get-version.outputs.new-ezno-version }} + LATEST_EZNO_VERSION: ${{ steps.get-version.outputs.LATEST_EZNO_VERSION }} + LATEST_EZNO_VERSION_DASH: ${{ steps.get-version.outputs.LATEST_EZNO_VERSION_DASH }} SPONSORS: ${{ steps.get-sponsors-and-contributors.outputs.SPONSORS }} CONTRIBUTORS: ${{ steps.get-sponsors-and-contributors.outputs.CONTRIBUTORS }} @@ -38,43 +39,32 @@ jobs: git for-each-ref --sort=creatordate --format '%(refname:short)' 'refs/tags/release/ezno-[0-9]*' echo "::endgroup::" - TAG=$(git for-each-ref --sort=creatordate --format '%(refname:short)' 'refs/tags/release/ezno-[0-9]*' | tail -n 1) - echo "Building GH release for ${TAG:13}" - echo "new-ezno-version=${TAG:13}" >> $GITHUB_OUTPUT + TAG=$(git for-each-ref --sort=creatordate --format '%(refname:short)' 'refs/tags/release/ezno-[0-9]*' | tail -n 1 | cut -c 14-) - # Replace '.' with '-' - NAME_VERSION=$(echo $VERSION | sed -e "s/\./-/g") + echo "::notice::Releasing with found version $TAG" + echo "LATEST_EZNO_VERSION=${TAG}" >> "$GITHUB_OUTPUT" + echo "LATEST_EZNO_VERSION_DASH=${TAG//./-}" >> "$GITHUB_OUTPUT" else - VERSION="${{ inputs.ezno-version }}" - echo "Building GH release for ${VERSION}" - echo "new-ezno-version=${VERSION}" >> $GITHUB_OUTPUT + TAG="${{ inputs.ezno-version }}" - # Replace '.' with '-' - NAME_VERSION=$(echo $VERSION | sed -e "s/\./-/g") + echo "::notice::Releasing with specific version $TAG" + echo "LATEST_EZNO_VERSION=${TAG}" >> "$GITHUB_OUTPUT" + echo "LATEST_EZNO_VERSION_DASH=${TAG//./-}" >> "$GITHUB_OUTPUT" fi - - id: get-sponsors-and-contributors + - name: Get sponsors and contributors + id: get-sponsors-and-contributors run: | - SPONSORS=$(gh api graphql -f query='{ - user(login: "kaleidawave") { - sponsorshipsAsMaintainer(first: 100, activeOnly: false) { - edges { - node { - sponsor { - name, login - } - } - } - } - } - }' -q '.data.user.sponsorshipsAsMaintainer.edges | map(.node.sponsor.name // .node.sponsor.login) | join(",")') - - CONTRIBUTORS=$( - gh pr list --state merged --json author | jq 'map(.author.name // .author.login) | unique | join(",")' --raw-output - ) + SQP='.data.user.sponsorshipsAsMaintainer.edges | map(.node.sponsor.name // .node.sponsor.login) | join(",")' + GQL_SQ='{ user(login: "kaleidawave") { sponsorshipsAsMaintainer(first: 100, activeOnly: false) { edges { node { sponsor { name, login } } } } } }' + SPONSORS=$(gh api graphql -f query="$GQL_SQ" -q "$SQP") + + CQP='map(.author.name // .author.login) | unique | join(",")' + CONTRIBUTORS=$(gh pr list --state merged --json author | jq "$CQP" --raw-output) - echo "SPONSORS=$SPONSORS" # >> $GITHUB_OUTPUT - echo "CONTRIBUTORS=$CONTRIBUTORS" + echo "SPONSORS=$SPONSORS" >> "$GITHUB_OUTPUT" + echo "CONTRIBUTORS=$CONTRIBUTORS" >> "$GITHUB_OUTPUT" + echo "::notice::CONTRIBUTORS=$CONTRIBUTORS and SPONSORS=$SPONSORS" shell: bash env: @@ -88,13 +78,16 @@ jobs: os: [ubuntu-latest, windows-latest] include: - os: windows-latest - executable-extension: .exe - platform_name: x86_64-pc-windows + platform-name: x86_64-pc-windows + executable-extension: ".exe" - os: ubuntu-latest - platform_name: x86_64-unknown-linux + platform-name: x86_64-unknown-linux runs-on: ${{ matrix.os }} + env: + LEVEL: release + # Important that everything here works in all the above OSes! steps: - uses: actions/checkout@v4 @@ -110,10 +103,10 @@ jobs: SPONSORS: ${{ needs.get-build-info.outputs.SPONSORS }} CONTRIBUTORS: ${{ needs.get-build-info.outputs.CONTRIBUTORS }} - - name: Rename and move release assets + - name: Rename and move ${{ env.LEVEL }} assets run: | mkdir artifacts - mv target/release/ezno${{ matrix.executable-extension }} "artifacts/ezno-${{ needs.get-build-info.outputs.new-ezno-version }}-${{ matrix.platform_name }}${{ matrix.executable-extension }}" + mv "target/${{ env.LEVEL }}/ezno${{ matrix.executable-extension }}" "artifacts/ezno-${{ needs.get-build-info.outputs.LATEST_EZNO_VERSION_DASH }}-${{ matrix.platform-name }}${{ matrix.executable-extension }}" - uses: actions/upload-artifact@v4 with: @@ -144,8 +137,8 @@ jobs: - name: GitHub release uses: softprops/action-gh-release@v1 with: - name: "Ezno ${{ needs.get-build-info.outputs.new-ezno-version }}" - tag_name: "release/ezno-${{ needs.get-build-info.outputs.new-ezno-version }}" + name: "Ezno ${{ needs.get-build-info.outputs.LATEST_EZNO_VERSION }}" + tag_name: "release/ezno-${{ needs.get-build-info.outputs.LATEST_EZNO_VERSION }}" body: "For @kaleidawave to update" files: | README.md diff --git a/.github/workflows/performance-and-size.yml b/.github/workflows/performance-and-size.yml index e58e86c5..6d5dcdb6 100644 --- a/.github/workflows/performance-and-size.yml +++ b/.github/workflows/performance-and-size.yml @@ -28,21 +28,47 @@ jobs: - uses: brndnmtthws/rust-action-cargo-binstall@v1 with: packages: hyperfine + + - name: Install valgrind + run: sudo apt-get install valgrind - name: Build Ezno run: cargo build --release env: CARGO_PROFILE_RELEASE_DEBUG: true + - name: Get base ezno + if: github.ref_name != 'main' + uses: actions/download-artifact@v4 + continue-on-error: true + with: + name: latest-checker + path: previous-ezno + + - name: Set compilers + id: compilers + shell: bash + run: | + if [ -d "previous-ezno" ]; then + echo "::notice::Comparing against previous" + echo "BINARIES=./target/release/ezno,./previous-ezno/ezno" >> "$GITHUB_OUTPUT" + else + echo "::notice::Running singularly" + echo "BINARIES=./target/release/ezno" >> "$GITHUB_OUTPUT" + fi + - name: Run checker performance shell: bash run: | # Generate a file which contains everything that Ezno currently implements - cargo run -p ezno-parser --example code_blocks_to_script ./checker/specification/specification.md --comment-headers --out ./demo.tsx + cargo run -p ezno-parser \ + --example code_blocks_to_script ./checker/specification/specification.md \ + --comment-headers \ + --out ./demo.tsx echo "### Checking \`\`\`shell - $(hyperfine -i './target/release/ezno check demo.tsx') + $(hyperfine -i -L compiler ${{ steps.compilers.outputs.BINARIES }} '{compiler} check demo.tsx') \`\`\`" >> $GITHUB_STEP_SUMMARY echo "

@@ -58,8 +84,8 @@ jobs: echo "::info::Wrote code to summary" command_output=$(./target/release/ezno check demo.tsx --timings --max-diagnostics all 2>&1 || true) + diagnostics=""; statistics=""; found_splitter=false; - while IFS= read -r line; do if [[ "$line" == "---"* ]]; then found_splitter=true; elif [[ "$found_splitter" == false ]]; then diagnostics+="$line"$'\n'; @@ -73,16 +99,35 @@ jobs: $diagnostics \`\`\`
- -
- Statistics - - \`\`\` - $statistics - \`\`\` -
" >> $GITHUB_STEP_SUMMARY + if [ -d "previous-ezno" ]; then + OUT=$(./previous-ezno/ezno check demo.tsx --timings --max-diagnostics all 2>&1 || true) + $base_statistics=$(echo $OUT | rg "Diagnostics:" -A 100) + echo " +
+ Statistics + + \`\`\` + $statistics + \`\`\` + against base + \`\`\` + $base_statistics + \`\`\` + against base +
+ " >> $GITHUB_STEP_SUMMARY + else + echo "
+ Statistics + + \`\`\` + $statistics + \`\`\` +
" >> $GITHUB_STEP_SUMMARY + fi + - name: Run checker performance w/staging shell: bash if: github.ref_name != 'main' @@ -93,7 +138,8 @@ jobs: cargo run -p ezno-parser --example code_blocks_to_script all.md --comment-headers --out ./all.tsx ./target/release/ezno check all.tsx --timings || true - hyperfine -i './target/release/ezno check all.tsx' + + hyperfine -i -L compiler ${{ steps.compilers.outputs.BINARIES }} '{compiler} check all.tsx' echo "::endgroup::" - name: Run checker performance on large file @@ -109,23 +155,58 @@ jobs: done ./target/release/ezno check large.tsx --timings --max-diagnostics 0 || true - hyperfine -i './target/release/ezno check large.tsx' + + hyperfine -i -L compiler ${{ steps.compilers.outputs.BINARIES }} '{compiler} check large.tsx' echo "::endgroup::" + - name: Valgrind and callgrind + shell: bash + continue-on-error: true + run: | + IFS=',' read -ra ITEMS <<< ${{ steps.compilers.outputs.BINARIES }} + + for compiler in ${ITEMS[@]}; do + echo "::group::Running $compiler" + + echo "::group::Callgrind" + valgrind --tool=callgrind --callgrind-out-file=cpu-out $compiler check demo.tsx | true + echo "CPU usage:" + head -n100 cpu-out + echo "::endgroup::" + + echo "::group::Valgrind" + valgrind --log-file=memory-out $compiler check demo.tsx | true + echo "Memory usage:" + cat memory-out + echo "::endgroup::" + + echo "::endgroup::" + done + - name: Run parsing & stringing (minfied) benchmarks shell: bash + continue-on-error: true run: | strings=( "https://esm.sh/v128/react-dom@18.2.0/es2022/react-dom.mjs" + "https://esm.sh/v135/typescript@5.3.3/es2022/typescript.mjs" ) - # Currently broken "https://esm.sh/v135/typescript@5.3.3/es2022/typescript.mjs" for url in "${strings[@]}"; do - curl -sS $url > input.js - echo "--- debug: $url ---" - cargo run -p ezno-parser --example parse input.js --timings --render-timings - echo "--- release: $url ---" - cargo run -p ezno-parser --release --example parse input.js --timings --render-timings - - hyperfine "./target/debug/examples/parse input.js" "./target/release/examples/parse input.js" + # TODO copy expression + curl -sS $url > input.js + + echo "::group::Comparison" + hyperfine \ + -L compiler ${{ steps.compilers.outputs.BINARIES }} \ + '{compiler} ast-explorer full input.js --timings' + echo "::endgroup::" done + + - name: Upload checker + if: github.ref == 'main' + uses: actions/upload-artifact@v4 + with: + name: latest-checker + path: target/release/ezno + retention-days: 90 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 193a1505..f7411899 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -58,30 +58,20 @@ jobs: echo "publish-json-args=$KEY_PAIR_ARGS_JSON" >> $GITHUB_OUTPUT shell: bash - - id: get-sponsors + # Needed for WASM + - name: Get sponsors and contributors + id: get-sponsors-and-contributors run: | - SPONSORS=$( - gh api graphql -f query='{ - user(login: "kaleidawave") { - sponsorshipsAsMaintainer(first: 100, activeOnly: false) { - edges { - node { - sponsor { - name, login - } - } - } - } - } - }' -q '.data.user.sponsorshipsAsMaintainer.edges | map(.node.sponsor.name // .node.sponsor.login) | join(",")' - ) - - CONTRIBUTORS=$( - gh pr list --state merged --json author | jq 'map(.author.name // .author.login) | unique | join(",")' --raw-output - ) - - echo "SPONSORS=$SPONSORS" >> $GITHUB_OUTPUT - echo "CONTRIBUTORS=$CONTRIBUTORS" >> $GITHUB_OUTPUT + SQP='.data.user.sponsorshipsAsMaintainer.edges | map(.node.sponsor.name // .node.sponsor.login) | join(",")' + GQL_SQ='{ user(login: "kaleidawave") { sponsorshipsAsMaintainer(first: 100, activeOnly: false) { edges { node { sponsor { name, login } } } } } }' + SPONSORS=$(gh api graphql -f query="$GQL_SQ" -q "$SQP") + + CQP='map(.author.name // .author.login) | unique | join(",")' + CONTRIBUTORS=$(gh pr list --state merged --json author | jq "$CQP" --raw-output) + + echo "SPONSORS=$SPONSORS" >> "$GITHUB_OUTPUT" + echo "CONTRIBUTORS=$CONTRIBUTORS" >> "$GITHUB_OUTPUT" + echo "::notice::CONTRIBUTORS=$CONTRIBUTORS and SPONSORS=$SPONSORS" shell: bash env: @@ -94,8 +84,8 @@ jobs: version: ${{ steps.set-arguments.outputs.publish-json-args }} crates-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} env: - SPONSORS: ${{ steps.get-sponsors.outputs.SPONSORS }} - CONTRIBUTORS: ${{ steps.get-sponsors.outputs.CONTRIBUTORS }} + SPONSORS: ${{ steps.get-sponsors-and-contributors.outputs.SPONSORS }} + CONTRIBUTORS: ${{ steps.get-sponsors-and-contributors.outputs.CONTRIBUTORS }} - name: Add WASM to rustup if: ${{ inputs.ezno-version != 'none' }} @@ -115,7 +105,8 @@ jobs: ls dist working-directory: src/js-cli-and-library env: - SPONSORS: ${{ steps.get-sponsors.outputs.sponsors }} + SPONSORS: ${{ steps.get-sponsors-and-contributors.outputs.SPONSORS }} + CONTRIBUTORS: ${{ steps.get-sponsors-and-contributors.outputs.CONTRIBUTORS }} - name: NPM publish (CLI and library) if: ${{ inputs.ezno-version != 'none' }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9a2efc66..e45c83c5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -88,6 +88,7 @@ jobs: run: | cargo test + # TODO more curl https://esm.sh/v128/react-dom@18.2.0/es2022/react-dom.mjs > react.js cargo run -p ezno-parser --example parse react.js working-directory: parser @@ -95,12 +96,19 @@ jobs: - name: Run checker specification if: (steps.changes.outputs.checker == 'true' && github.event_name != 'pull_request') || github.ref_name == 'main' run: cargo test - working-directory: checker/specification - name: Run checker specification (w/ staging) if: steps.changes.outputs.checker == 'true' && github.event_name == 'pull_request' - run: cargo test -F staging - working-directory: checker/specification + run: cargo test -F staging -p ezno-checker-specification + env: + EZNO_DEBUG: 1 + + - name: Run checker specification (just to implement) + continue-on-error: true + if: steps.changes.outputs.checker == 'true' && github.event_name == 'pull_request' + run: | + # Aim of this test is to catch anything that may have been fixed in this next commit or any bad regressions (stack overflows) + cargo test --no-default-features -F to_implement -p ezno-checker-specification env: EZNO_DEBUG: 1 @@ -108,10 +116,9 @@ jobs: if: steps.changes.outputs.checker == 'true' || github.ref_name == 'main' run: | # Test checker with the parser features - cargo test -F ezno-parser - working-directory: checker + cargo test -F ezno-parser -p ezno-checker - - name: Run base tests + - name: Run CLI and base tests run: cargo test extras: @@ -126,7 +133,7 @@ jobs: with: path: ${{ env.CACHE_PATHS }} key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - + - uses: dorny/paths-filter@v3 id: changes with: @@ -145,7 +152,7 @@ jobs: - uses: actions/setup-node@v4 if: steps.changes.outputs.src == 'true' || github.ref_name == 'main' with: - node-version: 18 + node-version: 23 - name: Check parser without extras if: steps.changes.outputs.parser == 'true' @@ -167,17 +174,19 @@ jobs: - name: Build and test WASM if: steps.changes.outputs.src == 'true' || github.ref_name == 'main' + timeout-minutes: 5 run: | # TODO `cargo check --target wasm32-unknown-unknown --lib` might be good enough rustup target add wasm32-unknown-unknown npm ci npm run build - npm run run-tests node ./dist/cli.cjs info deno run -A ./dist/cli.mjs info + npm run run-tests + npx -p typescript tsc --strict --pretty ./build/ezno_lib.d.ts echo "debug checked with TSC" cargo run -p ezno-parser --example parse ./build/ezno_lib.d.ts --type-definition-module @@ -190,6 +199,7 @@ jobs: working-directory: src/js-cli-and-library shell: bash + # WIP - uses: actions/upload-artifact@v4 if: steps.changes.outputs.src == 'true' || github.ref_name == 'main' with: @@ -198,6 +208,7 @@ jobs: retention-days: 3 fuzzing_parser: + if: ${{ github.ref == 'main' || !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'fuzz-me') }} needs: validity runs-on: ubuntu-latest timeout-minutes: 15 @@ -255,6 +266,7 @@ jobs: working-directory: parser/fuzz fuzzing_checker: + if: ${{ github.ref == 'main' || !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'fuzz-me') }} needs: validity runs-on: ubuntu-latest timeout-minutes: 15 @@ -347,3 +359,15 @@ jobs: fi done shell: bash + + performance-and-size: + # WIP + runs-on: ubuntu-latest + needs: validity + steps: + - uses: actions/checkout@v4 + - name: Kick off other workflow if the PR has a label + if: github.ref_name != 'main' && contains(github.event.pull_request.labels.*.name, 'compiler-performance') + run: gh workflow run performance-and-size.yml --ref "${{ github.head_ref }}" + env: + GH_TOKEN: ${{ github.token }} diff --git a/Cargo.lock b/Cargo.lock index fcfcbdfc..b6be9713 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,7 +39,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.87", ] [[package]] @@ -53,9 +53,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base64" @@ -90,15 +90,15 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" [[package]] name = "cc" -version = "1.1.15" +version = "1.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" dependencies = [ "shlex", ] @@ -190,7 +190,7 @@ checksum = "42e5ddace13a8459cb452b19e01f59f16d3e2049c8b808f338a13eeadc326e33" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.87", ] [[package]] @@ -211,7 +211,7 @@ dependencies = [ "either_n", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.87", ] [[package]] @@ -266,7 +266,7 @@ dependencies = [ "proc-macro2", "quote", "string-cases", - "syn 2.0.76", + "syn 2.0.87", ] [[package]] @@ -384,15 +384,6 @@ dependencies = [ "syn-helpers", ] -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.1.1" @@ -511,15 +502,6 @@ dependencies = [ "libc", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "iterator-endiate" version = "0.2.1" @@ -575,9 +557,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libredox" @@ -727,15 +709,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -754,7 +736,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.87", ] [[package]] @@ -765,9 +747,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -777,9 +759,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "4.2.2" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a91171844676f8c7990ce64959210cd2eaef32c2612c50f9fae9f8aaa6065a6" +checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e" dependencies = [ "num-traits", ] @@ -833,15 +815,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", @@ -849,9 +831,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -867,18 +849,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "regress" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16fe0a24af5daaae947294213d2fd2646fbf5e1fbacc1d4ba3e84b2393854842" +checksum = "1541daf4e4ed43a0922b7969bdc2170178bcacc5dabf7e39bc508a9fa3953a7a" dependencies = [ "hashbrown", "memchr", @@ -886,18 +868,18 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.48" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" dependencies = [ "bytemuck", ] [[package]] name = "rustix" -version = "0.38.35" +version = "0.38.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" dependencies = [ "bitflags 2.6.0", "errno", @@ -923,11 +905,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -951,9 +933,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -961,13 +943,13 @@ dependencies = [ [[package]] name = "self-replace" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7828a58998685d8bf5a3c5e7a3379a5867289c20828c3ee436280b44b598515" +checksum = "03ec815b5eab420ab893f63393878d89c90fdd94c0bcc44c07abb8ad95552fb7" dependencies = [ - "fastrand 1.9.0", + "fastrand", "tempfile", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -992,9 +974,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.209" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] @@ -1012,13 +994,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.87", ] [[package]] @@ -1029,14 +1011,14 @@ checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.87", ] [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -1094,9 +1076,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.76" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -1112,17 +1094,17 @@ dependencies = [ "either_n", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.87", ] [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", - "fastrand 2.1.1", + "fastrand", "once_cell", "rustix", "windows-sys 0.59.0", @@ -1174,20 +1156,20 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.76", + "syn 2.0.87", ] [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "vcpkg" @@ -1238,7 +1220,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -1282,7 +1264,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1495,9 +1477,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" @@ -1516,5 +1498,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.87", ] diff --git a/Cargo.toml b/Cargo.toml index 8e1d21e6..7f0a5fd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = [ [package] name = "ezno" -description = "A JavaScript type checker and compiler. For use as a library or through the CLI" +description = "A fast and correct TypeScript type checker and compiler with additional experiments. For use as a library or through the CLI" authors = ["Ben "] version = "0.0.22" edition = "2021" @@ -48,6 +48,7 @@ pretty_assertions = "1.3.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" simple-json-parser = "0.0.2" +js-sys = "0.3" [target.'cfg(not(target_family = "wasm"))'.dependencies] # For updating binary diff --git a/README.md b/README.md index 1a174c96..27dfc88b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -A JavaScript compiler and TypeScript checker written in Rust with a focus on static analysis and runtime performance. +A fast and correct TypeScript type checker and compiler with additional experiments > [!IMPORTANT] > Ezno is in active development and **does not currently support enough features to check existing projects** (see [blocking issues](https://github.com/kaleidawave/ezno/labels/blocking)). Check out the [getting started guide](./checker/documentation/getting-started.md) for experimenting with what it [currently supports](./checker/specification/specification.md). @@ -28,6 +28,7 @@ Read more about Ezno (in chronological order) - [Ezno in '23](https://kaleidawave.github.io/posts/ezno-23/) - [A preview of the checker](https://kaleidawave.github.io/posts/a-preview-of-the-checker/) - [The quest continues](https://kaleidawave.github.io/posts/the-quest-continues/) +- [Sets, types and type checking](https://kaleidawave.github.io/posts/sets-types-and-type-checking/) (*general post*) --- diff --git a/checker/Cargo.toml b/checker/Cargo.toml index 242c5608..2461cae6 100644 --- a/checker/Cargo.toml +++ b/checker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ezno-checker" -description = "A type checker for JavaScript" +description = "A fast and correct TypeScript type checker with additional experiments" version = "0.0.17" license = "MIT" repository = "https://github.com/kaleidawave/ezno" diff --git a/checker/examples/run_checker.rs b/checker/examples/run_checker.rs index aab40341..124c86e8 100644 --- a/checker/examples/run_checker.rs +++ b/checker/examples/run_checker.rs @@ -52,7 +52,7 @@ fn main() { let result = check_project::<_, synthesis::EznoParser>( entry_points, type_definition_files, - resolver, + &resolver, options, (), None, diff --git a/checker/fuzz/fuzz_targets/check_project_naive.rs b/checker/fuzz/fuzz_targets/check_project_naive.rs index 6a6039d9..014b25ea 100644 --- a/checker/fuzz/fuzz_targets/check_project_naive.rs +++ b/checker/fuzz/fuzz_targets/check_project_naive.rs @@ -19,7 +19,7 @@ fn do_fuzz(data: &str) -> Corpus { let _result = check_project::<_, synthesis::EznoParser>( vec![root.into()], type_definition_files, - |_path: &std::path::Path| Some(input.to_owned()), + &|_path: &std::path::Path| Some(input.to_owned()), options, (), None, diff --git a/checker/specification/build.rs b/checker/specification/build.rs index e31236a4..1b20193d 100644 --- a/checker/specification/build.rs +++ b/checker/specification/build.rs @@ -19,14 +19,18 @@ fn main() -> Result<(), Box> { if cfg!(feature = "staging") { let staging = read_to_string("./staging.md")?; - writeln!(&mut out, "mod staging {{ use super::check_errors; ").unwrap(); + writeln!(&mut out, "mod staging {{ ").unwrap(); + writeln!(&mut out, "use super::{{check_expected_diagnostics, TypeCheckOptions}}; ") + .unwrap(); markdown_lines_append_test_to_rust(staging.lines().enumerate(), &mut out)?; writeln!(&mut out, "}}").unwrap(); } if cfg!(feature = "all") { let to_implement = read_to_string("./to_implement.md")?; - writeln!(&mut out, "mod to_implement {{ use super::check_errors; ").unwrap(); + writeln!(&mut out, "mod to_implement {{ ").unwrap(); + writeln!(&mut out, "use super::{{check_expected_diagnostics, TypeCheckOptions}}; ") + .unwrap(); markdown_lines_append_test_to_rust(to_implement.lines().enumerate(), &mut out)?; writeln!(&mut out, "}}").unwrap(); } @@ -60,8 +64,20 @@ fn markdown_lines_append_test_to_rust( let heading = line.strip_prefix("####").unwrap().trim_start(); let test_title = heading_to_rust_identifier(heading); - let blocks = { - let mut blocks = Vec::new(); + pub struct File<'a> { + path: &'a str, + code: String, + } + + // pub struct Block { + // /// Vec for FS tests + // files: Vec, + // expected_diagnostics: Vec, + // options: Vec + // } + + let files = { + let mut files = Vec::::new(); let mut current_filename = None; for (_, line) in lines.by_ref() { // Also handles TSX @@ -74,10 +90,10 @@ fn markdown_lines_append_test_to_rust( for (_, line) in lines.by_ref() { if let Some(path) = line.strip_prefix("// in ") { if !code.trim().is_empty() { - blocks.push(( - current_filename.unwrap_or(DEFAULT_FILE_PATH), - mem::take(&mut code), - )); + files.push(File { + path: current_filename.unwrap_or(DEFAULT_FILE_PATH), + code: mem::take(&mut code), + }); } current_filename = Some(path); continue; @@ -88,40 +104,64 @@ fn markdown_lines_append_test_to_rust( code.push_str(line); code.push('\n') } - blocks.push((current_filename.unwrap_or(DEFAULT_FILE_PATH), code)); - blocks + files.push(File { path: current_filename.unwrap_or(DEFAULT_FILE_PATH), code }); + files }; - let errors = { - let mut errors = Vec::new(); + + let (expected_diagnostics, options) = { + let mut expected_diagnostics = Vec::new(); + let mut options = None::>; for (_, line) in lines.by_ref() { - if line.starts_with("#") { + if let (Some(args), false) = (line.strip_prefix("With "), options.is_some()) { + options = Some(args.split(',').collect()); + } else if line.starts_with("#") { panic!("block with no diagnostics or break between in {test_title}") - } else if line.starts_with('-') { - let error = - line.strip_prefix("- ").unwrap().replace('\\', "").replace('"', "\\\""); - errors.push(format!("\"{}\"", error)) - } else if !errors.is_empty() { + } else if let Some(diagnostic) = line.strip_prefix("-") { + let error = diagnostic.trim().replace('\\', "").replace('"', "\\\""); + expected_diagnostics.push(format!("\"{}\"", error)) + } else if !expected_diagnostics.is_empty() { break; } } - errors + (expected_diagnostics, options) }; - let errors = errors.join(", "); + let expected_diagnostics = expected_diagnostics.join(", "); let heading_idx = heading_idx + 1; - let code = blocks + // TODO don't allocate + let code_as_list = files .into_iter() - .map(|(path, content)| format!("(\"{path}\",r#\"{content}\"#),")) - .fold(String::new(), |mut acc, cur| { - acc.push_str(&cur); + .map(|File { path, code }| format!("(\"{path}\",r#\"{code}\"#),")) + .reduce(|mut acc, slice| { + acc.push_str(&slice); acc - }); + }) + .unwrap(); + + let options = if let Some(options) = options { + let arguments = options + .into_iter() + .map(|value| format!("{value}: true")) + .reduce(|mut acc, slice| { + acc.push_str(&slice); + acc.push_str(", "); + acc + }) + .unwrap(); + format!("Some(super::TypeCheckOptions {{ {arguments}, ..super::TypeCheckOptions::default() }})") + } else { + format!("None") + }; writeln!( out, "#[test] fn {test_title}() {{ - super::check_errors(\"{heading}\", {heading_idx}, &[{code}], &[{errors}]) + super::check_expected_diagnostics( + \"{heading}\", {heading_idx}, + &[{code_as_list}], &[{expected_diagnostics}], + {options} + ) }}", )?; } @@ -136,6 +176,6 @@ fn heading_to_rust_identifier(heading: &str) -> String { heading .replace("...", "") .replace([' ', '-', '/', '.', '+'], "_") - .replace(['*', '\'', '`', '"', '&', '!', '(', ')', ','], "") + .replace(['*', '\'', '`', '"', '&', '!', '(', ')', ',', ':'], "") .to_lowercase() } diff --git a/checker/specification/specification.md b/checker/specification/specification.md index 60dbc932..88a305b0 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -3684,18 +3684,6 @@ box(someNumber) satisfies boolean; - Expected string, found number - Expected boolean, found { item: number } -#### Template literal type restriction - -```ts -type Name = "Ben" -"test" satisfies `Hello ${Name}`; -"Hello Ben" satisfies `Hello ${Name}`; -``` - -> Should be `Expected "Hello Ben", found "test"`. See #188 - -- Expected `Hello ${Name}`, found \"test\" - #### Template literal type specialisation > Uses `+` logic behind the scenes diff --git a/checker/specification/test.rs b/checker/specification/test.rs index 85166a18..b98dd727 100644 --- a/checker/specification/test.rs +++ b/checker/specification/test.rs @@ -11,13 +11,14 @@ use checker::{ diagnostics, source_map::{Nullable, SourceId}, synthesis::EznoParser, + TypeCheckOptions, }; // This is here as it is used in the included `/specification.rs` use parser::ASTNode; mod specification { - use super::check_errors; + use super::{check_expected_diagnostics, TypeCheckOptions}; // from build.rs include!(concat!(env!("OUT_DIR"), "/specification.rs")); @@ -37,12 +38,13 @@ const SIMPLE_DTS: Option<&str> = None; const IN_CI: bool = option_env!("CI").is_some(); /// Called by each test -fn check_errors( +fn check_expected_diagnostics( heading: &'static str, - _line: usize, + line: usize, // (Path, Content) code: &[(&'static str, &'static str)], expected_diagnostics: &[&'static str], + type_check_options: Option, ) { // let global_buffer = Arc::new(Mutex::new(String::new())); // let old_panic_hook = panic::take_hook(); @@ -59,10 +61,7 @@ fn check_errors( // }) // }); - // TODO could test these - let type_check_options = Default::default(); - - // eprintln!("{:?}", code); + let type_check_options = type_check_options.unwrap_or_default(); // let result = panic::catch_unwind(|| { @@ -95,7 +94,7 @@ fn check_errors( let result = checker::check_project::<_, EznoParser>( vec![PathBuf::from("main.tsx")], type_definition_files, - resolver, + &resolver, type_check_options, (), None, @@ -125,7 +124,7 @@ fn check_errors( if diagnostics != expected_diagnostics { panic!( - "{}", + "In '{heading}' on line {line}, found\n{}", pretty_assertions::Comparison::new(expected_diagnostics, &diagnostics).to_string() ) } diff --git a/checker/src/context/invocation.rs b/checker/src/context/invocation.rs index b7218ade..c20fa8da 100644 --- a/checker/src/context/invocation.rs +++ b/checker/src/context/invocation.rs @@ -69,7 +69,7 @@ pub struct InvocationContext(Vec); /// TODO want to have type arguments on each of these pub(crate) enum InvocationKind { - Conditional(LocalInformation), + Conditional(Box), /// *Unconditional* /// /// TODO does this need [`LocalInformation`]?? @@ -89,15 +89,13 @@ impl CallCheckingBehavior for InvocationContext { self.0 .iter_mut() .rev() - .find_map( - |kind| { - if let InvocationKind::Conditional(info) = kind { - Some(info) - } else { - None - } - }, - ) + .find_map(|kind| -> Option<&mut LocalInformation> { + if let InvocationKind::Conditional(info) = kind { + Some(&mut *info) + } else { + None + } + }) .unwrap_or(&mut environment.info) } @@ -142,10 +140,10 @@ impl InvocationContext { &mut self, cb: impl for<'a> FnOnce(&'a mut InvocationContext) -> T, ) -> (LocalInformation, T) { - self.0.push(InvocationKind::Conditional(LocalInformation::default())); + self.0.push(InvocationKind::Conditional(Box::default())); let result = cb(self); if let Some(InvocationKind::Conditional(info)) = self.0.pop() { - (info, result) + (*info, result) } else { unreachable!() } diff --git a/checker/src/diagnostics.rs b/checker/src/diagnostics.rs index 94cecf0a..78943ade 100644 --- a/checker/src/diagnostics.rs +++ b/checker/src/diagnostics.rs @@ -114,18 +114,18 @@ pub struct DiagnosticsContainer { diagnostics: Vec, // Quick way to check whether a error was added #[cfg_attr(feature = "serde-serialize", serde(skip_serializing))] - has_error: bool, + contains_error: bool, } // TODO the add methods are the same... impl DiagnosticsContainer { #[must_use] pub fn new() -> Self { - Self { diagnostics: Default::default(), has_error: false } + Self { diagnostics: Default::default(), contains_error: false } } pub fn add_error>(&mut self, error: T) { - self.has_error = true; + self.contains_error = true; self.diagnostics.push(error.into()); } @@ -138,8 +138,8 @@ impl DiagnosticsContainer { } #[must_use] - pub fn has_error(&self) -> bool { - self.has_error + pub fn contains_error(&self) -> bool { + self.contains_error } pub fn sources(&self) -> impl Iterator + '_ { @@ -153,7 +153,7 @@ impl DiagnosticsContainer { } pub fn into_result(self) -> Result { - if self.has_error { + if self.contains_error { Err(self) } else { Ok(self) diff --git a/checker/src/features/regexp.rs b/checker/src/features/regexp.rs index 7797cef9..297d0334 100644 --- a/checker/src/features/regexp.rs +++ b/checker/src/features/regexp.rs @@ -15,7 +15,7 @@ pub struct RegExp { source: String, re: Regex, groups: u32, - named_group_indices: crate::Map, + group_names: Vec>, flags_unsupported: bool, used: bool, } @@ -65,13 +65,12 @@ impl RegExp { // let start_pred = compiled_regex.start_pred; // let loops = compiled_regex.loops; let groups = compiled_regex.groups + 1; - let named_group_indices = - compiled_regex.named_group_indices.iter().map(|(l, r)| (l.clone(), *r)).collect(); + let group_names = compiled_regex.group_names.to_vec(); // let flags = compiled_regex.flags; let re = Regex::from(compiled_regex); - Ok(Self { source, re, groups, named_group_indices, flags_unsupported, used: false }) + Ok(Self { source, re, groups, group_names, flags_unsupported, used: false }) } #[must_use] @@ -262,7 +261,7 @@ impl RegExp { &mut environment.info, ); - for name in self.named_group_indices.keys() { + for name in &self.group_names { let key = PropertyKey::String(name.to_string().into()); named_groups_object.append( diff --git a/checker/src/lib.rs b/checker/src/lib.rs index 39e60306..d7ee7802 100644 --- a/checker/src/lib.rs +++ b/checker/src/lib.rs @@ -459,13 +459,13 @@ impl CheckOutput { pub fn check_project( entry_points: Vec, type_definition_files: Vec, - resolver: T, + resolver: &T, options: TypeCheckOptions, parser_requirements: A::ParserRequirements, existing_files: Option>, ) -> CheckOutput { let mut checking_data = - CheckingData::::new(options, &resolver, existing_files, parser_requirements); + CheckingData::::new(options, resolver, existing_files, parser_requirements); let mut root = crate::context::RootContext::new_with_primitive_references(); @@ -478,7 +478,7 @@ pub fn check_project( add_definition_files_to_root(type_definition_files, &mut root, &mut checking_data); crate::utilities::unpause_debug_mode(); - if checking_data.diagnostics_container.has_error() { + if checking_data.diagnostics_container.contains_error() { return CheckOutput { types: checking_data.types, module_contents: checking_data.modules.files, @@ -753,7 +753,7 @@ pub fn generate_cache( add_definition_files_to_root(vec![on.to_path_buf()], &mut root, &mut checking_data); assert!( - !checking_data.diagnostics_container.has_error(), + !checking_data.diagnostics_container.contains_error(), "found error in definition file {:#?}", checking_data.diagnostics_container.get_diagnostics() ); diff --git a/checker/src/synthesis/interactive.rs b/checker/src/synthesis/interactive.rs new file mode 100644 index 00000000..54f8a620 --- /dev/null +++ b/checker/src/synthesis/interactive.rs @@ -0,0 +1,90 @@ +/// For the REPL in Ezno's CLI +use std::{mem, path::PathBuf}; + +use source_map::{FileSystem, MapFileStore, SourceId, WithPathMap}; + +use crate::{ + add_definition_files_to_root, types::printing::print_type, CheckingData, DiagnosticsContainer, + RootContext, TypeId, +}; + +use super::{block::synthesise_block, expressions::synthesise_multiple_expression}; + +pub struct State<'a, T: crate::ReadFromFS> { + checking_data: CheckingData<'a, T, super::EznoParser>, + root: RootContext, + source: SourceId, +} + +impl<'a, T: crate::ReadFromFS> State<'a, T> { + pub fn new( + resolver: &'a T, + type_definition_files: Vec, + ) -> Result)> { + let mut root = RootContext::new_with_primitive_references(); + let mut checking_data = + CheckingData::new(Default::default(), resolver, Default::default(), ()); + + add_definition_files_to_root(type_definition_files, &mut root, &mut checking_data); + + if checking_data.diagnostics_container.contains_error() { + Err((checking_data.diagnostics_container, checking_data.modules.files)) + } else { + let source = + checking_data.modules.files.new_source_id("CLI.tsx".into(), String::default()); + Ok(Self { checking_data, root, source }) + } + } + + pub fn check_item( + &mut self, + item: &parser::Module, + ) -> Result<(Option, DiagnosticsContainer), DiagnosticsContainer> { + let (ty, ..) = self.root.new_lexical_environment_fold_into_parent( + crate::Scope::PassThrough { source: self.source }, + &mut self.checking_data, + |environment, checking_data| { + if let Some(parser::StatementOrDeclaration::Statement( + parser::Statement::Expression(expression), + )) = item.items.last() + { + synthesise_block( + &item.items[..(item.items.len() - 1)], + environment, + checking_data, + ); + let result = synthesise_multiple_expression( + expression, + environment, + checking_data, + TypeId::ANY_TYPE, + ); + Some(print_type(result, &checking_data.types, environment, false)) + } else { + synthesise_block(&item.items, environment, checking_data); + None + } + }, + ); + let dc = mem::take(&mut self.checking_data.diagnostics_container); + if dc.contains_error() { + Err(dc) + } else { + Ok((ty, dc)) + } + } + + #[must_use] + pub fn get_source_id(&self) -> SourceId { + self.source + } + + #[must_use] + pub fn get_fs_ref(&self) -> &MapFileStore { + &self.checking_data.modules.files + } + + pub fn get_fs_mut(&mut self) -> &mut MapFileStore { + &mut self.checking_data.modules.files + } +} diff --git a/checker/src/synthesis/interfaces.rs b/checker/src/synthesis/interfaces.rs index 12863cd4..fed3dc65 100644 --- a/checker/src/synthesis/interfaces.rs +++ b/checker/src/synthesis/interfaces.rs @@ -58,7 +58,7 @@ pub(crate) enum InterfaceKey<'a> { } pub(crate) enum InterfaceValue { - Function(FunctionType, Option), + Function(Box, Option), Value(TypeId), } @@ -90,16 +90,16 @@ fn register( let value = match value { InterfaceValue::Function(function, getter_setter) => match getter_setter { Some(GetterSetter::Getter) => PropertyValue::Getter(Callable::new_from_function( - function, + *function, &mut checking_data.types, )), Some(GetterSetter::Setter) => PropertyValue::Setter(Callable::new_from_function( - function, + *function, &mut checking_data.types, )), None => { let function_id = function.id; - checking_data.types.functions.insert(function.id, function); + checking_data.types.functions.insert(function.id, *function); let ty = Type::FunctionReference(function_id); PropertyValue::Value(checking_data.types.register_type(ty)) } @@ -232,7 +232,7 @@ pub(super) fn synthesise_signatures { - checking_data: CheckingData<'a, T, super::EznoParser>, - root: RootContext, - source: SourceId, - } - - impl<'a, T: crate::ReadFromFS> State<'a, T> { - pub fn new( - resolver: &'a T, - type_definition_files: Vec, - ) -> Result)> { - let mut root = RootContext::new_with_primitive_references(); - let mut checking_data = - CheckingData::new(Default::default(), resolver, Default::default(), ()); - - add_definition_files_to_root(type_definition_files, &mut root, &mut checking_data); - - if checking_data.diagnostics_container.has_error() { - Err((checking_data.diagnostics_container, checking_data.modules.files)) - } else { - let source = - checking_data.modules.files.new_source_id("CLI.tsx".into(), String::default()); - Ok(Self { checking_data, root, source }) - } - } - - pub fn check_item( - &mut self, - item: &parser::Module, - ) -> Result<(Option, DiagnosticsContainer), DiagnosticsContainer> { - let (ty, ..) = self.root.new_lexical_environment_fold_into_parent( - crate::Scope::PassThrough { source: self.source }, - &mut self.checking_data, - |environment, checking_data| { - if let Some(parser::StatementOrDeclaration::Statement( - parser::Statement::Expression(expression), - )) = item.items.last() - { - synthesise_block( - &item.items[..(item.items.len() - 1)], - environment, - checking_data, - ); - let result = synthesise_multiple_expression( - expression, - environment, - checking_data, - TypeId::ANY_TYPE, - ); - Some(print_type(result, &checking_data.types, environment, false)) - } else { - synthesise_block(&item.items, environment, checking_data); - None - } - }, - ); - let dc = mem::take(&mut self.checking_data.diagnostics_container); - if dc.has_error() { - Err(dc) - } else { - Ok((ty, dc)) - } - } - - #[must_use] - pub fn get_source_id(&self) -> SourceId { - self.source - } - - #[must_use] - pub fn get_fs_ref(&self) -> &MapFileStore { - &self.checking_data.modules.files - } - - pub fn get_fs_mut(&mut self) -> &mut MapFileStore { - &mut self.checking_data.modules.files - } - } -} diff --git a/checker/tests/partial_source.rs b/checker/tests/partial_source.rs index 867b2f96..3b74f56b 100644 --- a/checker/tests/partial_source.rs +++ b/checker/tests/partial_source.rs @@ -21,7 +21,7 @@ fn partial_checking() { let result = check_project::<_, synthesis::EznoParser>( vec![root.into()], type_definition_files, - |_path: &std::path::Path| Some(text.to_owned()), + &|_path: &std::path::Path| Some(text.to_owned()), options, (), None, diff --git a/checker/tests/suggestions.rs b/checker/tests/suggestions.rs index 4a9f3e41..301b4a52 100644 --- a/checker/tests/suggestions.rs +++ b/checker/tests/suggestions.rs @@ -41,7 +41,7 @@ console.log(obj2.proberly); let result = check_project::<_, ezno_checker::synthesis::EznoParser>( vec![root.into()], type_definition_files, - resolver, + &resolver, options, (), None, diff --git a/checker/tests/type_mappings.rs b/checker/tests/type_mappings.rs index e6eef489..bccb65c3 100644 --- a/checker/tests/type_mappings.rs +++ b/checker/tests/type_mappings.rs @@ -17,7 +17,7 @@ y()"; let result = check_project::<_, synthesis::EznoParser>( vec![root.into()], type_definition_files, - |_path: &std::path::Path| Some(text.to_owned()), + &|_path: &std::path::Path| Some(text.to_owned()), options, (), None, diff --git a/parser/examples/duplicate_block.rs b/parser/examples/duplicate_block.rs new file mode 100644 index 00000000..21183a2f --- /dev/null +++ b/parser/examples/duplicate_block.rs @@ -0,0 +1,135 @@ +use ezno_parser::{ + declarations::VariableDeclaration, + visiting::{Chain, ImmutableVariableOrProperty, VisitOptions, Visitor, Visitors}, + ASTNode, Declaration, Expression, Module, StatementOrDeclaration, VariableField, +}; +use std::collections::{HashMap, HashSet}; + +struct Offsets { + pub offsets: Vec, + /// TODO use &str references + pub top_level_variables: HashSet, + pub top_level_types: HashSet, +} + +/// TODO this could use visting right? +/// TODO abstract to library +/// TODO do for funtions and types +fn get_top_level_identifiers(m: &Module) -> (HashSet, HashSet) { + let (mut variables, mut types): (HashSet<_>, HashSet<_>) = Default::default(); + for item in &m.items { + match item { + StatementOrDeclaration::Declaration(Declaration::Variable(variable)) => { + match variable { + VariableDeclaration::ConstDeclaration { declarations, position: _ } => { + for declaration in declarations { + if let VariableField::Name(identifier) = declaration.name.get_ast_ref() + { + variables.insert(identifier.as_option_str().unwrap().to_owned()); + } + } + } + VariableDeclaration::LetDeclaration { declarations, position: _ } => { + for declaration in declarations { + if let VariableField::Name(identifier) = declaration.name.get_ast_ref() + { + variables.insert(identifier.as_option_str().unwrap().to_owned()); + } + } + } + } + } + StatementOrDeclaration::Declaration(Declaration::Function(function)) => { + variables.insert(function.on.name.identifier.as_option_str().unwrap().to_owned()); + } + _ => {} + } + } + (variables, types) +} + +fn main() { + let code = " +let x = 2; +let y = x + 2; +let z = 6; +" + .trim(); + + // function func() {{ return [x, z] }} + let module = Module::from_string(code.into(), Default::default()).unwrap(); + + let (top_level_variables, top_level_types) = get_top_level_identifiers(&module); + + let mut visitors = Visitors { + expression_visitors: vec![Box::new(NameReferenceFinder)], + statement_visitors: Default::default(), + variable_visitors: vec![Box::new(NameIndexFinder)], + block_visitors: Default::default(), + }; + + // eprintln!("variables={:#?}", (&top_level_variables, &top_level_types)); + + let mut offsets: Offsets = + Offsets { offsets: Default::default(), top_level_variables, top_level_types }; + + module.visit::( + &mut visitors, + &mut offsets, + &VisitOptions { visit_nested_blocks: true, reverse_statements: false }, + source_map::Nullable::NULL, + ); + + // TODO why is this backwards + // eprintln!("offsets={:#?}", offsets); + + offsets.offsets.sort_unstable(); + let mut rest = code.to_owned(); + for (idx, offset) in offsets.offsets.iter_mut().enumerate().rev() { + let current_offset = *offset as usize; + rest.insert_str(current_offset, "000"); + // need to ammed offset now string has been changed + *offset += ("000".len() * idx) as u32; + } + rest.push('\n'); + + let mut total = rest.clone(); + const SIZE: usize = 10; + total.reserve(rest.len() * (SIZE - 1)); + + for i in 1..SIZE { + let name = format!("{:03}", i); + for offset in offsets.offsets.iter().copied() { + let range = offset as usize..(offset as usize + 3); + rest.replace_range(range, &name); + } + + total.push_str(&rest); + } + + eprintln!("{}", total); +} + +/// TODO this could be collected in the same process as above +struct NameIndexFinder; + +impl<'a> Visitor, Offsets> for NameIndexFinder { + fn visit(&mut self, item: &ImmutableVariableOrProperty<'a>, data: &mut Offsets, chain: &Chain) { + if chain.len() == 1 && item.get_variable_name().is_some() { + data.offsets.push(item.get_position().end); + // data.insert(name.to_owned()); + } + } +} + +struct NameReferenceFinder; + +impl Visitor for NameReferenceFinder { + fn visit(&mut self, item: &Expression, data: &mut Offsets, _chain: &Chain) { + if let Expression::VariableReference(name, position) = item { + if data.top_level_variables.contains(name) { + data.offsets.push(position.end); + } + } + } +} diff --git a/parser/examples/parse.rs b/parser/examples/parse.rs index cd189381..ccd5b852 100644 --- a/parser/examples/parse.rs +++ b/parser/examples/parse.rs @@ -1,8 +1,10 @@ -use std::{collections::VecDeque, time::Instant}; +use std::{collections::VecDeque, path::Path, time::Instant}; use ezno_parser::{ASTNode, Comments, Module, ParseOptions, ToStringOptions}; use source_map::FileSystem; +type Files = source_map::MapFileStore; + fn main() -> Result<(), Box> { let mut args: VecDeque<_> = std::env::args().skip(1).collect(); let path = args.pop_front().ok_or("expected argument")?; @@ -18,20 +20,18 @@ fn main() -> Result<(), Box> { let display_keywords = args.iter().any(|item| item == "--keywords"); let extras = args.iter().any(|item| item == "--extras"); let partial_syntax = args.iter().any(|item| item == "--partial"); - let source_maps = args.iter().any(|item| item == "--source-map"); + let print_source_maps = args.iter().any(|item| item == "--source-map"); let timings = args.iter().any(|item| item == "--timings"); - let render_timings = args.iter().any(|item| item == "--render-timings"); let type_definition_module = args.iter().any(|item| item == "--type-definition-module"); let type_annotations = !args.iter().any(|item| item == "--no-type-annotations"); let top_level_html = args.iter().any(|item| item == "--top-level-html"); + let parse_imports = args.iter().any(|item| item == "--parse-imports"); let print_ast = args.iter().any(|item| item == "--ast"); - let render_output = args.iter().any(|item| item == "--render"); + let to_string_output = args.iter().any(|item| item == "--to-string"); let pretty = args.iter().any(|item| item == "--pretty"); - let now = Instant::now(); - // TODO temp const STACK_SIZE_MB: usize = 32; let parse_options = ParseOptions { @@ -52,14 +52,48 @@ fn main() -> Result<(), Box> { ..ParseOptions::default() }; - let mut fs = source_map::MapFileStore::::default(); - - let source = std::fs::read_to_string(path.clone())?; + let mut fs = Files::default(); + + let to_string_options = to_string_output.then(|| ToStringOptions { + expect_markers: true, + include_type_annotations: type_annotations, + pretty, + comments: if pretty { Comments::All } else { Comments::None }, + // 60 is temp + max_line_length: if pretty { 60 } else { u8::MAX }, + ..Default::default() + }); + + parse_path( + path.as_ref(), + timings, + parse_imports, + &parse_options, + print_ast, + print_source_maps, + &to_string_options, + display_keywords, + &mut fs, + ) +} +fn parse_path( + path: &Path, + timings: bool, + parse_imports: bool, + parse_options: &ParseOptions, + print_ast: bool, + print_source_maps: bool, + to_string_options: &Option, + display_keywords: bool, + fs: &mut Files, +) -> Result<(), Box> { + let source = std::fs::read_to_string(path)?; let source_id = fs.new_source_id(path.into(), source.clone()); - eprintln!("parsing {:?} bytes", source.len()); - let result = Module::from_string_with_options(source.clone(), parse_options, None); + eprintln!("parsing {:?} ({:?} bytes)", path.display(), source.len()); + let now = Instant::now(); + let result = Module::from_string_with_options(source.clone(), parse_options.clone(), None); match result { Ok((module, state)) => { @@ -70,45 +104,55 @@ fn main() -> Result<(), Box> { if print_ast { println!("{module:#?}"); } - if source_maps || render_output || render_timings { - let now = Instant::now(); - let to_string_options = ToStringOptions { - expect_markers: true, - include_type_annotations: type_annotations, - pretty, - comments: if pretty { Comments::All } else { Comments::None }, - // 60 is temp - max_line_length: if pretty { 60 } else { u8::MAX }, - ..Default::default() - }; + if let Some(to_string_options) = to_string_options { + let now = Instant::now(); let (output, source_map) = - module.to_string_with_source_map(&to_string_options, source_id, &fs); + module.to_string_with_source_map(to_string_options, source_id, fs); - if timings || render_timings { + if timings { eprintln!("ToString'ed in: {:?}", now.elapsed()); } - if source_maps { - let sm = source_map.unwrap().to_json(&fs); - println!("{output}\n{sm}"); - } - if render_output { - println!("{output}"); + + println!("{output}"); + if print_source_maps { + let sm = source_map.unwrap().to_json(fs); + println!("{sm}"); } } if display_keywords { - println!("{:?}", state.keyword_positions.unwrap()); + println!("{:?}", state.keyword_positions.as_ref()); } + if parse_imports { + for import in state.constant_imports.iter() { + // Don't reparse files (+ catches cycles) + let resolved_path = path.parent().unwrap().join(import); + if fs.get_paths().contains_key(&resolved_path) { + continue; + } + let _ = parse_path( + &resolved_path, + timings, + parse_imports, + parse_options, + print_ast, + print_source_maps, + to_string_options, + display_keywords, + fs, + )?; + } + } Ok(()) } Err(parse_err) => { let mut line_column = parse_err .position .with_source(source_id) - .into_line_column_span::(&fs); + .into_line_column_span::(fs); { // Editor are one indexed line_column.line_start += 1; diff --git a/parser/examples/simple.rs b/parser/examples/simple.rs new file mode 100644 index 00000000..46475a22 --- /dev/null +++ b/parser/examples/simple.rs @@ -0,0 +1,9 @@ +#[allow(unused)] +use ezno_parser::{ASTNode, Expression, Module}; + +fn main() { + let source = "'Hello World!'".to_owned(); + let parse_options = Default::default(); + let result = Expression::from_string_with_options(source.clone(), parse_options, Some(40)); + eprintln!("{result:#?}"); +} diff --git a/parser/src/block.rs b/parser/src/block.rs index 3070dde8..e37b0f20 100644 --- a/parser/src/block.rs +++ b/parser/src/block.rs @@ -43,7 +43,8 @@ impl StatementOrDeclaration { on: ExportDeclaration::Default { .. } | ExportDeclaration::Item { exported: Exportable::ImportAll { .. } - | Exportable::ImportParts { .. } | Exportable::Parts { .. }, + | Exportable::ImportParts { .. } + | Exportable::Parts { .. }, .. }, .. @@ -392,10 +393,11 @@ pub(crate) fn parse_statements_and_declarations( expect_semi_colon(reader, &state.line_starts, end, options)? } else if options.retain_blank_lines { let Token(kind, next) = reader.peek().ok_or_else(crate::parse_lexing_error)?; - let lines = state.line_starts.byte_indexes_crosses_lines(end as usize, next.0 as usize); if let TSXToken::EOS = kind { - lines + 1 } else { + let lines = + state.line_starts.byte_indexes_crosses_lines(end as usize, next.0 as usize); lines.saturating_sub(1) } } else { diff --git a/parser/src/declarations/mod.rs b/parser/src/declarations/mod.rs index 53d3344a..080f7dc6 100644 --- a/parser/src/declarations/mod.rs +++ b/parser/src/declarations/mod.rs @@ -78,8 +78,10 @@ impl Declaration { token, TSXToken::Keyword( TSXKeyword::Let - | TSXKeyword::Const | TSXKeyword::Function - | TSXKeyword::Class | TSXKeyword::Export + | TSXKeyword::Const + | TSXKeyword::Function + | TSXKeyword::Class + | TSXKeyword::Export ) | TSXToken::At, ); @@ -123,7 +125,8 @@ impl Declaration { reader.peek_n(1), Some(Token( TSXToken::OpenBrace - | TSXToken::Keyword(..) | TSXToken::Identifier(..) + | TSXToken::Keyword(..) + | TSXToken::Identifier(..) | TSXToken::StringLiteral(..) | TSXToken::Multiply, _ diff --git a/parser/src/extensions/jsx.rs b/parser/src/extensions/jsx.rs index 88f58aac..ebec2d33 100644 --- a/parser/src/extensions/jsx.rs +++ b/parser/src/extensions/jsx.rs @@ -485,7 +485,7 @@ pub fn html_tag_is_self_closing(tag_name: &str) -> bool { | "hr" | "img" | "input" | "link" | "meta" | "param" - | "source" | "track" - | "wbr" + | "source" + | "track" | "wbr" ) } diff --git a/parser/src/functions/mod.rs b/parser/src/functions/mod.rs index 7d86a4bb..84bb66a1 100644 --- a/parser/src/functions/mod.rs +++ b/parser/src/functions/mod.rs @@ -644,22 +644,23 @@ pub(crate) fn get_method_name( state: &mut crate::ParsingState, options: &ParseOptions, ) -> Result<(MethodHeader, WithComment>), crate::ParseError> { - let is_named_get_set_or_async = - matches!( - reader.peek(), - Some(Token(TSXToken::Keyword(kw), _)) - if kw.is_in_method_header() - ) && matches!( - reader.peek_n(1), - Some(Token( - TSXToken::OpenParentheses - | TSXToken::Colon | TSXToken::OpenChevron - | TSXToken::CloseBrace - | TSXToken::Comma | TSXToken::QuestionMark - | TSXToken::OptionalMember, - _ - )) - ); + let is_named_get_set_or_async = matches!( + reader.peek(), + Some(Token(TSXToken::Keyword(kw), _)) + if kw.is_in_method_header() + ) && matches!( + reader.peek_n(1), + Some(Token( + TSXToken::OpenParentheses + | TSXToken::Colon + | TSXToken::OpenChevron + | TSXToken::CloseBrace + | TSXToken::Comma + | TSXToken::QuestionMark + | TSXToken::OptionalMember, + _ + )) + ); let (function_header, key) = if is_named_get_set_or_async { let token = reader.next().unwrap(); diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 49c202ed..e6d68467 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -47,14 +47,18 @@ fn is_number_delimiter(chr: char) -> bool { chr, ' ' | ',' | '\n' | '\r' - | ';' | '+' | '-' - | '*' | '/' | '&' - | '|' | '!' | '^' - | '(' | '{' | '[' - | ')' | '}' | ']' - | '%' | '=' | ':' - | '<' | '>' | '?' - | '"' | '\'' | '`' + | ';' | '+' + | '-' | '*' + | '/' | '&' + | '|' | '!' + | '^' | '(' + | '{' | '[' + | ')' | '}' + | ']' | '%' + | '=' | ':' + | '<' | '>' + | '?' | '"' + | '\'' | '`' | '#' ) } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 7944ac01..93633225 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -266,10 +266,10 @@ pub(crate) fn throw_unexpected_token_with_token( #[derive(Debug)] pub struct ParsingState { - pub(crate) line_starts: source_map::LineStarts, - pub(crate) length_of_source: u32, + pub line_starts: source_map::LineStarts, + pub length_of_source: u32, /// TODO as multithreaded channel + record is dynamic exists - pub(crate) constant_imports: Vec, + pub constant_imports: Vec, pub keyword_positions: Option, pub partial_points: Vec, } diff --git a/parser/src/modules.rs b/parser/src/modules.rs index f284b658..777e575f 100644 --- a/parser/src/modules.rs +++ b/parser/src/modules.rs @@ -44,7 +44,8 @@ impl ASTNode for Module { state: &mut crate::ParsingState, options: &ParseOptions, ) -> ParseResult { - let span = Span { start: 0, source: (), end: state.length_of_source }; + let start = reader.peek().map(|t| t.1 .0).unwrap_or_default(); + let span = Span { start, source: (), end: start + state.length_of_source }; let hashbang_comment = if let Some(crate::Token(TSXToken::HashBangComment(_), _)) = reader.peek() { diff --git a/parser/src/tokens.rs b/parser/src/tokens.rs index 9c61e8b9..1cedf7ed 100644 --- a/parser/src/tokens.rs +++ b/parser/src/tokens.rs @@ -456,7 +456,8 @@ impl TSXToken { | TSXToken::LogicalAnd | TSXToken::LogicalOr | TSXToken::Multiply - | TSXToken::Add | TSXToken::Subtract + | TSXToken::Add + | TSXToken::Subtract | TSXToken::Divide ) || self.is_assignment() } @@ -479,10 +480,14 @@ impl TSXToken { self, TSXToken::Keyword( TSXKeyword::Function - | TSXKeyword::If | TSXKeyword::For - | TSXKeyword::While | TSXKeyword::Const - | TSXKeyword::Let | TSXKeyword::Break - | TSXKeyword::Import | TSXKeyword::Export + | TSXKeyword::If + | TSXKeyword::For + | TSXKeyword::While + | TSXKeyword::Const + | TSXKeyword::Let + | TSXKeyword::Break + | TSXKeyword::Import + | TSXKeyword::Export ) ) } diff --git a/parser/src/types/type_annotations.rs b/parser/src/types/type_annotations.rs index 271e2b52..68009ab0 100644 --- a/parser/src/types/type_annotations.rs +++ b/parser/src/types/type_annotations.rs @@ -479,7 +479,8 @@ impl TypeAnnotation { TSXToken::CloseParentheses | TSXToken::CloseBracket | TSXToken::CloseBrace - | TSXToken::Comma | TSXToken::OpenChevron + | TSXToken::Comma + | TSXToken::OpenChevron ) || peek.is_assignment() || (start.map_or(false, |start| { peek.is_statement_or_declaration_start() diff --git a/src/ast_explorer.rs b/src/ast_explorer.rs index 7879d421..d8fd7b8a 100644 --- a/src/ast_explorer.rs +++ b/src/ast_explorer.rs @@ -1,16 +1,12 @@ #![allow(dead_code)] -use std::{fs, path::PathBuf}; +use std::path::PathBuf; use argh::FromArgs; -use console::style; use enum_variants_strings::EnumVariantsStrings; use parser::{source_map::FileSystem, ASTNode, Expression, Module, ToStringOptions}; -use crate::{ - reporting::report_diagnostics_to_cli, - utilities::{print_to_cli, print_to_cli_without_newline}, -}; +use crate::{reporting::report_diagnostics_to_cli, utilities::print_to_cli}; /// REPL for printing out AST from user input #[derive(FromArgs, Debug)] @@ -24,23 +20,25 @@ pub(crate) struct ExplorerArguments { } impl ExplorerArguments { + #[cfg(target_family = "wasm")] + pub(crate) fn run(&mut self, _fs_resolver: &T) { + panic!("Cannot run ast-explorer in WASM because of input callback. Consider reimplementing using library"); + } + #[allow(clippy::needless_continue)] - pub(crate) fn run( - &mut self, - fs_resolver: &T, - cli_input_resolver: U, - ) { + #[cfg(not(target_family = "wasm"))] + pub(crate) fn run(&mut self, fs_resolver: &T) { if let Some(ref file) = self.file { - let content = fs_resolver.get_content_at_path(file); + let content = fs_resolver.read_file(file); if let Some(content) = content { - self.nested.run(content, Some(file.to_owned())); + self.nested.run(String::from_utf8(content).unwrap(), Some(file.to_owned())); } else { eprintln!("Could not find file at {}", file.display()); } } else { print_to_cli(format_args!("ezno ast-explorer\nUse #exit to leave. Also #switch-mode *mode name* and #load-file *path*")); loop { - let input = cli_input_resolver(self.nested.to_str()).unwrap_or_default(); + let input = crate::utilities::cli_input_resolver(self.nested.to_str()); if input.is_empty() { continue; @@ -55,7 +53,7 @@ impl ExplorerArguments { } }; } else if let Some(path) = input.strip_prefix("#load-file ") { - let input = match fs::read_to_string(path.trim()) { + let input = match std::fs::read_to_string(path.trim()) { Ok(string) => string, Err(err) => { print_to_cli(format_args!("{err:?}")); @@ -80,7 +78,6 @@ pub(crate) enum ExplorerSubCommand { FullAST(FullASTArgs), Prettifier(PrettyArgs), Uglifier(UglifierArgs), - Lexer(LexerArgs), } /// Prints AST for a given expression @@ -99,6 +96,9 @@ pub(crate) struct FullASTArgs { /// print results as json #[argh(switch)] json: bool, + /// just print whether parse was successful + #[argh(switch)] + check: bool, } /// Prettifies source code (full whitespace) @@ -111,11 +111,6 @@ pub(crate) struct PrettyArgs {} #[argh(subcommand, name = "uglifier")] pub(crate) struct UglifierArgs {} -/// Prints sources with tokens over -#[derive(FromArgs, Debug, Default)] -#[argh(subcommand, name = "lexer")] -pub(crate) struct LexerArgs {} - impl ExplorerSubCommand { pub fn run(&self, input: String, path: Option) { match self { @@ -148,11 +143,20 @@ impl ExplorerSubCommand { ExplorerSubCommand::FullAST(cfg) => { let mut fs = parser::source_map::MapFileStore::::default(); - let source_id = fs.new_source_id(path.unwrap_or_default(), input.clone()); + let source_id = fs.new_source_id(path.clone().unwrap_or_default(), input.clone()); let res = Module::from_string(input, parser::ParseOptions::all_features()); match res { Ok(res) => { - if cfg.json { + if cfg.check { + if let Some(ref path) = path { + print_to_cli(format_args!( + "{path} parsed successfully", + path = path.display() + )); + } else { + print_to_cli(format_args!("Parsed successfully",)); + } + } else if cfg.json { print_to_cli(format_args!( "{}", serde_json::to_string_pretty(&res).unwrap() @@ -194,28 +198,6 @@ impl ExplorerSubCommand { .unwrap(), } } - ExplorerSubCommand::Lexer(_) => { - let mut color = console::Color::Red; - for (section, with) in parser::script_to_tokens(input) { - if with { - let value = style(section).bg(color); - // Cycle through colors - color = match color { - console::Color::Red => console::Color::Green, - console::Color::Green => console::Color::Yellow, - console::Color::Yellow => console::Color::Blue, - console::Color::Blue => console::Color::Magenta, - console::Color::Magenta => console::Color::Cyan, - console::Color::Cyan => console::Color::Red, - _ => unreachable!(), - }; - print_to_cli_without_newline(format_args!("{value}")); - } else { - print_to_cli_without_newline(format_args!("{section}")); - } - } - print_to_cli(format_args!("")); - } } } } diff --git a/src/build.rs b/src/build.rs index 0bc7f94f..e2b4fcb0 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,11 +1,8 @@ -use std::{ - mem, - path::{Path, PathBuf}, -}; +use std::{collections::HashMap, mem, path::PathBuf}; -use checker::{DiagnosticsContainer, TypeCheckOptions}; +use checker::TypeCheckOptions; use parser::{ - source_map::{MapFileStore, SourceMap, WithPathMap}, + source_map::{SourceId, SourceMap, WithPathMap}, ToStringOptions, }; @@ -17,45 +14,53 @@ pub struct Output { pub mappings: SourceMap, } -#[cfg_attr(target_family = "wasm", derive(serde::Serialize, tsify::Tsify))] pub struct BuildOutput { - pub outputs: Vec, - pub diagnostics: DiagnosticsContainer, - /// For diagnostics - /// TODO serde - #[cfg_attr(target_family = "wasm", serde(skip))] - pub fs: MapFileStore, + pub artifacts: Vec, + pub check_output: checker::CheckOutput, } -#[cfg_attr(target_family = "wasm", derive(serde::Serialize, tsify::Tsify))] -pub struct FailedBuildOutput { - pub diagnostics: DiagnosticsContainer, - /// For diagnostics - /// TODO serde - #[cfg_attr(target_family = "wasm", serde(skip))] - pub fs: MapFileStore, -} +pub struct FailedBuildOutput(pub checker::CheckOutput); -#[cfg_attr(target_family = "wasm", derive(serde::Deserialize))] +#[cfg_attr(target_family = "wasm", derive(serde::Deserialize, tsify::Tsify), serde(default))] pub struct BuildConfig { - #[cfg_attr(target_family = "wasm", serde(default))] + pub tree_shake: bool, pub strip_whitespace: bool, - #[cfg_attr(target_family = "wasm", serde(default))] pub source_maps: bool, + /// Run checker with partial syntax support + pub lsp_mode: bool, + pub output_path: PathBuf, + pub type_definition_module: Option, + #[cfg_attr(target_family = "wasm", serde(skip))] + pub other_transformers: Option, +} + +impl Default for BuildConfig { + fn default() -> BuildConfig { + BuildConfig { + tree_shake: false, + strip_whitespace: true, + source_maps: false, + lsp_mode: false, + type_definition_module: None, + // TODO not sure + output_path: PathBuf::from("out.js"), + other_transformers: None, + } + } } pub type EznoParsePostCheckVisitors = parser::visiting::VisitorsMut; +pub type OwnedEznoModule = + ::OwnedModule; +pub type SynthesisedEznoModule = checker::features::modules::SynthesisedModule; + +/// Subset of check output which is nicer for transformers to inferface on pub struct CheckingOutputWithoutDiagnostics { pub types: checker::types::TypeStore, - pub module_contents: parser::source_map::MapFileStore, - pub modules: std::collections::HashMap< - parser::SourceId, - checker::features::modules::SynthesisedModule< - ::OwnedModule, - >, - >, + pub module_contents: parser::source_map::MapFileStore, + pub modules: HashMap, } impl CheckingOutputWithoutDiagnostics { @@ -68,24 +73,33 @@ impl CheckingOutputWithoutDiagnostics { pub fn build( entry_points: Vec, fs_resolver: &T, - type_definition_module: Option<&Path>, - output_path: &Path, - config: &BuildConfig, - transformers: Option, + config: BuildConfig, ) -> Result { // TODO parse options + non_standard_library & non_standard_syntax - let type_check_options = TypeCheckOptions { store_type_mappings: true, ..Default::default() }; - - let result = - crate::check(entry_points, fs_resolver, type_definition_module, type_check_options); - - let mut data = CheckingOutputWithoutDiagnostics { - module_contents: result.module_contents, - modules: result.modules, - types: result.types, + let type_check_options = TypeCheckOptions { + store_type_mappings: true, + lsp_mode: config.lsp_mode, + ..Default::default() }; - if !result.diagnostics.has_error() { + let result = crate::check( + entry_points, + fs_resolver, + config.type_definition_module.as_deref(), + type_check_options, + ); + + if !result.diagnostics.contains_error() { + let checker::CheckOutput { + diagnostics, + module_contents, + chronometer, + types, + modules, + top_level_information, + } = result; + let mut data = CheckingOutputWithoutDiagnostics { module_contents, modules, types }; + // TODO For all modules let keys = data.modules.keys().cloned().collect::>(); @@ -95,9 +109,18 @@ pub fn build( span: parser::source_map::Nullable::NULL, }; - let mut outputs = Vec::new(); + let mut artifacts = Vec::new(); + let mut transformers = config.other_transformers.unwrap_or_default(); + + if config.tree_shake { + transformers + .expression_visitors_mut + .push(Box::new(crate::transformers::optimisations::ExpressionOptimiser)); - let mut transformers = transformers.unwrap_or_default(); + transformers + .statement_visitors_mut + .push(Box::new(crate::transformers::optimisations::StatementOptimiser)); + } for source in keys { // Remove the module @@ -115,26 +138,88 @@ pub fn build( source, ); - let to_string_options = if config.strip_whitespace { + let mut to_string_options = if config.strip_whitespace { ToStringOptions::minified() } else { ToStringOptions::default() }; - // TODO under cfg + // TODO temp fix + if config.lsp_mode { + to_string_options.expect_markers = true; + } + + // TODO source map creation not neccessary let (content, mappings) = module.to_string_with_source_map(&to_string_options, source, &data.module_contents); - outputs.push(Output { - output_path: output_path.to_path_buf(), + artifacts.push(Output { + output_path: config.output_path.to_path_buf(), content, mappings: mappings.unwrap(), - }) + }); } - Ok(BuildOutput { outputs, diagnostics: result.diagnostics, fs: data.module_contents }) + // Reconstruct + let check_output = checker::CheckOutput { + module_contents: data.module_contents, + modules: data.modules, + types: data.types, + diagnostics, + chronometer, + top_level_information, + }; + + Ok(BuildOutput { artifacts, check_output }) } else { - Err(FailedBuildOutput { diagnostics: result.diagnostics, fs: data.module_contents }) + Err(FailedBuildOutput(result)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[ignore = "not fixed implemented"] + fn tree_shaking() { + let source = r#" + function make_observable(obj) { + return new Proxy(obj, { + get(on, prop: string, _rec) { + return on[prop] + }, + }) + } + function get_a() { + return 1 + } + function get_b() { + return 1 + } + const obj = { + a() { return get_a() }, + b() { return get_b() }, + c: 2 + } + const value = make_observable(obj); + const a_value = value.a(); + const c_value = value.c; + "#; + + let config = BuildConfig { tree_shake: true, ..Default::default() }; + + if let Ok(output) = build( + vec!["index.tsx".into()], + &|_path: &std::path::Path| Some(source.to_owned()), + config, + ) { + let first_source = &output.artifacts[0].content; + // TODO assert output equal + panic!("{first_source:?}"); + } else { + panic!("build failed") + } } } diff --git a/src/check.rs b/src/check.rs index 436e6c28..b242f8b2 100644 --- a/src/check.rs +++ b/src/check.rs @@ -13,8 +13,12 @@ pub fn check( vec![checker::INTERNAL_DEFINITION_FILE_PATH.into()] }; - let read_from_fs = - |path: &Path| read_from_filesystem.get_content_at_path(path).map(String::into_bytes); - - checker::check_project(entry_points, definitions, read_from_fs, type_check_options, (), None) + checker::check_project( + entry_points, + definitions, + read_from_filesystem, + type_check_options, + (), + None, + ) } diff --git a/src/cli.rs b/src/cli.rs index a86bc4db..a62dab08 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -9,10 +9,10 @@ use std::{ }; use crate::{ - build::{build, BuildConfig, BuildOutput, EznoParsePostCheckVisitors, FailedBuildOutput}, + build::{build, BuildConfig, BuildOutput, FailedBuildOutput}, check::check, reporting::report_diagnostics_to_cli, - utilities::{self, print_to_cli, MaxDiagnostics}, + utilities::{print_to_cli, MaxDiagnostics}, }; use argh::FromArgs; use checker::{CheckOutput, TypeCheckOptions}; @@ -88,7 +88,7 @@ pub(crate) struct BuildArguments { pub compact_diagnostics: bool, /// enable optimising transforms (warning can currently break code) #[argh(switch)] - pub optimise: bool, + pub tree_shake: bool, /// maximum diagnostics to print (defaults to 30, pass `all` for all and `0` to count) #[argh(option, default = "MaxDiagnostics::default()")] pub max_diagnostics: MaxDiagnostics, @@ -182,13 +182,14 @@ fn run_checker( type_check_options: TypeCheckOptions, compact_diagnostics: bool, ) -> ExitCode { - let CheckOutput { diagnostics, module_contents, chronometer, types, .. } = - check(entry_points, read_file, definition_file.as_deref(), type_check_options); + let result = check(entry_points, read_file, definition_file.as_deref(), type_check_options); + + let CheckOutput { diagnostics, module_contents, chronometer, types, .. } = result; let diagnostics_count = diagnostics.count(); let current = timings.then(std::time::Instant::now); - let result = if diagnostics.has_error() { + let result = if diagnostics.contains_error() { if let MaxDiagnostics::FixedTo(0) = max_diagnostics { let count = diagnostics.into_iter().count(); print_to_cli(format_args!( @@ -235,11 +236,10 @@ fn run_checker( result } -pub fn run_cli( +pub fn run_cli( cli_arguments: &[&str], - read_file: &T, + read_file: T, write_file: U, - cli_input_resolver: V, ) -> ExitCode { let command = match FromArgs::from_args(&["ezno-cli"], cli_arguments) { Ok(TopLevel { nested }) => nested, @@ -264,18 +264,21 @@ pub fn run_cli entry_points, - Err(_) => return ExitCode::FAILURE, + Err(_) => { + print_to_cli(format_args!("Entry point error")); + return ExitCode::FAILURE; + } }; + // run_checker is written three times because cloning if watch { #[cfg(target_family = "wasm")] panic!("'watch' mode not supported on WASM"); @@ -293,13 +296,12 @@ pub fn run_cli { - run_checker( + let _out = run_checker( entry_points.clone(), - read_file, + &read_file, timings, definition_file.clone(), - max_diagnostics.clone(), + max_diagnostics, type_check_options.clone(), compact_diagnostics, ); @@ -320,57 +322,49 @@ pub fn run_cli eprintln!("Error: {error:?}"), } } - // Infinite loop here so the compiler is satisfied that this never returns - loop {} + + unreachable!() } + } else { + run_checker( + entry_points, + &read_file, + timings, + definition_file, + max_diagnostics, + type_check_options, + compact_diagnostics, + ) } - - run_checker( - entry_points, - read_file, - timings, - definition_file, - max_diagnostics, - type_check_options, - compact_diagnostics, - ) } CompilerSubCommand::Experimental(ExperimentalArguments { nested: ExperimentalSubcommand::Build(build_config), }) => { - let output_path = build_config.output.unwrap_or("ezno_output.js".into()); - - let mut default_builders = EznoParsePostCheckVisitors::default(); - - if build_config.optimise { - default_builders - .expression_visitors_mut - .push(Box::new(crate::transformers::optimisations::ExpressionOptimiser)); - - default_builders - .statement_visitors_mut - .push(Box::new(crate::transformers::optimisations::StatementOptimiser)); - } + let output_path = build_config.output.unwrap_or("ezno.out.js".into()); let entry_points = match get_entry_points(build_config.input) { Ok(entry_points) => entry_points, - Err(_) => return ExitCode::FAILURE, + Err(_) => { + print_to_cli(format_args!("Entry point error")); + return ExitCode::FAILURE; + } }; #[cfg(not(target_family = "wasm"))] let start = build_config.timings.then(std::time::Instant::now); - let output = build( - entry_points, - read_file, - build_config.definition_file.as_deref(), - &output_path, - &BuildConfig { - strip_whitespace: build_config.minify, - source_maps: build_config.source_maps, - }, - Some(default_builders), - ); + let config = BuildConfig { + tree_shake: build_config.tree_shake, + strip_whitespace: build_config.minify, + source_maps: build_config.source_maps, + type_definition_module: build_config.definition_file, + // TODO not sure + output_path: PathBuf::from(output_path), + other_transformers: None, + lsp_mode: false, + }; + + let output = build(entry_points, &read_file, config); #[cfg(not(target_family = "wasm"))] if let Some(start) = start { @@ -380,13 +374,16 @@ pub fn run_cli { - for output in outputs { + Ok(BuildOutput { + artifacts, + check_output: CheckOutput { module_contents, diagnostics, .. }, + }) => { + for output in artifacts { write_file(output.output_path.as_path(), output.content); } report_diagnostics_to_cli( diagnostics, - &fs, + &module_contents, compact_diagnostics, build_config.max_diagnostics, ) @@ -397,10 +394,10 @@ pub fn run_cli { + Err(FailedBuildOutput(CheckOutput { module_contents, diagnostics, .. })) => { report_diagnostics_to_cli( diagnostics, - &fs, + &module_contents, compact_diagnostics, build_config.max_diagnostics, ) @@ -467,7 +464,7 @@ pub fn run_cli match utilities::upgrade_self() { + }) => match crate::utilities::upgrade_self() { Ok(name) => { print_to_cli(format_args!("Successfully updated to {name}")); std::process::ExitCode::SUCCESS @@ -478,12 +475,12 @@ pub fn run_cli { - repl.run(read_file, cli_input_resolver); + repl.run(&read_file); // TODO not always true ExitCode::SUCCESS } CompilerSubCommand::Repl(argument) => { - crate::repl::run_repl(cli_input_resolver, argument); + crate::repl::run_repl(argument); // TODO not always true ExitCode::SUCCESS } // CompilerSubCommand::Run(run_arguments) => { diff --git a/src/js-based-plugin/index.mjs b/src/js-based-plugin/index.mjs index 827e6643..414f34b7 100644 --- a/src/js-based-plugin/index.mjs +++ b/src/js-based-plugin/index.mjs @@ -74,13 +74,12 @@ function plugin(options = {}) { } const output = build(path, readFile, false); - if (output.Ok) { - emitDiagnostics(code, output.Ok.diagnostics, this) + emitDiagnostics(code, output.diagnostics, this) + if (output.artifacts.length) { return { - code: output.Ok.outputs[0].content + code: output.artifacts[0] } } else { - emitDiagnostics(code, output.Err.diagnostics, this) this.warn("ezno had errors and did not transform"); return code; } diff --git a/src/js-cli-and-library/package-lock.json b/src/js-cli-and-library/package-lock.json index 5c68a52a..87ad852d 100644 --- a/src/js-cli-and-library/package-lock.json +++ b/src/js-cli-and-library/package-lock.json @@ -8,10 +8,14 @@ "name": "ezno", "version": "0.0.22", "license": "MIT", + "dependencies": { + "snapshot-fixtures": "^1.2.0" + }, "bin": { "ezno": "dist/cli.mjs" }, "devDependencies": { + "strip-ansi": "^7.1.0", "unbuild": "^2.0.0", "wasm-pack": "^0.13.0" }, @@ -21,70 +25,57 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -99,30 +90,40 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -130,63 +131,37 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" + "bin": { + "semver": "bin/semver.js" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -195,104 +170,54 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", - "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@babel/types": "^7.26.0" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", - "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -301,42 +226,39 @@ } }, "node_modules/@babel/standalone": { - "version": "7.23.10", - "resolved": "https://registry.npmjs.org/@babel/standalone/-/standalone-7.23.10.tgz", - "integrity": "sha512-xqWviI/pt1Zb/d+6ilWa5IDL2mkDzsBnlHbreqnfyP3/QB/ofQ1bNVcHj8YQX154Rf/xZKR6y0s1ydVF3nAS8g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/standalone/-/standalone-7.26.2.tgz", + "integrity": "sha512-i2VbegsRfwa9yq3xmfDX3tG2yh9K0cCqwpSyVG2nPxifh0EOnucAZUeO/g4lW2Zfg03aPJNtPfxQbDHzXc7H+w==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", - "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", - "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -345,14 +267,13 @@ } }, "node_modules/@babel/types": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", - "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -646,6 +567,22 @@ "node": ">=12" } }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/openbsd-x64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", @@ -727,47 +664,47 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -810,13 +747,10 @@ } }, "node_modules/@rollup/plugin-alias": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.1.0.tgz", - "integrity": "sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.1.1.tgz", + "integrity": "sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==", "dev": true, - "dependencies": { - "slash": "^4.0.0" - }, "engines": { "node": ">=14.0.0" }, @@ -830,9 +764,9 @@ } }, "node_modules/@rollup/plugin-commonjs": { - "version": "25.0.7", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz", - "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==", + "version": "25.0.8", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz", + "integrity": "sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -875,15 +809,14 @@ } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", - "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz", + "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", - "is-builtin-module": "^3.2.1", "is-module": "^1.0.0", "resolve": "^1.22.1" }, @@ -900,9 +833,9 @@ } }, "node_modules/@rollup/plugin-replace": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.5.tgz", - "integrity": "sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.7.tgz", + "integrity": "sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -921,14 +854,14 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", + "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==", "dev": true, "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" + "picomatch": "^4.0.2" }, "engines": { "node": ">=14.0.0" @@ -952,9 +885,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/@types/resolve": { @@ -963,10 +896,15 @@ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "dev": true }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -975,22 +913,25 @@ "node": ">=0.4.0" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "dependencies": { - "color-convert": "^1.9.0" + "engines": { + "node": ">=12" }, "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/autoprefixer": { - "version": "10.4.17", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", - "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -1007,11 +948,11 @@ } ], "dependencies": { - "browserslist": "^4.22.2", - "caniuse-lite": "^1.0.30001578", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -1069,21 +1010,21 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.22.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz", - "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -1100,10 +1041,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001580", - "electron-to-chromium": "^1.4.648", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -1112,18 +1053,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -1137,9 +1066,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001585", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001585.tgz", - "integrity": "sha512-yr2BWR1yLXQ8fMpdS/4ZZXpseBgE7o4g41x3a6AJOqZuOi+iE/WdJYAuZ6Y95i4Ohd2Y+9MzIWRR+uGABH4s3Q==", + "version": "1.0.30001677", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", + "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==", "dev": true, "funding": [ { @@ -1160,7 +1089,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -1177,30 +1105,29 @@ "node": ">=10" } }, - "node_modules/citty": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.5.tgz", - "integrity": "sha512-AS7n5NSc0OQVMV9v6wt3ByujNIrne0/cTjiC2MYqhvao57VNfiuVksTSr2p17nVOhEr2KtqiAkGwHcgMC/qUuQ==", - "dev": true, - "dependencies": { - "consola": "^3.2.3" + "node_modules/ci-info": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", + "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", "dev": true, "dependencies": { - "color-name": "1.1.3" + "consola": "^3.2.3" } }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -1228,6 +1155,12 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true + }, "node_modules/consola": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", @@ -1244,9 +1177,9 @@ "dev": true }, "node_modules/css-declaration-sorter": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.1.1.tgz", - "integrity": "sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", "dev": true, "engines": { "node": "^14 || ^16 || >=18" @@ -1309,16 +1242,16 @@ } }, "node_modules/cssnano": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.3.tgz", - "integrity": "sha512-MRq4CIj8pnyZpcI2qs6wswoYoDD1t0aL28n+41c1Ukcpm56m1h6mCexIHBGjfZfnTqtGSSCP4/fB1ovxgjBOiw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.6.tgz", + "integrity": "sha512-54woqx8SCbp8HwvNZYn68ZFAepuouZW4lTwiMVnBErM3VkO7/Sd4oTOt3Zz3bPx3kxQ36aISppyXj2Md4lg8bw==", "dev": true, "dependencies": { - "cssnano-preset-default": "^6.0.3", - "lilconfig": "^3.0.0" + "cssnano-preset-default": "^7.0.6", + "lilconfig": "^3.1.2" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "funding": { "type": "opencollective", @@ -1329,55 +1262,56 @@ } }, "node_modules/cssnano-preset-default": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.0.3.tgz", - "integrity": "sha512-4y3H370aZCkT9Ev8P4SO4bZbt+AExeKhh8wTbms/X7OLDo5E7AYUUy6YPxa/uF5Grf+AJwNcCnxKhZynJ6luBA==", - "dev": true, - "dependencies": { - "css-declaration-sorter": "^7.1.1", - "cssnano-utils": "^4.0.1", - "postcss-calc": "^9.0.1", - "postcss-colormin": "^6.0.2", - "postcss-convert-values": "^6.0.2", - "postcss-discard-comments": "^6.0.1", - "postcss-discard-duplicates": "^6.0.1", - "postcss-discard-empty": "^6.0.1", - "postcss-discard-overridden": "^6.0.1", - "postcss-merge-longhand": "^6.0.2", - "postcss-merge-rules": "^6.0.3", - "postcss-minify-font-values": "^6.0.1", - "postcss-minify-gradients": "^6.0.1", - "postcss-minify-params": "^6.0.2", - "postcss-minify-selectors": "^6.0.2", - "postcss-normalize-charset": "^6.0.1", - "postcss-normalize-display-values": "^6.0.1", - "postcss-normalize-positions": "^6.0.1", - "postcss-normalize-repeat-style": "^6.0.1", - "postcss-normalize-string": "^6.0.1", - "postcss-normalize-timing-functions": "^6.0.1", - "postcss-normalize-unicode": "^6.0.2", - "postcss-normalize-url": "^6.0.1", - "postcss-normalize-whitespace": "^6.0.1", - "postcss-ordered-values": "^6.0.1", - "postcss-reduce-initial": "^6.0.2", - "postcss-reduce-transforms": "^6.0.1", - "postcss-svgo": "^6.0.2", - "postcss-unique-selectors": "^6.0.2" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.6.tgz", + "integrity": "sha512-ZzrgYupYxEvdGGuqL+JKOY70s7+saoNlHSCK/OGn1vB2pQK8KSET8jvenzItcY+kA7NoWvfbb/YhlzuzNKjOhQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.3", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^5.0.0", + "postcss-calc": "^10.0.2", + "postcss-colormin": "^7.0.2", + "postcss-convert-values": "^7.0.4", + "postcss-discard-comments": "^7.0.3", + "postcss-discard-duplicates": "^7.0.1", + "postcss-discard-empty": "^7.0.0", + "postcss-discard-overridden": "^7.0.0", + "postcss-merge-longhand": "^7.0.4", + "postcss-merge-rules": "^7.0.4", + "postcss-minify-font-values": "^7.0.0", + "postcss-minify-gradients": "^7.0.0", + "postcss-minify-params": "^7.0.2", + "postcss-minify-selectors": "^7.0.4", + "postcss-normalize-charset": "^7.0.0", + "postcss-normalize-display-values": "^7.0.0", + "postcss-normalize-positions": "^7.0.0", + "postcss-normalize-repeat-style": "^7.0.0", + "postcss-normalize-string": "^7.0.0", + "postcss-normalize-timing-functions": "^7.0.0", + "postcss-normalize-unicode": "^7.0.2", + "postcss-normalize-url": "^7.0.0", + "postcss-normalize-whitespace": "^7.0.0", + "postcss-ordered-values": "^7.0.1", + "postcss-reduce-initial": "^7.0.2", + "postcss-reduce-transforms": "^7.0.0", + "postcss-svgo": "^7.0.1", + "postcss-unique-selectors": "^7.0.3" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/cssnano-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.1.tgz", - "integrity": "sha512-6qQuYDqsGoiXssZ3zct6dcMxiqfT6epy7x4R0TQJadd4LWO3sPR6JH6ZByOvVLoZ6EdwPGgd7+DR1EmX3tiXQQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.0.tgz", + "integrity": "sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==", "dev": true, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" @@ -1417,12 +1351,12 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1448,6 +1382,14 @@ "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", "dev": true }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1516,9 +1458,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.665", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.665.tgz", - "integrity": "sha512-UpyCWObBoD+nSZgOC2ToaIdZB0r9GhqT2WahPKiSki6ckkSuKhQNso8V2PrFcHBMleI/eqbKgVQgVC4Wni4ilw==", + "version": "1.5.52", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz", + "integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==", "dev": true }, "node_modules/entities": { @@ -1572,23 +1514,14 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -1620,10 +1553,24 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -1633,9 +1580,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true, "funding": [ { @@ -1665,20 +1612,6 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -1751,6 +1684,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -1806,25 +1740,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "dependencies": { "function-bind": "^1.1.2" @@ -1840,9 +1759,9 @@ "dev": true }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -1852,6 +1771,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -1864,28 +1784,16 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "dependencies": { - "builtin-modules": "^3.3.0" + "hasown": "^2.0.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1937,9 +1845,9 @@ } }, "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "dev": true, "bin": { "jiti": "bin/jiti.js" @@ -1952,15 +1860,15 @@ "dev": true }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json5": { @@ -1975,31 +1883,16 @@ "node": ">=6" } }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/lilconfig": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", - "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "dev": true, "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lodash.memoize": { @@ -2024,15 +1917,12 @@ } }, "node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/mdn-data": { @@ -2051,18 +1941,30 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -2128,31 +2030,32 @@ } }, "node_modules/mkdist": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/mkdist/-/mkdist-1.4.0.tgz", - "integrity": "sha512-LzzdzWDx6cWWPd8saIoO+kT5jnbijfeDaE6jZfmCYEi3YL2aJSyF23/tCFee/mDuh/ek1UQeSYdLeSa6oesdiw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mkdist/-/mkdist-1.6.0.tgz", + "integrity": "sha512-nD7J/mx33Lwm4Q4qoPgRBVA9JQNKgyE7fLo5vdPWVDdjz96pXglGERp/fRnGPCTB37Kykfxs5bDdXa9BWOT9nw==", "dev": true, "dependencies": { - "autoprefixer": "^10.4.14", - "citty": "^0.1.5", - "cssnano": "^6.0.1", - "defu": "^6.1.3", - "esbuild": "^0.19.7", - "fs-extra": "^11.1.1", - "globby": "^13.2.2", - "jiti": "^1.21.0", - "mlly": "^1.4.2", - "mri": "^1.2.0", - "pathe": "^1.1.1", - "postcss": "^8.4.26", - "postcss-nested": "^6.0.1" + "autoprefixer": "^10.4.20", + "citty": "^0.1.6", + "cssnano": "^7.0.6", + "defu": "^6.1.4", + "esbuild": "^0.24.0", + "jiti": "^1.21.6", + "mlly": "^1.7.1", + "pathe": "^1.1.2", + "pkg-types": "^1.2.0", + "postcss": "^8.4.45", + "postcss-nested": "^6.2.0", + "semver": "^7.6.3", + "tinyglobby": "^0.2.9" }, "bin": { "mkdist": "dist/cli.cjs" }, "peerDependencies": { - "sass": "^1.69.5", - "typescript": ">=5.3.2" + "sass": "^1.78.0", + "typescript": ">=5.5.4", + "vue-tsc": "^1.8.27 || ^2.0.21" }, "peerDependenciesMeta": { "sass": { @@ -2160,39 +2063,449 @@ }, "typescript": { "optional": true + }, + "vue-tsc": { + "optional": true } } }, - "node_modules/mlly": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.5.0.tgz", - "integrity": "sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==", + "node_modules/mkdist/node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "node_modules/mkdist/node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "node_modules/mkdist/node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/mkdist/node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + }, + "node_modules/mlly": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz", + "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", + "dev": true, + "dependencies": { + "acorn": "^8.12.1", + "pathe": "^1.1.2", + "pkg-types": "^1.2.0", + "ufo": "^1.5.4" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, "funding": [ @@ -2209,9 +2522,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/normalize-range": { @@ -2275,38 +2588,38 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/pkg-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", - "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz", + "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", "dev": true, "dependencies": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" + "confbox": "^0.1.8", + "mlly": "^1.7.2", + "pathe": "^1.1.2" } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -2324,412 +2637,422 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-calc": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", - "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.0.2.tgz", + "integrity": "sha512-DT/Wwm6fCKgpYVI7ZEWuPJ4az8hiEHtCUeYjZXqU7Ou4QqYh1Df2yCQ7Ca6N7xqKPFkxN3fhf+u9KSoOCJNAjg==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.11", + "postcss-selector-parser": "^6.1.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12 || ^20.9 || >=22.0" }, "peerDependencies": { - "postcss": "^8.2.2" + "postcss": "^8.4.38" } }, "node_modules/postcss-colormin": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.0.2.tgz", - "integrity": "sha512-TXKOxs9LWcdYo5cgmcSHPkyrLAh86hX1ijmyy6J8SbOhyv6ua053M3ZAM/0j44UsnQNIWdl8gb5L7xX2htKeLw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.2.tgz", + "integrity": "sha512-YntRXNngcvEvDbEjTdRWGU606eZvB5prmHG4BF0yLmVpamXbpsRJzevyy6MZVyuecgzI2AWAlvFi8DAeCqwpvA==", "dev": true, "dependencies": { - "browserslist": "^4.22.2", + "browserslist": "^4.23.3", "caniuse-api": "^3.0.0", - "colord": "^2.9.1", + "colord": "^2.9.3", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-convert-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.0.2.tgz", - "integrity": "sha512-aeBmaTnGQ+NUSVQT8aY0sKyAD/BaLJenEKZ03YK0JnDE1w1Rr8XShoxdal2V2H26xTJKr3v5haByOhJuyT4UYw==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.4.tgz", + "integrity": "sha512-e2LSXPqEHVW6aoGbjV9RsSSNDO3A0rZLCBxN24zvxF25WknMPpX8Dm9UxxThyEbaytzggRuZxaGXqaOhxQ514Q==", "dev": true, "dependencies": { - "browserslist": "^4.22.2", + "browserslist": "^4.23.3", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-discard-comments": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.1.tgz", - "integrity": "sha512-f1KYNPtqYLUeZGCHQPKzzFtsHaRuECe6jLakf/RjSRqvF5XHLZnM2+fXLhb8Qh/HBFHs3M4cSLb1k3B899RYIg==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.3.tgz", + "integrity": "sha512-q6fjd4WU4afNhWOA2WltHgCbkRhZPgQe7cXF74fuVB/ge4QbM9HEaOIzGSiMvM+g/cOsNAUGdf2JDzqA2F8iLA==", "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.1.2" + }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-discard-duplicates": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.1.tgz", - "integrity": "sha512-1hvUs76HLYR8zkScbwyJ8oJEugfPV+WchpnA+26fpJ7Smzs51CzGBHC32RS03psuX/2l0l0UKh2StzNxOrKCYg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.1.tgz", + "integrity": "sha512-oZA+v8Jkpu1ct/xbbrntHRsfLGuzoP+cpt0nJe5ED2FQF8n8bJtn7Bo28jSmBYwqgqnqkuSXJfSUEE7if4nClQ==", "dev": true, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-discard-empty": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.1.tgz", - "integrity": "sha512-yitcmKwmVWtNsrrRqGJ7/C0YRy53i0mjexBDQ9zYxDwTWVBgbU4+C9jIZLmQlTDT9zhml+u0OMFJh8+31krmOg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.0.tgz", + "integrity": "sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==", "dev": true, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-discard-overridden": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.1.tgz", - "integrity": "sha512-qs0ehZMMZpSESbRkw1+inkf51kak6OOzNRaoLd/U7Fatp0aN2HQ1rxGOrJvYcRAN9VpX8kUF13R2ofn8OlvFVA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.0.tgz", + "integrity": "sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==", "dev": true, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-merge-longhand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.2.tgz", - "integrity": "sha512-+yfVB7gEM8SrCo9w2lCApKIEzrTKl5yS1F4yGhV3kSim6JzbfLGJyhR1B6X+6vOT0U33Mgx7iv4X9MVWuaSAfw==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.4.tgz", + "integrity": "sha512-zer1KoZA54Q8RVHKOY5vMke0cCdNxMP3KBfDerjH/BYHh4nCIh+1Yy0t1pAEQF18ac/4z3OFclO+ZVH8azjR4A==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^6.0.2" + "stylehacks": "^7.0.4" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-merge-rules": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.0.3.tgz", - "integrity": "sha512-yfkDqSHGohy8sGYIJwBmIGDv4K4/WrJPX355XrxQb/CSsT4Kc/RxDi6akqn5s9bap85AWgv21ArcUWwWdGNSHA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.4.tgz", + "integrity": "sha512-ZsaamiMVu7uBYsIdGtKJ64PkcQt6Pcpep/uO90EpLS3dxJi6OXamIobTYcImyXGoW0Wpugh7DSD3XzxZS9JCPg==", "dev": true, "dependencies": { - "browserslist": "^4.22.2", + "browserslist": "^4.23.3", "caniuse-api": "^3.0.0", - "cssnano-utils": "^4.0.1", - "postcss-selector-parser": "^6.0.15" + "cssnano-utils": "^5.0.0", + "postcss-selector-parser": "^6.1.2" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-minify-font-values": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.1.tgz", - "integrity": "sha512-tIwmF1zUPoN6xOtA/2FgVk1ZKrLcCvE0dpZLtzyyte0j9zUeB8RTbCqrHZGjJlxOvNWKMYtunLrrl7HPOiR46w==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.0.tgz", + "integrity": "sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-minify-gradients": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.1.tgz", - "integrity": "sha512-M1RJWVjd6IOLPl1hYiOd5HQHgpp6cvJVLrieQYS9y07Yo8itAr6jaekzJphaJFR0tcg4kRewCk3kna9uHBxn/w==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.0.tgz", + "integrity": "sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==", "dev": true, "dependencies": { - "colord": "^2.9.1", - "cssnano-utils": "^4.0.1", + "colord": "^2.9.3", + "cssnano-utils": "^5.0.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-minify-params": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.0.2.tgz", - "integrity": "sha512-zwQtbrPEBDj+ApELZ6QylLf2/c5zmASoOuA4DzolyVGdV38iR2I5QRMsZcHkcdkZzxpN8RS4cN7LPskOkTwTZw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.2.tgz", + "integrity": "sha512-nyqVLu4MFl9df32zTsdcLqCFfE/z2+f8GE1KHPxWOAmegSo6lpV2GNy5XQvrzwbLmiU7d+fYay4cwto1oNdAaQ==", "dev": true, "dependencies": { - "browserslist": "^4.22.2", - "cssnano-utils": "^4.0.1", + "browserslist": "^4.23.3", + "cssnano-utils": "^5.0.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-minify-selectors": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.2.tgz", - "integrity": "sha512-0b+m+w7OAvZejPQdN2GjsXLv5o0jqYHX3aoV0e7RBKPCsB7TYG5KKWBFhGnB/iP3213Ts8c5H4wLPLMm7z28Sg==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.4.tgz", + "integrity": "sha512-JG55VADcNb4xFCf75hXkzc1rNeURhlo7ugf6JjiiKRfMsKlDzN9CXHZDyiG6x/zGchpjQS+UAgb1d4nqXqOpmA==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.15" + "cssesc": "^3.0.0", + "postcss-selector-parser": "^6.1.2" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "postcss-selector-parser": "^6.0.11" + "postcss-selector-parser": "^6.1.1" }, "engines": { "node": ">=12.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.2.14" } }, "node_modules/postcss-normalize-charset": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.1.tgz", - "integrity": "sha512-aW5LbMNRZ+oDV57PF9K+WI1Z8MPnF+A8qbajg/T8PP126YrGX1f9IQx21GI2OlGz7XFJi/fNi0GTbY948XJtXg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.0.tgz", + "integrity": "sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==", "dev": true, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-display-values": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.1.tgz", - "integrity": "sha512-mc3vxp2bEuCb4LgCcmG1y6lKJu1Co8T+rKHrcbShJwUmKJiEl761qb/QQCfFwlrvSeET3jksolCR/RZuMURudw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.0.tgz", + "integrity": "sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-positions": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.1.tgz", - "integrity": "sha512-HRsq8u/0unKNvm0cvwxcOUEcakFXqZ41fv3FOdPn916XFUrympjr+03oaLkuZENz3HE9RrQE9yU0Xv43ThWjQg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.0.tgz", + "integrity": "sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-repeat-style": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.1.tgz", - "integrity": "sha512-Gbb2nmCy6tTiA7Sh2MBs3fj9W8swonk6lw+dFFeQT68B0Pzwp1kvisJQkdV6rbbMSd9brMlS8I8ts52tAGWmGQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.0.tgz", + "integrity": "sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-string": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.1.tgz", - "integrity": "sha512-5Fhx/+xzALJD9EI26Aq23hXwmv97Zfy2VFrt5PLT8lAhnBIZvmaT5pQk+NuJ/GWj/QWaKSKbnoKDGLbV6qnhXg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.0.tgz", + "integrity": "sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-timing-functions": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.1.tgz", - "integrity": "sha512-4zcczzHqmCU7L5dqTB9rzeqPWRMc0K2HoR+Bfl+FSMbqGBUcP5LRfgcH4BdRtLuzVQK1/FHdFoGT3F7rkEnY+g==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.0.tgz", + "integrity": "sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-unicode": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.2.tgz", - "integrity": "sha512-Ff2VdAYCTGyMUwpevTZPZ4w0+mPjbZzLLyoLh/RMpqUqeQKZ+xMm31hkxBavDcGKcxm6ACzGk0nBfZ8LZkStKA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.2.tgz", + "integrity": "sha512-ztisabK5C/+ZWBdYC+Y9JCkp3M9qBv/XFvDtSw0d/XwfT3UaKeW/YTm/MD/QrPNxuecia46vkfEhewjwcYFjkg==", "dev": true, "dependencies": { - "browserslist": "^4.22.2", + "browserslist": "^4.23.3", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-url": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.1.tgz", - "integrity": "sha512-jEXL15tXSvbjm0yzUV7FBiEXwhIa9H88JOXDGQzmcWoB4mSjZIsmtto066s2iW9FYuIrIF4k04HA2BKAOpbsaQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.0.tgz", + "integrity": "sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-whitespace": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.1.tgz", - "integrity": "sha512-76i3NpWf6bB8UHlVuLRxG4zW2YykF9CTEcq/9LGAiz2qBuX5cBStadkk0jSkg9a9TCIXbMQz7yzrygKoCW9JuA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.0.tgz", + "integrity": "sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-ordered-values": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.1.tgz", - "integrity": "sha512-XXbb1O/MW9HdEhnBxitZpPFbIvDgbo9NK4c/5bOfiKpnIGZDoL2xd7/e6jW5DYLsWxBbs+1nZEnVgnjnlFViaA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.1.tgz", + "integrity": "sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==", "dev": true, "dependencies": { - "cssnano-utils": "^4.0.1", + "cssnano-utils": "^5.0.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-initial": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.0.2.tgz", - "integrity": "sha512-YGKalhNlCLcjcLvjU5nF8FyeCTkCO5UtvJEt0hrPZVCTtRLSOH4z00T1UntQPj4dUmIYZgMj8qK77JbSX95hSw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.2.tgz", + "integrity": "sha512-pOnu9zqQww7dEKf62Nuju6JgsW2V0KRNBHxeKohU+JkHd/GAH5uvoObqFLqkeB2n20mr6yrlWDvo5UBU5GnkfA==", "dev": true, "dependencies": { - "browserslist": "^4.22.2", + "browserslist": "^4.23.3", "caniuse-api": "^3.0.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-transforms": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.1.tgz", - "integrity": "sha512-fUbV81OkUe75JM+VYO1gr/IoA2b/dRiH6HvMwhrIBSUrxq3jNZQZitSnugcTLDi1KkQh1eR/zi+iyxviUNBkcQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.0.tgz", + "integrity": "sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -2740,31 +3063,31 @@ } }, "node_modules/postcss-svgo": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.2.tgz", - "integrity": "sha512-IH5R9SjkTkh0kfFOQDImyy1+mTCb+E830+9SV1O+AaDcoHTvfsvt6WwJeo7KwcHbFnevZVCsXhDmjFiGVuwqFQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.0.1.tgz", + "integrity": "sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0", - "svgo": "^3.2.0" + "svgo": "^3.3.2" }, "engines": { - "node": "^14 || ^16 || >= 18" + "node": "^18.12.0 || ^20.9.0 || >= 18" }, "peerDependencies": { "postcss": "^8.4.31" } }, "node_modules/postcss-unique-selectors": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.2.tgz", - "integrity": "sha512-8IZGQ94nechdG7Y9Sh9FlIY2b4uS8/k8kdKRX040XHsS3B6d1HrJAkXrBSsSu4SuARruSsUjW3nlSw8BHkaAYQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.3.tgz", + "integrity": "sha512-J+58u5Ic5T1QjP/LDV9g3Cx4CNOgB5vz+kM6+OxHHhFACdcDeKhBXjQmB7fnIZM12YSTvsL0Opwco83DmacW2g==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.15" + "postcss-selector-parser": "^6.1.2" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { "postcss": "^8.4.31" @@ -2895,9 +3218,9 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -2911,12 +3234,12 @@ } }, "node_modules/rollup-plugin-dts": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-6.1.0.tgz", - "integrity": "sha512-ijSCPICkRMDKDLBK9torss07+8dl9UpY9z1N/zTeA1cIqdzMlpkV3MOOC7zukyvQfDyxa1s3Dl2+DeiP/G6DOw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-6.1.1.tgz", + "integrity": "sha512-aSHRcJ6KG2IHIioYlvAOcEq6U99sVtqDDKVhnwt70rW6tsz3tv5OSjEiWcgzfsHdLyGXZ/3b/7b/+Za3Y6r1XA==", "dev": true, "dependencies": { - "magic-string": "^0.30.4" + "magic-string": "^0.30.10" }, "engines": { "node": ">=16" @@ -2925,7 +3248,7 @@ "url": "https://github.com/sponsors/Swatinem" }, "optionalDependencies": { - "@babel/code-frame": "^7.22.13" + "@babel/code-frame": "^7.24.2" }, "peerDependencies": { "rollup": "^3.29.4 || ^4", @@ -2962,12 +3285,15 @@ "dev": true }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/slash": { @@ -2982,41 +3308,82 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/snapshot-fixtures": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/snapshot-fixtures/-/snapshot-fixtures-1.2.0.tgz", + "integrity": "sha512-+JH/QfYxlFCD64/ifTAk39dHFzv+cHHeu+U1ocyBNcYhgTath5CFHH8m6yW7I27xODKQc8gIDQbsl8HwRnyWRg==", + "dependencies": { + "chalk": "^5.0.0", + "ci-info": "^4.0.0", + "diff": "^5.0.0", + "to-vfile": "^8.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/remcohaszing" + }, + "peerDependencies": { + "prettier": ">=3" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/stylehacks": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.2.tgz", "integrity": "sha512-00zvJGnCu64EpMjX8b5iCZ3us2Ptyw8+toEkb92VdmkEaRaSGBNKAoK6aWZckhXxmQP8zWiTaFaiMGIU8Ve8sg==", "dev": true, "dependencies": { - "browserslist": "^4.22.2", - "postcss-selector-parser": "^6.0.15" + "ansi-regex": "^6.0.1" }, "engines": { - "node": "^14 || ^16 || >=18.0" + "node": ">=12" }, - "peerDependencies": { - "postcss": "^8.4.31" + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/stylehacks": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.4.tgz", + "integrity": "sha512-i4zfNrGMt9SB4xRK9L83rlsFCgdGANfeDAYacO1pkqcE7cRHPdWHwnKZVz7WY17Veq/FvyYsRAU++Ga+qDFIww==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "browserslist": "^4.23.3", + "postcss-selector-parser": "^6.1.2" }, "engines": { - "node": ">=4" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -3032,9 +3399,9 @@ } }, "node_modules/svgo": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", - "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dev": true, "dependencies": { "@trysound/sax": "0.2.0", @@ -3079,13 +3446,17 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "node_modules/tinyglobby": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", + "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", "dev": true, + "dependencies": { + "fdir": "^6.4.2", + "picomatch": "^4.0.2" + }, "engines": { - "node": ">=4" + "node": ">=12.0.0" } }, "node_modules/to-regex-range": { @@ -3100,10 +3471,22 @@ "node": ">=8.0" } }, + "node_modules/to-vfile": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-8.0.0.tgz", + "integrity": "sha512-IcmH1xB5576MJc9qcfEC/m/nQCFt3fzMHz45sSlgJyTWjRbKW1HAkJpuf3DgE57YzIlZcwcBZA5ENQbBo4aLkg==", + "dependencies": { + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "peer": true, "bin": { @@ -3115,9 +3498,9 @@ } }, "node_modules/ufo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", - "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", "dev": true }, "node_modules/unbuild": { @@ -3163,37 +3546,49 @@ } } }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, "node_modules/untyped": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/untyped/-/untyped-1.4.2.tgz", - "integrity": "sha512-nC5q0DnPEPVURPhfPQLahhSTnemVtPzdx7ofiRxXpOB2SYnb3MfdU3DVGyJdS8Lx+tBWeAePO8BfU/3EgksM7Q==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/untyped/-/untyped-1.5.1.tgz", + "integrity": "sha512-reBOnkJBFfBZ8pCKaeHgfZLcehXtM6UTxc+vqs1JvCps0c4amLNp3fhdGBZwYp+VLyoY9n3X5KOP7lCyWBUX9A==", "dev": true, "dependencies": { - "@babel/core": "^7.23.7", - "@babel/standalone": "^7.23.8", - "@babel/types": "^7.23.6", + "@babel/core": "^7.25.7", + "@babel/standalone": "^7.25.7", + "@babel/types": "^7.25.7", "defu": "^6.1.4", - "jiti": "^1.21.0", + "jiti": "^2.3.1", "mri": "^1.2.0", - "scule": "^1.2.0" + "scule": "^1.3.0" }, "bin": { "untyped": "dist/cli.mjs" } }, + "node_modules/untyped/node_modules/jiti": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.0.tgz", + "integrity": "sha512-H5UpaUI+aHOqZXlYOaFP/8AzKsg+guWu+Pr3Y8i7+Y3zr1aXAvCvTAQ1RxSc6oVD8R8c7brgNtTVP91E7upH/g==", + "dev": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -3210,8 +3605,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -3226,10 +3621,36 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/wasm-pack": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.13.0.tgz", - "integrity": "sha512-AmboGZEnZoIcVCzSlkLEmNFEqJN+IwgshJ5S7pi30uNUTce4LvWkifQzsQRxnWj47G8gkqZxlyGlyQplsnIS7w==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.13.1.tgz", + "integrity": "sha512-P9exD4YkjpDbw68xUhF3MDm/CC/3eTmmthyG5bHJ56kalxOTewOunxTke4SyF8MTXV6jUtNjXggPgrGmMtczGg==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -3252,4 +3673,4 @@ "dev": true } } -} +} \ No newline at end of file diff --git a/src/js-cli-and-library/package.json b/src/js-cli-and-library/package.json index 7a6f1067..5653a853 100644 --- a/src/js-cli-and-library/package.json +++ b/src/js-cli-and-library/package.json @@ -20,13 +20,13 @@ }, "scripts": { "clean": "rmdir dist && rmdir build", - "build": "wasm-pack build --dev --out-dir src/js-cli-and-library/build --target web --no-pack && npm run build-js", - "build-release": "wasm-pack build --release --out-dir src/js-cli-and-library/build --target web --no-pack && npm run build-js", + "build": "wasm-pack build --dev --target web --no-pack --out-dir src/js-cli-and-library/build && npm run build-js", + "build-release": "wasm-pack build --release --target web --no-pack --out-dir src/js-cli-and-library/build && npm run build-js", "--": "Manually assembles the output", "build-manual-bind": "cargo build --lib --target wasm32-unknown-unknown --profile debug && wasm-bindgen --out-dir build --target web ../../target/wasm32-unknown-unknown/debug/ezno_lib.wasm && npm run build-js", "build-js": "unbuild && cp ./build/ezno_lib_bg.wasm dist/shared && cp src/cli_node.cjs dist/cli.cjs", "test": "npm run build && npm run run-tests", - "run-tests": "node test.mjs && deno run -A test.mjs" + "run-tests": "node --test" }, "keywords": [ "typescript", @@ -74,7 +74,9 @@ } }, "devDependencies": { - "unbuild": "^2.0.0", - "wasm-pack": "^0.13.0" + "strip-ansi": "^7.1.0", + "unbuild": "^2.0.0", + "wasm-pack": "^0.13.0", + "snapshot-fixtures": "^1.2.0" } -} +} \ No newline at end of file diff --git a/src/js-cli-and-library/src/cli.js b/src/js-cli-and-library/src/cli.js index 863f1e5e..d0f620b5 100644 --- a/src/js-cli-and-library/src/cli.js +++ b/src/js-cli-and-library/src/cli.js @@ -1,7 +1,9 @@ #!/usr/bin/env node -import { initSync, run_cli } from "../build/ezno_lib.js"; +import { initSync, run_cli, parse_module, ReplSystem } from "../build/ezno_lib.js"; import { readFileSync, writeFileSync } from "node:fs"; +import * as readline from 'node:readline/promises'; +import { stdin as input, stdout as output } from 'node:process'; const wasmPath = new URL("./shared/ezno_lib_bg.wasm", import.meta.url); if (wasmPath.protocol === "https:") { @@ -21,13 +23,33 @@ function writeFile(path, content) { writeFileSync(path, content) } -function readFromCLI(prompt_msg) { - if (typeof Deno !== "undefined") { - return prompt(`${prompt_msg}>`); - } else { - console.error("Prompt not supported in NodeJS (sync issue)"); - throw new Error("Prompt not supported in NodeJS"); +// Fix because REPL requires syncronous stdin input which isn't +// TODO also ast-explorer +if (cliArguments.length === 1 && (cliArguments[0] === "repl" || cliArguments[0] === "ast-explorer")) { + const kind = cliArguments[0]; + const rl = readline.createInterface({ input, output }); + + if (kind === "repl") { + console.log("Entering REPL"); + const system = ReplSystem.new_system({}, (path) => { console.trace(path); return "" }); + while (true) { + const answer = await rl.question('> '); + if (answer.length === 0 || answer === ".exit" || answer === "close()") { + break + } + system.execute_statement(answer); + } + } else if (kind === "ast-explorer") { + console.log("Entering ast-explorer"); + while (true) { + const answer = await rl.question('> '); + if (answer.length === 0 || answer === ".exit" || answer === "close()") { + break + } + console.dir(parse_module(answer), { depth: Number.POSITIVE_INFINITY }); + } } + rl.close(); +} else { + run_cli(cliArguments, readFile, writeFile); } - -run_cli(cliArguments, readFile, writeFile, readFromCLI); diff --git a/src/js-cli-and-library/test.mjs b/src/js-cli-and-library/test.mjs deleted file mode 100644 index 42c21cba..00000000 --- a/src/js-cli-and-library/test.mjs +++ /dev/null @@ -1,38 +0,0 @@ -import { check, parse_expression, get_version } from "./dist/initialised.mjs"; -import assert, { deepStrictEqual } from "node:assert"; - -function buildTest() { - // TODO -} - -buildTest() - -function checkTest() { - const example = "const x: 4 = 2;" - const output = check("input.ts", (_path) => example); - deepStrictEqual(output.diagnostics, [ - { - reason: "Type 2 is not assignable to type 4", - position: { start: 13, end: 14, source: 2 }, - labels: [ - [ - "Variable declared with type 4", - { - end: 10, - source: 2, - start: 9, - }, - ], - ], - kind: "error" - }, - ], - ); - console.log("WASM: check test passed") -} - -checkTest() - -console.log(parse_expression("x = 4 + 2")) - -console.log(get_version()) \ No newline at end of file diff --git a/src/js-cli-and-library/tests/cli.test.mjs b/src/js-cli-and-library/tests/cli.test.mjs new file mode 100644 index 00000000..e5fb66e6 --- /dev/null +++ b/src/js-cli-and-library/tests/cli.test.mjs @@ -0,0 +1,64 @@ +import { test } from "node:test"; +import { spawn } from "node:child_process"; +import { assertEqual } from "snapshot-fixtures"; +import stripAnsi from "strip-ansi"; + +const wait = (timeout = 500) => new Promise((res, rej) => setTimeout(res, timeout)); + +test("ast-explorer", { timeout: 10000 }, async (t) => { + await t.test("works", async () => { + const decoder = new TextDecoder(); + + function write(child, command) { + child.stdin.write(command + "\r\n"); + } + + const child = spawn("node", ["./dist/cli.mjs", "ast-explorer"], { stdio: "pipe" }); + + child.stdout.on("data", (d) => out.push(decoder.decode(d))); + child.stderr.on("data", (d) => out.push(decoder.decode(d))); + + const out = []; + + await wait(); + write(child, "'Hello World'"); + await wait(); + write(child, "close()") + await wait(); + + const expected = `Entering ast-explorer\n> {\n Ok: {\n hashbang_comment: undefined,\n items: [\n {\n Statement: {\n Expression: {\n Single: {\n StringLiteral: [ 'Hello World', 'Single', { start: 0, end: 13 } ]\n }\n }\n }\n }\n ],\n span: { start: 0, end: 13 }\n }\n}\n> `; + + // console.log(stripAnsi(out.join("")).replaceAll("\n", "\\n")); + + assertEqual(stripAnsi(out.join("")), expected); + }); +}); + +test("type checking repl", { timeout: 10000 }, async (t) => { + await t.test("works", async () => { + const decoder = new TextDecoder(); + + function write(child, command) { + child.stdin.write(command + "\r\n"); + } + + const child = spawn("node", ["./dist/cli.mjs", "repl"], { stdio: "pipe" }); + + child.stdout.on("data", (d) => out.push(decoder.decode(d))); + child.stderr.on("data", (d) => out.push(decoder.decode(d))); + + const out = []; + + await wait(); + write(child, "const var1: string = 5 + 6;"); + await wait(); + write(child, "close()") + await wait(); + + const expected = `Entering REPL\n> error: \n ┌─ CLI.tsx:1:22\n │\n1 │ const var1: string = 5 + 6;\n │ ------ ^^^^^ Type 11 is not assignable to type string\n │ │ \n │ Variable declared with type string\n\n\n> `; + + // console.log(stripAnsi(out.join("")).replaceAll("\n", "\\n")); + + assertEqual(stripAnsi(out.join("")), expected); + }); +}); diff --git a/src/js-cli-and-library/tests/library.test.mjs b/src/js-cli-and-library/tests/library.test.mjs new file mode 100644 index 00000000..0a8468fe --- /dev/null +++ b/src/js-cli-and-library/tests/library.test.mjs @@ -0,0 +1,66 @@ +import { check, parse_expression, get_version, experimental_build } from "../dist/initialised.mjs"; +import { deepStrictEqual } from "node:assert"; +import { test } from "node:test"; +import { inspect } from "node:util"; + +// console.log(`Running ezno@${get_version()}`) + +test("Type checking on code diagnostics", (t) => { + t.test("type check", () => { + const example = "const x: 4 = 2;" + const output = check("input.ts", (_path) => example); + deepStrictEqual(output.diagnostics, [ + { + reason: "Type 2 is not assignable to type 4", + position: { start: 13, end: 14, source: 2 }, + labels: [ + [ + "Variable declared with type 4", + { + end: 10, + source: 2, + start: 9, + }, + ], + ], + kind: "error" + }, + ]); + }); +}); + +test("Compiling", (t) => { + t.test("Compile", () => { + const example = "const x: 4 = 2 + 2;" + const output = experimental_build("input.ts", (_path) => example, { strip_whitespace: true }); + deepStrictEqual(output.diagnostics, []); + deepStrictEqual(output.artifacts, [{ content: 'const x=2+2', output_path: 'out.js' }]); + }) +}); + +test("Parsing", (t) => { + t.test("expressions", () => { + const expression = parse_expression("x = 4 + 2"); + + // console.log(inspect(expression, { depth: Infinity, colors: true })); + + deepStrictEqual(expression, { + Ok: { + Assignment: { + lhs: { + VariableOrPropertyAccess: { Variable: ['x', { start: 0, end: 1 }] } + }, + rhs: { + BinaryOperation: { + lhs: { NumberLiteral: [{ Number: 4 }, { start: 4, end: 5 }] }, + operator: 'Add', + rhs: { NumberLiteral: [{ Number: 2 }, { start: 8, end: 9 }] }, + position: { start: 4, end: 9 } + } + }, + position: { start: 0, end: 9 } + } + } + }); + }) +}) diff --git a/src/lib.rs b/src/lib.rs index e5b7a08e..6f0f12e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(target_family = "wasm", allow(unused))] + mod ast_explorer; mod build; mod check; @@ -11,6 +13,7 @@ pub mod transformers; pub use build::build; pub use check::check; +pub(crate) use checker::ReadFromFS; pub use checker::{Diagnostic, DiagnosticKind}; pub use parser::{source_map, ASTNode, ToStringOptions}; @@ -21,42 +24,6 @@ pub fn prettifier(input: String) -> Result { Ok(module.to_string(&ToStringOptions::default())) } -#[cfg(target_family = "wasm")] -pub trait ReadFromFS { - fn get_content_at_path(&self, path: &std::path::Path) -> Option; -} - -// Not WASM require `Sync + Send` for watch mode -#[cfg(not(target_family = "wasm"))] -pub trait ReadFromFS: Sync + Send { - fn get_content_at_path(&self, path: &std::path::Path) -> Option; -} - -#[cfg(target_family = "wasm")] -impl ReadFromFS for T -where - T: Fn(&std::path::Path) -> Option, -{ - fn get_content_at_path(&self, path: &std::path::Path) -> Option { - (self)(path) - } -} - -#[cfg(not(target_family = "wasm"))] -impl ReadFromFS for T -where - T: Fn(&std::path::Path) -> Option + Sync + Send, -{ - fn get_content_at_path(&self, path: &std::path::Path) -> Option { - (self)(path) - } -} - -/// prompt -> response -pub trait CLIInputResolver: Fn(&str) -> Option {} - -impl CLIInputResolver for T where T: Fn(&str) -> Option {} - pub trait WriteToFS: Fn(&std::path::Path, String) {} impl WriteToFS for T where T: Fn(&std::path::Path, String) {} diff --git a/src/main.rs b/src/main.rs index db171795..2c13db82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,28 +13,6 @@ #![warn(clippy::cast_precision_loss, clippy::cast_possible_truncation, clippy::cast_sign_loss)] use ezno_lib::cli::run_cli; -use std::io; - -#[cfg(target_family = "windows")] -pub(crate) fn cli_input_resolver(prompt: &str) -> String { - print!("{prompt}> "); - io::Write::flush(&mut io::stdout()).unwrap(); - let mut input = String::new(); - let std_in = &mut io::stdin(); - let _n = multiline_term_input::read_string(std_in, &mut input); - input -} - -#[cfg(target_family = "unix")] -#[allow(clippy::unnecessary_wraps)] -pub(crate) fn cli_input_resolver(prompt: &str) -> String { - print!("{prompt}> "); - io::Write::flush(&mut io::stdout()).unwrap(); - let mut input = String::new(); - let std_in = &mut io::stdin(); - let _n = std_in.read_line(&mut input).unwrap(); - input -} fn main() -> std::process::ExitCode { fn read_from_file(path: &std::path::Path) -> Option { @@ -48,5 +26,5 @@ fn main() -> std::process::ExitCode { let arguments = std::env::args().skip(1).collect::>(); let arguments = arguments.iter().map(String::as_str).collect::>(); - run_cli(&arguments, &read_from_file, write_to_file, |p| Some(cli_input_resolver(p))) + run_cli(&arguments, read_from_file, write_to_file) } diff --git a/src/repl.rs b/src/repl.rs index 1978a1a1..316bb9f1 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,17 +1,18 @@ -use std::fs; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use argh::FromArgs; -use parser::{visiting::VisitorsMut, ASTNode}; -use parser::{Expression, Module, Statement}; +use parser::{visiting::VisitorsMut, ASTNode, Expression, Module, SourceId, Statement}; use crate::reporting::report_diagnostics_to_cli; -use crate::utilities::print_to_cli; -/// Run project repl using deno. (`deno` command must be in path) -#[derive(FromArgs, PartialEq, Debug)] +#[cfg(target_family = "wasm")] +use wasm_bindgen::prelude::wasm_bindgen; + +/// Run type checking REPL +#[derive(FromArgs, PartialEq, Debug, Default)] #[argh(subcommand, name = "repl")] -pub(crate) struct ReplArguments { +#[cfg_attr(target_family = "wasm", derive(serde::Deserialize), serde(default))] +pub struct ReplArguments { /// use mutable variables everywhere #[argh(switch)] const_as_let: bool, @@ -20,68 +21,72 @@ pub(crate) struct ReplArguments { type_definition_module: Option, } -#[allow(unused)] -fn file_system_resolver(path: &Path) -> Option> { - // Cheaty - if path.to_str() == Some("BLANK") { - Some(Vec::new()) - } else { - match fs::read_to_string(path) { - Ok(source) => Some(source.into()), - Err(_) => None, - } - } +/// Wraps `checker::synthesis::interactive::State` +#[cfg_attr(target_family = "wasm", wasm_bindgen)] +pub struct ReplSystem { + arguments: ReplArguments, + source: SourceId, + state: checker::synthesis::interactive::State<'static, crate::utilities::FSFunction>, } -pub(crate) fn run_repl( - cli_input_resolver: U, - ReplArguments { const_as_let, type_definition_module }: ReplArguments, -) { - print_to_cli(format_args!("Entering REPL. Exit with `close()`")); +pub type ReplSystemErr = ( + checker::DiagnosticsContainer, + crate::source_map::MapFileStore, +); + +impl ReplSystem { + pub fn new( + arguments: ReplArguments, + file_system_resolver: crate::utilities::FSFunction, + ) -> Result { + let definitions = if let Some(tdm) = arguments.type_definition_module.clone() { + std::iter::once(tdm).collect() + } else { + std::iter::once(checker::INTERNAL_DEFINITION_FILE_PATH.into()).collect() + }; - let definitions = if let Some(tdm) = type_definition_module { - std::iter::once(tdm).collect() - } else { - std::iter::once(checker::INTERNAL_DEFINITION_FILE_PATH.into()).collect() - }; + // TOOD + let static_file_system_resolver = Box::leak(Box::new(file_system_resolver)); - let state = checker::synthesis::interactive::State::new(&file_system_resolver, definitions); + let state = + checker::synthesis::interactive::State::new(static_file_system_resolver, definitions)?; + let source = state.get_source_id(); - let mut state = match state { - Ok(state) => state, - Err((diagnostics, fs)) => { - report_diagnostics_to_cli( - diagnostics, - &fs, - false, - crate::utilities::MaxDiagnostics::All, - ) - .unwrap(); - return; - } - }; + Ok(ReplSystem { arguments, source, state }) + } +} - let source = state.get_source_id(); +#[cfg_attr(target_family = "wasm", wasm_bindgen)] +impl ReplSystem { + /// Not a constructor because returns result (can fail if can't find `d.ts` file) + /// Also `repl_arguments` rather than `arguments` otherwise breaks JS emit + #[cfg(target_family = "wasm")] + #[wasm_bindgen(js_name = "new_system")] + pub fn new_js( + repl_arguments: wasm_bindgen::JsValue, + cb: &js_sys::Function, + ) -> Option { + let repl_arguments: ReplArguments = + serde_wasm_bindgen::from_value(repl_arguments).expect("invalid ReplArguments"); + let cb = crate::utilities::FSFunction(cb.clone()); + Self::new(repl_arguments, cb).ok() + } - loop { - let input = cli_input_resolver(""); - let input = if let Some(input) = input { - if input.is_empty() { - continue; - } else if input.trim() == "close()" { - break; - } + #[cfg_attr(target_family = "wasm", wasm_bindgen)] + pub fn execute_statement(&mut self, mut input: String) { + // New line fixes #210 + input.truncate(input.trim_end().len()); + input.push('\n'); + let (start, _) = self.state.get_fs_mut().append_to_file(self.source, &input); - input - } else { - continue; - }; + let options = Default::default(); + let offset = Some(start as u32); - let (from_index, _) = state.get_fs_mut().append_to_file(source, &input); + // self.offset += input.len() as u32 + 1; - let options = Default::default(); - let offset = Some(from_index as u32); - let result = if input.trim_start().starts_with('{') { + // Fix to remain consistent with other JS REPLs + let starts_with_brace = input.trim_start().starts_with('{'); + let result = if starts_with_brace { Expression::from_string_with_options(input, options, offset).map(|(expression, _)| { Module { hashbang_comment: None, @@ -90,59 +95,99 @@ pub(crate) fn run_repl( } }) } else { - Module::from_string(input, options) + Module::from_string_with_options(input, options, offset).map(|(module, _state)| module) }; - let mut item = match result { - Ok(item) => item, + match result { + Ok(mut item) => { + // crate::utilities::print_to_cli(format_args!("item={item:?}")); + + if self.arguments.const_as_let { + item.visit_mut( + &mut VisitorsMut { + statement_visitors_mut: vec![Box::new(crate::transformers::ConstToLet)], + ..Default::default() + }, + &mut (), + &Default::default(), + self.source, + ); + } + + let result = self.state.check_item(&item); + + match result { + Ok((last_ty, diagnostics)) => { + report_diagnostics_to_cli( + diagnostics, + self.state.get_fs_ref(), + false, + crate::utilities::MaxDiagnostics::All, + ) + .unwrap(); + + if let Some(last_ty) = last_ty { + crate::utilities::print_to_cli(format_args!("{last_ty}")); + } + } + Err(diagnostics) => { + report_diagnostics_to_cli( + diagnostics, + self.state.get_fs_ref(), + false, + crate::utilities::MaxDiagnostics::All, + ) + .unwrap(); + } + } + } Err(err) => { report_diagnostics_to_cli( - std::iter::once((err, source).into()), - state.get_fs_ref(), + std::iter::once((err, self.source).into()), + self.state.get_fs_ref(), false, crate::utilities::MaxDiagnostics::All, ) .unwrap(); - continue; } - }; - - if const_as_let { - item.visit_mut( - &mut VisitorsMut { - statement_visitors_mut: vec![Box::new(crate::transformers::ConstToLet)], - ..Default::default() - }, - &mut (), - &Default::default(), - source, - ); } + } +} - let result = state.check_item(&item); +#[cfg(target_family = "wasm")] +pub(crate) fn run_repl(_arguments: ReplArguments) { + panic!( + "Cannot run repl in WASM because of input callback. Consider reimplementing using library" + ); +} - match result { - Ok((last_ty, diagnostics)) => { - report_diagnostics_to_cli( - diagnostics, - state.get_fs_ref(), - false, - crate::utilities::MaxDiagnostics::All, - ) - .unwrap(); - if let Some(last_ty) = last_ty { - println!("{last_ty}"); - } - } - Err(diagnostics) => { - report_diagnostics_to_cli( - diagnostics, - state.get_fs_ref(), - false, - crate::utilities::MaxDiagnostics::All, - ) - .unwrap(); - } +#[cfg(not(target_family = "wasm"))] +pub(crate) fn run_repl(arguments: ReplArguments) { + use crate::utilities::print_to_cli; + + print_to_cli(format_args!("Entering Repl. Exit with `close()`")); + + let mut system = match ReplSystem::new(arguments, crate::utilities::FSFunction) { + Ok(system) => system, + Err((diagnostics, fs)) => { + report_diagnostics_to_cli( + diagnostics, + &fs, + false, + crate::utilities::MaxDiagnostics::All, + ) + .unwrap(); + return; + } + }; + + loop { + let input = crate::utilities::cli_input_resolver(""); + if input.is_empty() { + continue; + } else if input.trim() == "close()" { + break; } + system.execute_statement(input) } } diff --git a/src/reporting.rs b/src/reporting.rs index 7a9ef81d..f3abc273 100644 --- a/src/reporting.rs +++ b/src/reporting.rs @@ -1,12 +1,6 @@ -use std::io::Write; - use codespan_reporting::{ diagnostic::{Diagnostic, Label, Severity}, - term::{ - emit, - termcolor::{BufferedStandardStream, ColorChoice}, - Config, DisplayStyle, - }, + term::{emit, Config, DisplayStyle}, }; use checker::source_map::{MapFileStore, PathMap, SourceId}; @@ -97,7 +91,9 @@ where }; #[cfg(not(target_family = "wasm"))] - let mut writer = BufferedStandardStream::stderr(ColorChoice::Auto); + let mut writer = codespan_reporting::term::termcolor::BufferedStandardStream::stderr( + codespan_reporting::term::termcolor::ColorChoice::Auto, + ); let files = fs.into_code_span_store(); let diagnostics = diagnostics.into_iter(); @@ -121,11 +117,13 @@ where } #[cfg(not(target_family = "wasm"))] - emit(&mut writer, &config, &files, &diagnostic)?; + { + emit(&mut writer, &config, &files, &diagnostic)?; + } } #[cfg(not(target_family = "wasm"))] - writer.flush().unwrap(); + std::io::Write::flush(&mut writer).unwrap(); if count > maximum { crate::utilities::print_to_cli(format_args!( diff --git a/src/utilities.rs b/src/utilities.rs index 5a3165bc..69889bce 100644 --- a/src/utilities.rs +++ b/src/utilities.rs @@ -2,64 +2,77 @@ use std::fmt::Arguments; pub(crate) fn print_info() { if let Some(run_id) = option_env!("GITHUB_RUN_ID") { - print_to_cli_with_break_after(format_args!( - "{}@{} (#{run_id})", + print_to_cli(format_args!( + "{}@{} (#{run_id})\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION") )); } else { - print_to_cli_with_break_after(format_args!( - "{}@{}", - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_VERSION") - )); + print_to_cli(format_args!("{}@{}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))); } print_to_cli(format_args!("{}", env!("CARGO_PKG_DESCRIPTION"))); - print_to_cli_with_break_after(format_args!( - "Repository: {}, License: {}", + print_to_cli(format_args!( + "Repository: {}, License: {}\n", env!("CARGO_PKG_REPOSITORY"), env!("CARGO_PKG_LICENSE") )); - print_to_cli_with_break_after(format_args!("For help run --help")); + print_to_cli(format_args!("For help run --help\n")); if let (Some(sponsors), Some(contributors)) = (option_env!("SPONSORS"), option_env!("CONTRIBUTORS")) { const SPONSORS_URL: &str = "https://github.com/sponsors/kaleidawave"; - print_to_cli_with_break_after(format_args!( - "With thanks to all supporters of the project including:" - )); + print_to_cli(format_args!("With thanks to all supporters of the project including:")); print_to_cli(format_args!( " Contributors (join them @ https://github.com/kaleidawave/ezno/issues):" )); - wrap_with_ident(contributors); + wrap_with_indent(contributors); print_to_cli(format_args!(" Sponsors (join them @ {SPONSORS_URL}):")); - wrap_with_ident(sponsors); + wrap_with_indent(sponsors); } } -fn wrap_with_ident(input: &str) { - // Four spaces is stable across terminals (unlike tabs) +pub(crate) fn cli_input_resolver(prompt: &str) -> String { + use std::io; + print!("{prompt}> "); + std::io::Write::flush(&mut io::stdout()).unwrap(); + let mut input = String::new(); + let std_in = &mut io::stdin(); + + // multiline_term_input only works on windows for now + #[cfg(target_family = "windows")] + let _n = multiline_term_input::read_string(std_in, &mut input); + + #[cfg(target_family = "unix")] + let _n = std_in.read_line(&mut input).unwrap(); + + input +} + +fn wrap_with_indent(input: &str) { + // Four spaces is consitently rendered across terminals (unlike tabs) const INDENT: &str = " "; + const MAX_WIDTH: usize = 60; + let mut buf = String::new(); - for part in input.split(',') { - buf.push_str(part); - buf.push_str(", "); - if buf.len() > 40 { - print_to_cli(format_args!("{INDENT}{buf}")); - buf.clear(); + let mut iter = input.split(',').peekable(); + while let Some(part) = iter.next() { + buf.push_str(part.trim()); + if let Some(next) = iter.peek() { + if buf.len() + next.len() > MAX_WIDTH { + buf.push(','); + print_to_cli(format_args!("{INDENT}{buf}")); + buf.clear(); + } else { + buf.push_str(", "); + } } } - if !buf.is_empty() { - print_to_cli_with_break_after(format_args!("{INDENT}{buf}")); - } else { - print_to_cli(format_args!("\n")) - } -} - -/// Adds and extra new line afterwards -fn print_to_cli_with_break_after(arguments: Arguments) { - print_to_cli(format_args!("{arguments}\n")); + // TODO double check whether there needs to be another new line? + print_to_cli(format_args!( + "{INDENT}{buf}\n", + INDENT = if buf.is_empty() { "" } else { INDENT } + )); } #[cfg(target_family = "wasm")] @@ -67,12 +80,6 @@ pub(crate) fn print_to_cli(arguments: Arguments) { super::wasm_bindings::log(&arguments.to_string()); } -#[cfg(target_family = "wasm")] -pub(crate) fn print_to_cli_without_newline(arguments: Arguments) { - // TODO :( - super::wasm_bindings::log(&arguments.to_string()); -} - #[cfg(not(target_family = "wasm"))] pub(crate) fn print_to_cli(arguments: Arguments) { use std::io; @@ -81,14 +88,6 @@ pub(crate) fn print_to_cli(arguments: Arguments) { io::Write::flush(&mut io::stdout()).unwrap(); } -#[cfg(not(target_family = "wasm"))] -pub(crate) fn print_to_cli_without_newline(arguments: Arguments) { - use std::io; - - print!("{arguments}"); - io::Write::flush(&mut io::stdout()).unwrap(); -} - #[derive(Debug, Copy, Clone)] pub(crate) enum MaxDiagnostics { All, @@ -114,6 +113,33 @@ impl Default for MaxDiagnostics { } } +#[cfg(target_family = "wasm")] +pub struct FSFunction(pub js_sys::Function); + +#[cfg(target_family = "wasm")] +impl checker::ReadFromFS for FSFunction { + fn read_file(&self, path: &std::path::Path) -> Option> { + self.0 + .call1( + &wasm_bindgen::JsValue::null(), + &wasm_bindgen::JsValue::from(path.display().to_string()), + ) + .ok() + .and_then(|s| s.as_string()) + .map(|s| s.into_bytes()) + } +} + +#[cfg(not(target_family = "wasm"))] +pub struct FSFunction; + +#[cfg(not(target_family = "wasm"))] +impl checker::ReadFromFS for FSFunction { + fn read_file(&self, path: &std::path::Path) -> Option> { + std::fs::read(path).ok() + } +} + // yes i implemented it only using `native_tls`... // TODO or(..., debug_assertions) #[cfg(not(target_family = "wasm"))] diff --git a/src/wasm_bindings.rs b/src/wasm_bindings.rs index 42436575..c11d7c9e 100644 --- a/src/wasm_bindings.rs +++ b/src/wasm_bindings.rs @@ -7,43 +7,7 @@ extern "C" { pub(crate) fn log(s: &str); } -#[derive(Clone, Copy, Default, serde::Deserialize)] -#[wasm_bindgen] -pub struct CheckOptions { - pub lsp_mode: bool, -} - -#[wasm_bindgen(typescript_custom_section)] -const TYPES_EXPERIMENTAL_BUILD: &str = r###" -export function experimental_build( - entry_path: string, fs_resolve_js: (path: string) => string | undefined, minify: boolean -): {Ok: BuildOutput} | {Err: FailedBuildOutput} -"###; -#[wasm_bindgen(js_name = experimental_build, skip_typescript)] -pub fn experimental_build_wasm( - entry_path: String, - fs_resolver_js: &js_sys::Function, - minify: bool, -) -> JsValue { - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - - let fs_resolver = |path: &std::path::Path| { - let res = - fs_resolver_js.call1(&JsValue::null(), &JsValue::from(path.display().to_string())); - res.ok().and_then(|res| res.as_string()) - }; - let result = crate::build::build( - vec![entry_path.into()], - &fs_resolver, - None, - Path::new("out.js"), - &crate::build::BuildConfig { strip_whitespace: minify, source_maps: false }, - None, - ); - - serde_wasm_bindgen::to_value(&result).unwrap() -} - +/// Wrapper the abstracts some of the properties #[wasm_bindgen(typescript_custom_section)] const TYPES_WASM_CHECK_OUTPUT: &str = r###" interface WASMCheckOutput { @@ -78,48 +42,112 @@ impl WASMCheckOutput { } #[wasm_bindgen(typescript_custom_section)] -const TYPES_CHECK: &str = r#" -export function check(entry_path: string, fs_resolver_js: (path: string) => string | undefined): WASMCheckOutput -"#; +const TYPES_CHECK_WITH_OPTIONS: &str = r#" +export function check( + entry_path: string, + fs_resolver_js: (path: string) => string | undefined, + options: TypeCheckOptions | undefined +): WASMCheckOutput"#; #[wasm_bindgen(js_name = check, skip_typescript)] -pub fn check_wasm(entry_path: String, fs_resolver_js: &js_sys::Function) -> WASMCheckOutput { +pub fn check_wasm( + entry_path: String, + fs_resolver_js: &js_sys::Function, + options: JsValue, +) -> WASMCheckOutput { std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + let options: checker::TypeCheckOptions = if options.is_undefined() || options.is_null() { + Default::default() + } else { + serde_wasm_bindgen::from_value(options).expect("invalid TypeCheckOptions") + }; + let fs_resolver = |path: &std::path::Path| { let res = fs_resolver_js.call1(&JsValue::null(), &JsValue::from(path.display().to_string())); res.ok().and_then(|res| res.as_string()) }; - WASMCheckOutput(crate::check::check( - vec![entry_path.into()], - &fs_resolver, - None, - checker::TypeCheckOptions::default(), - )) + + WASMCheckOutput(crate::check::check(vec![entry_path.into()], &fs_resolver, None, options)) } +/// Wrapper the abstracts some of the properties #[wasm_bindgen(typescript_custom_section)] -const TYPES_CHECK_WITH_OPTIONS: &str = r#" -export function check_with_options(entry_path: string, fs_resolver_js: (path: string) => string | undefined, options: TypeCheckOptions): WASMCheckOutput -"#; -#[wasm_bindgen(js_name = check_with_options, skip_typescript)] -pub fn check_wasm_with_options( +const TYPES_WASM_CHECK_OUTPUT: &str = r###" +interface WASMBuildOutput { + readonly artifacts: Array, + readonly diagnostics: DiagnosticsContainer, + get_type_at_position(path: string, pos: number): string; + get_type_at_position_debug(path: string, pos: number): string; +} +"###; +#[wasm_bindgen] +pub struct WASMBuildOutput { + artifacts: Vec, + check_output: WASMCheckOutput, +} + +#[wasm_bindgen] +impl WASMBuildOutput { + #[wasm_bindgen(js_name = artifacts, getter, skip_typescript)] + pub fn get_artifacts(&self) -> JsValue { + serde_wasm_bindgen::to_value(&self.artifacts).unwrap() + } + + #[wasm_bindgen(js_name = diagnostics, getter, skip_typescript)] + pub fn get_diagnostics(&self) -> JsValue { + self.check_output.get_diagnostics() + } + + pub fn get_type_at_position(&self, path: &str, pos: u32) -> Option { + self.check_output.get_type_at_position(path, pos) + } + + pub fn get_type_at_position_debug(&self, path: &str, pos: u32) -> Option { + self.check_output.get_type_at_position_debug(path, pos) + } +} + +#[wasm_bindgen(typescript_custom_section)] +const TYPES_EXPERIMENTAL_BUILD: &str = r###" +export function experimental_build( + entry_path: string, + fs_resolve_js: (path: string) => string | undefined, + config: BuildConfig | undefined +): WASMBuildOutput +"###; +#[wasm_bindgen(js_name = experimental_build, skip_typescript)] +pub fn experimental_build_wasm( entry_path: String, fs_resolver_js: &js_sys::Function, - options: JsValue, -) -> WASMCheckOutput { + config: JsValue, +) -> WASMBuildOutput { std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - let options: checker::TypeCheckOptions = - serde_wasm_bindgen::from_value(options).expect("invalid TypeCheckOptions"); let fs_resolver = |path: &std::path::Path| { let res = fs_resolver_js.call1(&JsValue::null(), &JsValue::from(path.display().to_string())); - res.ok().and_then(|res| res.as_string()) }; - WASMCheckOutput(crate::check::check(vec![entry_path.into()], &fs_resolver, None, options)) + + let config: crate::build::BuildConfig = if config.is_undefined() || config.is_null() { + Default::default() + } else { + serde_wasm_bindgen::from_value(config).expect("invalid BuildConfig") + }; + + let result = crate::build::build(vec![entry_path.into()], &fs_resolver, config); + + match result { + Ok(crate::build::BuildOutput { artifacts, check_output }) => { + WASMBuildOutput { artifacts, check_output: WASMCheckOutput(check_output) } + } + Err(crate::build::FailedBuildOutput(check_output)) => WASMBuildOutput { + artifacts: Default::default(), + check_output: WASMCheckOutput(check_output), + }, + } } #[wasm_bindgen(typescript_custom_section)] @@ -128,7 +156,6 @@ export function run_cli( cli_arguments: string[], read_from_file: (path: string) => string | undefined, write_to_file: (path: string, content: string) => void, - cli_input_resolver: (prompt: string) => string | undefined ): void "#; #[wasm_bindgen(js_name = run_cli, skip_typescript)] @@ -136,11 +163,10 @@ pub fn run_cli_wasm( cli_arguments: Box<[JsValue]>, read_from_file: &js_sys::Function, write_to_file: &js_sys::Function, - cli_input_resolver_js: &js_sys::Function, ) { std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - let arguments = cli_arguments.into_iter().flat_map(JsValue::as_string).collect::>(); + let arguments = cli_arguments.iter().flat_map(JsValue::as_string).collect::>(); let arguments = arguments.iter().map(String::as_str).collect::>(); let read_from_file = |path: &std::path::Path| { @@ -159,21 +185,12 @@ pub fn run_cli_wasm( .unwrap(); }; - let cli_input_resolver = |prompt: &str| { - cli_input_resolver_js - .call1(&JsValue::null(), &JsValue::from(prompt.to_owned())) - .ok() - .as_ref() - .and_then(JsValue::as_string) - }; - - crate::run_cli(&arguments, &read_from_file, write_to_file, cli_input_resolver); + crate::run_cli(&arguments, &read_from_file, write_to_file); } #[wasm_bindgen(typescript_custom_section)] -const TYPES_PARSE_EXPRESSION: &str = r#" -export function parse_expression(input: string): Expression | [string, Span] -"#; +const TYPES_PARSE_EXPRESSION: &str = + "export function parse_expression(input: string): Expression | [string, Span]"; #[wasm_bindgen(js_name = parse_expression, skip_typescript)] pub fn parse_expression_to_json(input: String) -> JsValue { use parser::{ASTNode, Expression}; @@ -189,9 +206,8 @@ pub fn parse_expression_to_json(input: String) -> JsValue { } #[wasm_bindgen(typescript_custom_section)] -const TYPES_PARSE_MODULE: &str = r#" -export function parse_module(input: string): Module | [string, Span] -"#; +const TYPES_PARSE_MODULE: &str = + "export function parse_module(input: string): Module | [string, Span]"; #[wasm_bindgen(js_name = parse_module, skip_typescript)] pub fn parse_module_to_json(input: String) -> JsValue { use parser::{ASTNode, Module}; @@ -237,9 +253,8 @@ pub fn parse_module_and_into_string( } } #[wasm_bindgen(typescript_custom_section)] -const TYPES_JUST_IMPORTS: &str = r#" -export function just_imports(input: string): string | [string, Span] -"#; +const TYPES_JUST_IMPORTS: &str = + "export function just_imports(input: string): string | [string, Span]"; #[wasm_bindgen(skip_typescript)] pub fn just_imports(input: String) -> JsValue { use parser::{ASTNode, Module}; From dab8faf97c2b54edd2696d6e5d11b7c86efeda73 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 13 Nov 2024 19:42:29 +0000 Subject: [PATCH 7/9] Fixes around disjoint implementation (#209) - Change over intersection to use disjoint function - Change ranges back to LessThan & GreaterThan - Narrowing fix - Add modulo_class and float_range helpers - Extract helpers and operator modules - Literal on object - Indentation in specification - Disjoint on template literals - Move specification tests --- .github/workflows/clippy-rustfmt-fix.yml | 1 + .github/workflows/performance-and-size.yml | 32 +- .github/workflows/rust.yml | 2 +- README.md | 2 +- checker/Cargo.toml | 2 +- checker/definitions/internal.ts.d.bin | Bin 35522 -> 34856 bytes checker/definitions/overrides.d.ts | 17 +- checker/definitions/simple.d.ts | 5 +- checker/examples/run_checker.rs | 9 +- checker/specification/Cargo.toml | 6 +- checker/specification/build.rs | 4 +- checker/specification/specification.md | 310 ++++--- checker/specification/to_implement.md | 54 ++ checker/src/context/environment.rs | 216 +++-- checker/src/context/information.rs | 83 +- checker/src/context/mod.rs | 13 +- checker/src/context/root.rs | 4 +- checker/src/diagnostics.rs | 17 +- checker/src/events/application.rs | 6 + checker/src/events/mod.rs | 4 +- checker/src/events/printing.rs | 3 + checker/src/features/assignments.rs | 4 +- checker/src/features/conditional.rs | 2 + checker/src/features/constant_functions.rs | 23 +- checker/src/features/functions.rs | 4 +- checker/src/features/iteration.rs | 40 +- checker/src/features/mod.rs | 117 +-- checker/src/features/narrowing.rs | 219 +++-- checker/src/features/operations.rs | 849 ------------------ checker/src/features/operations/logical.rs | 113 +++ .../operations/mathematical_bitwise.rs | 262 ++++++ checker/src/features/operations/mod.rs | 12 + checker/src/features/operations/relation.rs | 370 ++++++++ checker/src/features/operations/unary.rs | 87 ++ checker/src/features/template_literal.rs | 9 +- checker/src/options.rs | 6 + checker/src/synthesis/block.rs | 5 +- checker/src/synthesis/classes.rs | 52 +- checker/src/synthesis/expressions.rs | 222 +++-- checker/src/synthesis/functions.rs | 2 +- checker/src/synthesis/hoisting.rs | 6 +- checker/src/synthesis/interfaces.rs | 2 +- checker/src/synthesis/mod.rs | 2 + checker/src/synthesis/statements.rs | 4 + checker/src/synthesis/type_annotations.rs | 55 +- checker/src/synthesis/variables.rs | 3 +- checker/src/types/calling.rs | 17 +- checker/src/types/disjoint.rs | 264 ++++-- checker/src/types/generics/substitution.rs | 18 +- checker/src/types/helpers.rs | 471 ++++++++++ checker/src/types/intrinsics.rs | 317 +++++-- checker/src/types/mod.rs | 441 ++------- checker/src/types/printing.rs | 155 ++-- checker/src/types/properties/access.rs | 98 +- checker/src/types/properties/assignment.rs | 26 +- checker/src/types/properties/mod.rs | 10 +- checker/src/types/store.rs | 120 +-- checker/src/types/subtyping.rs | 321 ++++--- checker/src/utilities/debugging.rs | 2 +- checker/src/utilities/float_range.rs | 329 ++++--- checker/src/utilities/mod.rs | 1 + checker/src/utilities/modulo_class.rs | 162 ++++ parser/examples/code_blocks_to_script.rs | 266 ++++-- parser/src/expressions/arrow_function.rs | 18 +- src/cli.rs | 14 +- src/playground/main.js | 12 +- 66 files changed, 3888 insertions(+), 2434 deletions(-) delete mode 100644 checker/src/features/operations.rs create mode 100644 checker/src/features/operations/logical.rs create mode 100644 checker/src/features/operations/mathematical_bitwise.rs create mode 100644 checker/src/features/operations/mod.rs create mode 100644 checker/src/features/operations/relation.rs create mode 100644 checker/src/features/operations/unary.rs create mode 100644 checker/src/types/helpers.rs create mode 100644 checker/src/utilities/modulo_class.rs diff --git a/.github/workflows/clippy-rustfmt-fix.yml b/.github/workflows/clippy-rustfmt-fix.yml index fc2cfd76..16aa4d05 100644 --- a/.github/workflows/clippy-rustfmt-fix.yml +++ b/.github/workflows/clippy-rustfmt-fix.yml @@ -16,6 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable - uses: actions/cache@v4 with: path: ${{ env.CACHE_PATHS }} diff --git a/.github/workflows/performance-and-size.yml b/.github/workflows/performance-and-size.yml index 6d5dcdb6..e248bd27 100644 --- a/.github/workflows/performance-and-size.yml +++ b/.github/workflows/performance-and-size.yml @@ -37,13 +37,15 @@ jobs: env: CARGO_PROFILE_RELEASE_DEBUG: true - - name: Get base ezno - if: github.ref_name != 'main' - uses: actions/download-artifact@v4 - continue-on-error: true - with: - name: latest-checker - path: previous-ezno + # TODO need to lookup existing workflow on main + # even if this worked, it might have issues with newer features added in this run + # - name: Get base ezno + # if: github.ref_name != 'main' + # uses: actions/download-artifact@v4 + # continue-on-error: true + # with: + # name: latest-checker + # path: previous-ezno - name: Set compilers id: compilers @@ -197,16 +199,16 @@ jobs: curl -sS $url > input.js echo "::group::Comparison" - hyperfine \ + hyperfine -i \ -L compiler ${{ steps.compilers.outputs.BINARIES }} \ '{compiler} ast-explorer full input.js --timings' echo "::endgroup::" done - - name: Upload checker - if: github.ref == 'main' - uses: actions/upload-artifact@v4 - with: - name: latest-checker - path: target/release/ezno - retention-days: 90 + # - name: Upload checker + # if: github.ref == 'main' + # uses: actions/upload-artifact@v4 + # with: + # name: latest-checker + # path: target/release/ezno + # retention-days: 90 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e45c83c5..c6e1b695 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -95,7 +95,7 @@ jobs: - name: Run checker specification if: (steps.changes.outputs.checker == 'true' && github.event_name != 'pull_request') || github.ref_name == 'main' - run: cargo test + run: cargo test -p ezno-checker-specification - name: Run checker specification (w/ staging) if: steps.changes.outputs.checker == 'true' && github.event_name == 'pull_request' diff --git a/README.md b/README.md index 27dfc88b..f0c1165e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ What Ezno is: What Ezno is not - **eNZo, the Z is in front of the N** (pronounce as 'Fresno' without the 'fr') 😀 -- Be on parity with TSC or 1:1, it has some different behaviors **but** should work in existing projects using TSC +- Be on parity with TSC or 1:1, it has some different behaviors **but** should work in existing projects using TSC. [You can see a full comparison of emitted errors and warnings compared with TSC here](https://kaleidawave.github.io/ezno/comparison) - Faster as a means to serve large codebases. Cut out bloat and complex code first! - Smarter as a means to allow more *dynamic patterns*. Keep things simple! - A binary executable compiler. It takes in JavaScript (or a TypeScript or Ezno superset) and does similar processes to traditional compilers, but at the end emits JavaScript. However, in the future, it *could* generate a lower level format using its event (side-effect) representation. diff --git a/checker/Cargo.toml b/checker/Cargo.toml index 2461cae6..2b7614b9 100644 --- a/checker/Cargo.toml +++ b/checker/Cargo.toml @@ -39,7 +39,7 @@ path-absolutize = { version = "3.0", features = ["use_unix_paths_on_wasm"] } either = "1.6" levenshtein = "1" ordered-float = "4.2" -regress = { version = "0.10.0", features = [] } +regress = { version = "0.10", features = [] } serde = { version = "1.0", features = ["derive"], optional = true } simple-json-parser = "0.0.2" diff --git a/checker/definitions/internal.ts.d.bin b/checker/definitions/internal.ts.d.bin index c4f51fa0ba1d3479fd8d68f331a04a8444f46035..c2573ea25516655d3782a0b8598bb007c7ad600e 100644 GIT binary patch literal 34856 zcmdUY34mNhwf3p1?w+|blaPdc?*xGf36q6A5J=1dAuA*yV2GhJ(~~qzx`*zbBtt+1 zK?o>{;EMP}5fPvGKt&W4cLY=v1r<~jL~wf`E{OW%|Grx8y*<+zqCWWl|0bvB)~Qpc z&Z+g(sk-+T&ns6B?Jjh0C?48d>MP#1T;xD$>8_M(mA<0LrU@b!07Np23-t{mmyZ&8 z699;lmoHr+a?JrE?*xd9>nW}qSiiQ|+gt3e*Va}CYITvXOcQwsAQBCfdy2iKaw{5NS&|i#(5H%JR(4t5z#jk#|F@_gT%d%c_+vgCct!D6&65WH+Zt zeXzf{wuH=M3D$zefMwG_>H3 zzH951wH2@a-w-|aaPk|~P#YpGD1lu7_;n%`$~`Dt)|eCSHstp;n`iFS;`(`8`b7@g zS>zaih*?}fcp57Ha9jDg<#p(_US!o6ku?BhFefycopwhN0z`I7GVtwTCfdRx+a6j_ z>>Eop6wA)YVlSIb%ksgE>neRUk-Jc!U$%wn74fx+76m1~V-;!bsdNu)v`ypMDI)I$ zi0tmPE0#Bvs+BTh*VgKVIxKQKWl^bKtQPv9hwFQ%O6y!-AIp9Fj-+*drMhu$pR4?n15jDv1_VHoN-+rDJ8eJ5G79v@IU!tC#xw zic5Q^NGq6>%BA|?ZW5Z#QrXEh-+5b#-L{vVB;(N3z`H&;w^-}0mip_J>M4>b5A^l5 zNc)M^BAQ2)Wp}8AM%g9?WplWUj@3W4P+ng=L44mBEu$BdeZkn6SEt6eoU|@3mFM?W z3iaJ_Ti9Z6_i#h#i85wMaXmWqreY9&S90yHRC|t-9GV~cRTc&3$CpvE(;Sq^0yML7 zt;A$U$VzF~ktoUZ57aiuo7Cqpg6yotV@-dhUv5x)1CcW^aw9tMU1}l_Gcy61!;lZv z<@>4^xQ}N8K7&H-kzcEafcZ@>V6(+d#p&040OR44t#Z&cH5Si^D!5R7hj5qD(Onn~z z>N7!TYT#Vdw*c`#g6yu;q^y4n#Msd>WxY_Ab9EOGb7N$-x37W;psou*4aVq9?c8cz zHtTl)^RWcmy$;y(^nJkmJjR;7%6hp#{{x6!b_$tg>s3^lyk0K`su-g)rHuo9a*4hK zn44m(Uba9k)psC#XN*(xvKW<*FE^D;wp_dKAXkNq}~pMa8$*E4H_=Wyo9f zwTSpsI?{F@8+o<99}&NeBXfPl^7{G)C4T}+@h2J^xMpj8rS77fy83!q+%(Wa+VmG9`(P6Q90Kq> ziq;bOp?*J5cO~fc)yhD>{8&%h2U8`$QIEFhIhDS?LVpc5)X((&h&WpzLqv!?@@MwCho9SL-<9O)%Sl`4F+@Ky!}$+i5;#sXD>8)lPYsJ!F4* z0@&YJipgf_flM*QY=hz1uBU)~Mtp~&60J!wI z-fFRUUQuS4Wx$;s<1@8lp-*O-ZNS_RW5@PaF*cX$^S0DENx&qLS>}s~eljL$E!E~% zE9Vu+rf5?0%`9No#4#|xJx4Z*1?GIjTp!1dcGi5DT4jlO4sj<> z41D*1ic2?_Lwqvz%BkjNP(2q@X3i-N*5pj14?$-I_#)U;=o^69d$##Da92+X_!cNy zr9J#Q^L`*No*W|B>r3m)a+�7&Od#u(NzG)6}o+J|ElR{;ZxR_qy?24<(BL4UsD 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 diff --git a/checker/definitions/overrides.d.ts b/checker/definitions/overrides.d.ts index ad67900c..b5c0e66f 100644 --- a/checker/definitions/overrides.d.ts +++ b/checker/definitions/overrides.d.ts @@ -37,7 +37,7 @@ declare class Array { // TODO this argument map(cb: (t: T, i?: number) => U): Array { - const { length } = this, mapped: Array = []; + const { length } = this, mapped: U[] = []; let i: number = 0; while (i < length) { const value = this[i]; @@ -48,7 +48,7 @@ declare class Array { // // TODO any is debatable filter(cb: (t: T, i?: number) => any): Array { - const { length } = this, filtered: Array = []; + const { length } = this, filtered: T[] = []; let i: number = 0; while (i < length) { const value = this[i]; @@ -127,8 +127,9 @@ declare class Array { type Record = { [P in K]: T } -type LessThan = ExclusiveRange; -type GreaterThan = ExclusiveRange; +type ExclusiveRange = GreaterThan & LessThan; +type InclusiveRange = (GreaterThan & LessThan) | (F | C); + type Integer = MultipleOf<1>; declare class Map { @@ -202,7 +203,7 @@ declare class Promise { } declare class RegExp { @Constant("regexp:constructor") - constructor(pattern: string, flags?: string); + constructor(pattern: string, flags?: string); @Constant("regexp:exec") exec(input: string): RegExpExecArray | null; @@ -222,10 +223,10 @@ interface RegExpExecArray extends Array { * The first match. This will always be present because `null` will be returned if there are no matches. */ 0: string; -} + // } -// es2018 -interface RegExpExecArray { + // // es2018 + // interface RegExpExecArray { groups?: { [key: string]: string; }; diff --git a/checker/definitions/simple.d.ts b/checker/definitions/simple.d.ts index aba85ea2..01aed46e 100644 --- a/checker/definitions/simple.d.ts +++ b/checker/definitions/simple.d.ts @@ -211,8 +211,9 @@ declare class Map { type Record = { [P in K]: T } -type LessThan = ExclusiveRange; -type GreaterThan = ExclusiveRange; +type ExclusiveRange = GreaterThan & LessThan; +type InclusiveRange = (GreaterThan & LessThan) | (F | C); + type Integer = MultipleOf<1>; /** diff --git a/checker/examples/run_checker.rs b/checker/examples/run_checker.rs index 124c86e8..8c54fb7e 100644 --- a/checker/examples/run_checker.rs +++ b/checker/examples/run_checker.rs @@ -20,6 +20,7 @@ fn main() { let no_lib = args.iter().any(|item| item == "--no-lib"); let debug_dts = args.iter().any(|item| item == "--debug-dts"); let extras = args.iter().any(|item| item == "--extras"); + let advanced_numbers = args.iter().any(|item| item == "--advanced-numbers"); let now = Instant::now(); @@ -46,6 +47,7 @@ fn main() { max_inline_count: 600, debug_dts, extra_syntax: extras, + advanced_numbers, ..Default::default() }; @@ -60,8 +62,7 @@ fn main() { if args.iter().any(|arg| arg == "--types") { eprintln!("Types:"); - let types = result.types.into_vec_temp(); - for (type_id, item) in &types[types.len().saturating_sub(60)..] { + for (type_id, item) in result.types.user_types() { eprintln!("\t{type_id:?}: {item:?}"); } } @@ -74,6 +75,10 @@ fn main() { } } + if args.iter().any(|arg| arg == "--called-functions") { + eprintln!("Called function: {:?}", result.types.called_functions); + } + if args.iter().any(|arg| arg == "--time") { let end = now.elapsed(); let count = result.diagnostics.into_iter().len(); diff --git a/checker/specification/Cargo.toml b/checker/specification/Cargo.toml index 51591c22..81dbbd2b 100644 --- a/checker/specification/Cargo.toml +++ b/checker/specification/Cargo.toml @@ -7,9 +7,11 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -just-staging = [] +default = ["base"] +base = [] staging = [] -all = ["staging"] +to_implement = [] +all = ["base", "staging", "to_implement"] [[test]] name = "specification_test" diff --git a/checker/specification/build.rs b/checker/specification/build.rs index 1b20193d..1c597410 100644 --- a/checker/specification/build.rs +++ b/checker/specification/build.rs @@ -12,7 +12,7 @@ fn main() -> Result<(), Box> { let out_path = Path::new(&std::env::var("OUT_DIR")?).join("specification.rs"); let mut out = File::create(out_path)?; - if cfg!(not(feature = "just-staging")) { + if cfg!(feature = "base") { let specification = read_to_string("./specification.md")?; markdown_lines_append_test_to_rust(specification.lines().enumerate(), &mut out)?; } @@ -26,7 +26,7 @@ fn main() -> Result<(), Box> { writeln!(&mut out, "}}").unwrap(); } - if cfg!(feature = "all") { + if cfg!(feature = "to_implement") { let to_implement = read_to_string("./to_implement.md")?; writeln!(&mut out, "mod to_implement {{ ").unwrap(); writeln!(&mut out, "use super::{{check_expected_diagnostics, TypeCheckOptions}}; ") diff --git a/checker/specification/specification.md b/checker/specification/specification.md index 88a305b0..7e2841af 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -306,16 +306,6 @@ const b = x.b; - No property 'b' on { a: 2 } -#### `Object.keys`, `Object.values`, `Object.entries` - -```ts -Object.keys({ a: 1, b: 2 }) satisfies ["a", "b"]; -Object.values({ a: 1, b: 2 }) satisfies [1, 2]; -Object.entries({ a: 1, b: 2 }) satisfies boolean; -``` - -- Expected boolean, found [["a", 1], ["b", 2]] - #### Spread condition ```ts @@ -813,7 +803,7 @@ const z: false = true || 4 ```ts (4 === 2) satisfies true; -(4 !== 2) satisfies string; +(4 !== 5) satisfies string; ``` - Expected true, found false @@ -1330,7 +1320,7 @@ new MyClass("hi").value satisfies "hello" - Expected "hello", found "hi" -#### `new` on function prototype +#### `new` on function with assigned prototype ```ts function MyClass(value) { @@ -1841,13 +1831,13 @@ kestrel(3)(2) satisfies 4 ```ts function kestrel2(a) { - return _b => _c => a + return b => c => (a * b) + c } -kestrel2(3)(2)(6) satisfies 4 +kestrel2(3)(2)(1) satisfies 4 ``` -- Expected 4, found 3 +- Expected 4, found 7 #### Carry across objects @@ -1952,6 +1942,7 @@ function conditional(v: string) { a++ } } + conditional("x") a satisfies 2 conditional("value") @@ -2055,13 +2046,13 @@ stringIsHi(string) satisfies number; ```ts function func(param: boolean) { let a = 2; - if (param) { + if (param) { a = 3; - return a; - } else { + return a; + } else { a = 7; } - a satisfies string; + a satisfies string; } ``` @@ -2356,25 +2347,6 @@ try { checkedLn(-5) } catch {} - Conditional '[Error] { message: \"Cannot log\" }' was thrown in function -#### Throw through internal callback - -```ts -try { - [1, 2, 3].map((x: number) => { - if (x === 2) { - throw "error" - } - }); - console.log("unreachable") -} catch (e) { - e satisfies number; -} -``` - -- Conditional '"error"' was thrown in function -- Unreachable statement -- Expected number, found "error" - ### Collections > Some of these are built of exiting features. @@ -2411,16 +2383,6 @@ x.push("hi"); - Argument of type \"hi\" is not assignable to parameter of type number -#### Array map - -> TODO other arguments (index and `this`) - -```ts -[6, 8, 10].map(x => x + 1) satisfies [7, 8, 11]; -``` - -- Expected [7, 8, 11], found [7, 9, 11] - #### Mutation > This is part of [assignment mismatch](https://github.com/kaleidawave/ezno/issues/18) @@ -2542,8 +2504,8 @@ getNumberBetweenFive() === 2.2; getNumberBetweenFive() === 7; ``` -- This equality is always false as InclusiveRange<0, 5> & Integer and 2.2 have no overlap -- This equality is always false as InclusiveRange<0, 5> & Integer and 7 have no overlap +- This equality is always false as GreaterThan<0> & LessThan<5> & Integer | 0 | 5 and 2.2 have no overlap +- This equality is always false as GreaterThan<0> & LessThan<5> & Integer | 0 | 5 and 7 have no overlap #### Identity equality @@ -2552,14 +2514,14 @@ getNumberBetweenFive() === 7; ```ts function func(a: string, b: number) { (a === a) satisfies string; - (b === b) satisfies null; + (b === b) satisfies null; } ``` - Expected string, found true - Expected null, found boolean -#### Ranges for interal types +#### Ranges for internal types ```ts function func(a: number) { @@ -2579,20 +2541,87 @@ function func(a: number) { } ``` -- Expected null, found InclusiveRange\<-5, 5> -- Expected string, found InclusiveRange\<18, 22> +With advanced_numbers + +- Expected null, found GreaterThan<-5> & LessThan<5> | -5 | 5 +- Expected string, found GreaterThan<18> & LessThan<22> | 18 | 22 -#### Not disjoint +#### Disjoint multiple of with range + +> TODO need to redo range to use interesection of less than and greater than ```ts -function func(param: number) { - if (param !== 2) { - return param === 2 - } +function func1(a: number, b: number) { + if (a % 8 === 0 && 31 < b && b < 37) { + const x = a === b; + } + if (a % 10 === 0 && 31 < b && b < 37) { + const x = a === b; + } + if (a % 10 === 0 && 31 < b && b < 41) { + const x = a === b; + } } ``` -- This equality is always false as Not<2> and 2 have no overlap +With advanced_numbers + +- This equality is always false as MultipleOf<10> and GreaterThan<31> & LessThan<37> have no overlap + +#### Modulo range + +```ts +function func(x: number) { + return x % 5 === 6; +} +``` + +- This equality is always false as ExclusiveRange<-5, 5> and 6 have no overlap + +#### Transistivity + +```ts +function func(a: number, b: number, c: number) { + if (a < b && b < c) { + const cond = (a < c) satisfies 5; + } +} +``` + +- Expected 5, found true + +### Operators across conditions + +```ts +function func(param: boolean) { + const value = param ? 1 : 2; + return value + 1; +} + +func satisfies string; +``` + +With advanced_numbers + +- Expected string, found (param: boolean) => 2 | 3 + +#### Disjoint not + +```ts +function func1(param: Not) { + return "hi" === param; +} + +function func2(param: Not) { + return 4 === param; +} + +function func3(p1: Not, p2: Not) { + return p1 === p2; +} +``` + +- This equality is always false as "hi" and Not\ have no overlap ### Statements, declarations and expressions @@ -2709,6 +2738,9 @@ declare var x: number; declare var x: number; (x < 4) satisfies string; (x === 4) satisfies Math; +(x !== 4) satisfies boolean; +(x > 4) satisfies boolean; +(x >= 4) satisfies boolean; ``` - Expected string, found boolean @@ -2961,8 +2993,8 @@ let x: BoxString; ```ts interface X { - a: string - b: string + a: string + b: string } function func(x: X | null) { @@ -3330,11 +3362,11 @@ doThingWithX(new Y()) ```ts class Box { - value: T; + value: T; - constructor(value: T) { - this.value = value; - } + constructor(value: T) { + this.value = value; + } } const myBox = new Box("hi"); @@ -3747,10 +3779,29 @@ type Introduction = `Hello ${string}`; const first: Introduction = "Hello Ben"; const second: Introduction = "Hi Ben"; const third: `Hiya ${string}` = "Hello Ben"; + +// Edge cases +const invalidNum1: `${1}` = 1; +const invalidNum2: `${1}` = "1"; +const invalidNum3: `${1}` = "2"; ``` - Type "Hi Ben" is not assignable to type Introduction - Type "Hello Ben" is not assignable to type `Hiya ${string}` +- Type 1 is not assignable to type "1" +- Type \"2\" is not assignable to type "1" + +#### Disjoint template literals + +```ts +function func(a: `a${string}`, b: `b${string}`, c: string) { + const res1 = a === b; + const res2 = (b === c) satisfies string; +} +``` + +- This equality is always false as `a${string}` and `b${string}` have no overlap +- Expected string, found boolean #### Assigning to types as keys @@ -4071,6 +4122,8 @@ x.a = "hi"; x.a = 4; ``` + + - Type 4 does not meet property constraint "hi" #### `as` rewrite @@ -4097,7 +4150,7 @@ x.property_b ```ts function x(p: { readonly a: string, b: string }) { - p.a = "hi"; + p.a = "hi"; p.b = "hi"; } ``` @@ -4283,10 +4336,10 @@ function logicNarrow(thing: any, other: any) { ```ts function func(param: boolean | string | number) { - if (typeof param === "boolean") { - return 5 - } - param satisfies null; + if (typeof param === "boolean") { + return 5 + } + param satisfies null; } ``` @@ -4296,9 +4349,9 @@ function func(param: boolean | string | number) { ```ts function func(param: Array | string) { - if (param instanceof Array) { + if (param instanceof Array) { param satisfies null; - } + } } ``` @@ -4308,9 +4361,9 @@ function func(param: Array | string) { ```ts function func(param: any) { - if (param instanceof Array) { + if (param instanceof Array) { param satisfies null; - } + } } ``` @@ -4320,10 +4373,10 @@ function func(param: any) { ```ts function narrowPropertyEquals(param: { tag: "a", a: string } | { tag: "b", b: number }) { - if (param.tag === "a") { - param.a satisfies string; - param satisfies null; - } + if (param.tag === "a") { + param.a satisfies string; + param satisfies null; + } } ``` @@ -4333,10 +4386,10 @@ function narrowPropertyEquals(param: { tag: "a", a: string } | { tag: "b", b: nu ```ts function narrowFromTag(param: { tag: "a", a: string } | { tag: "b", b: number }) { - if ("a" in param) { - param.a satisfies string; - param satisfies null; - } + if ("a" in param) { + param.a satisfies string; + param satisfies null; + } } ``` @@ -4348,9 +4401,9 @@ function narrowFromTag(param: { tag: "a", a: string } | { tag: "b", b: number }) ```ts function buildObject(param: any) { - if ("a" in param) { - param satisfies null; - } + if ("a" in param) { + param satisfies null; + } } ``` @@ -4360,22 +4413,22 @@ function buildObject(param: any) { ```ts function conditional(param: boolean) { - const obj1 = {}, obj2 = {}; + const obj1 = { b: 2 }, obj2 = { c: 6 }; const sum = param ? obj1 : obj2; if (sum === obj1) { - sum.a = 2; + sum.a = 3; } [obj1, obj2] satisfies string; } ``` -- Expected string, found [{ a: 2 }, {}] +- Expected string, found [{ b: 2, a: 3 }, { c: 6 }] #### From condition equality ```ts function conditional(param: boolean) { - const obj1 = { a: 1 }, obj2 = {}; + const obj1 = { a: 1 }, obj2 = { b: 2}; const sum = param ? obj1 : obj2; if (param) { sum satisfies string; @@ -4405,19 +4458,19 @@ function conditional(param: boolean) { ```ts function func1(param: string | number) { - if (typeof param === "number" && param > 0) { - param satisfies number; - } else { - param satisfies null; + if (typeof param === "number" && param > 0) { + param satisfies number; + } else { + param satisfies null; } } function func2(param: string | number | boolean) { - if (typeof param === "string" || !(typeof param === "number")) { - param satisfies undefined; - } else { - param satisfies number; - } + if (typeof param === "string" || !(typeof param === "number")) { + param satisfies undefined; + } else { + param satisfies number; + } } ``` @@ -4432,10 +4485,10 @@ function func2(param: string | number | boolean) { function func(param: boolean) { let a = param; const inner = (value: boolean) => a = value; - if (a) { + if (a) { inner(false); a satisfies null; - } + } } ``` @@ -4445,15 +4498,15 @@ function func(param: boolean) { ```ts function func1(param: any): asserts param is number { - if (typeof param !== "string") { - throw "bad" - } + if (typeof param !== "string") { + throw "bad" + } } function func2(param: any): asserts param is boolean { - if (typeof param !== "boolean") { - throw "bad" - } + if (typeof param !== "boolean") { + throw "bad" + } } ``` @@ -4506,6 +4559,31 @@ function getName(name?: string) { - Expected undefined, found string +#### Implication from equality + +```ts +function func(a: boolean) { + const x = a ? 1 : 2; + if (x === 1) { + a satisfies "hi" + } +} +``` + +- Expected "hi", found true + +#### Narrowing in for loop + +> Can't do modulo because of post mutation + +```ts +for (let i = 0; i < 3; i++) { + const x = i === 50; +} +``` + +- This equality is always false as LessThan<3> and 50 have no overlap + ### Object constraint > Any references to a annotated variable **must** be within its LHS type. These test that it carries down to objects. @@ -4597,9 +4675,18 @@ const x = { a: 3 }; Object.setPrototypeOf(x, { a: 5, b: 2 }); x.a satisfies 3; x.b satisfies string; + +const obj = Object.setPrototypeOf( + {}, + Math.random() ? { a: 2 } : { get a() { return 0 } } +); + +const result = 'a' in obj; +result satisfies string; ``` - Expected string, found 2 +- Expected string, found true #### Get prototype @@ -5038,9 +5125,16 @@ function register(a: Literal) { register("something") // `document.title` is an unknown string, non-literal -register(document.title) +register(document.title); + +function func(param: object) { + const obj = { a: 2 }; + const obj1: Literal = obj; + const obj2: Literal = param; +} ``` +- Type object is not assignable to type Literal\ - Argument of type string is not assignable to parameter of type Literal\ #### Number intrinsics diff --git a/checker/specification/to_implement.md b/checker/specification/to_implement.md index 11cce36d..5856c16b 100644 --- a/checker/specification/to_implement.md +++ b/checker/specification/to_implement.md @@ -770,6 +770,47 @@ a satisfies 0; b satisfies string; - Expected string, found 1 +#### Array map + +> TODO other arguments (index and `this`) + +```ts +[6, 8, 10].map(x => x + 1) satisfies [7, 8, 11]; +``` + +- Expected [7, 8, 11], found [7, 9, 11] + +#### `Object.keys`, `Object.values`, `Object.entries` + +```ts +Object.keys({ a: 1, b: 2 }) satisfies ["a", "b"]; +Object.values({ a: 1, b: 2 }) satisfies [1, 2]; +Object.entries({ a: 1, b: 2 }) satisfies boolean; +``` + +- Expected boolean, found [["a", 1], ["b", 2]] + +#### Throw through internal callback + +> This test heavily relies on `.map` working + +```ts +try { + [1, 2, 3].map((x: number) => { + if (x === 2) { + throw "error" + } + }); + console.log("unreachable") +} catch (e) { + e satisfies number; +} +``` + +- Conditional '"error"' was thrown in function +- Unreachable statement +- Expected number, found "error" + ### Control flow #### Conditional break @@ -948,3 +989,16 @@ new RegExp("x").group.string ``` - ? + +### Properties + +#### Issue [#208](https://github.com/kaleidawave/ezno/issues/208) + +```ts +let d = {}; +let e = 0; +if (Math.random() > 0.2) { d.a = 0; } +print_type('a' in d); +``` + +- Hmmm diff --git a/checker/src/context/environment.rs b/checker/src/context/environment.rs index a4a2dece..2c5decc7 100644 --- a/checker/src/context/environment.rs +++ b/checker/src/context/environment.rs @@ -14,11 +14,7 @@ use crate::{ AssignmentKind, AssignmentReturnStatus, IncrementOrDecrement, Reference, }, modules::Exported, - objects::SpecialObject, - operations::{ - evaluate_logical_operation_with_expression, - evaluate_pure_binary_operation_handle_errors, MathematicalAndBitwise, - }, + operations::{evaluate_logical_operation_with_expression, MathematicalOrBitwiseOperation}, variables::{VariableMutability, VariableOrImport, VariableWithValue}, }, subtyping::{type_is_subtype, type_is_subtype_object, State, SubTypeResult, SubTypingOptions}, @@ -278,7 +274,7 @@ impl<'a> Environment<'a> { } AssignmentKind::PureUpdate(operator) => { // Order matters here - let reference_position = reference.get_position(); + // let reference_position = reference.get_position(); let existing = self.get_reference( reference.clone(), checking_data, @@ -286,8 +282,9 @@ impl<'a> Environment<'a> { ); let expression = expression.unwrap(); - let expression_pos = - A::expression_position(expression).with_source(self.get_source()); + // let expression_pos = + // A::expression_position(expression).with_source(self.get_source()); + let rhs = A::synthesise_expression( expression, TypeId::ANY_TYPE, @@ -295,22 +292,48 @@ impl<'a> Environment<'a> { checking_data, ); - let new = evaluate_pure_binary_operation_handle_errors( - (existing, reference_position), - operator.into(), - (rhs, expression_pos), - checking_data, + let result = crate::features::operations::evaluate_mathematical_operation( + existing, + operator, + rhs, self, + &mut checking_data.types, + checking_data.options.strict_casts, + checking_data.options.advanced_numbers, ); - let assignment_position = - assignment_position.with_source(self.get_source()); - self.set_reference_handle_errors( - reference, - new, - assignment_position, - checking_data, - ); - new + if let Ok(new) = result { + let assignment_position = + assignment_position.with_source(self.get_source()); + + self.set_reference_handle_errors( + reference, + new, + assignment_position, + checking_data, + ); + + new + } else { + checking_data.diagnostics_container.add_error( + crate::TypeCheckError::InvalidMathematicalOrBitwiseOperation { + operator, + lhs: crate::diagnostics::TypeStringRepresentation::from_type_id( + existing, + self, + &checking_data.types, + false, + ), + rhs: crate::diagnostics::TypeStringRepresentation::from_type_id( + rhs, + self, + &checking_data.types, + false, + ), + position: assignment_position.with_source(self.get_source()), + }, + ); + TypeId::ERROR_TYPE + } } AssignmentKind::IncrementOrDecrement(direction, return_kind) => { // let value = @@ -323,31 +346,57 @@ impl<'a> Environment<'a> { ); // TODO existing needs to be cast to number!! - - let new = evaluate_pure_binary_operation_handle_errors( - (existing, position), - match direction { - IncrementOrDecrement::Increment => MathematicalAndBitwise::Add, - IncrementOrDecrement::Decrement => MathematicalAndBitwise::Subtract, + let operator = match direction { + IncrementOrDecrement::Increment => MathematicalOrBitwiseOperation::Add, + IncrementOrDecrement::Decrement => { + MathematicalOrBitwiseOperation::Subtract } - .into(), - (TypeId::ONE, source_map::Nullable::NULL), - checking_data, - self, - ); + }; - let assignment_position = - assignment_position.with_source(self.get_source()); - self.set_reference_handle_errors( - reference, - new, - assignment_position, - checking_data, + let result = crate::features::operations::evaluate_mathematical_operation( + existing, + operator, + TypeId::ONE, + self, + &mut checking_data.types, + checking_data.options.strict_casts, + checking_data.options.advanced_numbers, ); + if let Ok(new) = result { + let assignment_position = + assignment_position.with_source(self.get_source()); + + self.set_reference_handle_errors( + reference, + new, + assignment_position, + checking_data, + ); - match return_kind { - AssignmentReturnStatus::Previous => existing, - AssignmentReturnStatus::New => new, + match return_kind { + AssignmentReturnStatus::Previous => existing, + AssignmentReturnStatus::New => new, + } + } else { + checking_data.diagnostics_container.add_error( + crate::TypeCheckError::InvalidMathematicalOrBitwiseOperation { + operator, + lhs: crate::diagnostics::TypeStringRepresentation::from_type_id( + existing, + self, + &checking_data.types, + false, + ), + rhs: crate::diagnostics::TypeStringRepresentation::from_type_id( + TypeId::ONE, + self, + &checking_data.types, + false, + ), + position, + }, + ); + TypeId::ERROR_TYPE } } AssignmentKind::ConditionalUpdate(operator) => { @@ -890,45 +939,41 @@ impl<'a> Environment<'a> { info.variable_current_value.get(&og_var.get_origin_variable_id()) }) .copied(); - let narrowed = current_value.and_then(|cv| self.get_narrowed(cv)); - if let Some(precise) = narrowed.or(current_value) { - let ty = checking_data.types.get_type_by_id(precise); - - // TODO temp for function - if let Type::SpecialObject(SpecialObject::Function(..)) = ty { - return Ok(VariableWithValue(og_var.clone(), precise)); - } else if let Type::RootPolyType(PolyNature::Open(_)) = ty { - crate::utilities::notify!( - "Open poly type '{}' treated as immutable free variable", - name - ); - return Ok(VariableWithValue(og_var.clone(), precise)); - } else if let Type::Constant(_) = ty { - return Ok(VariableWithValue(og_var.clone(), precise)); - } + // TODO WIP + let narrowed = current_value + .and_then(|cv| self.get_narrowed_or_object(cv, &checking_data.types)); - crate::utilities::notify!("Free variable with value!"); - } else { - crate::utilities::notify!("Free variable with no current value"); + if let Some(precise) = narrowed.or(current_value) { + // let ty = checking_data.types.get_type_by_id(precise); + + // // TODO temp for function + // let value = if let Type::SpecialObject(SpecialObject::Function(..)) = ty { + // return Ok(VariableWithValue(og_var.clone(), precise)); + // } else if let Type::RootPolyType(PolyNature::Open(_)) = ty { + // crate::utilities::notify!( + // "Open poly type '{}' treated as immutable free variable", + // name + // ); + // return Ok(VariableWithValue(og_var.clone(), precise)); + // } else if let Type::Constant(_) = ty { + // }; + + return Ok(VariableWithValue(og_var.clone(), precise)); } - if let Some(narrowed) = narrowed { - narrowed + crate::utilities::notify!("Free variable with no current value"); + let constraint = checking_data + .local_type_mappings + .variables_to_constraints + .0 + .get(&og_var.get_origin_variable_id()); + + if let Some(constraint) = constraint { + *constraint } else { - let constraint = checking_data - .local_type_mappings - .variables_to_constraints - .0 - .get(&og_var.get_origin_variable_id()); - if let Some(constraint) = constraint { - *constraint - } else { - crate::utilities::notify!( - "TODO record that free variable is `any` here" - ); - TypeId::ANY_TYPE - } + crate::utilities::notify!("TODO record that free variable is `any` here"); + TypeId::ANY_TYPE } } VariableMutability::Mutable { reassignment_constraint } => { @@ -940,12 +985,15 @@ impl<'a> Environment<'a> { for ctx in self.parents_iter() { if let GeneralContext::Syntax(s) = ctx { if s.possibly_mutated_variables.contains(&variable_id) { + crate::utilities::notify!("Possibly mutated variables"); break; } - if let Some(value) = + + if let Some(current_value) = get_on_ctx!(ctx.info.variable_current_value.get(&variable_id)) + .copied() { - return Ok(VariableWithValue(og_var.clone(), *value)); + return Ok(VariableWithValue(og_var.clone(), current_value)); } if s.context_type.scope.is_dynamic_boundary().is_some() { @@ -986,8 +1034,11 @@ impl<'a> Environment<'a> { } } - let ty = if let Some(value) = reused_reference { - value + let ty = if let Some(reused_reference) = reused_reference { + // TODO temp. I believe this can break type contracts because of mutations + // but needed here because of for loop narrowing + let narrowed = self.get_narrowed_or_object(reused_reference, &checking_data.types); + narrowed.unwrap_or(reused_reference) } else { // TODO dynamic ? let ty = Type::RootPolyType(crate::types::PolyNature::FreeVariable { @@ -1022,6 +1073,7 @@ impl<'a> Environment<'a> { self, of, None::<&crate::types::generics::substitution::SubstitutionArguments<'static>>, + &checking_data.types, ) .expect("import not assigned yet"); return Ok(VariableWithValue(og_var.clone(), current_value)); @@ -1031,7 +1083,9 @@ impl<'a> Environment<'a> { self, og_var.get_id(), None::<&crate::types::generics::substitution::SubstitutionArguments<'static>>, + &checking_data.types, ); + if let Some(current_value) = current_value { Ok(VariableWithValue(og_var.clone(), current_value)) } else { diff --git a/checker/src/context/information.rs b/checker/src/context/information.rs index 486587a4..6f13dcd8 100644 --- a/checker/src/context/information.rs +++ b/checker/src/context/information.rs @@ -252,6 +252,31 @@ pub trait InformationChain { fn get_narrowed(&self, for_ty: TypeId) -> Option { self.get_chain_of_info().find_map(|info| info.narrowed_values.get(&for_ty).copied()) } + + fn get_narrowed_or_object(&self, for_ty: TypeId, types: &TypeStore) -> Option { + let value = self.get_narrowed(for_ty); + if let Some(value) = value { + Some(value) + } else if let Type::Constructor(crate::types::Constructor::ConditionalResult { + condition, + truthy_result, + otherwise_result, + result_union: _, + }) = types.get_type_by_id(for_ty) + { + let narrowed_condition = self.get_narrowed(*condition)?; + if let crate::Decidable::Known(condition) = + crate::types::is_type_truthy_falsy(narrowed_condition, types) + { + let value = if condition { truthy_result } else { otherwise_result }; + Some(*value) + } else { + None + } + } else { + value + } + } } pub struct ModuleInformation<'a> { @@ -393,13 +418,61 @@ pub fn merge_info( onto.variable_current_value.insert(var, new); } - // TODO temp fix for `... ? { ... } : { ... }`. Breaks for the fact that property - // properties might be targeting something above the current condition (e.g. `x ? (y.a = 2) : false`); - onto.current_properties.extend(truthy.current_properties.drain()); - if let Some(ref mut otherwise) = otherwise { - onto.current_properties.extend(otherwise.current_properties.drain()); + // TODO temp fix for `... ? { ... } : { ... }`. + // TODO add undefineds to sides etc + for (on, properties) in truthy.current_properties { + // let properties = properties + // .into_iter() + // .map(|(publicity, key, value)| { + // let falsy_environment_property = otherwise + // .as_mut() + // .and_then(|otherwise| { + // pick_out_property(&mut otherwise.current_properties, (publicity, key), onto, types) + // }); + + // if let Some(existing) = falsy_environment_property { + // // Merging more complex properties has lots of issues + // todo!() + // } else { + // (publicity, key, PropertyValue::ConditionallyExists { condition, value }) + // } + // }) + // .collect(); + + if let Some(existing) = onto.current_properties.get_mut(&on) { + existing.extend(properties); + } else { + onto.current_properties.insert(on, properties); + } + } + + if let Some(otherwise) = otherwise { + for (on, properties) in otherwise.current_properties { + if let Some(existing) = onto.current_properties.get_mut(&on) { + existing.extend(properties); + } else { + onto.current_properties.insert(on, properties); + } + } } // TODO set more information? } } + +// `info_chain` and `types` are a bit excess, but `key_matches` requires it +// TODO needs to delete afterwards, to block it out for subsequent +fn _pick_out_property( + from: &mut Properties, + (want_publicity, want_key): (Publicity, &PropertyKey<'static>), + info_chain: &impl InformationChain, + types: &TypeStore, +) -> Option<(Publicity, PropertyKey<'static>, PropertyValue)> { + from.iter() + .position(|(publicity, key, _)| { + *publicity == want_publicity + && crate::types::key_matches((key, None), (want_key, None), info_chain, types).0 + }) + // TODO replace with deleted? + .map(|idx| from.remove(idx)) +} diff --git a/checker/src/context/mod.rs b/checker/src/context/mod.rs index 25126536..8749cdbe 100644 --- a/checker/src/context/mod.rs +++ b/checker/src/context/mod.rs @@ -920,9 +920,7 @@ impl Context { for (on, constraint) in object_constraints { match self.info.object_constraints.entry(on) { Entry::Occupied(mut existing) => { - let new = types - .new_and_type(*existing.get(), constraint) - .expect("creating impossible restriction"); + let new = types.new_and_type(*existing.get(), constraint); existing.insert(new); } Entry::Vacant(v) => { @@ -980,11 +978,11 @@ impl Context { } pub(crate) fn get_prototype(&self, on: TypeId) -> TypeId { - if let Some(prototype) = self.info.prototypes.get(&on) { + if let Some(prototype) = self.get_chain_of_info().find_map(|info| info.prototypes.get(&on)) + { *prototype - } else if let Some(parent) = self.context_type.get_parent() { - get_on_ctx!(parent.get_prototype(on)) } else { + crate::utilities::notify!("Could not find prototype"); TypeId::OBJECT_TYPE } } @@ -1017,6 +1015,7 @@ pub(crate) fn get_value_of_variable( info: &impl InformationChain, on: VariableId, closures: Option<&impl ClosureChain>, + types: &TypeStore, ) -> Option { for fact in info.get_chain_of_info() { let res = if let Some(closures) = closures { @@ -1032,7 +1031,7 @@ pub(crate) fn get_value_of_variable( // TODO in remaining info, don't loop again if let Some(res) = res { - let narrowed = info.get_narrowed(res); + let narrowed = info.get_narrowed_or_object(res, types); return Some(narrowed.unwrap_or(res)); } } diff --git a/checker/src/context/root.rs b/checker/src/context/root.rs index 69f0ffd7..2d57e70a 100644 --- a/checker/src/context/root.rs +++ b/checker/src/context/root.rs @@ -76,8 +76,8 @@ impl RootContext { ("Capitalize".to_owned(), TypeId::STRING_CAPITALIZE), ("Uncapitalize".to_owned(), TypeId::STRING_UNCAPITALIZE), ("NoInfer".to_owned(), TypeId::NO_INFER), - ("InclusiveRange".to_owned(), TypeId::INCLUSIVE_RANGE), - ("ExclusiveRange".to_owned(), TypeId::EXCLUSIVE_RANGE), + ("GreaterThan".to_owned(), TypeId::GREATER_THAN), + ("LessThan".to_owned(), TypeId::LESS_THAN), ("MultipleOf".to_owned(), TypeId::MULTIPLE_OF), ("NotNotANumber".to_owned(), TypeId::NUMBER_BUT_NOT_NOT_A_NUMBER), ("Not".to_owned(), TypeId::NOT_RESTRICTION), diff --git a/checker/src/diagnostics.rs b/checker/src/diagnostics.rs index 78943ade..2d1afed0 100644 --- a/checker/src/diagnostics.rs +++ b/checker/src/diagnostics.rs @@ -423,7 +423,7 @@ pub(crate) enum TypeCheckError<'a> { #[allow(clippy::upper_case_acronyms)] VariableUsedInTDZ(VariableUsedInTDZ), InvalidMathematicalOrBitwiseOperation { - operator: crate::features::operations::MathematicalAndBitwise, + operator: crate::features::operations::MathematicalOrBitwiseOperation, lhs: TypeStringRepresentation, rhs: TypeStringRepresentation, position: SpanWithSource, @@ -958,6 +958,7 @@ pub enum TypeCheckWarning { DisjointEquality { lhs: TypeStringRepresentation, rhs: TypeStringRepresentation, + result: bool, position: SpanWithSource, }, ItemMustBeUsedWithFlag { @@ -1039,11 +1040,15 @@ impl From for Diagnostic { kind, } } - TypeCheckWarning::DisjointEquality { lhs, rhs, position } => Diagnostic::Position { - reason: format!("This equality is always false as {lhs} and {rhs} have no overlap"), - position, - kind, - }, + TypeCheckWarning::DisjointEquality { lhs, rhs, position, result } => { + Diagnostic::Position { + reason: format!( + "This equality is always {result} as {lhs} and {rhs} have no overlap" + ), + position, + kind, + } + } } } } diff --git a/checker/src/events/application.rs b/checker/src/events/application.rs index d3ddba9f..dc6db249 100644 --- a/checker/src/events/application.rs +++ b/checker/src/events/application.rs @@ -88,6 +88,7 @@ pub(crate) fn apply_events( top_environment, *id, Some(type_arguments), + types, ); if let Some(ty) = value { ty @@ -778,6 +779,11 @@ pub(crate) fn apply_events( }; type_arguments.set_during_application(*referenced_in_scope_as, new_function_ty); } + super::MiscellaneousEvents::MarkFunctionAsCalled(id) => { + if top_environment.is_always_run() { + types.called_functions.insert(*id); + } + } }, Event::FinalEvent(final_event) => { // I think this is okay diff --git a/checker/src/events/mod.rs b/checker/src/events/mod.rs index a376db6f..7bf999d8 100644 --- a/checker/src/events/mod.rs +++ b/checker/src/events/mod.rs @@ -185,6 +185,9 @@ pub enum MiscellaneousEvents { }, /// Creates a new function or class CreateConstructor { referenced_in_scope_as: TypeId, function: FunctionId }, + /// While [`Event::CallsType`] marks callbacks (and parameters) as being called, it does not mark regular function as being called + /// this event has no effect but does add it to the global `HashSet` of called types + MarkFunctionAsCalled(FunctionId), } /// A break in application @@ -252,7 +255,6 @@ pub enum ApplicationResult { position: SpanWithSource, }, /// From a `throw ***` statement (or expression). - /// Throw { thrown: TypeId, position: SpanWithSource, diff --git a/checker/src/events/printing.rs b/checker/src/events/printing.rs index 9e128329..72f86321 100644 --- a/checker/src/events/printing.rs +++ b/checker/src/events/printing.rs @@ -162,6 +162,9 @@ pub fn debug_effects( buf.push_str("end"); } Event::Miscellaneous(misc) => match misc { + super::MiscellaneousEvents::MarkFunctionAsCalled(_) => { + buf.push_str("Calls inlined function"); + } super::MiscellaneousEvents::Has { .. } => { buf.push_str("Has"); } diff --git a/checker/src/features/assignments.rs b/checker/src/features/assignments.rs index 486aab57..2813e7b3 100644 --- a/checker/src/features/assignments.rs +++ b/checker/src/features/assignments.rs @@ -5,7 +5,7 @@ use crate::{ TypeId, }; -use super::operations::{LogicalOperator, MathematicalAndBitwise}; +use super::operations::{LogicalOperator, MathematicalOrBitwiseOperation}; /// A single or multiple items to assign to pub enum Assignable { @@ -50,7 +50,7 @@ pub enum AssignableArrayDestructuringField { /// Increment and decrement are are not binary add subtract as they cast their lhs to number pub enum AssignmentKind { Assign, - PureUpdate(MathematicalAndBitwise), + PureUpdate(MathematicalOrBitwiseOperation), ConditionalUpdate(LogicalOperator), IncrementOrDecrement(IncrementOrDecrement, AssignmentReturnStatus), } diff --git a/checker/src/features/conditional.rs b/checker/src/features/conditional.rs index caa7b332..d46db3a3 100644 --- a/checker/src/features/conditional.rs +++ b/checker/src/features/conditional.rs @@ -47,6 +47,7 @@ where &mut checking_data.types, ); + crate::utilities::notify!("Narrowed value {:?} in true branch", values); truthy_environment.info.narrowed_values = values; let result = then_evaluate(&mut truthy_environment, checking_data); @@ -81,6 +82,7 @@ where &mut checking_data.types, ); + crate::utilities::notify!("Narrowed value {:?} in false branch", values); falsy_environment.info.narrowed_values = values; let result = else_evaluate(&mut falsy_environment, checking_data); diff --git a/checker/src/features/constant_functions.rs b/checker/src/features/constant_functions.rs index a7b18ccf..ecf8497b 100644 --- a/checker/src/features/constant_functions.rs +++ b/checker/src/features/constant_functions.rs @@ -146,6 +146,13 @@ pub(crate) fn call_constant_function( Err(ConstantFunctionError::CannotComputeConstant) } } + "debug_number" => { + let arg = arguments.iter().next().unwrap(); + Ok(ConstantOutput::Diagnostic(format!( + "number: {:?}", + crate::types::intrinsics::get_range_and_mod_class(arg.value, types) + ))) + } "print_type" | "debug_type" | "print_and_debug_type" | "debug_type_independent" => { fn to_string( print: bool, @@ -291,12 +298,16 @@ pub(crate) fn call_constant_function( } "setPrototypeOf" => { if let [first, second] = arguments { - let _prototype = environment - .info - .prototypes - .insert(first.non_spread_type().unwrap(), second.non_spread_type().unwrap()); - // TODO - Ok(ConstantOutput::Value(TypeId::UNDEFINED_TYPE)) + if let (Ok(first), Ok(second)) = (first.non_spread_type(), second.non_spread_type()) + { + let _prototype = environment.info.prototypes.insert(first, second); + + crate::utilities::notify!("Set {:?} prototype to {:?}", first, second); + + Ok(ConstantOutput::Value(first)) + } else { + Err(ConstantFunctionError::CannotComputeConstant) + } } else { Err(ConstantFunctionError::CannotComputeConstant) } diff --git a/checker/src/features/functions.rs b/checker/src/features/functions.rs index 849e5bf8..43081834 100644 --- a/checker/src/features/functions.rs +++ b/checker/src/features/functions.rs @@ -822,6 +822,7 @@ where let closes_over = create_closed_over_references( &function_environment.context_type.closed_over_references, &function_environment, + &checking_data.types, ); let Syntax { @@ -1112,7 +1113,7 @@ pub fn new_name_expected_object( &mut environment.info, ); - types.new_and_type(expected, name_object.build_object()).unwrap() + types.new_and_type(expected, name_object.build_object()) // .unwrap() } /// Reverse of the above @@ -1137,6 +1138,7 @@ pub fn extract_name(expecting: TypeId, types: &TypeStore, environment: &Environm } } +#[must_use] pub fn class_generics_to_function_generics( prototype: TypeId, types: &TypeStore, diff --git a/checker/src/features/iteration.rs b/checker/src/features/iteration.rs index d7b2c0cf..e638cbb1 100644 --- a/checker/src/features/iteration.rs +++ b/checker/src/features/iteration.rs @@ -138,8 +138,6 @@ pub fn synthesise_iteration( // } } IterationBehavior::DoWhile(condition) => { - // let is_do_while = matches!(behavior, IterationBehavior::DoWhile(..)); - // Same as above but condition is evaluated at end. Don't know whether events should be evaluated once...? let (condition, result, ..) = environment.new_lexical_environment_fold_into_parent( Scope::Iteration { label }, @@ -252,6 +250,20 @@ pub fn synthesise_iteration( TypeId::TRUE }; + let values = + super::narrowing::narrow_based_on_expression_into_vec( + condition, + false, + environment, + &mut checking_data.types, + ); + + crate::utilities::notify!( + "Narrowed values in loop {:?}", + values + ); + environment.info.narrowed_values = values; + // TODO not always needed add_loop_described_break_event( condition, @@ -494,8 +506,9 @@ pub(crate) fn run_iteration_block( let events_to_be_applied = iterations * events.len(); crate::utilities::notify!( - "count = {:?}. with", - (events_to_be_applied, input.max_inline) + "applying {:?} events (max {:?})", + events_to_be_applied, + input.max_inline ); (events_to_be_applied < input.max_inline as usize).then_some(iterations) }); @@ -508,20 +521,6 @@ pub(crate) fn run_iteration_block( crate::utilities::notify!("Running {} times", iterations); - // crate::utilities::notify!( - // "Evaluating a constant amount of iterations {:?}", - // iterations - // ); - - // if let InitialVariablesInput::Calculated(initial) = initial { - // for (variable_id, initial_value) in initial.iter() { - // invocation_context - // .get_latest_info(top_environment) - // .variable_current_value - // .insert(*variable_id, *initial_value); - // } - // } - run_iteration_loop( invocation_context, iterations, @@ -672,7 +671,7 @@ fn evaluate_unknown_iteration_for_loop( let initial = match initial { RunBehavior::Run => ClosedOverVariables(Default::default()), RunBehavior::References(v) => { - crate::features::create_closed_over_references(&v, top_environment) + crate::features::create_closed_over_references(&v, top_environment, types) } }; @@ -909,8 +908,9 @@ fn calculate_result_of_loop( result: _, }) = value_after_running_expressions_in_loop { + let assignment = crate::types::helpers::get_origin(*assignment, types); debug_assert!( - assignment == less_than_reference_type_id, + assignment == *less_than_reference_type_id, "incrementor free variable type not the same as condition free variable type?" ); diff --git a/checker/src/features/mod.rs b/checker/src/features/mod.rs index 572559fe..0af0ae5f 100644 --- a/checker/src/features/mod.rs +++ b/checker/src/features/mod.rs @@ -28,7 +28,7 @@ use crate::{ diagnostics::TypeStringRepresentation, events::RootReference, types::{ - get_constraint, + get_constraint, helpers, logical::{Logical, LogicalOrValid}, properties, PartiallyAppliedGenerics, TypeStore, }, @@ -219,6 +219,7 @@ fn get_promise_value(constraint: TypeId, types: &TypeStore) -> Option { pub(crate) fn create_closed_over_references( closed_over_references: &ClosedOverReferencesInScope, current_environment: &Environment, + types: &TypeStore, ) -> ClosedOverVariables { ClosedOverVariables( closed_over_references @@ -229,9 +230,9 @@ pub(crate) fn create_closed_over_references( let c = None::< &crate::types::generics::substitution::SubstitutionArguments<'static>, >; - let get_value_of_variable = - get_value_of_variable(current_environment, *on, c); - let ty = if let Some(value) = get_value_of_variable { + let value = get_value_of_variable(current_environment, *on, c, types); + + let ty = if let Some(value) = value { value } else { // TODO think we are getting rid of this @@ -430,58 +431,72 @@ pub(crate) fn has_property( information: &impl InformationChain, types: &mut TypeStore, ) -> TypeId { - match types.get_type_by_id(rhs) { - Type::Interface { .. } - | Type::Class { .. } - | Type::Constant(_) - | Type::FunctionReference(_) - | Type::Object(_) - | Type::PartiallyAppliedGenerics(_) - | Type::And(_, _) - | Type::SpecialObject(_) - | Type::Narrowed { .. } - | Type::AliasTo { .. } => { - let result = properties::get_property_unbound( - (rhs, None), - (publicity, key, None), - false, - information, - types, - ); - match result { - Ok(LogicalOrValid::Logical(result)) => match result { - Logical::Pure(_) => TypeId::TRUE, - Logical::Or { .. } => { - crate::utilities::notify!("or or implies `in`"); - TypeId::UNIMPLEMENTED_ERROR_TYPE - } - Logical::Implies { .. } => { - crate::utilities::notify!("or or implies `in`"); + if let Some((condition, truthy, falsy)) = helpers::get_type_as_conditional(rhs, types) { + let truthy_result = has_property((publicity, key), truthy, information, types); + let otherwise_result = has_property((publicity, key), falsy, information, types); + types.new_conditional_type(condition, truthy_result, otherwise_result) + } else { + match types.get_type_by_id(rhs) { + Type::Interface { .. } + | Type::Class { .. } + | Type::Constant(_) + | Type::FunctionReference(_) + | Type::Object(_) + | Type::PartiallyAppliedGenerics(_) + | Type::And(_, _) + | Type::SpecialObject(_) + | Type::Narrowed { .. } + | Type::AliasTo { .. } => { + let result = properties::get_property_unbound( + (rhs, None), + (publicity, key, None), + false, + information, + types, + ); + match result { + Ok(LogicalOrValid::Logical(result)) => match result { + Logical::Pure(_) => TypeId::TRUE, + Logical::Or { condition, left, right } => { + // TODO some problems here, need to recurse + let (left, right) = (*left, *right); + if let (LogicalOrValid::Logical(_), LogicalOrValid::Logical(_)) = + (&left, right) + { + TypeId::TRUE + } else if let LogicalOrValid::Logical(_) = left { + condition + } else { + types.new_logical_negation_type(condition) + } + } + Logical::Implies { .. } => { + crate::utilities::notify!("or or implies `in`"); + TypeId::UNIMPLEMENTED_ERROR_TYPE + } + Logical::BasedOnKey { .. } => { + crate::utilities::notify!("mapped in"); + TypeId::UNIMPLEMENTED_ERROR_TYPE + } + }, + Ok(LogicalOrValid::NeedsCalculation(result)) => { + crate::utilities::notify!("TODO {:?}", result); TypeId::UNIMPLEMENTED_ERROR_TYPE } - Logical::BasedOnKey { .. } => { - crate::utilities::notify!("mapped in"); - TypeId::UNIMPLEMENTED_ERROR_TYPE + Err(err) => { + crate::utilities::notify!("TODO {:?}", err); + TypeId::FALSE } - }, - Ok(LogicalOrValid::NeedsCalculation(result)) => { - crate::utilities::notify!("TODO {:?}", result); - TypeId::UNIMPLEMENTED_ERROR_TYPE - } - Err(err) => { - crate::utilities::notify!("TODO {:?}", err); - TypeId::FALSE } } - } - Type::Or(_, _) => { - crate::utilities::notify!("Condtionally"); - TypeId::UNIMPLEMENTED_ERROR_TYPE - } - Type::RootPolyType(_) | Type::Constructor(_) => { - crate::utilities::notify!("Queue event / create dependent"); - let constraint = get_constraint(rhs, types).unwrap(); - has_property((publicity, key), constraint, information, types) + Type::Or(_, _) => { + unreachable!() + } + Type::RootPolyType(_) | Type::Constructor(_) => { + crate::utilities::notify!("Queue event / create dependent"); + let constraint = get_constraint(rhs, types).unwrap(); + has_property((publicity, key), constraint, information, types) + } } } } diff --git a/checker/src/features/narrowing.rs b/checker/src/features/narrowing.rs index 1720ff17..52d840be 100644 --- a/checker/src/features/narrowing.rs +++ b/checker/src/features/narrowing.rs @@ -2,13 +2,13 @@ use crate::{ context::InformationChain, types::{ self, as_logical_and, as_logical_not, as_logical_or, - helpers::{get_conditional, get_origin}, + helpers::{get_origin, get_type_as_conditional}, properties, Constant, Constructor, PolyNature, TypeOperator, TypeStore, }, Map, Type, TypeId, }; -use super::operations::{CanonicalEqualityAndInequality, MathematicalAndBitwise}; +use super::operations::{CanonicalEqualityAndInequality, MathematicalOrBitwiseOperation}; pub fn narrow_based_on_expression_into_vec( condition: TypeId, @@ -18,6 +18,7 @@ pub fn narrow_based_on_expression_into_vec( ) -> Map { let mut into = Default::default(); narrow_based_on_expression(condition, negate, &mut into, information, types); + into.iter_mut().for_each(|(on, value)| *value = types.new_narrowed(*on, *value)); into } @@ -36,28 +37,41 @@ pub fn narrow_based_on_expression( operator: CanonicalEqualityAndInequality::StrictEqual, rhs, } => { + let lhs_type = types.get_type_by_id(*lhs); if let Type::Constructor(Constructor::TypeOperator(TypeOperator::TypeOf(on))) = - types.get_type_by_id(*lhs) + lhs_type { - let from = get_origin(*on, types); + let from = *on; + let origin = get_origin(from, types); + if let Type::Constant(Constant::String(c)) = types.get_type_by_id(*rhs) { - let narrowed_to = crate::features::string_name_to_type(c); - if let Some(narrowed_to) = narrowed_to { + let type_from_name = crate::features::string_name_to_type(c); + if let Some(type_from_name) = type_from_name { if negate { - let mut result = Vec::new(); - build_union_from_filter( - from, - Filter::Not(&Filter::IsType(narrowed_to)), - &mut result, - information, - types, - ); - let narrowed_to = types.new_or_type_from_iterator(result); - let narrowed = types.new_narrowed(from, narrowed_to); - into.insert(from, narrowed); + crate::utilities::notify!("{:?}", from); + // TODO temp fix + let narrowed_to = if let Some(TypeId::ANY_TYPE) = + crate::types::get_constraint(from, types) + { + crate::types::intrinsics::new_intrinsic( + &crate::types::intrinsics::Intrinsic::Not, + type_from_name, + types, + ) + } else { + let mut result = Vec::new(); + build_union_from_filter( + from, + Filter::Not(&Filter::IsType(type_from_name)), + &mut result, + information, + types, + ); + types.new_or_type_from_iterator(result) + }; + into.insert(origin, narrowed_to); } else { - let narrowed = types.new_narrowed(from, narrowed_to); - into.insert(from, narrowed); + into.insert(origin, type_from_name); } } else { crate::utilities::notify!("Type name was (shouldn't be here)"); @@ -67,83 +81,102 @@ pub fn narrow_based_on_expression( } } else if let Type::Constructor(Constructor::BinaryOperator { lhs: operand, - operator: MathematicalAndBitwise::Modulo, + operator: MathematicalOrBitwiseOperation::Modulo, rhs: modulo, result: _, - }) = types.get_type_by_id(*lhs) + }) = lhs_type { - if *rhs == TypeId::ZERO { - crate::utilities::notify!("TODO only if sensible"); + if negate { + crate::utilities::notify!("TODO do we not divisable by?"); + return; + } + let (operand, rhs, modulo) = (*operand, *rhs, *modulo); + let operand = get_origin(operand, types); + crate::utilities::notify!("Here {:?}", types.get_type_by_id(modulo)); + let narrowed_to = crate::types::intrinsics::new_intrinsic( + &crate::types::intrinsics::Intrinsic::MultipleOf, + modulo, + types, + ); - let (from, modulo) = (*operand, *modulo); - if negate { - crate::utilities::notify!("TODO do we not divisable by?"); - } else { - let narrowed_to = crate::types::intrinsics::new_intrinsic( - &crate::types::intrinsics::Intrinsic::MultipleOf, - modulo, - types, - ); - let narrowed = types.new_narrowed(from, narrowed_to); - into.insert(from, narrowed); - } + // TODO also from == x - 1 etc + let narrowed_to = if rhs == TypeId::ZERO { + narrowed_to } else { - crate::utilities::notify!("maybe subtract LHS"); - } + types.register_type(Type::Constructor( + crate::types::Constructor::BinaryOperator { + lhs: narrowed_to, + operator: super::operations::MathematicalOrBitwiseOperation::Add, + rhs, + result: TypeId::NUMBER_TYPE, + }, + )) + }; + into.insert(operand, narrowed_to); } else { - if let Type::RootPolyType(PolyNature::Parameter { .. }) = - types.get_type_by_id(*lhs) - { - crate::utilities::notify!( - "lhs is {:?} with {:?}", - lhs, - types.get_type_by_id(*rhs) - ); + if let Type::RootPolyType(PolyNature::Parameter { .. }) = lhs_type { + crate::utilities::notify!("lhs is {:?} with {:?}", lhs_type, rhs); } if negate && lhs == rhs { - into.insert(*lhs, types.new_narrowed(*lhs, TypeId::NOT_NOT_A_NUMBER)); + into.insert(*lhs, TypeId::NOT_NOT_A_NUMBER); return; } - let lhs = get_origin(*lhs, types); + let lhs = *lhs; + let rhs = *rhs; let result = if negate { // TODO wip - let narrowed_to = if get_conditional(lhs, types).is_some() { + let narrowed_to = if get_type_as_conditional(lhs, types).is_some() { let mut result = Vec::new(); build_union_from_filter( lhs, - Filter::Not(&Filter::IsType(*rhs)), + Filter::Not(&Filter::IsType(rhs)), &mut result, information, types, ); // crate::utilities::notify!("Here {:?} {:?}", (filter, lhs), result); - let narrowed_to = types.new_or_type_from_iterator(result); - types.new_narrowed(lhs, narrowed_to) + types.new_or_type_from_iterator(result) } else { crate::types::intrinsics::new_intrinsic( &crate::types::intrinsics::Intrinsic::Not, - *rhs, + rhs, types, ) }; - types.new_narrowed(lhs, narrowed_to) + narrowed_to } else { - *rhs + rhs }; into.insert(lhs, result); - // PROPERTY HERE - if let Type::Constructor(Constructor::Property { + // CONDITION NARROWING HERE ((x ? 1 : 2) == 1 => x) + // There are missed conditons around things like `typeof` etc (oh well) + // it should be done higher up + if let Type::Constructor(Constructor::ConditionalResult { + condition, + truthy_result, + otherwise_result, + result_union: _, + }) = types.get_type_by_id(get_origin(lhs, types)) + { + if crate::types::helpers::type_equal(*truthy_result, rhs, types) { + narrow_based_on_expression(*condition, false, into, information, types); + } else if crate::types::helpers::type_equal(*otherwise_result, rhs, types) { + narrow_based_on_expression(*condition, true, into, information, types); + } + } + // PROPERTY NARROWING HERE (x.a: b => x: {a: b}) + else if let Type::Constructor(Constructor::Property { on, under, result: _, mode: _, - }) = types.get_type_by_id(lhs) + }) = types.get_type_by_id(get_origin(lhs, types)) { let on = *on; let narrowed_to = if !negate @@ -170,7 +203,7 @@ pub fn narrow_based_on_expression( // on, // types.get_type_by_id(narrowed_to) // ); - into.insert(on, types.new_narrowed(on, narrowed_to)); + into.insert(on, narrowed_to); } } } @@ -179,26 +212,41 @@ pub fn narrow_based_on_expression( operator: CanonicalEqualityAndInequality::LessThan, rhs, } => { - let (lhs, rhs) = (*lhs, *rhs); if negate { + crate::utilities::notify!("Skipping negate on less"); return; } + let lhs = get_origin(*lhs, types); + let rhs = get_origin(*rhs, types); if types.get_type_by_id(lhs).is_dependent() { let narrowed_to = crate::types::intrinsics::new_intrinsic( &crate::types::intrinsics::Intrinsic::LessThan, rhs, types, ); - let narrowed = types.new_narrowed(lhs, narrowed_to); - into.insert(lhs, narrowed); - } else if types.get_type_by_id(rhs).is_dependent() { + // TODO need to merge. This is very bad + let narrowed_to = if let Some(existing) = into.get(&lhs) { + crate::utilities::notify!("Here"); + types.new_and_type(*existing, narrowed_to) + } else { + narrowed_to + }; + into.insert(lhs, narrowed_to); + } + if types.get_type_by_id(rhs).is_dependent() { let narrowed_to = crate::types::intrinsics::new_intrinsic( &crate::types::intrinsics::Intrinsic::GreaterThan, lhs, types, ); - let narrowed = types.new_narrowed(rhs, narrowed_to); - into.insert(rhs, narrowed); + // TODO need to merge. This is very bad + let narrowed_to = if let Some(existing) = into.get(&rhs) { + crate::utilities::notify!("Here"); + types.new_and_type(narrowed_to, *existing) + } else { + narrowed_to + }; + into.insert(rhs, narrowed_to); } } Constructor::TypeOperator(TypeOperator::IsPrototype { lhs, rhs_prototype }) => { @@ -214,8 +262,7 @@ pub fn narrow_based_on_expression( build_union_from_filter(constraint, filter, &mut result, information, types); types.new_or_type_from_iterator(result) }; - let narrowed = types.new_narrowed(lhs, narrowed_to); - into.insert(lhs, narrowed); + into.insert(lhs, narrowed_to); } Constructor::TypeExtends(crate::types::TypeExtends { item, extends }) => { let (item, extends) = (*item, *extends); @@ -231,8 +278,7 @@ pub fn narrow_based_on_expression( build_union_from_filter(constraint, filter, &mut result, information, types); types.new_or_type_from_iterator(result) }; - let narrowed = types.new_narrowed(item, narrowed_to); - into.insert(item, narrowed); + into.insert(item, narrowed_to); } Constructor::TypeOperator(TypeOperator::HasProperty(on, under)) => { let on = *on; @@ -253,11 +299,10 @@ pub fn narrow_based_on_expression( ); types.new_or_type_from_iterator(items) }; - into.insert(on, types.new_narrowed(on, narrowed_to)); + into.insert(on, narrowed_to); } constructor => { if let Some(condition) = as_logical_not(constructor, types) { - crate::utilities::notify!("Here"); narrow_based_on_expression(condition, !negate, into, information, types); } else if let Some((lhs, rhs)) = as_logical_and(constructor, types) { // De Morgan's laws @@ -311,8 +356,6 @@ pub fn narrow_based_on_expression( crate::types::get_constraint(rhs_request, types) .unwrap_or(rhs_request), ); - // TODO - // let narrowed = types.new_narrowed(rhs, narrowed_to); into.insert(on, types.new_or_type(lhs_request, rhs_request)); } else { // Only when we have two results is it useful @@ -338,10 +381,7 @@ pub fn narrow_based_on_expression( types, ); let narrowed_to = types.new_or_type_from_iterator(result); - into.insert( - condition, - types.register_type(Type::Narrowed { from: condition, narrowed_to }), - ); + into.insert(condition, narrowed_to); } } } @@ -364,10 +404,11 @@ pub(crate) enum Filter<'a> { static NULL_OR_UNDEFINED: Filter<'static> = Filter::NullOrUndefined; pub(crate) static NOT_NULL_OR_UNDEFINED: Filter<'static> = Filter::Not(&NULL_OR_UNDEFINED); -static FASLY: Filter<'static> = Filter::Falsy; +pub(crate) static FASLY: Filter<'static> = Filter::Falsy; pub(crate) static NOT_FASLY: Filter<'static> = Filter::Not(&FASLY); impl<'a> Filter<'a> { + // Returns `true` if `value` passes filter pub(crate) fn type_matches_filter( &self, value: TypeId, @@ -415,8 +456,12 @@ impl<'a> Filter<'a> { } } Filter::HasProperty { property, filter } => { - let value = - types::properties::get_simple_value(information, value, property, types); + let value = types::properties::get_simple_property_value( + information, + value, + property, + types, + ); if let Some(value) = value { let matches = filter.type_matches_filter(value, information, types, negate); crate::utilities::notify!("Value {:?}", (value, negate, matches)); @@ -432,6 +477,12 @@ impl<'a> Filter<'a> { (allowed_match && is_null_or_undefined) || (!allowed_match && !is_null_or_undefined) } Filter::Falsy => { + let can_be_falsy = [TypeId::NUMBER_TYPE, TypeId::STRING_TYPE, TypeId::BOOLEAN_TYPE]; + + if can_be_falsy.contains(&value) { + return true; + } + let is_falsy = [ TypeId::NULL_TYPE, TypeId::UNDEFINED_TYPE, @@ -447,6 +498,7 @@ impl<'a> Filter<'a> { } } +/// Important that this does not handle `any` well with negated filters. It needs to generate negated types but only has non-mutable access to `TypeStore` #[allow(clippy::used_underscore_binding)] pub(crate) fn build_union_from_filter( on: TypeId, @@ -455,11 +507,14 @@ pub(crate) fn build_union_from_filter( information: &impl InformationChain, types: &TypeStore, ) { - if let Some((_condition, lhs, rhs)) = get_conditional(on, types) { + if let Some(constraint) = crate::types::get_constraint(on, types) { + build_union_from_filter(constraint, filter, found, information, types); + } else if let TypeId::BOOLEAN_TYPE = on { + build_union_from_filter(TypeId::TRUE, filter, found, information, types); + build_union_from_filter(TypeId::FALSE, filter, found, information, types); + } else if let Some((_condition, lhs, rhs)) = get_type_as_conditional(on, types) { build_union_from_filter(lhs, filter, found, information, types); build_union_from_filter(rhs, filter, found, information, types); - } else if let Some(constraint) = crate::types::get_constraint(on, types) { - build_union_from_filter(constraint, filter, found, information, types); } else { let not_already_added = !found.contains(&on); if not_already_added && filter.type_matches_filter(on, information, types, false) { diff --git a/checker/src/features/operations.rs b/checker/src/features/operations.rs deleted file mode 100644 index 78a88f83..00000000 --- a/checker/src/features/operations.rs +++ /dev/null @@ -1,849 +0,0 @@ -//! Contains logic for mathematical, bitwise and logical operators - -use derive_enum_from_into::EnumFrom; -use source_map::{Span, SpanWithSource}; - -use crate::{ - diagnostics::{TypeCheckError, TypeStringRepresentation}, - features::conditional::new_conditional_context, - types::{ - cast_as_number, cast_as_string, helpers::simple_subtype, intrinsics, is_type_truthy_falsy, - Constructor, PartiallyAppliedGenerics, TypeStore, - }, - CheckingData, Constant, Decidable, Environment, Type, TypeId, -}; - -use super::objects::SpecialObject; - -/// For these **binary** operations both operands are synthesised -#[derive(Clone, Copy, Debug, binary_serialize_derive::BinarySerializable)] -pub enum MathematicalAndBitwise { - Add, - Subtract, - Multiply, - Divide, - Modulo, - Exponent, - BitwiseShiftLeft, - BitwiseShiftRight, - BitwiseShiftRightUnsigned, - BitwiseAnd, - BitwiseXOr, - BitwiseOr, -} - -#[derive(Clone, Copy, Debug, EnumFrom)] -pub enum PureBinaryOperation { - MathematicalAndBitwise(MathematicalAndBitwise), - // Some of these can be reduced - EqualityAndInequality(EqualityAndInequality), -} - -/// TODO report errors better here -pub fn evaluate_pure_binary_operation_handle_errors< - T: crate::ReadFromFS, - A: crate::ASTImplementation, ->( - (lhs, lhs_pos): (TypeId, SpanWithSource), - operator: PureBinaryOperation, - (rhs, rhs_pos): (TypeId, SpanWithSource), - checking_data: &mut CheckingData, - environment: &mut Environment, -) -> TypeId { - match operator { - PureBinaryOperation::MathematicalAndBitwise(operator) => { - if let (MathematicalAndBitwise::Exponent, TypeId::ZERO) = (operator, rhs) { - // This holds for NaN. Thus can do in every case - return TypeId::ONE; - } - - let result = evaluate_mathematical_operation( - lhs, - operator, - rhs, - environment, - &mut checking_data.types, - checking_data.options.strict_casts, - ); - - match result { - Ok(result) => result, - Err(_err) => { - let position = lhs_pos - .without_source() - .union(rhs_pos.without_source()) - .with_source(environment.get_source()); - - checking_data.diagnostics_container.add_error( - TypeCheckError::InvalidMathematicalOrBitwiseOperation { - operator, - lhs: TypeStringRepresentation::from_type_id( - lhs, - environment, - &checking_data.types, - false, - ), - rhs: TypeStringRepresentation::from_type_id( - rhs, - environment, - &checking_data.types, - false, - ), - position, - }, - ); - TypeId::ERROR_TYPE - } - } - } - PureBinaryOperation::EqualityAndInequality(operator) => { - // Cannot error, but can be always true or false - let result = evaluate_equality_inequality_operation( - lhs, - &operator, - rhs, - environment, - &mut checking_data.types, - checking_data.options.strict_casts, - ); - - if let Ok((result, warning)) = result { - if let EqualityAndInequalityResultKind::Disjoint = warning { - let position = lhs_pos - .without_source() - .union(rhs_pos.without_source()) - .with_source(environment.get_source()); - - checking_data.diagnostics_container.add_warning( - crate::TypeCheckWarning::DisjointEquality { - lhs: TypeStringRepresentation::from_type_id( - lhs, - environment, - &checking_data.types, - false, - ), - rhs: TypeStringRepresentation::from_type_id( - rhs, - environment, - &checking_data.types, - false, - ), - position, - }, - ); - } - - result - } else { - let position = lhs_pos - .without_source() - .union(rhs_pos.without_source()) - .with_source(environment.get_source()); - - checking_data.diagnostics_container.add_error( - crate::TypeCheckError::InvalidEqualityOperation { - operator, - lhs: TypeStringRepresentation::from_type_id( - lhs, - environment, - &checking_data.types, - false, - ), - rhs: TypeStringRepresentation::from_type_id( - rhs, - environment, - &checking_data.types, - false, - ), - position, - }, - ); - - TypeId::ERROR_TYPE - } - } - } -} - -/// TODO proper error type -pub fn evaluate_mathematical_operation( - lhs: TypeId, - operator: MathematicalAndBitwise, - rhs: TypeId, - info: &impl crate::context::InformationChain, - types: &mut TypeStore, - strict_casts: bool, -) -> Result { - fn attempt_constant_math_operator( - lhs: TypeId, - operator: MathematicalAndBitwise, - rhs: TypeId, - types: &mut TypeStore, - strict_casts: bool, - ) -> Result { - if let MathematicalAndBitwise::Add = operator { - let constant = match (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) { - (Type::Constant(Constant::Number(lhs)), Type::Constant(Constant::Number(rhs))) => { - Constant::Number(lhs + rhs) - } - (Type::Constant(lhs), Type::Constant(rhs)) => { - let mut first = cast_as_string(lhs, strict_casts)?; - let second = cast_as_string(rhs, strict_casts)?; - // Concatenate strings - first.push_str(&second); - Constant::String(first) - } - _ => return Err(()), - }; - Ok(types.new_constant_type(constant)) - } else { - match (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) { - (Type::Constant(c1), Type::Constant(c2)) => { - let lhs = cast_as_number(c1, strict_casts).unwrap_or(f64::NAN); - let rhs = cast_as_number(c2, strict_casts).unwrap_or(f64::NAN); - // TODO hopefully Rust implementation is the same as JS - #[allow(clippy::cast_possible_truncation)] - let value = match operator { - MathematicalAndBitwise::Add => unreachable!(), - MathematicalAndBitwise::Subtract => lhs - rhs, - MathematicalAndBitwise::Multiply => lhs * rhs, - MathematicalAndBitwise::Divide => lhs / rhs, - MathematicalAndBitwise::Modulo => lhs % rhs, - MathematicalAndBitwise::Exponent => lhs.powf(rhs), - MathematicalAndBitwise::BitwiseShiftLeft => { - f64::from((lhs as i32).checked_shl(rhs as u32).unwrap_or(0)) - } - MathematicalAndBitwise::BitwiseShiftRight => { - f64::from((lhs as i32).checked_shr(rhs as u32).unwrap_or(0)) - } - MathematicalAndBitwise::BitwiseShiftRightUnsigned => { - (lhs as i32).wrapping_shr(rhs as u32).into() - } - MathematicalAndBitwise::BitwiseAnd => { - f64::from((lhs as i32) & (rhs as i32)) - } - MathematicalAndBitwise::BitwiseXOr => { - f64::from((lhs as i32) ^ (rhs as i32)) - } - MathematicalAndBitwise::BitwiseOr => f64::from((lhs as i32) | (rhs as i32)), - }; - let value = ordered_float::NotNan::try_from(value); - let ty = match value { - Ok(value) => types.new_constant_type(Constant::Number(value)), - Err(_) => TypeId::NAN, - }; - Ok(ty) - } - _ => Err(()), - } - } - } - - if lhs == TypeId::ERROR_TYPE || rhs == TypeId::ERROR_TYPE { - return Ok(TypeId::ERROR_TYPE); - } - - let is_dependent = - types.get_type_by_id(lhs).is_dependent() || types.get_type_by_id(rhs).is_dependent(); - - if is_dependent { - let can_be_string = if let MathematicalAndBitwise::Add = operator { - let left_is_string = simple_subtype(lhs, TypeId::STRING_TYPE, info, types); - let right_is_string = simple_subtype(lhs, TypeId::STRING_TYPE, info, types); - let left_is_string_or_number = - left_is_string || simple_subtype(lhs, TypeId::NUMBER_TYPE, info, types); - let right_is_string_or_number = - right_is_string || simple_subtype(rhs, TypeId::NUMBER_TYPE, info, types); - if !left_is_string_or_number || !right_is_string_or_number { - return Err(()); - } - left_is_string || right_is_string - } else { - let left_is_number = simple_subtype(lhs, TypeId::NUMBER_TYPE, info, types); - if !left_is_number || !simple_subtype(rhs, TypeId::NUMBER_TYPE, info, types) { - return Err(()); - } - false - }; - - // :) - if let (MathematicalAndBitwise::Exponent, TypeId::ONE, true) = - (operator, rhs, intrinsics::is_not_not_a_number(lhs, types)) - { - return Ok(lhs); - } else if let (MathematicalAndBitwise::Add, TypeId::ZERO) - | (MathematicalAndBitwise::Multiply, TypeId::ONE) = (operator, rhs) - { - return Ok(lhs); - } else if let (MathematicalAndBitwise::Add, TypeId::ZERO) - | (MathematicalAndBitwise::Multiply, TypeId::ONE) = (operator, lhs) - { - return Ok(rhs); - } - - let result = if can_be_string { - TypeId::STRING_TYPE - } else if let ( - MathematicalAndBitwise::Add | MathematicalAndBitwise::Multiply, - Some(lhs_range), - Some(rhs_range), - ) = (operator, intrinsics::get_range(lhs, types), intrinsics::get_range(rhs, types)) - { - match operator { - MathematicalAndBitwise::Add => { - intrinsics::range_to_type(lhs_range.space_addition(rhs_range), types) - } - MathematicalAndBitwise::Multiply => { - intrinsics::range_to_type(lhs_range.space_multiplication(rhs_range), types) - } - _ => unreachable!(), - } - } else { - TypeId::NUMBER_TYPE - }; - - let constructor = crate::types::Constructor::BinaryOperator { lhs, operator, rhs, result }; - Ok(types.register_type(crate::Type::Constructor(constructor))) - } else { - attempt_constant_math_operator(lhs, operator, rhs, types, strict_casts) - } -} - -/// Not canonical / reducible form of [`CanonicalEqualityAndInequality`]. -/// (for examples `a > b` is equivalent to `b < a` (after side effects) and `a !== b` is equivalent to `!(a === b)`) -#[derive(Clone, Copy, Debug)] -pub enum EqualityAndInequality { - StrictEqual, - StrictNotEqual, - Equal, - NotEqual, - GreaterThan, - LessThan, - LessThanOrEqual, - GreaterThanOrEqual, -} - -/// Canonical / irreducible form of [`EqualityAndInequality`]. -#[derive(Clone, Copy, Debug, binary_serialize_derive::BinarySerializable)] -pub enum CanonicalEqualityAndInequality { - StrictEqual, - LessThan, -} - -pub enum EqualityAndInequalityResultKind { - Constant, - Disjoint, - Condition, -} - -pub fn evaluate_equality_inequality_operation( - mut lhs: TypeId, - operator: &EqualityAndInequality, - mut rhs: TypeId, - info: &impl crate::context::InformationChain, - types: &mut TypeStore, - strict_casts: bool, -) -> Result<(TypeId, EqualityAndInequalityResultKind), ()> { - // `NaN == t` is always true - if lhs == TypeId::NAN || rhs == TypeId::NAN { - return Ok((TypeId::FALSE, EqualityAndInequalityResultKind::Constant)); - } - - match operator { - EqualityAndInequality::StrictEqual => { - // crate::utilities::notify!("{:?} === {:?}", lhs, rhs); - - let left_dependent = types.get_type_by_id(lhs).is_dependent(); - let is_dependent = left_dependent || types.get_type_by_id(rhs).is_dependent(); - - if is_dependent { - if lhs == rhs - && intrinsics::is_not_not_a_number(lhs, types) - && intrinsics::is_not_not_a_number(rhs, types) - { - // I think this is okay - return Ok((TypeId::TRUE, EqualityAndInequalityResultKind::Constant)); - } - - // Checks lhs and rhs type to see if they overlap - if crate::types::disjoint::types_are_disjoint( - lhs, - rhs, - &mut Vec::new(), - info, - types, - ) { - return Ok((TypeId::FALSE, EqualityAndInequalityResultKind::Disjoint)); - } - - // Sort if `*constant* == ...`. Ideally want constant type on the RHS - let (lhs, rhs) = if left_dependent { (lhs, rhs) } else { (rhs, rhs) }; - let constructor = crate::types::Constructor::CanonicalRelationOperator { - lhs, - operator: CanonicalEqualityAndInequality::StrictEqual, - rhs, - }; - - Ok(( - types.register_type(crate::Type::Constructor(constructor)), - EqualityAndInequalityResultKind::Condition, - )) - } else { - match attempt_constant_equality(lhs, rhs, types) { - Ok(ty) => Ok(( - if ty { TypeId::TRUE } else { TypeId::FALSE }, - EqualityAndInequalityResultKind::Constant, - )), - Err(()) => { - unreachable!( - "should have been caught `is_dependent` above, {:?} === {:?}", - types.get_type_by_id(lhs), - types.get_type_by_id(rhs) - ) - } - } - } - } - EqualityAndInequality::LessThan => { - fn attempt_less_than( - lhs: TypeId, - rhs: TypeId, - types: &mut TypeStore, - strict_casts: bool, - ) -> Result { - // Similar but reversed semantics to add - match (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) { - ( - Type::Constant(Constant::String(string1)), - Type::Constant(Constant::String(string2)), - ) => { - // Yah rust includes string alphanumerical equivalence of strings - Ok(string1 < string2) - } - (Type::Constant(c1), Type::Constant(c2)) => { - let lhs = cast_as_number(c1, strict_casts)?; - let rhs = cast_as_number(c2, strict_casts)?; - Ok(lhs < rhs) - } - (lhs, rhs) => { - crate::utilities::notify!("{:?}", (lhs, rhs)); - // Ok(TypeId::OPEN_BOOLEAN_TYPE) - Err(()) - } - } - } - - let is_dependent = types.get_type_by_id(lhs).is_dependent() - || types.get_type_by_id(rhs).is_dependent(); - - if is_dependent { - { - if let Type::Constructor(Constructor::BinaryOperator { - lhs: op_lhs, - operator, - rhs: op_rhs, - result: _, - }) = types.get_type_by_id(lhs) - { - if let ( - Type::Constant(Constant::Number(add)), - MathematicalAndBitwise::Add, - Type::Constant(Constant::Number(lt)), - ) = (types.get_type_by_id(*op_rhs), operator, types.get_type_by_id(rhs)) - { - crate::utilities::notify!("Shifted LT"); - lhs = *op_lhs; - rhs = types.register_type(Type::Constant(Constant::Number(lt - add))); - } - } - } - - { - // let lhs = get_constraint(lhs, types).unwrap_or(lhs); - // let rhs = get_constraint(rhs, types).unwrap_or(rhs); - - if !simple_subtype(lhs, TypeId::NUMBER_TYPE, info, types) - || !simple_subtype(rhs, TypeId::NUMBER_TYPE, info, types) - { - return Err(()); - } - - // Tidies some things for counting loop iterations - - // Checking disjoint-ness for inequalities (TODO under option) via distribution - if let (Some(lhs_range), Some(rhs_range)) = - (intrinsics::get_range(lhs, types), intrinsics::get_range(rhs, types)) - { - if lhs_range.below(rhs_range) { - return Ok((TypeId::TRUE, EqualityAndInequalityResultKind::Constant)); - } - if lhs_range.above(rhs_range) { - return Ok((TypeId::FALSE, EqualityAndInequalityResultKind::Disjoint)); - } - } - } - - let constructor = Constructor::CanonicalRelationOperator { - lhs, - operator: CanonicalEqualityAndInequality::LessThan, - rhs, - }; - Ok(( - types.register_type(crate::Type::Constructor(constructor)), - EqualityAndInequalityResultKind::Condition, - )) - } else { - attempt_less_than(lhs, rhs, types, strict_casts).map(|value| { - ( - if value { TypeId::TRUE } else { TypeId::FALSE }, - EqualityAndInequalityResultKind::Constant, - ) - }) - } - } - // equal OR less than - EqualityAndInequality::LessThanOrEqual => { - let (equality_result, warning) = evaluate_equality_inequality_operation( - lhs, - &EqualityAndInequality::StrictEqual, - rhs, - info, - types, - strict_casts, - )?; - - if equality_result == TypeId::TRUE { - Ok((equality_result, warning)) - } else if equality_result == TypeId::FALSE { - evaluate_equality_inequality_operation( - lhs, - &EqualityAndInequality::LessThan, - rhs, - info, - types, - strict_casts, - ) - } else { - let (less_than_result, warning) = evaluate_equality_inequality_operation( - lhs, - &EqualityAndInequality::LessThan, - rhs, - info, - types, - strict_casts, - )?; - Ok((types.new_logical_or_type(equality_result, less_than_result), warning)) - } - } - EqualityAndInequality::StrictNotEqual => { - let (equality_result, kind) = evaluate_equality_inequality_operation( - lhs, - &EqualityAndInequality::StrictEqual, - rhs, - info, - types, - strict_casts, - )?; - if let EqualityAndInequalityResultKind::Condition = kind { - Ok((types.new_logical_negation_type(equality_result), kind)) - } else { - let negated = if let TypeId::TRUE = equality_result { - TypeId::FALSE - } else if let TypeId::FALSE = equality_result { - TypeId::TRUE - } else { - todo!() - }; - Ok((negated, kind)) - } - } - EqualityAndInequality::Equal => { - crate::utilities::notify!("TODO equal operator"); - Ok((TypeId::OPEN_BOOLEAN_TYPE, EqualityAndInequalityResultKind::Condition)) - } - EqualityAndInequality::NotEqual => { - let (equality_result, kind) = evaluate_equality_inequality_operation( - lhs, - &EqualityAndInequality::Equal, - rhs, - info, - types, - strict_casts, - )?; - if let EqualityAndInequalityResultKind::Condition = kind { - Ok((types.new_logical_negation_type(equality_result), kind)) - } else { - let negated = if let TypeId::TRUE = equality_result { - TypeId::FALSE - } else if let TypeId::FALSE = equality_result { - TypeId::TRUE - } else { - todo!() - }; - Ok((negated, kind)) - } - } - // Swapping operands! - EqualityAndInequality::GreaterThan => evaluate_equality_inequality_operation( - rhs, - &EqualityAndInequality::LessThan, - lhs, - info, - types, - strict_casts, - ), - // Swapping operands! - EqualityAndInequality::GreaterThanOrEqual => evaluate_equality_inequality_operation( - rhs, - &EqualityAndInequality::LessThanOrEqual, - lhs, - info, - types, - strict_casts, - ), - } -} - -#[allow(clippy::let_and_return)] -pub fn is_null_or_undefined( - ty: TypeId, - info: &impl crate::context::InformationChain, - types: &mut TypeStore, -) -> TypeId { - let is_null = evaluate_equality_inequality_operation( - ty, - &EqualityAndInequality::StrictEqual, - TypeId::NULL_TYPE, - info, - types, - false, - ) - .map_or(TypeId::ERROR_TYPE, |(left, _)| left); - - if let TypeId::TRUE = is_null { - is_null - } else { - let is_undefined = evaluate_equality_inequality_operation( - ty, - &EqualityAndInequality::StrictEqual, - TypeId::UNDEFINED_TYPE, - info, - types, - false, - ) - .map_or(TypeId::ERROR_TYPE, |(left, _)| left); - - if let TypeId::FALSE = is_null { - is_undefined - } else { - types.new_logical_or_type(is_null, is_undefined) - } - } -} - -#[derive(Copy, Clone, Debug)] -pub enum LogicalOperator { - And, - Or, - /// TODO is this canonical? - NullCoalescing, -} - -/// TODO strict casts! -pub fn evaluate_logical_operation_with_expression< - 'a, - T: crate::ReadFromFS, - A: crate::ASTImplementation, ->( - lhs: (TypeId, Span), - operator: LogicalOperator, - rhs: &'a A::Expression<'a>, - checking_data: &mut CheckingData, - environment: &mut Environment, - expecting: TypeId, -) -> Result { - match operator { - LogicalOperator::And => Ok(new_conditional_context( - environment, - lhs, - |env: &mut Environment, data: &mut CheckingData| { - A::synthesise_expression(rhs, expecting, env, data) - }, - Some(|_env: &mut Environment, _data: &mut CheckingData| lhs.0), - checking_data, - )), - LogicalOperator::Or => Ok(new_conditional_context( - environment, - lhs, - |env: &mut Environment, checking_data: &mut CheckingData| { - if let Some(constraint) = crate::types::get_constraint(lhs.0, &checking_data.types) - { - let mut result = Vec::new(); - super::narrowing::build_union_from_filter( - constraint, - super::narrowing::NOT_FASLY, - &mut result, - env, - &checking_data.types, - ); - let narrowed_to = checking_data.types.new_or_type_from_iterator(result); - checking_data.types.register_type(Type::Narrowed { from: lhs.0, narrowed_to }) - } else { - lhs.0 - } - }, - Some(|env: &mut Environment, data: &mut CheckingData| { - A::synthesise_expression(rhs, expecting, env, data) - }), - checking_data, - )), - LogicalOperator::NullCoalescing => { - let is_lhs_null_or_undefined = - is_null_or_undefined(lhs.0, environment, &mut checking_data.types); - // Equivalent to: `(lhs is null or undefined) ? lhs : rhs` - Ok(new_conditional_context( - environment, - (is_lhs_null_or_undefined, lhs.1), - |env: &mut Environment, checking_data: &mut CheckingData| { - if let Some(constraint) = - crate::types::get_constraint(lhs.0, &checking_data.types) - { - let mut result = Vec::new(); - super::narrowing::build_union_from_filter( - constraint, - super::narrowing::NOT_NULL_OR_UNDEFINED, - &mut result, - env, - &checking_data.types, - ); - let narrowed_to = checking_data.types.new_or_type_from_iterator(result); - checking_data - .types - .register_type(Type::Narrowed { from: lhs.0, narrowed_to }) - } else { - lhs.0 - } - }, - Some(|env: &mut Environment, data: &mut CheckingData| { - A::synthesise_expression(rhs, expecting, env, data) - }), - checking_data, - )) - } - } -} - -/// `typeof` and some others done elsewhere -#[derive(Clone, Copy, Debug, binary_serialize_derive::BinarySerializable)] -pub enum UnaryOperation { - /// Treated as `(value ? false : true)` - LogicalNot, - /// Treated as `0 - value` (could also do -1 * value?) - Negation, - /// Treated as `value ^ 0xFFFF_FFFF` - BitwiseNot, -} - -/// Tries to evaluate unary operation for constant terms. Else delegates to binary operations that handle equivalent thing -pub fn evaluate_unary_operator( - operator: UnaryOperation, - operand: TypeId, - info: &impl crate::context::InformationChain, - types: &mut TypeStore, - strict_casts: bool, -) -> Result { - if operand == TypeId::ERROR_TYPE { - return Ok(operand); - } - - match operator { - UnaryOperation::LogicalNot => { - if let Decidable::Known(value) = is_type_truthy_falsy(operand, types) { - if value { - Ok(TypeId::FALSE) - } else { - Ok(TypeId::TRUE) - } - } else { - let is_boolean = simple_subtype(operand, TypeId::BOOLEAN_TYPE, info, types); - if is_boolean { - Ok(types.new_logical_negation_type(operand)) - } else { - Err(()) - } - } - } - UnaryOperation::Negation | UnaryOperation::BitwiseNot => { - if let Type::Constant(cst) = types.get_type_by_id(operand) { - let value = cast_as_number(cst, strict_casts).expect("hmm"); - let value = match operator { - UnaryOperation::BitwiseNot => f64::from(!(value as i32)), - UnaryOperation::Negation => -value, - UnaryOperation::LogicalNot => unreachable!(), - }; - let value = ordered_float::NotNan::try_from(value); - Ok(match value { - Ok(value) => types.new_constant_type(Constant::Number(value)), - Err(_) => TypeId::NAN, - }) - } else { - match operator { - UnaryOperation::BitwiseNot => evaluate_mathematical_operation( - TypeId::MAX_U32, - MathematicalAndBitwise::BitwiseXOr, - operand, - info, - types, - strict_casts, - ), - UnaryOperation::Negation => evaluate_mathematical_operation( - TypeId::ZERO, - MathematicalAndBitwise::Subtract, - operand, - info, - types, - strict_casts, - ), - UnaryOperation::LogicalNot => unreachable!("handled above"), - } - } - } - } -} - -/// Returns whether lhs and rhs are always equal or never equal. TODO more -/// -/// TODO return decidable. -fn attempt_constant_equality(lhs: TypeId, rhs: TypeId, types: &mut TypeStore) -> Result { - if lhs == rhs { - Ok(true) - } else if matches!(lhs, TypeId::NULL_TYPE | TypeId::UNDEFINED_TYPE) - || matches!(rhs, TypeId::NULL_TYPE | TypeId::UNDEFINED_TYPE) - { - // If above `==`` failed => false (as always have same `TypeId`) - Ok(false) - } else { - let lhs = types.get_type_by_id(lhs); - let rhs = types.get_type_by_id(rhs); - if let (Type::Constant(cst1), Type::Constant(cst2)) = (lhs, rhs) { - Ok(cst1 == cst2) - } else if let (Type::Object(..) | Type::SpecialObject(SpecialObject::Function(..)), _) - | (_, Type::Object(..) | Type::SpecialObject(SpecialObject::Function(..))) = (lhs, rhs) - { - // Same objects and functions always have same type id. Poly case doesn't occur here - Ok(false) - } - // Temp fix for closures - else if let ( - Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on: on_lhs, .. }), - Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on: on_rhs, .. }), - ) = (lhs, rhs) - { - // TODO does this work? - attempt_constant_equality(*on_lhs, *on_rhs, types) - } else { - crate::utilities::notify!("{:?} === {:?} is apparently false", lhs, rhs); - Err(()) - } - } -} diff --git a/checker/src/features/operations/logical.rs b/checker/src/features/operations/logical.rs new file mode 100644 index 00000000..25ecfc72 --- /dev/null +++ b/checker/src/features/operations/logical.rs @@ -0,0 +1,113 @@ +use super::{ + super::{conditional, narrowing}, + relation::is_null_or_undefined, +}; +use crate::{Type, TypeId}; + +#[derive(Copy, Clone, Debug)] +pub enum LogicalOperator { + And, + Or, + /// TODO is this canonical? + NullCoalescing, +} + +/// TODO strict casts! +pub fn evaluate_logical_operation_with_expression< + 'a, + T: crate::ReadFromFS, + A: crate::ASTImplementation, +>( + lhs: (TypeId, source_map::Span), + operator: LogicalOperator, + rhs: &'a A::Expression<'a>, + checking_data: &mut crate::CheckingData, + environment: &mut crate::Environment, + expecting: TypeId, +) -> Result { + match operator { + LogicalOperator::And => Ok(conditional::new_conditional_context( + environment, + lhs, + |env: &mut crate::Environment, data: &mut crate::CheckingData| { + A::synthesise_expression(rhs, expecting, env, data) + }, + Some(|env: &mut crate::Environment, checking_data: &mut crate::CheckingData| { + if let Some(constraint) = crate::types::get_constraint(lhs.0, &checking_data.types) + { + let mut result = Vec::new(); + narrowing::build_union_from_filter( + constraint, + narrowing::FASLY, + &mut result, + env, + &checking_data.types, + ); + let narrowed_to = checking_data.types.new_or_type_from_iterator(result); + checking_data.types.register_type(Type::Narrowed { from: lhs.0, narrowed_to }) + } else { + lhs.0 + } + }), + checking_data, + )), + LogicalOperator::Or => Ok(conditional::new_conditional_context( + environment, + lhs, + |env: &mut crate::Environment, checking_data: &mut crate::CheckingData| { + if let Some(constraint) = crate::types::get_constraint(lhs.0, &checking_data.types) + { + let mut result = Vec::new(); + narrowing::build_union_from_filter( + constraint, + narrowing::NOT_FASLY, + &mut result, + env, + &checking_data.types, + ); + let narrowed_to = checking_data.types.new_or_type_from_iterator(result); + checking_data.types.register_type(Type::Narrowed { from: lhs.0, narrowed_to }) + } else { + lhs.0 + } + }, + Some(|env: &mut crate::Environment, data: &mut crate::CheckingData| { + A::synthesise_expression(rhs, expecting, env, data) + }), + checking_data, + )), + LogicalOperator::NullCoalescing => { + let is_lhs_null_or_undefined = + is_null_or_undefined(lhs.0, environment, &mut checking_data.types); + // Equivalent to: `(lhs is null or undefined) ? lhs : rhs` + Ok(conditional::new_conditional_context( + environment, + (is_lhs_null_or_undefined, lhs.1), + |env: &mut crate::Environment, checking_data: &mut crate::CheckingData| { + if let Some(constraint) = + crate::types::get_constraint(lhs.0, &checking_data.types) + { + let mut result = Vec::new(); + narrowing::build_union_from_filter( + constraint, + narrowing::NOT_NULL_OR_UNDEFINED, + &mut result, + env, + &checking_data.types, + ); + let narrowed_to = checking_data.types.new_or_type_from_iterator(result); + checking_data + .types + .register_type(Type::Narrowed { from: lhs.0, narrowed_to }) + } else { + lhs.0 + } + }, + Some(|env: &mut crate::Environment, data: &mut crate::CheckingData| { + A::synthesise_expression(rhs, expecting, env, data) + }), + checking_data, + )) + } + } +} diff --git a/checker/src/features/operations/mathematical_bitwise.rs b/checker/src/features/operations/mathematical_bitwise.rs new file mode 100644 index 00000000..240e659a --- /dev/null +++ b/checker/src/features/operations/mathematical_bitwise.rs @@ -0,0 +1,262 @@ +use crate::{ + types::{cast_as_number, cast_as_string, helpers, intrinsics, Constant, Type}, + TypeId, +}; + +/// For these **binary** operations both operands are synthesised +#[derive(Clone, Copy, Debug, binary_serialize_derive::BinarySerializable)] +pub enum MathematicalOrBitwiseOperation { + Add, + Subtract, + Multiply, + Divide, + Modulo, + Exponent, + BitwiseShiftLeft, + BitwiseShiftRight, + BitwiseShiftRightUnsigned, + BitwiseAnd, + BitwiseXOr, + BitwiseOr, +} + +/// TODO proper error type +pub fn evaluate_mathematical_operation( + lhs: TypeId, + operator: MathematicalOrBitwiseOperation, + rhs: TypeId, + info: &impl crate::context::InformationChain, + types: &mut crate::TypeStore, + strict_casts: bool, + operate_on_number_intrinsics: bool, +) -> Result { + fn attempt_constant_math_operator( + lhs: TypeId, + operator: MathematicalOrBitwiseOperation, + rhs: TypeId, + types: &mut crate::TypeStore, + strict_casts: bool, + ) -> Result { + if let MathematicalOrBitwiseOperation::Add = operator { + match (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) { + (Type::Constant(Constant::Number(lhs)), Type::Constant(Constant::Number(rhs))) => { + Ok(types.new_constant_type(Constant::Number(lhs + rhs))) + } + (Type::Constant(lhs), Type::Constant(rhs)) => { + let mut first = cast_as_string(lhs, strict_casts)?; + let second = cast_as_string(rhs, strict_casts)?; + // Concatenate strings + first.push_str(&second); + Ok(types.new_constant_type(Constant::String(first))) + } + (lhs, rhs) => { + crate::utilities::notify!("here {:?} + {:?}", lhs, rhs); + Err(()) + } + } + } else { + match (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) { + (Type::Constant(c1), Type::Constant(c2)) => { + let lhs = cast_as_number(c1, strict_casts).unwrap_or(f64::NAN); + let rhs = cast_as_number(c2, strict_casts).unwrap_or(f64::NAN); + // TODO hopefully Rust implementation is the same as JS + #[allow(clippy::cast_possible_truncation)] + let value = match operator { + MathematicalOrBitwiseOperation::Add => unreachable!(), + MathematicalOrBitwiseOperation::Subtract => lhs - rhs, + MathematicalOrBitwiseOperation::Multiply => lhs * rhs, + MathematicalOrBitwiseOperation::Divide => lhs / rhs, + MathematicalOrBitwiseOperation::Modulo => lhs % rhs, + MathematicalOrBitwiseOperation::Exponent => lhs.powf(rhs), + MathematicalOrBitwiseOperation::BitwiseShiftLeft => { + f64::from((lhs as i32).checked_shl(rhs as u32).unwrap_or(0)) + } + MathematicalOrBitwiseOperation::BitwiseShiftRight => { + f64::from((lhs as i32).checked_shr(rhs as u32).unwrap_or(0)) + } + MathematicalOrBitwiseOperation::BitwiseShiftRightUnsigned => { + (lhs as i32).wrapping_shr(rhs as u32).into() + } + MathematicalOrBitwiseOperation::BitwiseAnd => { + f64::from((lhs as i32) & (rhs as i32)) + } + MathematicalOrBitwiseOperation::BitwiseXOr => { + f64::from((lhs as i32) ^ (rhs as i32)) + } + MathematicalOrBitwiseOperation::BitwiseOr => { + f64::from((lhs as i32) | (rhs as i32)) + } + }; + let value = ordered_float::NotNan::try_from(value); + let ty = match value { + Ok(value) => types.new_constant_type(Constant::Number(value)), + Err(_) => TypeId::NAN, + }; + Ok(ty) + } + (lhs, rhs) => { + crate::utilities::notify!("here {:?} @ {:?}", lhs, rhs); + Err(()) + } + } + } + } + + let lhs_ty = types.get_type_by_id(lhs); + let rhs_ty = types.get_type_by_id(rhs); + + if lhs == TypeId::ERROR_TYPE || rhs == TypeId::ERROR_TYPE { + return Ok(TypeId::ERROR_TYPE); + } + + crate::utilities::notify!("lhs={:?}, rhs={:?}", lhs_ty, rhs_ty); + let either_dependent = lhs_ty.is_dependent() || rhs_ty.is_dependent(); + + if either_dependent { + let can_be_string = if let MathematicalOrBitwiseOperation::Add = operator { + let left_is_string = helpers::simple_subtype(lhs, TypeId::STRING_TYPE, info, types); + let right_is_string = helpers::simple_subtype(lhs, TypeId::STRING_TYPE, info, types); + let left_is_string_or_number = + left_is_string || helpers::simple_subtype(lhs, TypeId::NUMBER_TYPE, info, types); + let right_is_string_or_number = + right_is_string || helpers::simple_subtype(rhs, TypeId::NUMBER_TYPE, info, types); + if !left_is_string_or_number || !right_is_string_or_number { + crate::utilities::notify!("Here"); + return Err(()); + } + + left_is_string || right_is_string + } else { + if !helpers::simple_subtype(lhs, TypeId::NUMBER_TYPE, info, types) + || !helpers::simple_subtype(rhs, TypeId::NUMBER_TYPE, info, types) + { + crate::utilities::notify!("Here :/"); + return Err(()); + } + + false + }; + + // :) + if let (MathematicalOrBitwiseOperation::Exponent, TypeId::ZERO) = (operator, rhs) { + // This holds for NaN. Thus can do in every case + return Ok(TypeId::ONE); + } else if let (MathematicalOrBitwiseOperation::Exponent, TypeId::ONE, true) = + (operator, rhs, intrinsics::is_not_not_a_number(lhs, types)) + { + return Ok(lhs); + } else if let (MathematicalOrBitwiseOperation::Add, TypeId::ZERO) + | (MathematicalOrBitwiseOperation::Multiply, TypeId::ONE) = (operator, rhs) + { + return Ok(lhs); + } else if let (MathematicalOrBitwiseOperation::Add, TypeId::ZERO) + | (MathematicalOrBitwiseOperation::Multiply, TypeId::ONE) = (operator, lhs) + { + return Ok(rhs); + } + + if let ( + MathematicalOrBitwiseOperation::Add | MathematicalOrBitwiseOperation::Multiply, + (lhs_range, lhs_modulo), + (rhs_range, rhs_modulo), + true, + ) = ( + operator, + intrinsics::get_range_and_mod_class(lhs, types), + intrinsics::get_range_and_mod_class(rhs, types), + operate_on_number_intrinsics, + ) { + crate::utilities::notify!( + "{:?} with {:?}. {:?} & {:?}", + (lhs_range, lhs_modulo), + (rhs_range, rhs_modulo), + rhs_range.and_then(crate::utilities::float_range::FloatRange::as_single), + lhs_range.and_then(crate::utilities::float_range::FloatRange::as_single) + ); + if let (Some(lhs_range), Some(rhs_range)) = (lhs_range, rhs_range) { + let range = match operator { + MathematicalOrBitwiseOperation::Add => lhs_range.space_addition(rhs_range), + MathematicalOrBitwiseOperation::Multiply => { + lhs_range.space_multiplication(rhs_range) + } + _ => unreachable!(), + }; + // That is a lot types + let result = intrinsics::range_to_type(range, types); + let constructor = + crate::types::Constructor::BinaryOperator { lhs, operator, rhs, result }; + + return Ok(types.register_type(crate::Type::Constructor(constructor))); + } + + // else if let (Some(lhs_modulo), Some(offset)) = + // (lhs_modulo, rhs_range.and_then(crate::utilities::float_range::FloatRange::as_single)) + // { + // crate::utilities::notify!("Here"); + // let mod_class = lhs_modulo.offset(offset); + // intrinsics::modulo_to_type(mod_class, types) + // } else if let (Some(rhs_modulo), Some(offset)) = + // (rhs_modulo, lhs_range.and_then(crate::utilities::float_range::FloatRange::as_single)) + // { + // let mod_class = rhs_modulo.offset(offset); + // intrinsics::modulo_to_type(mod_class, types) + // } + } + + if let (Some((condition, lhs_truthy_result, lhs_falsy_result)), true) = + (helpers::get_type_as_conditional(lhs, types), operate_on_number_intrinsics) + { + crate::utilities::notify!("Here lhs dependent"); + let truthy_result = evaluate_mathematical_operation( + lhs_truthy_result, + operator, + rhs, + info, + types, + strict_casts, + operate_on_number_intrinsics, + )?; + let falsy_result = evaluate_mathematical_operation( + lhs_falsy_result, + operator, + rhs, + info, + types, + strict_casts, + operate_on_number_intrinsics, + )?; + + Ok(types.new_conditional_type(condition, truthy_result, falsy_result)) + } else if let (true, Some((condition, rhs_truthy_result, rhs_falsy_result))) = + (operate_on_number_intrinsics, helpers::get_type_as_conditional(rhs, types)) + { + let truthy_result = evaluate_mathematical_operation( + lhs, + operator, + rhs_truthy_result, + info, + types, + strict_casts, + operate_on_number_intrinsics, + )?; + let falsy_result = evaluate_mathematical_operation( + lhs, + operator, + rhs_falsy_result, + info, + types, + strict_casts, + operate_on_number_intrinsics, + )?; + + Ok(types.new_conditional_type(condition, truthy_result, falsy_result)) + } else { + let result = if can_be_string { TypeId::STRING_TYPE } else { TypeId::NUMBER_TYPE }; + let constructor = + crate::types::Constructor::BinaryOperator { lhs, operator, rhs, result }; + Ok(types.register_type(crate::Type::Constructor(constructor))) + } + } else { + attempt_constant_math_operator(lhs, operator, rhs, types, strict_casts) + } +} diff --git a/checker/src/features/operations/mod.rs b/checker/src/features/operations/mod.rs new file mode 100644 index 00000000..a0e3855f --- /dev/null +++ b/checker/src/features/operations/mod.rs @@ -0,0 +1,12 @@ +mod logical; +mod mathematical_bitwise; +mod relation; +mod unary; + +pub use logical::{evaluate_logical_operation_with_expression, LogicalOperator}; +pub use mathematical_bitwise::{evaluate_mathematical_operation, MathematicalOrBitwiseOperation}; +pub use relation::{ + evaluate_equality_inequality_operation, is_null_or_undefined, CanonicalEqualityAndInequality, + EqualityAndInequality, EqualityAndInequalityResultKind, +}; +pub use unary::{evaluate_unary_operator, UnaryOperation}; diff --git a/checker/src/features/operations/relation.rs b/checker/src/features/operations/relation.rs new file mode 100644 index 00000000..b703e135 --- /dev/null +++ b/checker/src/features/operations/relation.rs @@ -0,0 +1,370 @@ +use super::MathematicalOrBitwiseOperation; +use crate::types::{ + cast_as_number, disjoint, get_constraint, helpers, intrinsics, Constant, Constructor, + PartiallyAppliedGenerics, Type, TypeId, +}; + +/// Not canonical / reducible form of [`CanonicalEqualityAndInequality`]. +/// (for examples `a > b` is equivalent to `b < a` (after side effects) and `a !== b` is equivalent to `!(a === b)`) +#[derive(Clone, Copy, Debug)] +pub enum EqualityAndInequality { + StrictEqual, + StrictNotEqual, + Equal, + NotEqual, + GreaterThan, + LessThan, + LessThanOrEqual, + GreaterThanOrEqual, +} + +/// Canonical / irreducible form of [`EqualityAndInequality`]. +#[derive(Clone, Copy, Debug, binary_serialize_derive::BinarySerializable)] +pub enum CanonicalEqualityAndInequality { + StrictEqual, + LessThan, +} + +pub enum EqualityAndInequalityResultKind { + Constant, + Disjoint, + Condition, +} + +pub fn evaluate_equality_inequality_operation( + mut lhs: TypeId, + operator: &EqualityAndInequality, + mut rhs: TypeId, + info: &impl crate::context::InformationChain, + types: &mut crate::types::TypeStore, + strict_casts: bool, +) -> Result<(TypeId, EqualityAndInequalityResultKind), ()> { + // `NaN == t` is always true + if lhs == TypeId::NAN || rhs == TypeId::NAN { + return Ok((TypeId::FALSE, EqualityAndInequalityResultKind::Constant)); + } + + match operator { + EqualityAndInequality::StrictEqual => { + // crate::utilities::notify!("{:?} === {:?}", lhs, rhs); + + if let (Type::Constant(lhs), Type::Constant(rhs)) = + (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) + { + let result = if lhs == rhs { TypeId::TRUE } else { TypeId::FALSE }; + return Ok((result, EqualityAndInequalityResultKind::Constant)); + } + + if let ( + Type::Object(_) | Type::SpecialObject(_), + Type::Object(_) | Type::SpecialObject(_), + ) = (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) + { + let result = if lhs == rhs { TypeId::TRUE } else { TypeId::FALSE }; + return Ok((result, EqualityAndInequalityResultKind::Constant)); + } + + // if is_dependent { + if lhs == rhs + && intrinsics::is_not_not_a_number(lhs, types) + && intrinsics::is_not_not_a_number(rhs, types) + { + // I think this is okay + return Ok((TypeId::TRUE, EqualityAndInequalityResultKind::Constant)); + } + + // Checks lhs and rhs type to see if they overlap + if disjoint::types_are_disjoint(lhs, rhs, &mut Vec::new(), info, types) { + return Ok((TypeId::FALSE, EqualityAndInequalityResultKind::Disjoint)); + } + + let left_dependent = types.get_type_by_id(lhs).is_dependent(); + + // Sort if `*constant* == ...`. Ideally want constant type on the RHS + let (lhs, rhs) = if left_dependent { (lhs, rhs) } else { (rhs, rhs) }; + let operator = CanonicalEqualityAndInequality::StrictEqual; + let result_ty = + types.register_type(Type::Constructor(Constructor::CanonicalRelationOperator { + lhs, + operator, + rhs, + })); + + Ok((result_ty, EqualityAndInequalityResultKind::Condition)) + + // } else { + // match attempt_constant_equality(lhs, rhs, types) { + // Ok(ty) => Ok(( + // if ty { TypeId::TRUE } else { TypeId::FALSE }, + // EqualityAndInequalityResultKind::Constant, + // )), + // Err(()) => { + // unreachable!( + // "should have been caught `is_dependent` above, {:?} === {:?}", + // types.get_type_by_id(lhs), + // types.get_type_by_id(rhs) + // ) + // } + // } + // } + } + EqualityAndInequality::LessThan => { + fn attempt_less_than( + lhs: TypeId, + rhs: TypeId, + types: &mut crate::types::TypeStore, + strict_casts: bool, + ) -> Result { + // Similar but reversed semantics to add + match (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) { + ( + Type::Constant(Constant::String(string1)), + Type::Constant(Constant::String(string2)), + ) => { + // Yah rust includes string alphanumerical equivalence of strings + Ok(string1 < string2) + } + (Type::Constant(c1), Type::Constant(c2)) => { + let lhs = cast_as_number(c1, strict_casts)?; + let rhs = cast_as_number(c2, strict_casts)?; + Ok(lhs < rhs) + } + (lhs, rhs) => { + crate::utilities::notify!("{:?}", (lhs, rhs)); + // Ok(TypeId::OPEN_BOOLEAN_TYPE) + Err(()) + } + } + } + + let either_is_dependent = types.get_type_by_id(lhs).is_dependent() + || types.get_type_by_id(rhs).is_dependent(); + + if either_is_dependent { + { + if let Type::Constructor(Constructor::BinaryOperator { + lhs: op_lhs, + operator, + rhs: op_rhs, + result: _, + }) = types.get_type_by_id(lhs) + { + if let ( + Type::Constant(Constant::Number(add)), + MathematicalOrBitwiseOperation::Add, + Type::Constant(Constant::Number(lt)), + ) = (types.get_type_by_id(*op_rhs), operator, types.get_type_by_id(rhs)) + { + crate::utilities::notify!("Shifted LT"); + lhs = *op_lhs; + rhs = types.register_type(Type::Constant(Constant::Number(lt - add))); + } + } + } + + { + if !helpers::simple_subtype(lhs, TypeId::NUMBER_TYPE, info, types) + || !helpers::simple_subtype(rhs, TypeId::NUMBER_TYPE, info, types) + { + return Err(()); + } + + let lhs = get_constraint(lhs, types).unwrap_or(lhs); + let rhs = get_constraint(rhs, types).unwrap_or(rhs); + + // Tidies some things for counting loop iterations + + // Checking disjoint-ness for inequalities (TODO under option) via distribution + if let ((Some(lhs_range), _), (Some(rhs_range), _)) = ( + intrinsics::get_range_and_mod_class(lhs, types), + intrinsics::get_range_and_mod_class(rhs, types), + ) { + crate::utilities::notify!("{:?}", (lhs_range, rhs_range)); + if lhs_range.below(rhs_range) { + return Ok((TypeId::TRUE, EqualityAndInequalityResultKind::Constant)); + } + if lhs_range.above(rhs_range) { + return Ok((TypeId::FALSE, EqualityAndInequalityResultKind::Disjoint)); + } + } + + // Transitivity + // TODO extra from & + if let ( + Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::LESS_THAN, + arguments: larg, + }), + Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::GREATER_THAN, + arguments: rarg, + }), + ) = (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) + { + crate::utilities::notify!("{:?} {:?}", larg, rarg); + // Only one level + let transitivity = larg.get_structure_restriction(TypeId::NUMBER_GENERIC) + == rarg.get_structure_restriction(TypeId::NUMBER_GENERIC); + if transitivity { + return Ok((TypeId::TRUE, EqualityAndInequalityResultKind::Constant)); + } + } + } + + let constructor = Constructor::CanonicalRelationOperator { + lhs, + operator: CanonicalEqualityAndInequality::LessThan, + rhs, + }; + Ok(( + types.register_type(crate::Type::Constructor(constructor)), + EqualityAndInequalityResultKind::Condition, + )) + } else { + attempt_less_than(lhs, rhs, types, strict_casts).map(|value| { + ( + if value { TypeId::TRUE } else { TypeId::FALSE }, + EqualityAndInequalityResultKind::Constant, + ) + }) + } + } + // equal OR less than + EqualityAndInequality::LessThanOrEqual => { + let (equality_result, warning) = evaluate_equality_inequality_operation( + lhs, + &EqualityAndInequality::StrictEqual, + rhs, + info, + types, + strict_casts, + )?; + + if equality_result == TypeId::TRUE { + Ok((equality_result, warning)) + } else if equality_result == TypeId::FALSE { + evaluate_equality_inequality_operation( + lhs, + &EqualityAndInequality::LessThan, + rhs, + info, + types, + strict_casts, + ) + } else { + let (less_than_result, warning) = evaluate_equality_inequality_operation( + lhs, + &EqualityAndInequality::LessThan, + rhs, + info, + types, + strict_casts, + )?; + Ok((types.new_logical_or_type(equality_result, less_than_result), warning)) + } + } + EqualityAndInequality::StrictNotEqual => { + let (equality_result, kind) = evaluate_equality_inequality_operation( + lhs, + &EqualityAndInequality::StrictEqual, + rhs, + info, + types, + strict_casts, + )?; + if let EqualityAndInequalityResultKind::Condition = kind { + Ok((types.new_logical_negation_type(equality_result), kind)) + } else { + let negated = if let TypeId::TRUE = equality_result { + TypeId::FALSE + } else if let TypeId::FALSE = equality_result { + TypeId::TRUE + } else { + todo!() + }; + Ok((negated, kind)) + } + } + EqualityAndInequality::Equal => { + crate::utilities::notify!("TODO equal operator"); + Ok((TypeId::OPEN_BOOLEAN_TYPE, EqualityAndInequalityResultKind::Condition)) + } + EqualityAndInequality::NotEqual => { + let (equality_result, kind) = evaluate_equality_inequality_operation( + lhs, + &EqualityAndInequality::Equal, + rhs, + info, + types, + strict_casts, + )?; + if let EqualityAndInequalityResultKind::Condition = kind { + Ok((types.new_logical_negation_type(equality_result), kind)) + } else { + let negated = if let TypeId::TRUE = equality_result { + TypeId::FALSE + } else if let TypeId::FALSE = equality_result { + TypeId::TRUE + } else { + todo!() + }; + Ok((negated, kind)) + } + } + // Swapping operands! + EqualityAndInequality::GreaterThan => evaluate_equality_inequality_operation( + rhs, + &EqualityAndInequality::LessThan, + lhs, + info, + types, + strict_casts, + ), + // Swapping operands! + EqualityAndInequality::GreaterThanOrEqual => evaluate_equality_inequality_operation( + rhs, + &EqualityAndInequality::LessThanOrEqual, + lhs, + info, + types, + strict_casts, + ), + } +} + +#[allow(clippy::let_and_return)] +pub fn is_null_or_undefined( + ty: TypeId, + info: &impl crate::context::InformationChain, + types: &mut crate::types::TypeStore, +) -> TypeId { + let is_null = evaluate_equality_inequality_operation( + ty, + &EqualityAndInequality::StrictEqual, + TypeId::NULL_TYPE, + info, + types, + false, + ) + .map_or(TypeId::ERROR_TYPE, |(left, _)| left); + + if let TypeId::TRUE = is_null { + is_null + } else { + let is_undefined = evaluate_equality_inequality_operation( + ty, + &EqualityAndInequality::StrictEqual, + TypeId::UNDEFINED_TYPE, + info, + types, + false, + ) + .map_or(TypeId::ERROR_TYPE, |(left, _)| left); + + if let TypeId::FALSE = is_null { + is_undefined + } else { + types.new_logical_or_type(is_null, is_undefined) + } + } +} diff --git a/checker/src/features/operations/unary.rs b/checker/src/features/operations/unary.rs new file mode 100644 index 00000000..5c44e324 --- /dev/null +++ b/checker/src/features/operations/unary.rs @@ -0,0 +1,87 @@ +use crate::{ + types::{ + cast_as_number, helpers::simple_subtype, is_type_truthy_falsy, Constant, Type, TypeId, + }, + Decidable, +}; + +/// `typeof` and some others done elsewhere +#[derive(Clone, Copy, Debug, binary_serialize_derive::BinarySerializable)] +pub enum UnaryOperation { + /// Treated as `(value ? false : true)` + LogicalNot, + /// Treated as `0 - value` (could also do -1 * value?) + Negation, + /// Treated as `value ^ 0xFFFF_FFFF` + BitwiseNot, +} + +/// Tries to evaluate unary operation for constant terms. Else delegates to binary operations that handle equivalent thing +pub fn evaluate_unary_operator( + operator: UnaryOperation, + operand: TypeId, + info: &impl crate::context::InformationChain, + types: &mut crate::TypeStore, + strict_casts: bool, +) -> Result { + if operand == TypeId::ERROR_TYPE { + return Ok(operand); + } + + match operator { + UnaryOperation::LogicalNot => { + if let Decidable::Known(value) = is_type_truthy_falsy(operand, types) { + if value { + Ok(TypeId::FALSE) + } else { + Ok(TypeId::TRUE) + } + } else { + let is_boolean = simple_subtype(operand, TypeId::BOOLEAN_TYPE, info, types); + if is_boolean { + Ok(types.new_logical_negation_type(operand)) + } else { + Err(()) + } + } + } + UnaryOperation::Negation | UnaryOperation::BitwiseNot => { + if let Type::Constant(cst) = types.get_type_by_id(operand) { + let value = cast_as_number(cst, strict_casts).expect("hmm"); + let value = match operator { + UnaryOperation::BitwiseNot => f64::from(!(value as i32)), + UnaryOperation::Negation => -value, + UnaryOperation::LogicalNot => unreachable!(), + }; + let value = ordered_float::NotNan::try_from(value); + Ok(match value { + Ok(value) => types.new_constant_type(Constant::Number(value)), + Err(_) => TypeId::NAN, + }) + } else { + match operator { + UnaryOperation::BitwiseNot => super::evaluate_mathematical_operation( + TypeId::MAX_U32, + super::MathematicalOrBitwiseOperation::BitwiseXOr, + operand, + info, + types, + strict_casts, + false, + ), + UnaryOperation::Negation => super::evaluate_mathematical_operation( + TypeId::ZERO, + super::MathematicalOrBitwiseOperation::Subtract, + operand, + info, + types, + strict_casts, + // TODO + true, + ), + UnaryOperation::LogicalNot => unreachable!("handled above"), + } + } + } + } +} diff --git a/checker/src/features/template_literal.rs b/checker/src/features/template_literal.rs index 747877b2..ab68d775 100644 --- a/checker/src/features/template_literal.rs +++ b/checker/src/features/template_literal.rs @@ -144,11 +144,12 @@ where checking_data.types.new_constant_type(Constant::String(static_part.to_owned())); let result = super::operations::evaluate_mathematical_operation( acc, - crate::features::operations::MathematicalAndBitwise::Add, + crate::features::operations::MathematicalOrBitwiseOperation::Add, lhs, environment, &mut checking_data.types, checking_data.options.strict_casts, + checking_data.options.advanced_numbers, ); if let Ok(result) = result { acc = result; @@ -164,11 +165,12 @@ where ); let result = super::operations::evaluate_mathematical_operation( acc, - crate::features::operations::MathematicalAndBitwise::Add, + crate::features::operations::MathematicalOrBitwiseOperation::Add, rhs, environment, &mut checking_data.types, checking_data.options.strict_casts, + checking_data.options.advanced_numbers, ); if let Ok(result) = result { acc = result; @@ -184,11 +186,12 @@ where checking_data.types.new_constant_type(Constant::String(final_part.to_owned())); let result = super::operations::evaluate_mathematical_operation( acc, - crate::features::operations::MathematicalAndBitwise::Add, + crate::features::operations::MathematicalOrBitwiseOperation::Add, value, environment, &mut checking_data.types, checking_data.options.strict_casts, + checking_data.options.advanced_numbers, ); if let Ok(result) = result { result diff --git a/checker/src/options.rs b/checker/src/options.rs index 5fd2919c..19fc0ef5 100644 --- a/checker/src/options.rs +++ b/checker/src/options.rs @@ -54,6 +54,11 @@ pub struct TypeCheckOptions { pub measure_time: bool, + /// Enables two things: + /// - range and modulo class inference + /// - modifications to ranges and classes based on operations + pub advanced_numbers: bool, + /// Printing internal diagnostics in dts pub debug_dts: bool, } @@ -79,6 +84,7 @@ impl Default for TypeCheckOptions { measure_time: false, debug_dts: false, extra_syntax: true, + advanced_numbers: false, } } } diff --git a/checker/src/synthesis/block.rs b/checker/src/synthesis/block.rs index 9f0bf7ea..ee9b8af5 100644 --- a/checker/src/synthesis/block.rs +++ b/checker/src/synthesis/block.rs @@ -42,7 +42,10 @@ pub(super) fn synthesise_block( !matches!( e, StatementOrDeclaration::Statement( - Statement::Comment(..) | Statement::MultiLineComment(..) | Statement::Empty(..) + Statement::Comment(..) + | Statement::MultiLineComment(..) + | Statement::Empty(..) + | Statement::AestheticSemiColon(..) ) | StatementOrDeclaration::Declaration(Declaration::Function(..)) ) }) { diff --git a/checker/src/synthesis/classes.rs b/checker/src/synthesis/classes.rs index 294df65e..9479c1bc 100644 --- a/checker/src/synthesis/classes.rs +++ b/checker/src/synthesis/classes.rs @@ -125,7 +125,7 @@ fn synthesise_class_declaration_extends_and_members< let _name = P::as_option_str(&class.name).map_or_else(String::new, str::to_owned); let class_prototype = class_type; - crate::utilities::notify!("At start {:?}", environment.context_type.free_variables); + // crate::utilities::notify!("At start {:?}", environment.context_type.free_variables); let extends = class.extends.as_ref().map(|extends_expression| { let extends = @@ -192,8 +192,6 @@ fn synthesise_class_declaration_extends_and_members< } }; - crate::utilities::notify!("{:?}", (getter_setter, is_async, is_generator)); - let internal_marker = if let (true, ParserPropertyKey::Identifier(name, _, _)) = (is_declare, method.name.get_ast_ref()) { @@ -209,6 +207,25 @@ fn synthesise_class_declaration_extends_and_members< let internal = internal_marker.is_some(); let has_defined_this = method.parameters.leading.0.is_some(); + let this_shape = if internal && !has_defined_this { + TypeId::ANY_TYPE + } else if let Type::Class { type_parameters: Some(type_parameters), .. } = + checking_data.types.get_type_by_id(class_prototype) + { + use source_map::Nullable; + let arguments = crate::types::GenericArguments::ExplicitRestrictions( + type_parameters + .iter() + .map(|id| (*id, (*id, crate::SpanWithSource::NULL))) + .collect(), + ); + checking_data.types.register_type(Type::PartiallyAppliedGenerics( + crate::types::PartiallyAppliedGenerics { on: class_prototype, arguments }, + )) + } else { + class_prototype + }; + let behavior = FunctionRegisterBehavior::ClassMethod { is_async, is_generator, @@ -216,11 +233,7 @@ fn synthesise_class_declaration_extends_and_members< super_type: None, // TODO expecting: TypeId::ANY_TYPE, - this_shape: if internal && !has_defined_this { - TypeId::ANY_TYPE - } else { - class_prototype - }, + this_shape, internal_marker, name: key.into_name_type(&mut checking_data.types), }; @@ -291,7 +304,13 @@ fn synthesise_class_declaration_extends_and_members< ); static_property_keys.push(key); } - ClassMember::Indexer { name, indexer_type, return_type, is_readonly, position } => { + ClassMember::Indexer { + name: _name, + indexer_type, + return_type, + is_readonly, + position, + } => { // TODO this redoes work done at registration. Because the info gets overwritten let key = synthesise_type_annotation(indexer_type, environment, checking_data); let value = synthesise_type_annotation(return_type, environment, checking_data); @@ -306,7 +325,7 @@ fn synthesise_class_declaration_extends_and_members< // TODO check declare // TODO WIP - crate::utilities::notify!("Indexing (again) for '{}'", name); + // crate::utilities::notify!("Indexing (again) for '{}' {:?}", name, value); let value = PropertyValue::Value(value); environment.info.register_property_on_type( class_type, @@ -367,8 +386,6 @@ fn synthesise_class_declaration_extends_and_members< environment.info.variable_current_value.insert(variable, class_variable_type); } - crate::utilities::notify!("At end {:?}", environment.context_type.free_variables); - { // Static items and blocks static_property_keys.reverse(); @@ -699,7 +716,13 @@ fn register_extends_and_member( ); } } - ClassMember::Indexer { name, indexer_type, return_type, is_readonly, position } => { + ClassMember::Indexer { + name: _name, + indexer_type, + return_type, + is_readonly, + position, + } => { // TODO think this is okay let key = synthesise_type_annotation(indexer_type, environment, checking_data); let value = synthesise_type_annotation(return_type, environment, checking_data); @@ -714,9 +737,8 @@ fn register_extends_and_member( // TODO check declare // TODO WIP - crate::utilities::notify!("Indexing for '{}'", name); + // crate::utilities::notify!("Indexing for '{}' {:?}", name, class_type); let value = PropertyValue::Value(value); - crate::utilities::notify!("{:?}", class_type); environment.info.register_property_on_type( class_type, Publicity::Public, diff --git a/checker/src/synthesis/expressions.rs b/checker/src/synthesis/expressions.rs index 08e95b32..b7ab5cfa 100644 --- a/checker/src/synthesis/expressions.rs +++ b/checker/src/synthesis/expressions.rs @@ -32,9 +32,10 @@ use crate::{ objects::ObjectBuilder, operations::is_null_or_undefined, operations::{ - evaluate_logical_operation_with_expression, - evaluate_pure_binary_operation_handle_errors, evaluate_unary_operator, - EqualityAndInequality, MathematicalAndBitwise, UnaryOperation, + evaluate_equality_inequality_operation, evaluate_logical_operation_with_expression, + evaluate_mathematical_operation, evaluate_unary_operator, EqualityAndInequality, + EqualityAndInequalityResultKind, LogicalOperator, MathematicalOrBitwiseOperation, + UnaryOperation, }, template_literal::synthesise_template_literal_expression, variables::VariableWithValue, @@ -46,7 +47,7 @@ use crate::{ }, types::{ calling::{CallingInput, UnsynthesisedArgument}, - get_larger_type, + helpers::get_larger_type, logical::{Logical, LogicalOrValid}, printing::{print_property_key, print_type}, properties::{ @@ -251,11 +252,9 @@ pub(super) fn synthesise_expression( | BinaryOperator::NullCoalescing = operator { let operator = match operator { - BinaryOperator::LogicalAnd => crate::features::operations::LogicalOperator::And, - BinaryOperator::LogicalOr => crate::features::operations::LogicalOperator::Or, - BinaryOperator::NullCoalescing => { - crate::features::operations::LogicalOperator::NullCoalescing - } + BinaryOperator::LogicalAnd => LogicalOperator::And, + BinaryOperator::LogicalOr => LogicalOperator::Or, + BinaryOperator::NullCoalescing => LogicalOperator::NullCoalescing, _ => unreachable!(), }; return evaluate_logical_operation_with_expression( @@ -267,49 +266,120 @@ pub(super) fn synthesise_expression( expecting, // TODO unwrap ) .unwrap(); - } + } else if let BinaryOperator::StrictEqual + | BinaryOperator::StrictNotEqual + | BinaryOperator::Equal + | BinaryOperator::NotEqual + | BinaryOperator::GreaterThan + | BinaryOperator::LessThan + | BinaryOperator::LessThanEqual + | BinaryOperator::GreaterThanEqual = operator + { + let rhs_ty = + synthesise_expression(rhs, environment, checking_data, TypeId::ANY_TYPE); + let operator = match operator { + BinaryOperator::StrictEqual => EqualityAndInequality::StrictEqual, + BinaryOperator::StrictNotEqual => EqualityAndInequality::StrictNotEqual, + BinaryOperator::Equal => EqualityAndInequality::Equal, + BinaryOperator::NotEqual => EqualityAndInequality::NotEqual, + BinaryOperator::GreaterThan => EqualityAndInequality::GreaterThan, + BinaryOperator::LessThan => EqualityAndInequality::LessThan, + BinaryOperator::LessThanEqual => EqualityAndInequality::LessThanOrEqual, + BinaryOperator::GreaterThanEqual => EqualityAndInequality::GreaterThanOrEqual, + _ => unreachable!(), + }; + let result = evaluate_equality_inequality_operation( + lhs_ty, + &operator, + rhs_ty, + environment, + &mut checking_data.types, + checking_data.options.strict_casts, + ); - let rhs_ty = synthesise_expression(rhs, environment, checking_data, TypeId::ANY_TYPE); + if let Ok((result, warning)) = result { + if let EqualityAndInequalityResultKind::Disjoint = warning { + let lhs_pos = + ASTNode::get_position(&**lhs).with_source(environment.get_source()); + let rhs_pos = + ASTNode::get_position(&**rhs).with_source(environment.get_source()); + let position = lhs_pos + .without_source() + .union(rhs_pos.without_source()) + .with_source(environment.get_source()); + + checking_data.diagnostics_container.add_warning( + crate::TypeCheckWarning::DisjointEquality { + lhs: TypeStringRepresentation::from_type_id( + lhs_ty, + environment, + &checking_data.types, + false, + ), + rhs: TypeStringRepresentation::from_type_id( + rhs_ty, + environment, + &checking_data.types, + false, + ), + result: result == TypeId::TRUE, + position, + }, + ); + } + + return result; + } + + let lhs_pos = ASTNode::get_position(&**lhs).with_source(environment.get_source()); + let rhs_pos = ASTNode::get_position(&**rhs).with_source(environment.get_source()); + let position = lhs_pos + .without_source() + .union(rhs_pos.without_source()) + .with_source(environment.get_source()); + + checking_data.diagnostics_container.add_error( + crate::TypeCheckError::InvalidEqualityOperation { + operator, + lhs: TypeStringRepresentation::from_type_id( + lhs_ty, + environment, + &checking_data.types, + false, + ), + rhs: TypeStringRepresentation::from_type_id( + rhs_ty, + environment, + &checking_data.types, + false, + ), + position, + }, + ); - if lhs_ty == TypeId::ERROR_TYPE || rhs_ty == TypeId::ERROR_TYPE { return TypeId::ERROR_TYPE; } - let lhs_pos = ASTNode::get_position(&**lhs).with_source(environment.get_source()); - let rhs_pos = ASTNode::get_position(&**rhs).with_source(environment.get_source()); - + let rhs_ty = synthesise_expression(rhs, environment, checking_data, TypeId::ANY_TYPE); let operator = match operator { - BinaryOperator::Add => MathematicalAndBitwise::Add.into(), - BinaryOperator::Subtract => MathematicalAndBitwise::Subtract.into(), - BinaryOperator::Multiply => MathematicalAndBitwise::Multiply.into(), - BinaryOperator::Divide => MathematicalAndBitwise::Divide.into(), - BinaryOperator::Modulo => MathematicalAndBitwise::Modulo.into(), - BinaryOperator::Exponent => MathematicalAndBitwise::Exponent.into(), - BinaryOperator::BitwiseShiftLeft => MathematicalAndBitwise::BitwiseShiftLeft.into(), + BinaryOperator::Add => MathematicalOrBitwiseOperation::Add, + BinaryOperator::Subtract => MathematicalOrBitwiseOperation::Subtract, + BinaryOperator::Multiply => MathematicalOrBitwiseOperation::Multiply, + BinaryOperator::Divide => MathematicalOrBitwiseOperation::Divide, + BinaryOperator::Modulo => MathematicalOrBitwiseOperation::Modulo, + BinaryOperator::Exponent => MathematicalOrBitwiseOperation::Exponent, + BinaryOperator::BitwiseShiftLeft => { + MathematicalOrBitwiseOperation::BitwiseShiftLeft + } BinaryOperator::BitwiseShiftRight => { - MathematicalAndBitwise::BitwiseShiftRight.into() + MathematicalOrBitwiseOperation::BitwiseShiftRight } BinaryOperator::BitwiseShiftRightUnsigned => { - MathematicalAndBitwise::BitwiseShiftRightUnsigned.into() - } - BinaryOperator::BitwiseAnd => MathematicalAndBitwise::BitwiseAnd.into(), - BinaryOperator::BitwiseXOr => MathematicalAndBitwise::BitwiseXOr.into(), - BinaryOperator::BitwiseOr => MathematicalAndBitwise::BitwiseOr.into(), - BinaryOperator::StrictEqual => EqualityAndInequality::StrictEqual.into(), - BinaryOperator::StrictNotEqual => EqualityAndInequality::StrictNotEqual.into(), - BinaryOperator::Equal => EqualityAndInequality::Equal.into(), - BinaryOperator::NotEqual => EqualityAndInequality::NotEqual.into(), - BinaryOperator::GreaterThan => EqualityAndInequality::GreaterThan.into(), - BinaryOperator::LessThan => EqualityAndInequality::LessThan.into(), - BinaryOperator::LessThanEqual => EqualityAndInequality::LessThanOrEqual.into(), - BinaryOperator::GreaterThanEqual => { - EqualityAndInequality::GreaterThanOrEqual.into() - } - BinaryOperator::LogicalAnd - | BinaryOperator::LogicalOr - | BinaryOperator::NullCoalescing => { - unreachable!() + MathematicalOrBitwiseOperation::BitwiseShiftRightUnsigned } + BinaryOperator::BitwiseAnd => MathematicalOrBitwiseOperation::BitwiseAnd, + BinaryOperator::BitwiseXOr => MathematicalOrBitwiseOperation::BitwiseXOr, + BinaryOperator::BitwiseOr => MathematicalOrBitwiseOperation::BitwiseOr, BinaryOperator::Pipe | BinaryOperator::Compose => { checking_data.raise_unimplemented_error( "special operations", @@ -317,14 +387,49 @@ pub(super) fn synthesise_expression( ); return TypeId::UNIMPLEMENTED_ERROR_TYPE; } + _ => { + unreachable!() + } }; - Instance::RValue(evaluate_pure_binary_operation_handle_errors( - (lhs_ty, lhs_pos), + let result = evaluate_mathematical_operation( + lhs_ty, operator, - (rhs_ty, rhs_pos), - checking_data, + rhs_ty, environment, - )) + &mut checking_data.types, + checking_data.options.strict_casts, + checking_data.options.advanced_numbers, + ); + if let Ok(value) = result { + Instance::RValue(value) + } else { + let lhs_pos = ASTNode::get_position(&**lhs).with_source(environment.get_source()); + let rhs_pos = ASTNode::get_position(&**rhs).with_source(environment.get_source()); + let position = lhs_pos + .without_source() + .union(rhs_pos.without_source()) + .with_source(environment.get_source()); + + checking_data.diagnostics_container.add_error( + TypeCheckError::InvalidMathematicalOrBitwiseOperation { + operator, + lhs: TypeStringRepresentation::from_type_id( + lhs_ty, + environment, + &checking_data.types, + false, + ), + rhs: TypeStringRepresentation::from_type_id( + rhs_ty, + environment, + &checking_data.types, + false, + ), + position, + }, + ); + return TypeId::ERROR_TYPE; + } } Expression::UnaryOperation { operand, operator, position } => { match operator { @@ -1060,37 +1165,37 @@ fn operator_to_assignment_kind( ), BinaryAssignmentOperator::AddAssign | BinaryAssignmentOperator::BitwiseShiftRightUnsigned => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::Add) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::Add) } BinaryAssignmentOperator::SubtractAssign => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::Subtract) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::Subtract) } BinaryAssignmentOperator::MultiplyAssign => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::Multiply) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::Multiply) } BinaryAssignmentOperator::DivideAssign => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::Divide) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::Divide) } BinaryAssignmentOperator::ModuloAssign => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::Modulo) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::Modulo) } BinaryAssignmentOperator::ExponentAssign => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::Exponent) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::Exponent) } BinaryAssignmentOperator::BitwiseShiftLeftAssign => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::BitwiseShiftLeft) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::BitwiseShiftLeft) } BinaryAssignmentOperator::BitwiseShiftRightAssign => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::BitwiseShiftRight) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::BitwiseShiftRight) } BinaryAssignmentOperator::BitwiseAndAssign => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::BitwiseAnd) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::BitwiseAnd) } BinaryAssignmentOperator::BitwiseXOrAssign => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::BitwiseXOr) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::BitwiseXOr) } BinaryAssignmentOperator::BitwiseOrAssign => { - AssignmentKind::PureUpdate(MathematicalAndBitwise::BitwiseOr) + AssignmentKind::PureUpdate(MathematicalOrBitwiseOperation::BitwiseOr) } } } @@ -1234,6 +1339,7 @@ pub(super) fn synthesise_object_literal( print_type(*truthy_result, &checking_data.types, environment, true), print_type(*otherwise_result, &checking_data.types, environment, true) ); + // Concatenate some types here if they all have the same keys // && matches!( // (lv, rv), diff --git a/checker/src/synthesis/functions.rs b/checker/src/synthesis/functions.rs index 9f628135..ab5dbf65 100644 --- a/checker/src/synthesis/functions.rs +++ b/checker/src/synthesis/functions.rs @@ -856,7 +856,7 @@ pub(super) fn build_overloaded_function( let func = types.new_hoisted_function_type(as_function); // IMPORTANT THAT RESULT IS ON THE RIGHT OF AND TYPE - result = types.new_and_type(func, result).unwrap(); + result = types.new_and_type(func, result); } result diff --git a/checker/src/synthesis/hoisting.rs b/checker/src/synthesis/hoisting.rs index 4f9a2ea3..0781f2bf 100644 --- a/checker/src/synthesis/hoisting.rs +++ b/checker/src/synthesis/hoisting.rs @@ -501,8 +501,7 @@ pub(crate) fn hoist_statements( &mut sub_environment, checking_data, ); - extends = - checking_data.types.new_and_type(extends, new).unwrap(); + extends = checking_data.types.new_and_type(extends, new); } checking_data.types.set_extends_on_interface(ty, extends); } @@ -539,8 +538,7 @@ pub(crate) fn hoist_statements( environment, checking_data, ); - extends = - checking_data.types.new_and_type(extends, new).unwrap(); + extends = checking_data.types.new_and_type(extends, new); } checking_data.types.set_extends_on_interface(ty, extends); } diff --git a/checker/src/synthesis/interfaces.rs b/checker/src/synthesis/interfaces.rs index fed3dc65..edd068e5 100644 --- a/checker/src/synthesis/interfaces.rs +++ b/checker/src/synthesis/interfaces.rs @@ -448,7 +448,7 @@ pub(super) fn synthesise_signatures( environment, checking_data, |environment, checking_data| { + crate::utilities::notify!( + "environment.info.narrowed_values={:?}", + environment.info.narrowed_values + ); synthesise_block_or_single_statement(&stmt.inner, environment, checking_data); }, position.with_source(environment.get_source()), diff --git a/checker/src/synthesis/type_annotations.rs b/checker/src/synthesis/type_annotations.rs index bcbb40c8..304b3333 100644 --- a/checker/src/synthesis/type_annotations.rs +++ b/checker/src/synthesis/type_annotations.rs @@ -11,13 +11,11 @@ use crate::{ features::objects::ObjectBuilder, types::{ generics::generic_type_arguments::GenericArguments, - properties::{PropertyKey, PropertyValue, Publicity}, - Constant, Constructor, PartiallyAppliedGenerics, PolyNature, Type, TypeId, - }, - types::{ generics::ExplicitTypeArguments, + helpers::{ArrayItem, Counter}, intrinsics::{self, distribute_tsc_string_intrinsic}, - ArrayItem, Counter, + properties::{PropertyKey, PropertyValue, Publicity}, + Constant, Constructor, PartiallyAppliedGenerics, PolyNature, Type, TypeId, }, CheckingData, Map, }; @@ -248,10 +246,10 @@ pub fn synthesise_type_annotation( // crate::utilities::notify!( // "{:?} and {:?}", // inner_type_alias_id, - // inner_type_alias_id.is_some_and(intrinsics::tsc_string_intrinsic) + // inner_type_alias_id.is_some_and(intrinsics::is_tsc_string_intrinsic) // ); - if intrinsics::tsc_string_intrinsic(inner_type_id) { + if intrinsics::is_tsc_string_intrinsic(inner_type_id) { distribute_tsc_string_intrinsic( inner_type_id, type_arguments.get(&TypeId::STRING_GENERIC).unwrap().0, @@ -354,10 +352,15 @@ pub fn synthesise_type_annotation( let mut acc = iterator.next().expect("Empty intersection"); for right in iterator { - if let Ok(new_ty) = checking_data.types.new_and_type(acc, right) { - acc = new_ty; - } else { - checking_data.diagnostics_container.add_error( + let is_disjoint = crate::types::disjoint::types_are_disjoint( + acc, + right, + &mut Vec::new(), + environment, + &checking_data.types, + ); + if is_disjoint { + checking_data.diagnostics_container.add_warning( TypeCheckWarning::TypesDoNotIntersect { left: TypeStringRepresentation::from_type_id( acc, @@ -374,8 +377,10 @@ pub fn synthesise_type_annotation( position: position.with_source(environment.get_source()), }, ); - return TypeId::ERROR_TYPE; + return TypeId::NEVER_TYPE; } + + acc = checking_data.types.new_and_type(acc, right); } acc } @@ -483,7 +488,7 @@ pub fn synthesise_type_annotation( items.push((pos, ArrayItem::Optional(annotation_ty))); } TupleElementKind::Spread => { - let slice = crate::types::as_slice( + let slice = crate::types::helpers::as_slice( annotation_ty, &checking_data.types, environment, @@ -514,15 +519,15 @@ pub fn synthesise_type_annotation( ); for (ty_position, item) in items { + use crate::types::helpers::ArrayItem; + let value = match item { - crate::types::ArrayItem::Member(item_ty) => PropertyValue::Value(item_ty), - crate::types::ArrayItem::Optional(item_ty) => { - PropertyValue::ConditionallyExists { - condition: TypeId::OPEN_BOOLEAN_TYPE, - truthy: Box::new(PropertyValue::Value(item_ty)), - } - } - crate::types::ArrayItem::Wildcard(on) => { + ArrayItem::Member(item_ty) => PropertyValue::Value(item_ty), + ArrayItem::Optional(item_ty) => PropertyValue::ConditionallyExists { + condition: TypeId::OPEN_BOOLEAN_TYPE, + truthy: Box::new(PropertyValue::Value(item_ty)), + }, + ArrayItem::Wildcard(on) => { crate::utilities::notify!("found wildcard"); let after = idx.into_type(&mut checking_data.types); @@ -716,7 +721,8 @@ pub fn synthesise_type_annotation( checking_data.types.register_type(Type::Constructor( crate::types::Constructor::BinaryOperator { lhs: acc, - operator: crate::features::operations::MathematicalAndBitwise::Add, + operator: + crate::features::operations::MathematicalOrBitwiseOperation::Add, rhs: lhs, result: TypeId::STRING_TYPE, }, @@ -733,7 +739,7 @@ pub fn synthesise_type_annotation( }; let constructor = crate::types::Constructor::BinaryOperator { lhs: acc, - operator: crate::features::operations::MathematicalAndBitwise::Add, + operator: crate::features::operations::MathematicalOrBitwiseOperation::Add, rhs, result: TypeId::STRING_TYPE, }; @@ -749,7 +755,8 @@ pub fn synthesise_type_annotation( checking_data.types.register_type(Type::Constructor( crate::types::Constructor::BinaryOperator { lhs: acc, - operator: crate::features::operations::MathematicalAndBitwise::Add, + operator: + crate::features::operations::MathematicalOrBitwiseOperation::Add, rhs: lhs, result: TypeId::STRING_TYPE, }, diff --git a/checker/src/synthesis/variables.rs b/checker/src/synthesis/variables.rs index 236d890f..56286456 100644 --- a/checker/src/synthesis/variables.rs +++ b/checker/src/synthesis/variables.rs @@ -15,7 +15,8 @@ use crate::{ }, synthesis::parser_property_key_to_checker_property_key, types::{ - get_larger_type, printing, + helpers::get_larger_type, + printing, properties::{ get_properties_on_single_type, get_property_key_names_on_a_single_type, PropertyKey, Publicity, diff --git a/checker/src/types/calling.rs b/checker/src/types/calling.rs index 63f46696..9ea6c083 100644 --- a/checker/src/types/calling.rs +++ b/checker/src/types/calling.rs @@ -22,7 +22,7 @@ use crate::{ types::{ functions::{FunctionBehavior, FunctionEffect, FunctionType}, generics::substitution::SubstitutionArguments, - get_structure_arguments_based_on_object_constraint, + helpers::get_structure_arguments_based_on_object_constraint, logical::{Invalid, Logical, LogicalOrValid, NeedsCalculation, PossibleLogical}, properties::AccessMode, substitute, GenericChainLink, ObjectNature, PartiallyAppliedGenerics, Type, @@ -474,7 +474,8 @@ fn get_logical_callable_from_type( // } if ty == TypeId::ANY_TYPE { crate::utilities::notify!("Calling ANY"); - return Ok(NeedsCalculation::Infer { on: from.unwrap() }.into()); + // TODO temp + return Ok(NeedsCalculation::Infer { on: from.unwrap_or(TypeId::ERROR_TYPE) }.into()); } let le_ty = types.get_type_by_id(ty); @@ -613,7 +614,10 @@ fn call_logical( || is_independent_function || matches!( const_fn_ident.as_str(), - "satisfies" | "is_dependent" | "bind" | "proxy:constructor" + "satisfies" + | "is_dependent" | "bind" + | "proxy:constructor" | "setPrototypeOf" + | "getPrototypeOf" ); // { @@ -862,6 +866,13 @@ fn call_logical( call_site: input.call_site, possibly_thrown, }); + } else { + // This fixes tree shaking of functions that are called within callbacks + // TODO this could be done under an option + let value = Event::Miscellaneous( + crate::events::MiscellaneousEvents::MarkFunctionAsCalled(function.function), + ); + behavior.get_latest_info(top_environment).events.push(value); } Ok(result) diff --git a/checker/src/types/disjoint.rs b/checker/src/types/disjoint.rs index a49c5f59..9e088ea6 100644 --- a/checker/src/types/disjoint.rs +++ b/checker/src/types/disjoint.rs @@ -1,4 +1,7 @@ -use super::{Constant, PartiallyAppliedGenerics, Type, TypeId, TypeStore}; +use super::{ + helpers, Constant, Constructor, MathematicalOrBitwiseOperation, PartiallyAppliedGenerics, Type, + TypeId, TypeStore, +}; use crate::context::InformationChain; /// For equality + [`crate::intrinsics::Intrinsics::Not`] @@ -30,15 +33,9 @@ pub fn types_are_disjoint( if let Type::Or(lhs_lhs, lhs_rhs) = lhs_ty { types_are_disjoint(*lhs_lhs, rhs, already_checked, information, types) && types_are_disjoint(*lhs_rhs, rhs, already_checked, information, types) - } else if let Type::And(lhs_lhs, lhs_rhs) = lhs_ty { - types_are_disjoint(*lhs_lhs, rhs, already_checked, information, types) - || types_are_disjoint(*lhs_rhs, rhs, already_checked, information, types) } else if let Type::Or(rhs_lhs, rhs_rhs) = rhs_ty { types_are_disjoint(lhs, *rhs_lhs, already_checked, information, types) && types_are_disjoint(lhs, *rhs_rhs, already_checked, information, types) - } else if let Type::And(rhs_lhs, rhs_rhs) = rhs_ty { - types_are_disjoint(lhs, *rhs_lhs, already_checked, information, types) - || types_are_disjoint(lhs, *rhs_rhs, already_checked, information, types) } else if let Type::AliasTo { to, parameters: None, name: _ } = lhs_ty { // TODO temp fix, need infer ANY if matches!(*to, TypeId::ANY_TYPE) { @@ -74,7 +71,7 @@ pub fn types_are_disjoint( }) = lhs_ty { use super::subtyping; - let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); + let lhs_inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); let mut state = subtyping::State { // TODO already_checked: already_checked.clone(), @@ -84,16 +81,16 @@ pub fn types_are_disjoint( object_constraints: None, }; - crate::utilities::notify!("{:?}", (lhs, inner)); + // crate::utilities::notify!("{:?}", (lhs, lhs_inner)); - subtyping::type_is_subtype(rhs, inner, &mut state, information, types).is_subtype() + subtyping::type_is_subtype(lhs_inner, rhs, &mut state, information, types).is_subtype() } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on: TypeId::NOT_RESTRICTION, arguments, }) = rhs_ty { use super::subtyping; - let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); + let rhs_inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); let mut state = subtyping::State { // TODO already_checked: already_checked.clone(), @@ -103,75 +100,99 @@ pub fn types_are_disjoint( object_constraints: None, }; - crate::utilities::notify!("{:?}", (lhs, inner)); + crate::utilities::notify!("{:?}", (rhs, rhs_inner)); - subtyping::type_is_subtype(lhs, inner, &mut state, information, types).is_subtype() - } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, - arguments: _, - }) = lhs_ty + subtyping::type_is_subtype(rhs_inner, lhs, &mut state, information, types).is_subtype() + } else if let Type::And(rhs_lhs, rhs_rhs) = rhs_ty { + types_are_disjoint(lhs, *rhs_lhs, already_checked, information, types) + || types_are_disjoint(lhs, *rhs_rhs, already_checked, information, types) + } else if let Type::And(lhs_lhs, lhs_rhs) = lhs_ty { + types_are_disjoint(*lhs_lhs, rhs, already_checked, information, types) + || types_are_disjoint(*lhs_rhs, rhs, already_checked, information, types) + } else if let Type::PartiallyAppliedGenerics( + args @ PartiallyAppliedGenerics { on: TypeId::MULTIPLE_OF, arguments: _ }, + ) = lhs_ty { - let range = super::intrinsics::get_range(lhs, types).unwrap(); - if let Some(rhs_range) = super::intrinsics::get_range(rhs, types) { - let overlap = range.overlaps(rhs_range); - crate::utilities::notify!("{:?}", overlap); - !overlap - } else { - crate::utilities::notify!("Here"); - true - } - } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, - arguments: _, - }) = rhs_ty + // TODO also offset + number_modulo_disjoint(args, rhs, types) + } else if let Type::PartiallyAppliedGenerics( + args @ PartiallyAppliedGenerics { on: TypeId::MULTIPLE_OF, arguments: _ }, + ) = rhs_ty { - let range = super::intrinsics::get_range(rhs, types).unwrap(); - if let Some(lhs_range) = super::intrinsics::get_range(lhs, types) { - let overlap = range.overlaps(lhs_range); - crate::utilities::notify!("{:?}", overlap); - !overlap - } else { - crate::utilities::notify!("Here"); - true - } - } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::MULTIPLE_OF, - arguments, + // TODO also offset + number_modulo_disjoint(args, lhs, types) + } else if let Type::PartiallyAppliedGenerics( + args @ PartiallyAppliedGenerics { + on: TypeId::GREATER_THAN | TypeId::LESS_THAN, + arguments: _, + }, + ) = rhs_ty + { + number_range_disjoint(args, lhs, types) + } else if let Type::PartiallyAppliedGenerics( + args @ PartiallyAppliedGenerics { + on: TypeId::GREATER_THAN | TypeId::LESS_THAN, + arguments: _, + }, + ) = lhs_ty + { + number_range_disjoint(args, rhs, types) + } else if let Type::Constructor(Constructor::BinaryOperator { + lhs: _lhs, + operator: MathematicalOrBitwiseOperation::Modulo, + rhs, + result: _, }) = lhs_ty { - // Little bit complex here because dealing with decimal types, not integers - if let (Type::Constant(Constant::Number(lhs)), Type::Constant(Constant::Number(rhs))) = ( - types.get_type_by_id( - arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(), - ), - types.get_type_by_id(rhs), - ) { - let result = rhs % lhs != 0.; - crate::utilities::notify!("{:?} {:?}", rhs, lhs); - result + if let ( + Type::Constant(Constant::Number(lhs_mod)), + Type::Constant(Constant::Number(num)), + ) = (types.get_type_by_id(*rhs), rhs_ty) + { + crate::utilities::notify!("{:?}", (num, lhs_mod)); + // Modulos return negative for negative number :( + // Checking whether out of range here + num.abs() > **lhs_mod } else { - crate::utilities::notify!("Here"); false } - } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::MULTIPLE_OF, - arguments, + } else if let Type::Constructor(Constructor::BinaryOperator { + lhs: _lhs, + operator: MathematicalOrBitwiseOperation::Modulo, + rhs, + result: _, }) = rhs_ty { - // Little bit complex here because dealing with decimal types, not integers - if let (Type::Constant(Constant::Number(lhs)), Type::Constant(Constant::Number(rhs))) = ( - types.get_type_by_id(lhs), - types.get_type_by_id( - arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(), - ), - ) { - let result = lhs % rhs != 0.; - crate::utilities::notify!("{:?} {:?}", lhs, rhs); - result + if let ( + Type::Constant(Constant::Number(num)), + Type::Constant(Constant::Number(rhs_mod)), + ) = (types.get_type_by_id(*rhs), lhs_ty) + { + crate::utilities::notify!("{:?}", (num, rhs_mod)); + // Modulos return negative for negative number :( + // Checking whether out of range here + num.abs() > **rhs_mod } else { - crate::utilities::notify!("Here"); false } + } else if let Type::Constructor(Constructor::BinaryOperator { + operator: MathematicalOrBitwiseOperation::Add, + result: TypeId::STRING_TYPE, + .. + }) = rhs_ty + { + let lhs = helpers::TemplatelLiteralExpansion::from_type(lhs, types); + let rhs = helpers::TemplatelLiteralExpansion::from_type(rhs, types); + lhs.is_disjoint(&rhs) + } else if let Type::Constructor(Constructor::BinaryOperator { + operator: MathematicalOrBitwiseOperation::Add, + result: TypeId::STRING_TYPE, + .. + }) = lhs_ty + { + let lhs = helpers::TemplatelLiteralExpansion::from_type(lhs, types); + let rhs = helpers::TemplatelLiteralExpansion::from_type(rhs, types); + lhs.is_disjoint(&rhs) } else if let Some(lhs) = super::get_constraint(lhs, types) { // TODO not sure whether these should be here? types_are_disjoint(lhs, rhs, already_checked, information, types) @@ -192,6 +213,18 @@ pub fn types_are_disjoint( } } else if let Type::Constant(rhs_cst) = rhs_ty { types_are_disjoint(rhs_cst.get_backing_type(), lhs, already_checked, information, types) + } else if let Type::Object(crate::types::ObjectNature::AnonymousTypeAnnotation( + _properties, + )) = lhs_ty + { + // TODO check properties + false + } else if let Type::Object(crate::types::ObjectNature::AnonymousTypeAnnotation( + _properties, + )) = rhs_ty + { + // TODO check properties + false } else { crate::utilities::notify!( "{:?} cap {:?} == empty ? cases. Might be missing, calling disjoint", @@ -202,3 +235,100 @@ pub fn types_are_disjoint( } } } + +fn number_modulo_disjoint( + this: &PartiallyAppliedGenerics, + other: TypeId, + types: &TypeStore, +) -> bool { + crate::utilities::notify!("Here number_modulo_disjoint"); + let PartiallyAppliedGenerics { arguments, .. } = this; + let other_ty = types.get_type_by_id(other); + let argument = + types.get_type_by_id(arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap()); + + let Type::Constant(Constant::Number(argument)) = argument else { + crate::utilities::notify!("Gets complex here"); + return false; + }; + + let offset = 0f64.try_into().unwrap(); + let this = crate::utilities::modulo_class::ModuloClass::new(*argument, offset); + + // Little bit complex here because dealing with decimal types, not integers + if let Type::Constant(Constant::Number(other)) = other_ty { + !this.contains(*other) + } else { + let (range, modulo_class) = super::intrinsics::get_range_and_mod_class(other, types); + crate::utilities::notify!("Disjoint with modulo, {:?}, {:?}", range, modulo_class); + if let Some(range) = range { + return !range.contains_multiple_of(*argument); + } + if let Some(modulo_class) = modulo_class { + return this.disjoint(modulo_class); + } + crate::utilities::notify!("Here {:?}", other_ty); + false + } +} + +fn number_range_disjoint( + this: &PartiallyAppliedGenerics, + other: TypeId, + types: &TypeStore, +) -> bool { + let PartiallyAppliedGenerics { on, arguments, .. } = this; + let greater_than = *on == TypeId::GREATER_THAN; + let this_ty = + types.get_type_by_id(arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap()); + + if let Type::Constant(Constant::Number(this)) = this_ty { + let other_ty = types.get_type_by_id(other); + if let Type::Constant(Constant::Number(other)) = other_ty { + crate::utilities::notify!("{:?} {} {}", on, other, this); + if greater_than { + other < this + } else { + other > this + } + } else { + crate::utilities::notify!("Unsure here {:?}", (other_ty, this_ty)); + false + } + } else { + crate::utilities::notify!("Unsure here"); + false + } +} + +// fn todo() { +// // else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { +// // on: TypeId::GREATER_THAN +// // arguments: _, +// // }) = lhs_ty +// // { +// // let range = super::intrinsics::get_range(lhs, types).unwrap(); +// // if let Some(rhs_range) = super::intrinsics::get_range(rhs, types) { +// // let overlap = range.overlaps(rhs_range); +// // crate::utilities::notify!("{:?}", overlap); +// // !overlap +// // } else { +// // crate::utilities::notify!("Here"); +// // true +// // } +// // } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { +// // on: TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, +// // arguments: _, +// // }) = rhs_ty +// // { +// // let range = super::intrinsics::get_range(rhs, types).unwrap(); +// // if let Some(lhs_range) = super::intrinsics::get_range(lhs, types) { +// // let overlap = range.overlaps(lhs_range); +// // crate::utilities::notify!("{:?}", overlap); +// // !overlap +// // } else { +// // crate::utilities::notify!("Here"); +// // true +// // } +// // } +// } diff --git a/checker/src/types/generics/substitution.rs b/checker/src/types/generics/substitution.rs index dacfe81a..1a6c6900 100644 --- a/checker/src/types/generics/substitution.rs +++ b/checker/src/types/generics/substitution.rs @@ -146,7 +146,7 @@ pub(crate) fn substitute( let generic_arguments = structure_arguments.clone(); // Fold intrinsic type - if intrinsics::tsc_string_intrinsic(on) { + if intrinsics::is_tsc_string_intrinsic(on) { let arg = structure_arguments.get_structure_restriction(TypeId::STRING_GENERIC).unwrap(); let value = substitute(arg, arguments, environment, types); @@ -195,7 +195,7 @@ pub(crate) fn substitute( let rhs = *rhs; let lhs = substitute(*lhs, arguments, environment, types); let rhs = substitute(rhs, arguments, environment, types); - types.new_and_type(lhs, rhs).unwrap_or(TypeId::NEVER_TYPE) + types.new_and_type(lhs, rhs) // .unwrap_or(TypeId::NEVER_TYPE) } Type::Or(lhs, rhs) => { let rhs = *rhs; @@ -237,8 +237,18 @@ pub(crate) fn substitute( let lhs = substitute(lhs, arguments, environment, types); let rhs = substitute(rhs, arguments, environment, types); - match evaluate_mathematical_operation(lhs, operator, rhs, environment, types, false) - { + // TODO + let advanced_numbers = false; + + match evaluate_mathematical_operation( + lhs, + operator, + rhs, + environment, + types, + false, + advanced_numbers, + ) { Ok(result) => result, Err(()) => { unreachable!( diff --git a/checker/src/types/helpers.rs b/checker/src/types/helpers.rs new file mode 100644 index 00000000..f8b6ab18 --- /dev/null +++ b/checker/src/types/helpers.rs @@ -0,0 +1,471 @@ +use super::{ + get_constraint, properties, subtyping, Constant, Constructor, GenericArguments, GenericChain, + InformationChain, MathematicalOrBitwiseOperation, PartiallyAppliedGenerics, PolyNature, Type, + TypeId, TypeStore, +}; + +pub(crate) fn get_structure_arguments_based_on_object_constraint<'a, C: InformationChain>( + object: TypeId, + info_chain: &C, + types: &'a TypeStore, +) -> Option<&'a GenericArguments> { + if let Some(object_constraint) = + info_chain.get_chain_of_info().find_map(|c| c.object_constraints.get(&object).copied()) + { + let ty = types.get_type_by_id(object_constraint); + if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { arguments, .. }) = ty { + Some(arguments) + } else { + crate::utilities::notify!("Generics might be missed here {:?}", ty); + None + } + } else { + None + } +} + +pub(crate) fn tuple_like(id: TypeId, types: &TypeStore, environment: &crate::Environment) -> bool { + // TODO should be `ObjectNature::AnonymousObjectType` or something else + let ty = types.get_type_by_id(id); + if let Type::Object(super::ObjectNature::RealDeal) = ty { + environment + .get_chain_of_info() + .any(|info| info.prototypes.get(&id).is_some_and(|p| *p == TypeId::ARRAY_TYPE)) + } else if let Type::AliasTo { to, .. } = ty { + tuple_like(*to, types, environment) + } else { + false + } +} + +pub(crate) fn _unfold_tuple(_ty: TypeId) -> TypeId { + // return Type::PropertyOf() + todo!() +} + +pub(crate) fn _assign_to_tuple(_ty: TypeId) -> TypeId { + todo!() + // if let PropertyKey::Type(slice) = +} + +pub fn get_array_length( + ctx: &impl InformationChain, + on: TypeId, + types: &TypeStore, +) -> Result, Option> { + let length_property = properties::PropertyKey::String(std::borrow::Cow::Borrowed("length")); + let id = properties::get_simple_property_value(ctx, on, &length_property, types).ok_or(None)?; + if let Type::Constant(Constant::Number(n)) = types.get_type_by_id(id) { + Ok(*n) + } else { + Err(Some(id)) + } +} + +/// TODO name? +#[derive(Clone, Copy, Debug)] +pub enum ArrayItem { + Member(TypeId), + Optional(TypeId), + Wildcard(TypeId), +} + +/// WIP +pub(crate) fn as_slice( + ty: TypeId, + types: &TypeStore, + environment: &crate::Environment, +) -> Result, ()> { + if tuple_like(ty, types, environment) { + let ty = if let Type::AliasTo { to, .. } = types.get_type_by_id(ty) { *to } else { ty }; + let properties = + environment.get_chain_of_info().find_map(|info| info.current_properties.get(&ty)); + if let Some(properties) = properties { + Ok(properties + .iter() + .filter_map(|(_, key, value)| { + let not_length_value = !key.is_equal_to("length"); + not_length_value.then(|| { + crate::utilities::notify!("key (should be incremental) {:?}", key); + if key.as_number(types).is_some() { + if let crate::PropertyValue::ConditionallyExists { .. } = value { + ArrayItem::Optional(value.as_get_type(types)) + } else { + ArrayItem::Member(value.as_get_type(types)) + } + } else { + ArrayItem::Wildcard(value.as_get_type(types)) + } + }) + }) + .collect()) + } else { + crate::utilities::notify!("BAD"); + Err(()) + } + } else { + Err(()) + } +} + +/// WIP for counting slice indexes +#[derive(derive_enum_from_into::EnumFrom, Clone, Copy, Debug)] +pub enum Counter { + On(usize), + AddTo(TypeId), +} + +impl Counter { + /// TODO &mut or Self -> Self? + pub fn increment(&mut self, types: &mut TypeStore) { + match self { + Counter::On(value) => { + *value += 1; + } + Counter::AddTo(value) => { + *value = types.register_type(Type::Constructor(Constructor::BinaryOperator { + lhs: *value, + operator: MathematicalOrBitwiseOperation::Add, + rhs: TypeId::ONE, + // TODO could be greater than + result: TypeId::NUMBER_TYPE, + })); + } + } + } + + pub fn add_type(&mut self, ty: TypeId, types: &mut TypeStore) { + let current = self.into_type(types); + let new = types.register_type(Type::Constructor(Constructor::BinaryOperator { + lhs: ty, + operator: MathematicalOrBitwiseOperation::Add, + rhs: current, + // TODO could be greater than + result: TypeId::NUMBER_TYPE, + })); + *self = Counter::AddTo(new); + } + + pub(crate) fn into_property_key(self) -> properties::PropertyKey<'static> { + match self { + Counter::On(value) => properties::PropertyKey::from_usize(value), + Counter::AddTo(ty) => properties::PropertyKey::Type(ty), + } + } + + pub(crate) fn into_type(self, types: &mut TypeStore) -> TypeId { + match self { + Counter::On(value) => { + types.new_constant_type(Constant::Number((value as f64).try_into().unwrap())) + } + Counter::AddTo(ty) => ty, + } + } +} + +/// To fill in for TSC behavior for mapped types +#[must_use] +pub fn references_key_of(id: TypeId, types: &TypeStore) -> bool { + match types.get_type_by_id(id) { + Type::AliasTo { to, .. } => references_key_of(*to, types), + Type::Or(lhs, rhs) | Type::And(lhs, rhs) => { + references_key_of(*lhs, types) || references_key_of(*rhs, types) + } + Type::RootPolyType(c) => references_key_of(c.get_constraint(), types), + Type::Constructor(c) => { + if let Constructor::KeyOf(..) = c { + true + } else if let Constructor::BinaryOperator { lhs, rhs, operator: _, result: _ } = c { + references_key_of(*lhs, types) || references_key_of(*rhs, types) + } else { + crate::utilities::notify!("TODO might have missed keyof {:?}", c); + false + } + } + Type::PartiallyAppliedGenerics(a) => { + if let GenericArguments::ExplicitRestrictions(ref e) = a.arguments { + e.0.iter().any(|(_, (lhs, _))| references_key_of(*lhs, types)) + } else { + false + } + } + Type::Interface { .. } + | Type::Class { .. } + | Type::Constant(_) + | Type::Narrowed { .. } + | Type::FunctionReference(_) + | Type::Object(_) + | Type::SpecialObject(_) => false, + } +} + +#[allow(clippy::match_like_matches_macro)] +#[must_use] +pub fn _type_is_error(ty: TypeId, types: &TypeStore) -> bool { + if ty == TypeId::UNIMPLEMENTED_ERROR_TYPE { + true + } else if let Type::RootPolyType(PolyNature::Error(_)) = types.get_type_by_id(ty) { + true + } else { + false + } +} + +/// TODO want to skip mapped generics because that would break subtyping +#[must_use] +pub fn get_type_as_conditional(ty: TypeId, types: &TypeStore) -> Option<(TypeId, TypeId, TypeId)> { + match types.get_type_by_id(ty) { + Type::Constructor(crate::types::Constructor::ConditionalResult { + condition, + truthy_result, + otherwise_result, + result_union: _, + }) => Some((*condition, *truthy_result, *otherwise_result)), + Type::Or(left, right) => Some((TypeId::OPEN_BOOLEAN_TYPE, *left, *right)), + // For reasons ! + Type::RootPolyType(PolyNature::MappedGeneric { .. }) => None, + _ => { + if let Some(constraint) = get_constraint(ty, types) { + get_type_as_conditional(constraint, types) + } else { + None + } + } + } +} + +/// TODO wip +#[must_use] +pub fn is_pseudo_continous((ty, generics): (TypeId, GenericChain), types: &TypeStore) -> bool { + if let TypeId::NUMBER_TYPE | TypeId::STRING_TYPE = ty { + true + } else if let Some(arg) = generics.as_ref().and_then(|args| args.get_single_argument(ty)) { + is_pseudo_continous((arg, generics), types) + } else { + let ty = types.get_type_by_id(ty); + if let Type::Or(left, right) = ty { + is_pseudo_continous((*left, generics), types) + || is_pseudo_continous((*right, generics), types) + } else if let Type::And(left, right) = ty { + is_pseudo_continous((*left, generics), types) + && is_pseudo_continous((*right, generics), types) + } else if let Type::RootPolyType(PolyNature::MappedGeneric { extends, .. }) = ty { + is_pseudo_continous((*extends, generics), types) + } else { + false + } + } +} + +#[must_use] +pub fn is_inferrable_type(ty: TypeId) -> bool { + matches!(ty, TypeId::ANY_TO_INFER_TYPE | TypeId::OBJECT_TYPE) +} + +/// For quick checking. Wraps [`subtyping::type_is_subtype`] +#[must_use] +pub fn simple_subtype( + expr_ty: TypeId, + to_satisfy: TypeId, + information: &impl InformationChain, + types: &TypeStore, +) -> bool { + let mut state = subtyping::State { + already_checked: Default::default(), + mode: Default::default(), + contributions: Default::default(), + others: subtyping::SubTypingOptions { allow_errors: true }, + object_constraints: None, + }; + + subtyping::type_is_subtype(to_satisfy, expr_ty, &mut state, information, types).is_subtype() +} + +// unfolds narrowing +#[must_use] +pub fn get_origin(ty: TypeId, types: &TypeStore) -> TypeId { + if let Type::Narrowed { from, .. } = types.get_type_by_id(ty) { + // Hopefully don't have a nested from + *from + } else { + ty + } +} + +/// Temp fix for equality of narrowing stuff +#[must_use] +pub fn is_not_of_constant(ty: TypeId, types: &TypeStore) -> bool { + if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::NOT_RESTRICTION, + arguments, + }) = types.get_type_by_id(ty) + { + let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); + types.get_type_by_id(inner).is_constant() + } else { + false + } +} + +// TODO narrowed as well +#[must_use] +pub fn type_equal(lhs: TypeId, rhs: TypeId, types: &TypeStore) -> bool { + if lhs == rhs { + true + } else if let (Type::Constant(lhs), Type::Constant(rhs)) = + (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) + { + lhs == rhs + } else { + false + } +} + +#[derive(Debug, Copy, Clone)] +pub struct AndCondition(pub TypeId); + +#[derive(Debug)] +pub struct OrCase(pub Vec); + +#[must_use] +pub fn into_conditions(id: TypeId, types: &TypeStore) -> Vec { + let ty = types.get_type_by_id(id); + + // Temp fix + if let Type::Constructor(Constructor::BinaryOperator { result, .. }) = ty { + return if matches!(*result, TypeId::NUMBER_TYPE | TypeId::STRING_TYPE) { + vec![AndCondition(id)] + } else { + into_conditions(*result, types) + }; + } + + if let Type::And(lhs, rhs) = ty { + let mut buf = into_conditions(*lhs, types); + buf.append(&mut into_conditions(*rhs, types)); + buf + } else if let Some(backing) = get_constraint_or_alias(id, types) { + into_conditions(backing, types) + } else { + vec![AndCondition(id)] + } +} + +#[must_use] +pub fn into_cases(id: TypeId, types: &TypeStore) -> Vec { + let ty = types.get_type_by_id(id); + if let Type::Or(lhs, rhs) = ty { + let mut buf = into_cases(*lhs, types); + buf.append(&mut into_cases(*rhs, types)); + buf + } else if let Some(backing) = get_constraint_or_alias(id, types) { + into_cases(backing, types) + } else { + vec![OrCase(into_conditions(id, types))] + } +} + +/// Returns the constraint or base of a constant for a type. Otherwise just return the type +#[must_use] +pub fn get_larger_type(on: TypeId, types: &TypeStore) -> TypeId { + if let Some(poly_base) = get_constraint(on, types) { + poly_base + } else if let Type::Constant(cst) = types.get_type_by_id(on) { + cst.get_backing_type() + } else { + on + } +} + +#[must_use] +pub fn get_constraint_or_alias(on: TypeId, types: &TypeStore) -> Option { + match types.get_type_by_id(on) { + Type::RootPolyType(rpt) => Some(rpt.get_constraint()), + Type::Constructor(constr) => Some(constr.get_constraint()), + Type::AliasTo { to, parameters: None, .. } => Some(*to), + Type::Narrowed { narrowed_to, .. } => Some(*narrowed_to), + _ => None, + } +} + +#[derive(Debug)] +pub struct TemplatelLiteralExpansion { + pub parts: Vec<(String, TypeId)>, + pub rest: String, +} + +impl TemplatelLiteralExpansion { + // pub const EMPTY: TemplatelLiteralExpansion = + // TemplatelLiteralExpansion { parts: Vec::new(), rest: String::default() }; + + /// TODO more, maybe involving types + #[must_use] + pub fn is_disjoint(&self, other: &Self) -> bool { + crate::utilities::notify!("{:?}", (self, other)); + if let (Some((lhs, _)), Some((rhs, _))) = (self.parts.first(), other.parts.first()) { + let prefix_length = std::cmp::min(lhs.len(), rhs.len()); + if lhs[..prefix_length] != rhs[..prefix_length] { + return true; + } + } + + let postfix_len = std::cmp::min(self.rest.len(), other.rest.len()); + self.rest[..postfix_len] != other.rest[..postfix_len] + } + + #[must_use] + pub fn as_single_string(&self) -> Option<&str> { + if self.parts.is_empty() { + Some(&self.rest) + } else { + None + } + } + + #[must_use] + pub fn is_empty(&self) -> bool { + self.parts.is_empty() && self.rest.is_empty() + } + + #[must_use] + pub fn concatenate(self, mut other: Self) -> Self { + let mut parts: Vec<(String, TypeId)> = self.parts; + let rest = if other.parts.is_empty() { + format!("{}{}", self.rest, other.rest) + } else { + other.parts[0].0.insert_str(0, &self.rest); + parts.append(&mut other.parts); + other.rest + }; + Self { parts, rest } + } + + /// Forms a TL no matter what + #[must_use] + pub fn from_type(id: TypeId, types: &TypeStore) -> Self { + let ty = types.get_type_by_id(id); + if let Type::Constant(constant) = ty { + TemplatelLiteralExpansion { + parts: Default::default(), + rest: constant.as_js_string().into_owned(), + } + } else if let Type::Constructor(Constructor::BinaryOperator { + lhs, + operator: MathematicalOrBitwiseOperation::Add, + rhs, + result: TypeId::STRING_TYPE, + }) = ty + { + let lhs = Self::from_type(*lhs, types); + let rhs = Self::from_type(*rhs, types); + lhs.concatenate(rhs) + } else if let Some(base) = get_constraint_or_alias(id, types) { + Self::from_type(base, types) + } else { + TemplatelLiteralExpansion { + parts: vec![(Default::default(), id)], + rest: Default::default(), + } + } + } +} diff --git a/checker/src/types/intrinsics.rs b/checker/src/types/intrinsics.rs index 17581d27..cde00b31 100644 --- a/checker/src/types/intrinsics.rs +++ b/checker/src/types/intrinsics.rs @@ -2,15 +2,18 @@ use source_map::SpanWithSource; use crate::{ types::{ - generics::generic_type_arguments::GenericArguments, get_constraint, - PartiallyAppliedGenerics, TypeStore, + generics::generic_type_arguments::GenericArguments, get_constraint, helpers::into_cases, + Constant, Constructor, MathematicalOrBitwiseOperation, PartiallyAppliedGenerics, TypeStore, }, TypeId, }; use super::Type; -pub use crate::utilities::float_range::FloatRange; +pub use crate::utilities::{ + float_range::{FloatRange, InclusiveExclusive}, + modulo_class::ModuloClass, +}; /// These are special marker types (using [`Type::Alias`]) /// @@ -93,7 +96,7 @@ pub(crate) fn apply_string_intrinsic(on: TypeId, s: &str) -> String { } #[must_use] -pub fn tsc_string_intrinsic(id: TypeId) -> bool { +pub fn is_tsc_string_intrinsic(id: TypeId) -> bool { matches!( id, TypeId::STRING_UPPERCASE @@ -105,8 +108,8 @@ pub fn tsc_string_intrinsic(id: TypeId) -> bool { #[must_use] pub fn is_intrinsic(id: TypeId) -> bool { - tsc_string_intrinsic(id) - || ezno_number_intrinsic(id) + is_tsc_string_intrinsic(id) + || is_ezno_number_intrinsic(id) || matches!( id, TypeId::LITERAL_RESTRICTION @@ -119,8 +122,8 @@ pub fn is_intrinsic(id: TypeId) -> bool { } #[must_use] -pub fn ezno_number_intrinsic(id: TypeId) -> bool { - matches!(id, TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE | TypeId::MULTIPLE_OF) +pub fn is_ezno_number_intrinsic(id: TypeId) -> bool { + matches!(id, TypeId::GREATER_THAN | TypeId::LESS_THAN | TypeId::MULTIPLE_OF) } #[must_use] @@ -128,15 +131,17 @@ pub fn get_greater_than(on: TypeId, types: &TypeStore) -> Option<(bool, TypeId)> let on = get_constraint(on, types).unwrap_or(on); let ty = types.get_type_by_id(on); if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: on @ (TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE), + on: TypeId::GREATER_THAN, arguments, }) = ty { - let inclusive = *on == TypeId::INCLUSIVE_RANGE; - let floor = arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(); - Some((inclusive, floor)) + let floor = arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + Some((false, floor)) } else if let Type::And(lhs, rhs) = ty { get_greater_than(*lhs, types).or_else(|| get_greater_than(*rhs, types)) + } else if let Type::Or(lhs, rhs) = ty { + // TODO temp + get_greater_than(*lhs, types).or_else(|| get_greater_than(*rhs, types)) } else if let Type::Constant(crate::Constant::Number(..)) = ty { Some((true, on)) } else { @@ -149,17 +154,17 @@ pub fn get_less_than(on: TypeId, types: &TypeStore) -> Option<(bool, TypeId)> { let on = get_constraint(on, types).unwrap_or(on); let ty = types.get_type_by_id(on); if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: on @ (TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE), + on: TypeId::LESS_THAN, arguments, }) = ty { - let inclusive = *on == TypeId::INCLUSIVE_RANGE; - Some(( - inclusive, - arguments.get_structure_restriction(TypeId::NUMBER_CEILING_GENERIC).unwrap(), - )) + let floor = arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + Some((false, floor)) } else if let Type::And(lhs, rhs) = ty { get_less_than(*lhs, types).or_else(|| get_less_than(*rhs, types)) + } else if let Type::Or(lhs, rhs) = ty { + // TODO temp + get_less_than(*lhs, types).or_else(|| get_less_than(*rhs, types)) } else if let Type::Constant(crate::Constant::Number(..)) = ty { Some((true, on)) } else { @@ -168,60 +173,50 @@ pub fn get_less_than(on: TypeId, types: &TypeStore) -> Option<(bool, TypeId)> { } #[must_use] -pub fn get_range(on: TypeId, types: &TypeStore) -> Option { - let on = get_constraint(on, types).unwrap_or(on); - let ty = types.get_type_by_id(on); - if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: on @ (TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE), - arguments, - }) = ty - { - let inclusive = *on == TypeId::INCLUSIVE_RANGE; - crate::utilities::notify!("{:?} {:?}", on, arguments); - let floor = arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(); - let ceiling = arguments.get_structure_restriction(TypeId::NUMBER_CEILING_GENERIC).unwrap(); - if let ( - Type::Constant(crate::Constant::Number(floor)), - Type::Constant(crate::Constant::Number(ceiling)), - ) = (types.get_type_by_id(floor), types.get_type_by_id(ceiling)) - { - let (floor, ceiling) = (*floor, *ceiling); - Some(if inclusive { - FloatRange::Inclusive { floor, ceiling } - } else { - FloatRange::Exclusive { floor, ceiling } - }) - } else { - crate::utilities::notify!("Not bottom top number"); - None - } - } else if let Type::Constant(crate::Constant::Number(number)) = ty { - Some(FloatRange::single(*number)) +pub fn range_to_type(range: FloatRange, types: &mut TypeStore) -> TypeId { + // TODO skip if infinite + let floor_ty = types.new_constant_type(Constant::Number(range.floor.1)); + let ceiling_ty = types.new_constant_type(Constant::Number(range.ceiling.1)); + let floor = range + .get_greater_than() + .map(|_number| new_intrinsic(&Intrinsic::GreaterThan, floor_ty, types)); + + let ceiling = + range.get_less_than().map(|_number| new_intrinsic(&Intrinsic::LessThan, ceiling_ty, types)); + + let mut ty = if let (Some(f), Some(c)) = (floor, ceiling) { + types.new_and_type(f, c) } else { - None + floor.or(ceiling).unwrap() + }; + + if let InclusiveExclusive::Inclusive = range.floor.0 { + ty = types.new_or_type(ty, floor_ty); } + if let InclusiveExclusive::Inclusive = range.ceiling.0 { + ty = types.new_or_type(ty, ceiling_ty); + } + ty } -#[must_use] -pub fn range_to_type(range: FloatRange, types: &mut TypeStore) -> TypeId { - use source_map::Nullable; - - let on = if let FloatRange::Inclusive { .. } = range { - TypeId::INCLUSIVE_RANGE +pub fn modulo_to_type(mod_class: ModuloClass, types: &mut TypeStore) -> TypeId { + // TODO skip if infinite + let modulo = types.new_constant_type(Constant::Number(mod_class.modulo)); + let ty = new_intrinsic(&Intrinsic::MultipleOf, modulo, types); + if mod_class.offset == 0. { + ty } else { - TypeId::EXCLUSIVE_RANGE - }; - let (FloatRange::Inclusive { floor, ceiling } | FloatRange::Exclusive { floor, ceiling }) = - range; - let floor = types.new_constant_type(crate::Constant::Number(floor)); - let ceiling = types.new_constant_type(crate::Constant::Number(ceiling)); - let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([ - (TypeId::NUMBER_FLOOR_GENERIC, (floor, SpanWithSource::NULL)), - (TypeId::NUMBER_CEILING_GENERIC, (ceiling, SpanWithSource::NULL)), - ])); - types.register_type(Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments })) + let offset = types.new_constant_type(Constant::Number(mod_class.offset)); + types.register_type(Type::Constructor(Constructor::BinaryOperator { + lhs: ty, + operator: MathematicalOrBitwiseOperation::Add, + rhs: offset, + result: TypeId::NUMBER_TYPE, + })) + } } +// TODO offsets as well #[must_use] pub fn get_multiple(on: TypeId, types: &TypeStore) -> Option { let on = get_constraint(on, types).unwrap_or(on); @@ -231,7 +226,7 @@ pub fn get_multiple(on: TypeId, types: &TypeStore) -> Option { arguments, }) = ty { - arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC) + arguments.get_structure_restriction(TypeId::NUMBER_GENERIC) } else if let Type::And(lhs, rhs) = ty { get_multiple(*lhs, types).or_else(|| get_multiple(*rhs, types)) } else if let Type::Constant(crate::Constant::Number(..)) = ty { @@ -255,7 +250,7 @@ pub fn is_not_not_a_number(on: TypeId, types: &TypeStore) -> bool { } else if let Type::Or(lhs, rhs) = ty { is_not_not_a_number(*lhs, types) && is_not_not_a_number(*rhs, types) } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::MULTIPLE_OF | TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, + on: TypeId::MULTIPLE_OF | TypeId::GREATER_THAN | TypeId::LESS_THAN, arguments: _, }) = ty { @@ -281,26 +276,26 @@ pub fn new_intrinsic(intrinsic: &Intrinsic, argument: TypeId, types: &mut TypeSt Intrinsic::NoInfer => (TypeId::NO_INFER, TypeId::T_TYPE), Intrinsic::Literal => (TypeId::LITERAL_RESTRICTION, TypeId::T_TYPE), Intrinsic::LessThan => { - let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([ - (TypeId::NUMBER_FLOOR_GENERIC, (TypeId::NEG_INFINITY, SpanWithSource::NULL)), - (TypeId::NUMBER_CEILING_GENERIC, (argument, SpanWithSource::NULL)), - ])); + let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([( + TypeId::NUMBER_GENERIC, + (argument, SpanWithSource::NULL), + )])); return types.register_type(Type::PartiallyAppliedGenerics( - PartiallyAppliedGenerics { on: TypeId::EXCLUSIVE_RANGE, arguments }, + PartiallyAppliedGenerics { on: TypeId::LESS_THAN, arguments }, )); } Intrinsic::GreaterThan => { - let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([ - (TypeId::NUMBER_FLOOR_GENERIC, (argument, SpanWithSource::NULL)), - (TypeId::NUMBER_CEILING_GENERIC, (TypeId::INFINITY, SpanWithSource::NULL)), - ])); + let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([( + TypeId::NUMBER_GENERIC, + (argument, SpanWithSource::NULL), + )])); return types.register_type(Type::PartiallyAppliedGenerics( - PartiallyAppliedGenerics { on: TypeId::EXCLUSIVE_RANGE, arguments }, + PartiallyAppliedGenerics { on: TypeId::GREATER_THAN, arguments }, )); } - Intrinsic::MultipleOf => (TypeId::MULTIPLE_OF, TypeId::NUMBER_FLOOR_GENERIC), + Intrinsic::MultipleOf => (TypeId::MULTIPLE_OF, TypeId::NUMBER_GENERIC), Intrinsic::Exclusive => (TypeId::EXCLUSIVE_RESTRICTION, TypeId::T_TYPE), Intrinsic::Not => { // Double negation @@ -316,6 +311,7 @@ pub fn new_intrinsic(intrinsic: &Intrinsic, argument: TypeId, types: &mut TypeSt } Intrinsic::CaseInsensitive => (TypeId::CASE_INSENSITIVE, TypeId::STRING_GENERIC), }; + let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([( to_pair, (argument, SpanWithSource::NULL), @@ -323,3 +319,168 @@ pub fn new_intrinsic(intrinsic: &Intrinsic, argument: TypeId, types: &mut TypeSt types.register_type(Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments })) } + +#[must_use] +pub fn get_range_and_mod_class( + ty: TypeId, + types: &TypeStore, +) -> (Option, Option) { + let cases = into_cases(ty, types); + let interesting = cases.iter().enumerate().find_map(|(idx, case)| { + case.0 + .iter() + .copied() + .try_fold(Vec::new(), |mut acc, ty| { + if let Ok(item) = PureNumberIntrinsic::try_from_type(ty.0, types) { + acc.push(item); + Some(acc) + } else { + None + } + }) + .map(|value| (idx, value)) + }); + + if let Some((idx, interesting)) = interesting { + let mut range: Option = None; + let mut modulo_class: Option = None; + for number_condition in interesting { + match number_condition { + PureNumberIntrinsic::GreaterThan(greater_than) => { + range = Some(match range { + None => FloatRange::new_greater_than(greater_than), + Some(mut range) => { + range.floor.1 = range.floor.1.max(greater_than); + range + } + }); + } + PureNumberIntrinsic::LessThan(less_than) => { + range = Some(match range { + None => FloatRange::new_less_than(less_than), + Some(mut range) => { + range.ceiling.1 = range.ceiling.1.min(less_than); + range + } + }); + } + PureNumberIntrinsic::Modulo { modulo, offset } => { + modulo_class = Some(if modulo_class.is_none() { + ModuloClass::new(modulo, offset) + } else { + crate::utilities::notify!("TODO intersection"); + return (None, None); + }); + } + } + } + + for (other_idx, case) in cases.into_iter().enumerate() { + // Skip the interesting case + if idx == other_idx { + continue; + } + // Test whether all cases fit inside this new condition. Also modify ranges for inclusion if not + if let [value] = &case.0[..] { + if let Type::Constant(Constant::Number(num)) = types.get_type_by_id(value.0) { + let num = *num; + if let Some(ref modulo_class) = modulo_class { + if !modulo_class.contains(num) { + return (None, None); + } + } + if let Some(ref mut range) = range { + if range.floor.1 == num { + range.floor.0 = InclusiveExclusive::Inclusive; + } else if range.ceiling.1 == num { + range.ceiling.0 = InclusiveExclusive::Inclusive; + } else if !range.contains(num) { + crate::utilities::notify!( + "Here, number not contained in current range" + ); + return (None, None); + } + } + } + } else { + return (None, None); + } + } + (range, modulo_class) + } else if let Type::Constant(Constant::Number(num)) = types.get_type_by_id(ty) { + (Some(FloatRange::new_single(*num)), None) + } else { + // crate::utilities::notify!("Not interesting or constant {:?}", ty); + (None, None) + } +} + +type BetterF64 = ordered_float::NotNan; + +/// Unit. No combinations at this point +#[derive(Debug)] +pub enum PureNumberIntrinsic { + GreaterThan(BetterF64), + LessThan(BetterF64), + Modulo { modulo: BetterF64, offset: BetterF64 }, +} + +impl PureNumberIntrinsic { + pub fn try_from_type(ty: TypeId, types: &TypeStore) -> Result { + let ty = types.get_type_by_id(ty); + if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: on @ (TypeId::GREATER_THAN | TypeId::LESS_THAN | TypeId::MULTIPLE_OF), + arguments, + }) = ty + { + let arg = arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + if let Type::Constant(Constant::Number(number)) = types.get_type_by_id(arg) { + match *on { + TypeId::GREATER_THAN => Ok(PureNumberIntrinsic::GreaterThan(*number)), + TypeId::LESS_THAN => Ok(PureNumberIntrinsic::LessThan(*number)), + TypeId::MULTIPLE_OF => Ok(PureNumberIntrinsic::Modulo { + modulo: *number, + offset: 0f64.try_into().unwrap(), + }), + _ => todo!(), + } + } else { + Err(()) + } + } else if let Type::Constructor(Constructor::BinaryOperator { + lhs, + operator: MathematicalOrBitwiseOperation::Add, + rhs, + .. + }) = ty + { + let lhs_ty = types.get_type_by_id(super::get_constraint(*lhs, types).unwrap_or(*lhs)); + crate::utilities::notify!("{:?}", lhs_ty); + let lhs_modulo = if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::MULTIPLE_OF, + arguments, + }) = lhs_ty + { + let arg = arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + if let Type::Constant(Constant::Number(number)) = types.get_type_by_id(arg) { + Some(number) + } else { + None + } + } else { + None + }; + + if let (Some(modulo), Type::Constant(Constant::Number(offset))) = + (lhs_modulo, types.get_type_by_id(*rhs)) + { + Ok(PureNumberIntrinsic::Modulo { modulo: *modulo, offset: *offset }) + } else { + Err(()) + } + } else { + // crate::utilities::notify!("err here {:?}", ty); + Err(()) + } + } +} diff --git a/checker/src/types/mod.rs b/checker/src/types/mod.rs index a4bd6d90..81dcd86b 100644 --- a/checker/src/types/mod.rs +++ b/checker/src/types/mod.rs @@ -4,6 +4,7 @@ pub mod classes; pub mod disjoint; pub mod functions; pub mod generics; +pub mod helpers; pub mod intrinsics; pub mod logical; pub mod printing; @@ -33,13 +34,12 @@ pub use crate::features::objects::SpecialObject; use crate::{ context::InformationChain, events::RootReference, - features::operations::{CanonicalEqualityAndInequality, MathematicalAndBitwise}, + features::operations::{CanonicalEqualityAndInequality, MathematicalOrBitwiseOperation}, subtyping::SliceArguments, Decidable, FunctionId, }; use derive_debug_extras::DebugExtras; -use derive_enum_from_into::EnumFrom; use source_map::SpanWithSource; pub type ExplicitTypeArgument = (TypeId, SpanWithSource); @@ -143,32 +143,29 @@ impl TypeId { pub const WRITABLE_KEY_ARGUMENT: Self = Self(43); // Ezno intrinsics + pub const NUMBER_GENERIC: Self = Self(44); + pub const GREATER_THAN: Self = Self(45); + pub const LESS_THAN: Self = Self(46); + pub const MULTIPLE_OF: Self = Self(47); + pub const NOT_NOT_A_NUMBER: Self = Self(48); + pub const NUMBER_BUT_NOT_NOT_A_NUMBER: Self = Self(49); - /// Used in [`Self::INCLUSIVE_RANGE`], [`Self::EXCLUSIVE_RANGE`] and [`Self::MULTIPLE_OF`] - pub const NUMBER_FLOOR_GENERIC: Self = Self(44); - pub const NUMBER_CEILING_GENERIC: Self = Self(45); - pub const INCLUSIVE_RANGE: Self = Self(46); - pub const EXCLUSIVE_RANGE: Self = Self(47); - pub const MULTIPLE_OF: Self = Self(48); - pub const NOT_NOT_A_NUMBER: Self = Self(49); - pub const NUMBER_BUT_NOT_NOT_A_NUMBER: Self = Self(50); - - pub const LITERAL_RESTRICTION: Self = Self(51); - pub const EXCLUSIVE_RESTRICTION: Self = Self(52); - pub const NOT_RESTRICTION: Self = Self(53); + pub const LITERAL_RESTRICTION: Self = Self(50); + pub const EXCLUSIVE_RESTRICTION: Self = Self(51); + pub const NOT_RESTRICTION: Self = Self(52); /// This is needed for the TSC string intrinsics - pub const CASE_INSENSITIVE: Self = Self(54); + pub const CASE_INSENSITIVE: Self = Self(53); /// WIP - pub const OPEN_BOOLEAN_TYPE: Self = Self(55); - pub const OPEN_NUMBER_TYPE: Self = Self(56); + pub const OPEN_BOOLEAN_TYPE: Self = Self(54); + pub const OPEN_NUMBER_TYPE: Self = Self(55); /// For `+` operator - pub const STRING_OR_NUMBER: Self = Self(57); + pub const STRING_OR_NUMBER: Self = Self(56); /// Above add one (because [`TypeId`] starts at zero). Used to assert that the above is all correct - pub(crate) const INTERNAL_TYPE_COUNT: usize = 58; + pub(crate) const INTERNAL_TYPE_COUNT: usize = 57; } #[derive(Debug, binary_serialize_derive::BinarySerializable)] @@ -193,13 +190,11 @@ pub enum Type { /// - Can be used for subtypes (aka N aliases number then more types on top) /// - **Does not imply .prototype = ** AliasTo { - to: TypeId, name: String, parameters: Option>, + to: TypeId, }, - /// For number and other rooted types - /// - /// Although they all alias Object + /// Properties are in environment (for declaration merging) Interface { name: String, parameters: Option>, @@ -334,9 +329,11 @@ impl Type { // Fine Type::Narrowed { .. } | Type::Constructor(_) | Type::RootPolyType(_) => true, // TODO what about if left or right - Type::And(_, _) | Type::Or(_, _) => false, + // TODO should not be true here + Type::And(_, _) | Type::Or(_, _) => true, // TODO what about if it aliases dependent? - Type::AliasTo { .. } => false, + // TODO should not be true here + Type::AliasTo { .. } => true, Type::Interface { .. } | Type::Class { .. } => false, Type::Constant(_) | Type::SpecialObject(_) @@ -374,7 +371,7 @@ impl Type { pub enum Constructor { BinaryOperator { lhs: TypeId, - operator: MathematicalAndBitwise, + operator: MathematicalOrBitwiseOperation, rhs: TypeId, /// for add + number intrinsics result: TypeId, @@ -420,18 +417,33 @@ pub enum Constructor { } impl Constructor { - fn get_base(&self) -> Option { + #[must_use] + pub fn get_constraint(&self) -> TypeId { match self { - Constructor::ConditionalResult { result_union: result, .. } - | Constructor::Awaited { result, .. } - | Constructor::Property { result, .. } - | Constructor::Image { result, .. } => Some(*result), - Constructor::BinaryOperator { .. } - | Constructor::CanonicalRelationOperator { .. } - | Constructor::TypeExtends(_) - | Constructor::TypeOperator(_) => None, - // TODO or symbol - Constructor::KeyOf(_) => Some(TypeId::STRING_TYPE), + Constructor::BinaryOperator { result, .. } + | Constructor::Awaited { on: _, result } + | Constructor::Image { on: _, with: _, result } => *result, + Constructor::Property { on: _, under: _, result, mode: _ } => { + // crate::utilities::notify!("Here, result of a property get"); + *result + } + Constructor::ConditionalResult { result_union, .. } => { + // TODO dynamic and open poly + *result_union + } + Constructor::TypeOperator(op) => match op { + // TODO union of names + TypeOperator::TypeOf(_) => TypeId::STRING_TYPE, + TypeOperator::IsPrototype { .. } | TypeOperator::HasProperty(..) => { + TypeId::BOOLEAN_TYPE + } + }, + Constructor::CanonicalRelationOperator { .. } => TypeId::BOOLEAN_TYPE, + Constructor::TypeExtends(op) => { + let crate::types::TypeExtends { .. } = op; + TypeId::BOOLEAN_TYPE + } + Constructor::KeyOf(_) => TypeId::STRING_TYPE, } } } @@ -605,7 +617,6 @@ pub fn is_type_truthy_falsy(id: TypeId, types: &TypeStore) -> Decidable { | Type::Constructor(_) | Type::Interface { .. } | Type::PartiallyAppliedGenerics(..) - | Type::Narrowed { .. } | Type::Class { .. } => { // TODO some of these case are known Decidable::Unknown(id) @@ -617,6 +628,7 @@ pub fn is_type_truthy_falsy(id: TypeId, types: &TypeStore) -> Decidable { // TODO strict casts Decidable::Known(cast_as_boolean(cst, false).unwrap()) } + Type::Narrowed { narrowed_to, .. } => is_type_truthy_falsy(*narrowed_to, types), } } } @@ -671,32 +683,7 @@ pub(crate) fn is_explicit_generic(on: TypeId, types: &TypeStore) -> bool { pub(crate) fn get_constraint(on: TypeId, types: &TypeStore) -> Option { match types.get_type_by_id(on) { Type::RootPolyType(nature) => Some(nature.get_constraint()), - Type::Constructor(constructor) => match constructor.clone() { - Constructor::BinaryOperator { result, .. } - | Constructor::Awaited { on: _, result } - | Constructor::Image { on: _, with: _, result } => Some(result), - Constructor::Property { on: _, under: _, result, mode: _ } => { - // crate::utilities::notify!("Here, result of a property get"); - Some(result) - } - Constructor::ConditionalResult { result_union, .. } => { - // TODO dynamic and open poly - Some(result_union) - } - Constructor::TypeOperator(op) => match op { - // TODO union of names - TypeOperator::TypeOf(_) => Some(TypeId::STRING_TYPE), - TypeOperator::IsPrototype { .. } | TypeOperator::HasProperty(..) => { - Some(TypeId::BOOLEAN_TYPE) - } - }, - Constructor::CanonicalRelationOperator { .. } => Some(TypeId::BOOLEAN_TYPE), - Constructor::TypeExtends(op) => { - let crate::types::TypeExtends { .. } = op; - Some(TypeId::BOOLEAN_TYPE) - } - Constructor::KeyOf(_) => Some(TypeId::STRING_TYPE), - }, + Type::Constructor(constructor) => Some(constructor.get_constraint()), Type::Narrowed { from: _, narrowed_to } => Some(*narrowed_to), Type::Object(ObjectNature::RealDeal) => { // crate::utilities::notify!("Might be missing some mutations that are possible here"); @@ -706,18 +693,6 @@ pub(crate) fn get_constraint(on: TypeId, types: &TypeStore) -> Option { } } -/// Returns the constraint or base of a constant for a type. Otherwise just return the type -#[must_use] -pub fn get_larger_type(on: TypeId, types: &TypeStore) -> TypeId { - if let Some(poly_base) = get_constraint(on, types) { - poly_base - } else if let Type::Constant(cst) = types.get_type_by_id(on) { - cst.get_backing_type() - } else { - on - } -} - /// TODO T on `Array`, U, V on `Map` etc. Works around not having `&mut TypeStore` and mutations in-between #[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)] pub enum LookUpGeneric { @@ -791,317 +766,3 @@ impl TypeCombinable for TypeId { TypeId::UNDEFINED_TYPE } } - -/// Used for **both** inference and narrowing -pub enum Confirmation { - HasProperty { on: (), property: () }, - IsType { on: (), ty: () }, -} - -pub(crate) fn get_structure_arguments_based_on_object_constraint<'a, C: InformationChain>( - object: TypeId, - info_chain: &C, - types: &'a TypeStore, -) -> Option<&'a GenericArguments> { - if let Some(object_constraint) = - info_chain.get_chain_of_info().find_map(|c| c.object_constraints.get(&object).copied()) - { - let ty = types.get_type_by_id(object_constraint); - if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { arguments, .. }) = ty { - Some(arguments) - } else { - crate::utilities::notify!("Generics might be missed here {:?}", ty); - None - } - } else { - None - } -} - -pub(crate) fn tuple_like(id: TypeId, types: &TypeStore, environment: &crate::Environment) -> bool { - // TODO should be `ObjectNature::AnonymousObjectType` or something else - let ty = types.get_type_by_id(id); - if let Type::Object(ObjectNature::RealDeal) = ty { - environment - .get_chain_of_info() - .any(|info| info.prototypes.get(&id).is_some_and(|p| *p == TypeId::ARRAY_TYPE)) - } else if let Type::AliasTo { to, .. } = ty { - tuple_like(*to, types, environment) - } else { - false - } -} - -pub(crate) fn _unfold_tuple(_ty: TypeId) -> TypeId { - // return Type::PropertyOf() - todo!() -} - -pub(crate) fn _assign_to_tuple(_ty: TypeId) -> TypeId { - todo!() - // if let PropertyKey::Type(slice) = -} - -fn get_array_length( - ctx: &impl InformationChain, - on: TypeId, - types: &TypeStore, -) -> Result, Option> { - let length_property = properties::PropertyKey::String(std::borrow::Cow::Borrowed("length")); - let id = get_simple_value(ctx, on, &length_property, types).ok_or(None)?; - if let Type::Constant(Constant::Number(n)) = types.get_type_by_id(id) { - Ok(*n) - } else { - Err(Some(id)) - } -} - -/// TODO name? -#[derive(Clone, Copy, Debug)] -pub enum ArrayItem { - Member(TypeId), - Optional(TypeId), - Wildcard(TypeId), -} - -/// WIP -pub(crate) fn as_slice( - ty: TypeId, - types: &TypeStore, - environment: &crate::Environment, -) -> Result, ()> { - if tuple_like(ty, types, environment) { - let ty = if let Type::AliasTo { to, .. } = types.get_type_by_id(ty) { *to } else { ty }; - let properties = - environment.get_chain_of_info().find_map(|info| info.current_properties.get(&ty)); - if let Some(properties) = properties { - Ok(properties - .iter() - .filter_map(|(_, key, value)| { - let not_length_value = !key.is_equal_to("length"); - not_length_value.then(|| { - crate::utilities::notify!("key (should be incremental) {:?}", key); - if key.as_number(types).is_some() { - if let crate::PropertyValue::ConditionallyExists { .. } = value { - ArrayItem::Optional(value.as_get_type(types)) - } else { - ArrayItem::Member(value.as_get_type(types)) - } - } else { - ArrayItem::Wildcard(value.as_get_type(types)) - } - }) - }) - .collect()) - } else { - crate::utilities::notify!("BAD"); - Err(()) - } - } else { - Err(()) - } -} - -/// WIP for counting slice indexes -#[derive(EnumFrom, Clone, Copy, Debug)] -pub enum Counter { - On(usize), - AddTo(TypeId), -} - -impl Counter { - /// TODO &mut or Self -> Self? - pub fn increment(&mut self, types: &mut TypeStore) { - match self { - Counter::On(value) => { - *value += 1; - } - Counter::AddTo(value) => { - *value = types.register_type(Type::Constructor(Constructor::BinaryOperator { - lhs: *value, - operator: MathematicalAndBitwise::Add, - rhs: TypeId::ONE, - // TODO could be greater than - result: TypeId::NUMBER_TYPE, - })); - } - } - } - - pub fn add_type(&mut self, ty: TypeId, types: &mut TypeStore) { - let current = self.into_type(types); - let new = types.register_type(Type::Constructor(Constructor::BinaryOperator { - lhs: ty, - operator: MathematicalAndBitwise::Add, - rhs: current, - // TODO could be greater than - result: TypeId::NUMBER_TYPE, - })); - *self = Counter::AddTo(new); - } - - pub(crate) fn into_property_key(self) -> properties::PropertyKey<'static> { - match self { - Counter::On(value) => properties::PropertyKey::from_usize(value), - Counter::AddTo(ty) => properties::PropertyKey::Type(ty), - } - } - - pub(crate) fn into_type(self, types: &mut TypeStore) -> TypeId { - match self { - Counter::On(value) => { - types.new_constant_type(Constant::Number((value as f64).try_into().unwrap())) - } - Counter::AddTo(ty) => ty, - } - } -} - -pub(crate) mod helpers { - use super::{ - get_constraint, subtyping, Constructor, GenericArguments, GenericChain, InformationChain, - PartiallyAppliedGenerics, PolyNature, Type, TypeId, TypeStore, - }; - - /// To fill in for TSC behavior for mapped types - #[must_use] - pub fn references_key_of(id: TypeId, types: &TypeStore) -> bool { - match types.get_type_by_id(id) { - Type::AliasTo { to, .. } => references_key_of(*to, types), - Type::Or(lhs, rhs) | Type::And(lhs, rhs) => { - references_key_of(*lhs, types) || references_key_of(*rhs, types) - } - Type::RootPolyType(c) => references_key_of(c.get_constraint(), types), - Type::Constructor(c) => { - if let Constructor::KeyOf(..) = c { - true - } else if let Constructor::BinaryOperator { lhs, rhs, operator: _, result: _ } = c { - references_key_of(*lhs, types) || references_key_of(*rhs, types) - } else { - crate::utilities::notify!("TODO might have missed keyof {:?}", c); - false - } - } - Type::PartiallyAppliedGenerics(a) => { - if let GenericArguments::ExplicitRestrictions(ref e) = a.arguments { - e.0.iter().any(|(_, (lhs, _))| references_key_of(*lhs, types)) - } else { - false - } - } - Type::Interface { .. } - | Type::Class { .. } - | Type::Constant(_) - | Type::Narrowed { .. } - | Type::FunctionReference(_) - | Type::Object(_) - | Type::SpecialObject(_) => false, - } - } - - #[allow(clippy::match_like_matches_macro)] - #[must_use] - pub fn _type_is_error(ty: TypeId, types: &TypeStore) -> bool { - if ty == TypeId::UNIMPLEMENTED_ERROR_TYPE { - true - } else if let Type::RootPolyType(PolyNature::Error(_)) = types.get_type_by_id(ty) { - true - } else { - false - } - } - - /// TODO want to skip mapped generics because that would break subtyping - #[must_use] - pub fn get_conditional(ty: TypeId, types: &TypeStore) -> Option<(TypeId, TypeId, TypeId)> { - match types.get_type_by_id(ty) { - Type::Constructor(crate::types::Constructor::ConditionalResult { - condition, - truthy_result, - otherwise_result, - result_union: _, - }) => Some((*condition, *truthy_result, *otherwise_result)), - Type::Or(left, right) => Some((TypeId::OPEN_BOOLEAN_TYPE, *left, *right)), - // For reasons ! - Type::RootPolyType(PolyNature::MappedGeneric { .. }) => None, - _ => { - if let Some(constraint) = get_constraint(ty, types) { - get_conditional(constraint, types) - } else { - None - } - } - } - } - - /// TODO wip - #[must_use] - pub fn is_pseudo_continous((ty, generics): (TypeId, GenericChain), types: &TypeStore) -> bool { - if let TypeId::NUMBER_TYPE | TypeId::STRING_TYPE = ty { - true - } else if let Some(arg) = generics.as_ref().and_then(|args| args.get_single_argument(ty)) { - is_pseudo_continous((arg, generics), types) - } else { - let ty = types.get_type_by_id(ty); - if let Type::Or(left, right) = ty { - is_pseudo_continous((*left, generics), types) - || is_pseudo_continous((*right, generics), types) - } else if let Type::And(left, right) = ty { - is_pseudo_continous((*left, generics), types) - && is_pseudo_continous((*right, generics), types) - } else if let Type::RootPolyType(PolyNature::MappedGeneric { extends, .. }) = ty { - is_pseudo_continous((*extends, generics), types) - } else { - false - } - } - } - - #[must_use] - pub fn is_inferrable_type(ty: TypeId) -> bool { - matches!(ty, TypeId::ANY_TO_INFER_TYPE | TypeId::OBJECT_TYPE) - } - - /// For quick checking - #[must_use] - pub fn simple_subtype( - expr_ty: TypeId, - to_satisfy: TypeId, - information: &impl InformationChain, - types: &TypeStore, - ) -> bool { - let mut state = subtyping::State { - already_checked: Default::default(), - mode: Default::default(), - contributions: Default::default(), - others: subtyping::SubTypingOptions { allow_errors: true }, - object_constraints: None, - }; - - subtyping::type_is_subtype(to_satisfy, expr_ty, &mut state, information, types).is_subtype() - } - - // unfolds narrowing - pub fn get_origin(ty: TypeId, types: &TypeStore) -> TypeId { - if let Type::Narrowed { from, .. } = types.get_type_by_id(ty) { - // Hopefully don't have a nested from - *from - } else { - ty - } - } - - /// Temp fix for equality of narrowing stuff - pub fn is_not_of_constant(ty: TypeId, types: &TypeStore) -> bool { - if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::NOT_RESTRICTION, - arguments, - }) = types.get_type_by_id(ty) - { - let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); - types.get_type_by_id(inner).is_constant() - } else { - false - } - } -} diff --git a/checker/src/types/printing.rs b/checker/src/types/printing.rs index 63cf693a..81a2f1ca 100644 --- a/checker/src/types/printing.rs +++ b/checker/src/types/printing.rs @@ -9,7 +9,8 @@ use crate::{ calling::ThisValue, functions::{FunctionBehavior, FunctionEffect}, generics::generic_type_arguments::GenericArguments, - get_array_length, get_constraint, get_simple_value, + get_constraint, get_simple_property_value, + helpers::get_array_length, properties::{get_properties_on_single_type, AccessMode, PropertyKey, Publicity}, Constructor, GenericChainLink, ObjectNature, PartiallyAppliedGenerics, TypeExtends, }, @@ -57,24 +58,6 @@ pub fn print_type_with_type_arguments( buf } -pub fn print_inner_template_literal_type_into_buf( - ty: TypeId, - buf: &mut String, - cycles: &mut HashSet, - args: GenericChain, - types: &TypeStore, - info: &C, - debug: bool, -) { - if let Type::Constant(cst) = types.get_type_by_id(ty) { - buf.push_str(&cst.as_js_string()); - } else { - buf.push_str("${"); - print_type_into_buf(ty, buf, cycles, args, types, info, debug); - buf.push('}'); - } -} - /// Recursion safe + reuses buffer pub fn print_type_into_buf( ty: TypeId, @@ -96,27 +79,35 @@ pub fn print_type_into_buf( let r#type = types.get_type_by_id(ty); match r#type { Type::And(a, b) => { + // let value = crate::types::intrinsics::get_range_and_mod_class(ty, types); + // if value.0.is_some() || value.1.is_some() { + // crate::utilities::notify!("{:?}", value); + // } + print_type_into_buf(*a, buf, cycles, args, types, info, debug); buf.push_str(" & "); print_type_into_buf(*b, buf, cycles, args, types, info, debug); } Type::Or(a, b) => { + // let value = crate::types::intrinsics::get_range_and_mod_class(ty, types); + // if value.0.is_some() || value.1.is_some() { + // crate::utilities::notify!("{:?}", value); + // } + print_type_into_buf(*a, buf, cycles, args, types, info, debug); buf.push_str(" | "); print_type_into_buf(*b, buf, cycles, args, types, info, debug); } Type::Narrowed { narrowed_to, from } => { if debug { - buf.push_str("(narrowed from "); - print_type_into_buf(*from, buf, cycles, args, types, info, debug); - buf.push_str(") "); + write!(buf, "(narrowed from {from:?}) ").unwrap(); } print_type_into_buf(*narrowed_to, buf, cycles, args, types, info, debug); } Type::RootPolyType(nature) => match nature { PolyNature::MappedGeneric { name, extends } => { if debug { - write!(buf, "[mg {} {}] ", ty.0, name).unwrap(); + write!(buf, "(mg {} {}) ", ty.0, name).unwrap(); } crate::utilities::notify!("args={:?}", args); if let Some(crate::types::CovariantContribution::String(property)) = @@ -131,13 +122,13 @@ pub fn print_type_into_buf( | PolyNature::StructureGeneric { name, extends: _ } => { if debug { if let PolyNature::FunctionGeneric { .. } = nature { - write!(buf, "[fg {} {}] ", ty.0, name).unwrap(); + write!(buf, "(fg {} {}) ", ty.0, name).unwrap(); } } if let Some(arg) = args.and_then(|args| args.get_argument_covariant(ty)) { use crate::types::CovariantContribution; if debug { - buf.push_str(" (specialised with) "); + buf.push_str("(specialised with) "); } match arg { CovariantContribution::TypeId(id) => { @@ -159,7 +150,7 @@ pub fn print_type_into_buf( if let PolyNature::FunctionGeneric { extends, .. } = nature { print_type_into_buf(*extends, buf, cycles, args, types, info, debug); } else { - write!(buf, "[sg {}] ", ty.0).unwrap(); + write!(buf, "(sg {})", ty.0).unwrap(); } } buf.push_str(name); @@ -167,7 +158,7 @@ pub fn print_type_into_buf( } PolyNature::InferGeneric { name, extends } => { if debug { - write!(buf, "[IG {}] @ ", ty.0).unwrap(); + write!(buf, "(IG {}) @ ", ty.0).unwrap(); } buf.push_str("infer "); buf.push_str(name); @@ -179,19 +170,19 @@ pub fn print_type_into_buf( PolyNature::FreeVariable { based_on: to, .. } => { if debug { // FV = free variable - write!(buf, "[FV {}] @ ", ty.0).unwrap(); + write!(buf, "(FV {}) @ ", ty.0).unwrap(); } print_type_into_buf(*to, buf, cycles, args, types, info, debug); } PolyNature::Parameter { fixed_to: to } => { if debug { - write!(buf, "[param {}] @ ", ty.0).unwrap(); + write!(buf, "(param {}) @ ", ty.0).unwrap(); } print_type_into_buf(*to, buf, cycles, args, types, info, debug); } PolyNature::Open(to) => { if debug { - write!(buf, "[open {}] ", ty.0).unwrap(); + write!(buf, "(open {}) ", ty.0).unwrap(); } print_type_into_buf(*to, buf, cycles, args, types, info, debug); } @@ -212,31 +203,30 @@ pub fn print_type_into_buf( } PolyNature::CatchVariable(constraint) => { if debug { - write!(buf, "[catch variable {ty:?}] ").unwrap(); + write!(buf, "(CV {ty:?}) ").unwrap(); } print_type_into_buf(*constraint, buf, cycles, args, types, info, debug); } }, Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments }) => { - // TypeId::INCLUSIVE_RANGE | - if let TypeId::EXCLUSIVE_RANGE = *on { - // let inclusive = *on == TypeId::INCLUSIVE_RANGE; - let floor = - arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(); - let ceiling = - arguments.get_structure_restriction(TypeId::NUMBER_CEILING_GENERIC).unwrap(); - if let TypeId::NEG_INFINITY = floor { - buf.push_str("LessThan<"); - print_type_into_buf(ceiling, buf, cycles, args, types, info, debug); - buf.push('>'); - return; - } else if let TypeId::INFINITY = ceiling { - buf.push_str("GreaterThan<"); - print_type_into_buf(floor, buf, cycles, args, types, info, debug); - buf.push('>'); - return; - } - } + // if let TypeId::Great = *on { + // // let inclusive = *on == TypeId::INCLUSIVE_RANGE; + // let floor = + // arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + // let ceiling = + // arguments.get_structure_restriction(TypeId::NUMBER_CEILING_GENERIC).unwrap(); + // if let TypeId::NEG_INFINITY = floor { + // buf.push_str("LessThan<"); + // print_type_into_buf(ceiling, buf, cycles, args, types, info, debug); + // buf.push('>'); + // return; + // } else if let TypeId::INFINITY = ceiling { + // buf.push_str("GreaterThan<"); + // print_type_into_buf(floor, buf, cycles, args, types, info, debug); + // buf.push('>'); + // return; + // } + // } if debug { write!(buf, "SG({:?})(", ty.0).unwrap(); @@ -428,6 +418,18 @@ pub fn print_type_into_buf( print_type_into_buf(*result, buf, cycles, args, types, info, debug); } } + Constructor::BinaryOperator { + lhs: _, + operator: crate::features::operations::MathematicalOrBitwiseOperation::Modulo, + rhs, + result: _, + } if matches!(types.get_type_by_id(*rhs), Type::Constant(_)) => { + if let Type::Constant(crate::Constant::Number(num)) = types.get_type_by_id(*rhs) { + write!(buf, "ExclusiveRange<-{num}, {num}>").unwrap(); + } else { + unreachable!() + } + } constructor if debug => match constructor { Constructor::BinaryOperator { lhs, operator, rhs, result: _ } => { print_type_into_buf(*lhs, buf, cycles, args, types, info, debug); @@ -456,7 +458,7 @@ pub fn print_type_into_buf( } Constructor::Image { on: _, with: _, result } => { // TODO arguments - write!(buf, "[func result {}] (*args*)", ty.0).unwrap(); + write!(buf, "(func result {}) (*args*)", ty.0).unwrap(); buf.push_str(" -> "); print_type_into_buf(*result, buf, cycles, args, types, info, debug); } @@ -488,23 +490,26 @@ pub fn print_type_into_buf( } }, constructor => { - if let Constructor::BinaryOperator { result: result_ty, lhs, rhs, .. } = constructor + if let Constructor::BinaryOperator { result: TypeId::STRING_TYPE, .. } = constructor { - if *result_ty != TypeId::NUMBER_TYPE - && !matches!( - types.get_type_by_id(*result_ty), - Type::PartiallyAppliedGenerics(_) | Type::RootPolyType(_) - ) { + let slice = + crate::types::helpers::TemplatelLiteralExpansion::from_type(ty, types); + if let Some(single) = slice.as_single_string() { + buf.push('"'); + buf.push_str(single); + buf.push('"'); + } else { buf.push('`'); - print_inner_template_literal_type_into_buf( - *lhs, buf, cycles, args, types, info, debug, - ); - print_inner_template_literal_type_into_buf( - *rhs, buf, cycles, args, types, info, debug, - ); + for (s, ty) in &slice.parts { + buf.push_str(s); + buf.push_str("${"); + print_type_into_buf(*ty, buf, cycles, args, types, info, debug); + buf.push('}'); + } + buf.push_str(&slice.rest); buf.push('`'); - return; } + return; } let base = get_constraint(ty, types).unwrap(); print_type_into_buf(base, buf, cycles, args, types, info, debug); @@ -514,12 +519,12 @@ pub fn print_type_into_buf( | Type::Interface { name, parameters: _, .. } | Type::AliasTo { to: _, name, parameters: _ }) => { if debug { - write!(buf, "{name}#{}", ty.0).unwrap(); + write!(buf, "{name}#{} ", ty.0).unwrap(); if let Type::AliasTo { to, .. } = t { - buf.push_str(" = "); + buf.push_str("= "); print_type_into_buf(*to, buf, cycles, args, types, info, debug); } else if let Type::Class { .. } = t { - buf.push_str(" (class)"); + buf.push_str("(class)"); } } else { buf.push_str(name); @@ -567,7 +572,7 @@ pub fn print_type_into_buf( if debug { let kind = if matches!(r#type, Type::FunctionReference(_)) { "ref" } else { "" }; - write!(buf, "[func{kind} #{}, kind {:?}, effect ", ty.0, func.behavior).unwrap(); + write!(buf, "(func{kind} #{}, kind {:?}, effect ", ty.0, func.behavior).unwrap(); if let FunctionEffect::SideEffects { events: _, free_variables, @@ -585,7 +590,7 @@ pub fn print_type_into_buf( buf.push_str(", this "); print_type_into_buf(*p, buf, cycles, args, types, info, debug); } - buf.push_str("] = "); + buf.push_str(") = "); } if let Some(ref parameters) = func.type_parameters { buf.push('<'); @@ -639,9 +644,9 @@ pub fn print_type_into_buf( Type::Object(kind) => { if debug { if let ObjectNature::RealDeal = kind { - write!(buf, "[obj {}] ", ty.0).unwrap(); + write!(buf, "(obj {}) ", ty.0).unwrap(); } else { - write!(buf, "[aol {}] ", ty.0).unwrap(); + write!(buf, "(anom {}) ", ty.0).unwrap(); } } let prototype = @@ -655,8 +660,12 @@ pub fn print_type_into_buf( if i != 0 { buf.push_str(", "); } - let value = - get_simple_value(info, ty, &PropertyKey::from_usize(i), types); + let value = get_simple_property_value( + info, + ty, + &PropertyKey::from_usize(i), + types, + ); if let Some(value) = value { print_type_into_buf(value, buf, cycles, args, types, info, debug); diff --git a/checker/src/types/properties/access.rs b/checker/src/types/properties/access.rs index 84d52df1..6b17a5c7 100644 --- a/checker/src/types/properties/access.rs +++ b/checker/src/types/properties/access.rs @@ -10,7 +10,7 @@ use crate::{ contributions::CovariantContribution, generic_type_arguments::GenericArguments, }, get_constraint, - helpers::{get_conditional, is_inferrable_type, is_pseudo_continous}, + helpers::{get_type_as_conditional, is_inferrable_type, is_pseudo_continous}, logical::{ BasedOnKey, Invalid, Logical, LogicalOrValid, NeedsCalculation, PossibleLogical, PropertyOn, @@ -336,7 +336,8 @@ pub(crate) fn get_property_unbound( Type::Constructor(crate::types::Constructor::ConditionalResult { .. }) | Type::Or(..) => { let (condition, truthy_result, otherwise_result) = - get_conditional(on, types).expect("case above != get_conditional"); + get_type_as_conditional(on, types) + .expect("case above != get_type_as_conditional"); if require_both_logical { let left = get_property_on_type_unbound( @@ -372,14 +373,16 @@ pub(crate) fn get_property_unbound( types, ); if left.is_err() && right.is_err() { + crate::utilities::notify!( + "One side invalid {:?}", + (left.is_err(), right.is_err()) + ); Err(Invalid(on)) } else { - let left = left.unwrap_or(LogicalOrValid::Logical(Logical::Pure( - PropertyValue::Deleted, - ))); - let right = right.unwrap_or(LogicalOrValid::Logical(Logical::Pure( - PropertyValue::Deleted, - ))); + const DELETED_PROPERTY: LogicalOrValid = + LogicalOrValid::Logical(Logical::Pure(PropertyValue::Deleted)); + let left = left.unwrap_or(DELETED_PROPERTY); + let right = right.unwrap_or(DELETED_PROPERTY); Ok(Logical::Or { condition, left: Box::new(left), right: Box::new(right) } .into()) } @@ -421,7 +424,7 @@ pub(crate) fn get_property_unbound( } Type::Object(ObjectNature::RealDeal) => { let object_constraint_structure_generics = - crate::types::get_structure_arguments_based_on_object_constraint( + crate::types::helpers::get_structure_arguments_based_on_object_constraint( on, info_chain, types, ); @@ -430,17 +433,6 @@ pub(crate) fn get_property_unbound( .find_map(|facts| facts.prototypes.get(&on)) .copied(); - let generics = if let Some(generics) = object_constraint_structure_generics { - // TODO clone - Some(generics.clone()) - } else if prototype - .is_some_and(|prototype| types.lookup_generic_map.contains_key(&prototype)) - { - Some(GenericArguments::LookUp { on }) - } else { - None - }; - let on_self = resolver( (on, on_type_arguments), (publicity, under, under_type_arguments), @@ -448,31 +440,39 @@ pub(crate) fn get_property_unbound( types, ); - let result = if let (Some(prototype), None) = (prototype, &on_self) { - resolver( + if let Some(property) = on_self { + let generics = if let Some(generics) = object_constraint_structure_generics { + // TODO clone + Some(generics.clone()) + } else if prototype + .is_some_and(|prototype| types.lookup_generic_map.contains_key(&prototype)) + { + Some(GenericArguments::LookUp { on }) + } else { + None + }; + + let property = wrap(property); + let property = if let Some(ref generics) = generics { + // TODO clone + Logical::Implies { on: Box::new(property), antecedent: generics.clone() } + } else { + property + }; + Ok(LogicalOrValid::Logical(property)) + } else if let Some(prototype) = prototype { + crate::utilities::notify!("{:?}", types.get_type_by_id(prototype)); + + get_property_on_type_unbound( (prototype, on_type_arguments), (publicity, under, under_type_arguments), + require_both_logical, info_chain, types, ) } else { - on_self - }; - - // crate::utilities::notify!("result={:?}", result); - - result - .map(wrap) - .map(|result| { - if let Some(ref generics) = generics { - // TODO clone - Logical::Implies { on: Box::new(result), antecedent: generics.clone() } - } else { - result - } - }) - .map(LogicalOrValid::Logical) - .ok_or(Invalid(on)) + Err(Invalid(on)) + } } Type::Interface { extends, .. } => resolver( (on, on_type_arguments), @@ -543,14 +543,12 @@ pub(crate) fn get_property_unbound( } } Type::Constant(Constant::String(s)) if under.is_equal_to("length") => { - // TODO temp TypeId::NUMBER_FLOOR_GENERIC for slice member + // TODO temp TypeId::NUMBER_GENERIC for slice member let count = s.chars().count(); Ok(Logical::BasedOnKey(BasedOnKey::Left { - value: Box::new(Logical::Pure(PropertyValue::Value( - TypeId::NUMBER_FLOOR_GENERIC, - ))), + value: Box::new(Logical::Pure(PropertyValue::Value(TypeId::NUMBER_GENERIC))), key_arguments: crate::Map::from_iter([( - TypeId::NUMBER_FLOOR_GENERIC, + TypeId::NUMBER_GENERIC, (CovariantContribution::Number(count as f64), 0), )]), }) @@ -604,7 +602,9 @@ pub(crate) fn get_property_unbound( // if *key == TypeId::ERROR_TYPE { // return Err(MissingOrToCalculate::Error); // } else - if let Some((condition, truthy_result, otherwise_result)) = get_conditional(key, types) { + if let Some((condition, truthy_result, otherwise_result)) = + get_type_as_conditional(key, types) + { let left = get_property_unbound( (on, on_type_arguments), (publicity, &PropertyKey::Type(truthy_result), under_type_arguments), @@ -736,9 +736,9 @@ pub(crate) fn get_property( types, ); - { - crate::utilities::notify!("Access {:?} result {:?}", under, result); - } + // { + // crate::utilities::notify!("Access {:?} result {:?}", under, result); + // } match result { Ok(LogicalOrValid::Logical(logical)) => { @@ -1096,7 +1096,7 @@ fn resolve_property_on_logical( ) } BasedOnKey::Right(property_on) => { - crate::utilities::notify!("{:?}", generics); + crate::utilities::notify!("generics are {:?}", generics); let result = property_on.get_on(generics, environment, types)?; // { diff --git a/checker/src/types/properties/assignment.rs b/checker/src/types/properties/assignment.rs index 31538e68..14eb510a 100644 --- a/checker/src/types/properties/assignment.rs +++ b/checker/src/types/properties/assignment.rs @@ -9,8 +9,9 @@ use crate::{ types::{ calling::{CallingDiagnostics, CallingOutput, SynthesisedArgument}, get_constraint, + helpers::tuple_like, logical::{BasedOnKey, Logical, LogicalOrValid, NeedsCalculation}, - tuple_like, Constructor, GenericChain, PartiallyAppliedGenerics, TypeStore, + Constructor, GenericChain, PartiallyAppliedGenerics, TypeStore, }, Environment, Type, TypeId, }; @@ -129,7 +130,10 @@ pub fn set_property( environment, types, ); + if let SubTypeResult::IsNotSubType(reason) = result { + crate::utilities::notify!("Here {:?} {:?}", property_constraint, new); + let is_modifying_tuple_length = under.is_equal_to("length") && tuple_like(object_constraint, types, environment); @@ -203,7 +207,7 @@ pub fn set_property( result_union: _, }) = types.get_type_by_id(on) { - crate::utilities::notify!("Here"); + crate::utilities::notify!("Cascading assigment bc of conditional result"); let truthy = *truthy_result; let otherwise_result = *otherwise_result; @@ -226,6 +230,13 @@ pub fn set_property( ); } + // Important that this goes below the condition above + let on = if let Type::Narrowed { narrowed_to, .. } = types.get_type_by_id(on) { + *narrowed_to + } else { + on + }; + // IMPORTANT: THIS ALSO CAPTURES POLY CONSTRAINTS let current_property = get_property_unbound((on, None), (publicity, under, None), false, environment, types); @@ -263,12 +274,13 @@ pub fn set_property( types, ), } - } else if get_constraint(on, types).is_some() { - Err(SetPropertyError::AssigningToNonExistent { - property: PropertyKeyRepresentation::new(under, environment, types), - position, - }) } else { + if get_constraint(on, types).is_some() { + return Err(SetPropertyError::AssigningToNonExistent { + property: PropertyKeyRepresentation::new(under, environment, types), + position, + }); + } // Sealed & no extensions check for NEW property (frozen case covered above) { if object_protection.is_some() { diff --git a/checker/src/types/properties/mod.rs b/checker/src/types/properties/mod.rs index dd608419..804c740b 100644 --- a/checker/src/types/properties/mod.rs +++ b/checker/src/types/properties/mod.rs @@ -186,7 +186,7 @@ impl<'a> PropertyKey<'a> { } /// For getting `length` and stuff -pub(crate) fn get_simple_value( +pub(crate) fn get_simple_property_value( ctx: &impl InformationChain, on: TypeId, property: &PropertyKey, @@ -508,10 +508,10 @@ pub(crate) fn key_matches( ); let contributions = state.contributions.unwrap(); - crate::utilities::notify!( - "Here contributions {:?}", - &contributions.staging_contravariant - ); + // crate::utilities::notify!( + // "Here contributions {:?}", + // &contributions.staging_contravariant + // ); (result.is_subtype(), contributions.staging_contravariant) } diff --git a/checker/src/types/store.rs b/checker/src/types/store.rs index f6bcb3bb..263ced34 100644 --- a/checker/src/types/store.rs +++ b/checker/src/types/store.rs @@ -148,30 +148,20 @@ impl Default for TypeStore { name: "T".into(), extends: TypeId::NUMBER_TYPE, }), - Type::RootPolyType(PolyNature::StructureGeneric { - name: "U".into(), - extends: TypeId::NUMBER_TYPE, - }), Type::AliasTo { + name: "GreaterThan".into(), + parameters: Some(vec![TypeId::NUMBER_GENERIC]), to: TypeId::NUMBER_TYPE, - name: "InclusiveRange".into(), - parameters: Some(vec![ - TypeId::NUMBER_FLOOR_GENERIC, - TypeId::NUMBER_CEILING_GENERIC, - ]), }, Type::AliasTo { + name: "LessThan".into(), + parameters: Some(vec![TypeId::NUMBER_GENERIC]), to: TypeId::NUMBER_TYPE, - name: "ExclusiveRange".into(), - parameters: Some(vec![ - TypeId::NUMBER_FLOOR_GENERIC, - TypeId::NUMBER_CEILING_GENERIC, - ]), }, Type::AliasTo { - to: TypeId::NUMBER_TYPE, name: "MultipleOf".into(), - parameters: Some(vec![TypeId::NUMBER_FLOOR_GENERIC]), + parameters: Some(vec![TypeId::NUMBER_GENERIC]), + to: TypeId::NUMBER_TYPE, }, // Intermediate for the below Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { @@ -182,26 +172,25 @@ impl Default for TypeStore { )])), }), Type::And(TypeId::NUMBER_TYPE, TypeId::NOT_NOT_A_NUMBER), - // TODO WIP Type::AliasTo { name: "Literal".into(), - to: TypeId::T_TYPE, parameters: Some(vec![TypeId::T_TYPE]), + to: TypeId::T_TYPE, }, Type::AliasTo { name: "Exclusive".into(), - to: TypeId::T_TYPE, parameters: Some(vec![TypeId::T_TYPE]), + to: TypeId::T_TYPE, }, Type::AliasTo { name: "Not".into(), - to: TypeId::ANY_TYPE, parameters: Some(vec![TypeId::T_TYPE]), + to: TypeId::ANY_TYPE, }, Type::AliasTo { name: "CaseInsensitive".into(), - to: TypeId::STRING_TYPE, parameters: Some(vec![TypeId::STRING_GENERIC]), + to: TypeId::STRING_TYPE, }, Type::RootPolyType(PolyNature::Open(TypeId::BOOLEAN_TYPE)), Type::RootPolyType(PolyNature::Open(TypeId::NUMBER_TYPE)), @@ -302,46 +291,39 @@ impl TypeStore { iter.into_iter().reduce(|acc, n| self.new_or_type(acc, n)).unwrap_or(TypeId::NEVER_TYPE) } - pub fn new_and_type(&mut self, lhs: TypeId, rhs: TypeId) -> Result { + // intersection. Does not calculate disjoint + pub fn new_and_type(&mut self, lhs: TypeId, rhs: TypeId) -> TypeId { + // string & string = string if lhs == rhs { - return Ok(lhs); - } - - let left_ty = self.get_type_by_id(lhs); - let right_ty = self.get_type_by_id(rhs); - - // TODO more cases - if let (Type::Constant(l), Type::Constant(r)) = (left_ty, right_ty) { - if l != r { - return Err(()); - } - } else if left_ty.is_nominal() && right_ty.is_nominal() { - return Err(()); + return lhs; } - // (left and right) distributivity. - let result = if let Type::Or(or_lhs, or_rhs) = left_ty { + // (left and right) distributivity & some other reductions on singleton types bc why not + // TODO sort intrinsics? + let lhs_type = self.get_type_by_id(lhs); + let rhs_type = self.get_type_by_id(rhs); + if let Type::Or(or_lhs, or_rhs) = lhs_type { let (or_lhs, or_rhs) = (*or_lhs, *or_rhs); - let new_lhs = self.new_and_type(or_lhs, rhs)?; - let new_rhs = self.new_and_type(or_rhs, rhs)?; + let new_lhs = self.new_and_type(or_lhs, rhs); + let new_rhs = self.new_and_type(or_rhs, rhs); self.new_or_type(new_lhs, new_rhs) - } else if let Type::Or(or_lhs, or_rhs) = right_ty { + } else if let Type::Or(or_lhs, or_rhs) = rhs_type { let (or_lhs, or_rhs) = (*or_lhs, *or_rhs); - let new_lhs = self.new_and_type(lhs, or_lhs)?; - let new_rhs = self.new_and_type(lhs, or_rhs)?; + let new_lhs = self.new_and_type(lhs, or_lhs); + let new_rhs = self.new_and_type(lhs, or_rhs); self.new_or_type(new_lhs, new_rhs) + } else if let Type::Constant(_) = lhs_type { + lhs + } else if let Type::Constant(_) = rhs_type { + rhs + } else if let Type::And(rhs_lhs, rhs_rhs) = rhs_type { + let (rhs_lhs, rhs_rhs) = (*rhs_lhs, *rhs_rhs); + let lhs = self.new_and_type(lhs, rhs_lhs); + self.new_and_type(lhs, rhs_rhs) } else { let ty = Type::And(lhs, rhs); self.register_type(ty) - }; - - Ok(result) - } - - /// TODO temp - #[must_use] - pub fn into_vec_temp(self) -> Vec<(TypeId, Type)> { - self.types.into_iter().enumerate().map(|(idx, ty)| (TypeId(idx as u16), ty)).collect() + } } /// From something like: let a: number => string. Rather than a actual function @@ -554,7 +536,34 @@ impl TypeStore { self.register_type(Type::RootPolyType(PolyNature::Open(base))) } + /// Will provide origin rewriting as well pub fn new_narrowed(&mut self, from: TypeId, narrowed_to: TypeId) -> TypeId { + let from_ty = self.get_type_by_id(from); + let new_constraint = self.get_type_by_id(narrowed_to); + let (from, existing) = if let Type::Narrowed { from, narrowed_to } = from_ty { + (*from, Some(*narrowed_to)) + } else { + (from, None) + }; + // temp fix for adding things. + let narrowed_to = if let ( + Some(existing), + Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: + TypeId::GREATER_THAN + | TypeId::LESS_THAN + | TypeId::MULTIPLE_OF + | TypeId::NOT_RESTRICTION, + arguments: _, + }), + ) = (existing, new_constraint) + { + self.new_and_type(existing, narrowed_to) + } else { + // crate::utilities::notify!("{:?}", from_ty); + narrowed_to + }; + self.register_type(Type::Narrowed { from, narrowed_to }) } @@ -633,4 +642,13 @@ impl TypeStore { pub(crate) fn new_key_of(&mut self, of: TypeId) -> TypeId { self.register_type(Type::Constructor(Constructor::KeyOf(of))) } + + /// TODO temp for debugging + pub fn user_types(&self) -> impl Iterator + '_ { + self.types + .iter() + .enumerate() + .skip(TypeId::INTERNAL_TYPE_COUNT) + .map(|(idx, ty)| (TypeId(idx as u16), ty)) + } } diff --git a/checker/src/types/subtyping.rs b/checker/src/types/subtyping.rs index b1ae45e0..1fe9fa0c 100644 --- a/checker/src/types/subtyping.rs +++ b/checker/src/types/subtyping.rs @@ -6,7 +6,7 @@ use crate::{ context::{GeneralContext, InformationChain}, features::{ objects::{self, SpecialObject}, - operations::MathematicalAndBitwise, + operations::MathematicalOrBitwiseOperation, }, Constant, Environment, PropertyValue, TypeId, }; @@ -267,12 +267,11 @@ pub(crate) fn type_is_subtype_with_generics( types, ); // Temp fix for narrowing constants - crate::utilities::notify!("{:?}", super::helpers::is_not_of_constant(*right, types)); + // crate::utilities::notify!("{:?}", super::helpers::is_not_of_constant(*right, types)); // SubTypeResult::IsNotSubType(_) return if let (Type::Narrowed { from, .. }, _, true) = (subtype, &result, super::helpers::is_not_of_constant(*right, types)) { - crate::utilities::notify!("Here"); type_is_subtype_with_generics( (base_type, base_type_arguments), (*from, ty_structure_arguments), @@ -308,61 +307,15 @@ pub(crate) fn type_is_subtype_with_generics( left_result }; } - // Type::And(left, right) => { - // let left_is_operator_right_is_not = - // supertype.is_operator(); - - // // edge cases on edge cases - // // If any of these are true. Then do not perform constraint argument lookup - // let edge_case = left_is_operator_right_is_not; - - // if !edge_case { - - // let right = *right; - // let left_result = type_is_subtype_with_generics( - // (base_type, base_type_arguments), - // (*left, ty_structure_arguments), - // state, - // information, - // types, - // ); - - // return if let SubTypeResult::IsSubType = left_result { - // left_result - // } else { - // type_is_subtype_with_generics( - // (base_type, base_type_arguments), - // (right, ty_structure_arguments), - // state, - // information, - // types, - // ) - // }; - // } - // } - // TODO others Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on: on @ TypeId::NOT_RESTRICTION, - arguments, + arguments: _, }) => { match *on { TypeId::NOT_RESTRICTION => { - let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); - // https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.subset_compl_comm -> https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.subset_compl_iff_disjoint_left - - let result = super::disjoint::types_are_disjoint( - base_type, - inner, - &mut state.already_checked, - information, - types, - ); - // crate::utilities::notify!("Here {:?}", (&result, inner)); - return if result { - SubTypeResult::IsSubType - } else { - SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) - }; + // This only happens when subtype ∪ supertype = `any`. This is only true when + // one is `any`. `Not` is already `never` and `supertype = any` is handled above + return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch); } _ => unreachable!(), } @@ -378,7 +331,7 @@ pub(crate) fn type_is_subtype_with_generics( }; } - crate::utilities::notify!("Looking for {:?} with {:?}", ty, ty_structure_arguments); + // crate::utilities::notify!("Looking for {:?} with {:?}", ty, ty_structure_arguments); if let Some(arg) = ty_structure_arguments.and_then(|tas| tas.get_argument_covariant(ty)) { @@ -438,20 +391,20 @@ pub(crate) fn type_is_subtype_with_generics( // If lhs is not operator unless argument is operator // if !T::INFER_GENERICS && ty_structure_arguments.is_none() { - let right_arg = get_constraint(ty, types).unwrap(); + let right_constraint = get_constraint(ty, types).unwrap(); - crate::utilities::notify!( - "edge case {:?}", - ( - types.get_type_by_id(ty), - types.get_type_by_id(right_arg), - types.get_type_by_id(right_arg).is_operator() - ) - ); + // crate::utilities::notify!( + // "RHS is parameter, edge case results to {:?}", + // ( + // types.get_type_by_id(ty), + // types.get_type_by_id(right_constraint), + // types.get_type_by_id(right_constraint).is_operator() + // ) + // ); // This is important that LHS is not operator let left_is_operator_right_is_not = - supertype.is_operator() && !types.get_type_by_id(right_arg).is_operator(); + supertype.is_operator() && !types.get_type_by_id(right_constraint).is_operator(); // edge cases on edge cases // If any of these are true. Then do not perform constraint argument lookup @@ -460,12 +413,14 @@ pub(crate) fn type_is_subtype_with_generics( supertype, Type::RootPolyType(rpt) if rpt.is_substitutable() - ) || matches!(supertype, Type::Constructor(..)); - + ) || matches!(supertype, Type::Constructor(..)) + || base_type_arguments + .and_then(|args| args.get_argument_covariant(base_type)) + .is_some(); if !edge_case { let result = type_is_subtype_with_generics( (base_type, base_type_arguments), - (right_arg, ty_structure_arguments), + (right_constraint, ty_structure_arguments), state, information, types, @@ -505,7 +460,7 @@ pub(crate) fn type_is_subtype_with_generics( } } else { // TODO what about if LHS has inferred constraint - crate::utilities::notify!("Constant {:?} against RHS {:#?}", lhs, subtype); + // crate::utilities::notify!("Constant {:?} against RHS {:#?}", lhs, subtype); SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } } @@ -692,10 +647,8 @@ pub(crate) fn type_is_subtype_with_generics( // TODO temp fix if let Type::Constructor(c) = subtype { crate::utilities::notify!("TODO right hand side maybe okay"); - if let Some(to) = c.get_base() { - if to == base_type { - return SubTypeResult::IsSubType; - } + if c.get_constraint() == base_type { + return SubTypeResult::IsSubType; } } if let PolyNature::FunctionGeneric { .. } = nature { @@ -845,10 +798,13 @@ pub(crate) fn type_is_subtype_with_generics( // Ezno intrinsic TypeId::LITERAL_RESTRICTION => { let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); - return if let Type::Constant(rhs_constant) = subtype { + return if let Type::Constant(_) + | Type::Object(ObjectNature::RealDeal) + | Type::SpecialObject(..) = subtype + { type_is_subtype_with_generics( (inner, base_type_arguments), - (rhs_constant.get_backing_type(), ty_structure_arguments), + (ty, ty_structure_arguments), state, information, types, @@ -881,7 +837,7 @@ pub(crate) fn type_is_subtype_with_generics( } TypeId::MULTIPLE_OF => { let argument = - arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(); + arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); let right_multiple = crate::types::intrinsics::get_multiple(ty, types); return if let ( @@ -901,18 +857,93 @@ pub(crate) fn type_is_subtype_with_generics( SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) }; } - TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE => { - return if let (super_range, Some(sub_range)) = ( - intrinsics::get_range(base_type, types).unwrap(), - intrinsics::get_range(ty, types), - ) { - if sub_range.contained_in(super_range) { + TypeId::GREATER_THAN => { + let argument = + arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + let argument_type = types.get_type_by_id(argument); + return if let ( + Type::Constant(Constant::Number(value)), + Type::Constant(Constant::Number(subtype_number)), + ) = (argument_type, subtype) + { + if subtype_number > value { + SubTypeResult::IsSubType + } else { + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + } + } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::GREATER_THAN, + arguments, + }) = subtype + { + let subtype_argument = + arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + // Transitivity + if argument == subtype_argument { + SubTypeResult::IsSubType + } else { + let subtype_argument = types.get_type_by_id(subtype_argument); + if let ( + Type::Constant(Constant::Number(subtype_number)), + Type::Constant(Constant::Number(value)), + ) = (argument_type, subtype_argument) + { + if subtype_number > value { + SubTypeResult::IsSubType + } else { + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + } + } else { + crate::utilities::notify!("Here"); + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + } + } + } else { + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + }; + } + TypeId::LESS_THAN => { + let argument = + arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + let argument_type = types.get_type_by_id(argument); + return if let ( + Type::Constant(Constant::Number(value)), + Type::Constant(Constant::Number(subtype_number)), + ) = (argument_type, subtype) + { + if subtype_number < value { SubTypeResult::IsSubType } else { SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } + } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::GREATER_THAN, + arguments, + }) = subtype + { + let subtype_argument = + arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + // Transitivity + if argument == subtype_argument { + SubTypeResult::IsSubType + } else { + let subtype_argument = types.get_type_by_id(subtype_argument); + if let ( + Type::Constant(Constant::Number(subtype_number)), + Type::Constant(Constant::Number(value)), + ) = (argument_type, subtype_argument) + { + if subtype_number < value { + SubTypeResult::IsSubType + } else { + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + } + } else { + crate::utilities::notify!("Here"); + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + } + } } else { - crate::utilities::notify!("TODO"); SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) }; } @@ -1113,7 +1144,8 @@ pub(crate) fn type_is_subtype_with_generics( Type::Constructor(cst) => match cst { // For template literal types Constructor::BinaryOperator { - operator: crate::types::MathematicalAndBitwise::Add, + operator: crate::types::MathematicalOrBitwiseOperation::Add, + result: TypeId::STRING_TYPE, .. } => { if let Type::Constant(Constant::String(rs)) = subtype { @@ -1131,17 +1163,26 @@ pub(crate) fn type_is_subtype_with_generics( // TODO clear contributions SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } + } else if let Type::Constructor(Constructor::BinaryOperator { + operator: crate::types::MathematicalOrBitwiseOperation::Add, + result: TypeId::STRING_TYPE, + .. + }) = subtype + { + crate::utilities::notify!("TODO test prefixes"); + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } else { - crate::utilities::notify!("RHS not string"); - type_is_subtype_with_generics( - (TypeId::NUMBER_TYPE, base_type_arguments), - (ty, ty_structure_arguments), - state, - information, - types, - ) + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } } + Constructor::BinaryOperator { + operator: crate::types::MathematicalOrBitwiseOperation::Add, + result: TypeId::NUMBER_TYPE, + .. + } => { + crate::utilities::notify!("TODO here!"); + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + } Constructor::BinaryOperator { .. } | Constructor::CanonicalRelationOperator { .. } => { unreachable!("invalid constructor on LHS") } @@ -1415,9 +1456,15 @@ pub(crate) fn type_is_subtype_with_generics( if *prototype == base_type { SubTypeResult::IsSubType } else { + crate::utilities::notify!( + "Mismatched prototype {:?} != {:?}", + prototype, + base_type + ); SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } } else { + crate::utilities::notify!("No prototype"); SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } } @@ -1433,13 +1480,26 @@ pub(crate) fn type_is_subtype_with_generics( types, ) } - Type::And(a, b) => { - // TODO more - crate::utilities::notify!("Here LHS class, RHS and"); - if *a == base_type || *b == base_type { - SubTypeResult::IsSubType + Type::And(left, right) => { + // This only happens in predicate edge cases (with numbers) + let left_result = type_is_subtype_with_generics( + (base_type, base_type_arguments), + (*left, ty_structure_arguments), + state, + information, + types, + ); + + if let SubTypeResult::IsSubType = left_result { + left_result } else { - SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + type_is_subtype_with_generics( + (base_type, base_type_arguments), + (*right, ty_structure_arguments), + state, + information, + types, + ) } } Type::SpecialObject(SpecialObject::Function(..)) | Type::FunctionReference(..) @@ -1447,8 +1507,8 @@ pub(crate) fn type_is_subtype_with_generics( { SubTypeResult::IsSubType } - ty => { - crate::utilities::notify!("Does {:?} not match class", ty); + _ty => { + // crate::utilities::notify!("{:?} does not match class", base_type); SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } }, @@ -2588,7 +2648,7 @@ pub(crate) fn slice_matches_type( allow_casts, ) } else if let Some(contributions) = contributions { - assert!(rpt.is_substitutable()); + // assert!(rpt.is_substitutable(), "{:?}", rpt); let constraint = rpt.get_constraint(); let res = slice_matches_type( (constraint, base_type_arguments), @@ -2735,23 +2795,6 @@ pub(crate) fn slice_matches_type( allow_casts, ) } - Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::MULTIPLE_OF | TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, - arguments: _, - }) if allow_casts => { - // Special behavior here to treat numerical property keys (which are strings) as numbers - if let Ok(value) = slice.parse::() { - number_matches_type( - (base, base_type_arguments), - value, - contributions, - information, - types, - ) - } else { - false - } - } Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on: TypeId::NOT_RESTRICTION, arguments, @@ -2770,6 +2813,22 @@ pub(crate) fn slice_matches_type( // crate::utilities::notify!("negated slice arguments={:?}", _k); !matches } + Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments: _ }) + if allow_casts && intrinsics::is_ezno_number_intrinsic(*on) => + { + // Special behavior here to treat numerical property keys (which are strings) as numbers + if let Ok(value) = slice.parse::() { + number_matches_type( + (base, base_type_arguments), + value, + contributions, + information, + types, + ) + } else { + false + } + } Type::Constructor(super::Constructor::KeyOf(on)) => { let argument = (Publicity::Public, &PropertyKey::String(std::borrow::Cow::Borrowed(slice)), None); @@ -2836,10 +2895,18 @@ pub(crate) fn slice_matches_type( false } } + Type::Constant(Constant::Number(base)) => { + crate::utilities::notify!("Here"); + if let Ok(slice_as_float) = slice.parse::() { + *base == slice_as_float + } else { + false + } + } Type::Constructor(super::Constructor::BinaryOperator { lhs, rhs, - operator: MathematicalAndBitwise::Add, + operator: MathematicalOrBitwiseOperation::Add, result: _, }) => { let lhs = base_type_arguments @@ -2922,8 +2989,7 @@ pub(crate) fn number_matches_type( on: TypeId::MULTIPLE_OF, arguments, }) => { - let argument = - arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(); + let argument = arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); if let Type::Constant(Constant::Number(argument)) = types.get_type_by_id(argument) { let number: ordered_float::NotNan = number.try_into().unwrap(); (number % argument) == 0. @@ -2933,11 +2999,20 @@ pub(crate) fn number_matches_type( } } Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, + on: TypeId::LESS_THAN, + arguments: _, + }) => { + todo!() + // let lhs_range = intrinsics::get_range(base, types).unwrap(); + // intrinsics::FloatRange::new_single(number.try_into().unwrap()).contained_in(lhs_range) + } + Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::GREATER_THAN, arguments: _, }) => { - let lhs_range = intrinsics::get_range(base, types).unwrap(); - intrinsics::FloatRange::single(number.try_into().unwrap()).contained_in(lhs_range) + todo!() + // let lhs_range = intrinsics::get_range(base, types).unwrap(); + // intrinsics::FloatRange::new_single(number.try_into().unwrap()).contained_in(lhs_range) } Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on: TypeId::NOT_RESTRICTION, diff --git a/checker/src/utilities/debugging.rs b/checker/src/utilities/debugging.rs index 6ef42907..2a19cee2 100644 --- a/checker/src/utilities/debugging.rs +++ b/checker/src/utilities/debugging.rs @@ -17,7 +17,7 @@ pub(crate) fn is_debug_mode() -> bool { value } else { let new_value = std::env::var("EZNO_DEBUG") - .map(|value| !value.is_empty() || value == "0") + .map(|value| !(value.is_empty() || value == "0")) .unwrap_or_default(); IS_DEBUG_MODE.set(Some(new_value)); new_value diff --git a/checker/src/utilities/float_range.rs b/checker/src/utilities/float_range.rs index 133e226e..c0870212 100644 --- a/checker/src/utilities/float_range.rs +++ b/checker/src/utilities/float_range.rs @@ -1,18 +1,97 @@ type BetterF64 = ordered_float::NotNan; -// TODO #[derive(Debug, Clone, Copy, PartialEq)] -pub enum FloatRange { - /// yes or `===` - Inclusive { floor: BetterF64, ceiling: BetterF64 }, - /// but not necessarily `===` - Exclusive { floor: BetterF64, ceiling: BetterF64 }, +pub enum InclusiveExclusive { + Inclusive, + Exclusive, } +use InclusiveExclusive::{Exclusive, Inclusive}; + +impl InclusiveExclusive { + #[must_use] + pub fn mix(self, other: Self) -> Self { + if let (Inclusive, Inclusive) = (self, other) { + Inclusive + } else { + Exclusive + } + } + + #[must_use] + pub fn is_inclusive(self) -> bool { + matches!(self, Inclusive) + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct FloatRange { + pub floor: (InclusiveExclusive, BetterF64), + pub ceiling: (InclusiveExclusive, BetterF64), +} + +impl Default for FloatRange { + fn default() -> Self { + Self { + floor: (Exclusive, f64::NEG_INFINITY.try_into().unwrap()), + ceiling: (Exclusive, f64::INFINITY.try_into().unwrap()), + } + } +} + +// TODO try_from (assert ceiling > floor etc) impl FloatRange { #[must_use] - pub fn single(on: BetterF64) -> Self { - Self::Inclusive { floor: on, ceiling: on } + pub fn new_single(on: BetterF64) -> Self { + Self { floor: (Inclusive, on), ceiling: (Inclusive, on) } + } + + #[must_use] + pub fn as_single(self) -> Option { + if let FloatRange { floor: (Inclusive, floor), ceiling: (Inclusive, ceiling) } = self { + (floor == ceiling).then_some(floor) + } else { + None + } + } + + #[must_use] + pub fn new_greater_than(greater_than: BetterF64) -> Self { + FloatRange { + floor: (Exclusive, greater_than), + ceiling: (Exclusive, f64::INFINITY.try_into().unwrap()), + } + } + + #[must_use] + pub fn get_greater_than(self) -> Option { + (self.floor.1 != f64::NEG_INFINITY).then_some(self.floor.1) + } + + #[must_use] + pub fn new_less_than(less_than: BetterF64) -> Self { + FloatRange { + floor: (Exclusive, f64::NEG_INFINITY.try_into().unwrap()), + ceiling: (Exclusive, less_than), + } + } + + #[must_use] + pub fn get_less_than(self) -> Option { + (self.ceiling.1 != f64::INFINITY).then_some(self.ceiling.1) + } + + #[must_use] + pub fn contains(self, value: BetterF64) -> bool { + if self.floor.1 < value && value < self.ceiling.1 { + true + } else if self.floor.1 == value { + self.floor.0.is_inclusive() + } else if self.ceiling.1 == value { + self.ceiling.0.is_inclusive() + } else { + false + } } /// For disjointness. TODO Think this is correct @@ -20,30 +99,23 @@ impl FloatRange { pub fn overlaps(self, other: Self) -> bool { crate::utilities::notify!("{:?} ∩ {:?} != ∅", self, other); - if let ( - Self::Inclusive { floor: l_floor, ceiling: l_ceiling }, - Self::Inclusive { floor: r_floor, ceiling: r_ceiling }, - ) = (self, other) - { - if l_floor <= r_floor { - l_ceiling >= r_floor - } else if l_ceiling >= r_ceiling { - l_floor <= r_ceiling - } else { - false - } + // TODO more with inclusivity etc + other.floor.1 <= self.ceiling.1 || other.ceiling.1 <= self.floor.1 + } + + pub fn intersection(self, other: Self) -> Result { + crate::utilities::notify!("{:?} ∩ {:?}", self, other); + + let max_floor = self.floor.1.max(other.floor.1); + let min_ceiling = self.ceiling.1.min(other.ceiling.1); + + if max_floor <= min_ceiling { + Ok(Self { + floor: (self.floor.0.mix(other.floor.0), max_floor), + ceiling: (self.ceiling.0.mix(other.ceiling.0), min_ceiling), + }) } else { - let (Self::Inclusive { floor: l_floor, ceiling: l_ceiling } - | Self::Exclusive { floor: l_floor, ceiling: l_ceiling }) = self; - let (Self::Inclusive { floor: r_floor, ceiling: r_ceiling } - | Self::Exclusive { floor: r_floor, ceiling: r_ceiling }) = other; - if l_floor < r_floor { - l_ceiling > r_floor - } else if l_ceiling > r_ceiling { - l_floor < r_ceiling - } else { - false - } + Err(()) } } @@ -52,37 +124,27 @@ impl FloatRange { pub fn contained_in(self, other: Self) -> bool { crate::utilities::notify!("{:?} ⊆ {:?}", self, other); // Edge case - if let ( - Self::Inclusive { floor: l_floor, ceiling: l_ceiling }, - Self::Exclusive { floor: r_floor, ceiling: r_ceiling }, - ) = (self, other) - { - l_floor > r_floor && l_ceiling < r_ceiling + let lhs = if let (Inclusive, Exclusive) = (self.floor.0, other.floor.0) { + self.floor.1 > other.floor.1 } else { - let (Self::Inclusive { floor: l_floor, ceiling: l_ceiling } - | Self::Exclusive { floor: l_floor, ceiling: l_ceiling }) = self; - let (Self::Inclusive { floor: r_floor, ceiling: r_ceiling } - | Self::Exclusive { floor: r_floor, ceiling: r_ceiling }) = other; - l_floor >= r_floor && l_ceiling <= r_ceiling - } + self.floor.1 >= other.floor.1 + }; + let rhs = if let (Inclusive, Exclusive) = (self.ceiling.0, other.ceiling.0) { + self.ceiling.1 < other.ceiling.1 + } else { + self.ceiling.1 <= other.ceiling.1 + }; + lhs && rhs } /// ∀ a in self, ∀ b in other: a > b #[must_use] pub fn above(self, other: Self) -> bool { crate::utilities::notify!("{:?} > {:?}", self, other); - if let ( - Self::Inclusive { floor: l_floor, ceiling: _ }, - Self::Inclusive { floor: _, ceiling: r_ceiling }, - ) = (self, other) - { - l_floor > r_ceiling + if let (Inclusive, Inclusive) = (self.ceiling.0, other.floor.0) { + self.floor.1 > other.ceiling.1 } else { - let (Self::Inclusive { floor: l_floor, ceiling: _ } - | Self::Exclusive { floor: l_floor, ceiling: _ }) = self; - let (Self::Inclusive { floor: _, ceiling: r_ceiling } - | Self::Exclusive { floor: _, ceiling: r_ceiling }) = other; - l_floor >= r_ceiling + self.floor.1 >= other.ceiling.1 } } @@ -90,118 +152,147 @@ impl FloatRange { #[must_use] pub fn below(self, other: Self) -> bool { crate::utilities::notify!("{:?} < {:?}", self, other); - if let ( - Self::Inclusive { floor: _, ceiling: l_ceiling }, - Self::Inclusive { floor: r_floor, ceiling: _ }, - ) = (self, other) - { - l_ceiling < r_floor + if let (Inclusive, Inclusive) = (self.ceiling.0, other.floor.0) { + self.ceiling.1 < other.floor.1 } else { - let (Self::Inclusive { floor: _, ceiling: l_ceiling } - | Self::Exclusive { floor: _, ceiling: l_ceiling }) = self; - let (Self::Inclusive { floor: r_floor, ceiling: _ } - | Self::Exclusive { floor: r_floor, ceiling: _ }) = other; - l_ceiling <= r_floor + self.ceiling.1 <= other.floor.1 } } #[must_use] pub fn space_addition(self, other: Self) -> Self { - if let ( - Self::Inclusive { floor: l_floor, ceiling: l_ceiling }, - Self::Inclusive { floor: r_floor, ceiling: r_ceiling }, - ) = (self, other) - { - Self::Inclusive { floor: l_floor + r_floor, ceiling: l_ceiling + r_ceiling } - } else { - let (Self::Inclusive { floor: l_floor, ceiling: l_ceiling } - | Self::Exclusive { floor: l_floor, ceiling: l_ceiling }) = self; - let (Self::Inclusive { floor: r_floor, ceiling: r_ceiling } - | Self::Exclusive { floor: r_floor, ceiling: r_ceiling }) = other; - Self::Exclusive { floor: l_floor + r_floor, ceiling: l_ceiling + r_ceiling } + let floor_bound = self.floor.0.mix(other.floor.0); + let ceiling_bound = self.ceiling.0.mix(other.ceiling.0); + Self { + floor: (floor_bound, self.floor.1 + other.floor.1), + ceiling: (ceiling_bound, self.ceiling.1 + other.ceiling.1), } } #[must_use] pub fn space_multiplication(self, other: Self) -> Self { - let inclusive = matches!((self, other), (Self::Inclusive { .. }, Self::Inclusive { .. })); - let (Self::Inclusive { floor: l_floor, ceiling: l_ceiling } - | Self::Exclusive { floor: l_floor, ceiling: l_ceiling }) = self; - let (Self::Inclusive { floor: r_floor, ceiling: r_ceiling } - | Self::Exclusive { floor: r_floor, ceiling: r_ceiling }) = other; - // being lazy + let (l_floor, l_ceiling, r_floor, r_ceiling) = + (self.floor.1, self.ceiling.1, other.floor.1, other.ceiling.1); + // there may be a faster way but being lazy let corners = [l_floor * r_floor, l_floor * r_ceiling, r_floor * l_ceiling, l_ceiling * r_ceiling]; let floor = *corners.iter().min().unwrap(); let ceiling = *corners.iter().max().unwrap(); - if inclusive { - Self::Inclusive { floor, ceiling } + + let floor_bound = self.floor.0.mix(other.floor.0); + let ceiling_bound = self.ceiling.0.mix(other.ceiling.0); + Self { floor: (floor_bound, floor), ceiling: (ceiling_bound, ceiling) } + } + + #[must_use] + pub fn contains_multiple_of(self, multiple_of: BetterF64) -> bool { + let (floor, ceiling) = (self.floor.1, self.ceiling.1); + + let floor = floor / multiple_of; + let ceiling = ceiling / multiple_of; + + // TODO >= ? + ceiling.floor() > *floor + } + + // This will try to get cover + // A union like above might create gaps. aka if try_get_cover (0, 1) (3, 4) = (0, 4) then it implies 2 + // exists is in one of the ranges. Thus in this case it returns None + #[must_use] + pub fn try_get_cover(self, other: Self) -> Option { + if self.contained_in(other) { + Some(other) + } else if other.contained_in(self) { + Some(self) } else { - Self::Exclusive { floor, ceiling } + None } } // TODO more :) } +impl From> for FloatRange { + fn from(range: std::ops::Range) -> FloatRange { + FloatRange { floor: (Exclusive, range.start), ceiling: (Exclusive, range.end) } + } +} +impl TryFrom> for FloatRange { + type Error = ordered_float::FloatIsNan; + + fn try_from(range: std::ops::Range) -> Result { + let floor = ordered_float::NotNan::new(range.start)?; + let ceiling = ordered_float::NotNan::new(range.end)?; + Ok(FloatRange { floor: (Exclusive, floor), ceiling: (Exclusive, ceiling) }) + } +} + // TODO more #[cfg(test)] mod tests { - use super::{BetterF64, FloatRange}; + use super::{BetterF64, FloatRange, InclusiveExclusive}; + + fn e(a: f64) -> (InclusiveExclusive, BetterF64) { + (InclusiveExclusive::Exclusive, a.try_into().unwrap()) + } + + fn i(a: f64) -> (InclusiveExclusive, BetterF64) { + (InclusiveExclusive::Inclusive, a.try_into().unwrap()) + } #[test] fn contained_in() { - assert!(FloatRange::single(2.into()) - .contained_in(FloatRange::Exclusive { floor: 0.into(), ceiling: 5.into() })); + let zero_to_four: FloatRange = FloatRange::try_from(0f64..4f64).unwrap(); + assert!(FloatRange::new_single(2.into()).contained_in(zero_to_four)); } #[test] fn overlaps() { - assert!(FloatRange::Exclusive { floor: 0.into(), ceiling: 4.into() } - .overlaps(FloatRange::Exclusive { floor: 2.into(), ceiling: 5.into() })); - assert!(!FloatRange::Exclusive { floor: 0.into(), ceiling: 1.into() } - .overlaps(FloatRange::Exclusive { floor: 2.into(), ceiling: 5.into() })); + assert!(FloatRange { floor: e(0.), ceiling: e(4.) } + .overlaps(FloatRange { floor: e(2.), ceiling: e(5.) })); + + assert!(!FloatRange { floor: e(0.), ceiling: e(1.) } + .overlaps(FloatRange { floor: e(2.), ceiling: e(5.) })); } #[test] fn above() { - assert!(FloatRange::Exclusive { floor: 8.into(), ceiling: 10.into() } - .above(FloatRange::Exclusive { floor: 6.into(), ceiling: 7.into() })); - assert!(!FloatRange::Exclusive { floor: 0.into(), ceiling: 1.into() } - .above(FloatRange::Exclusive { floor: 0.into(), ceiling: 5.into() })); + assert!(FloatRange { floor: e(8.), ceiling: e(10.) } + .above(FloatRange { floor: e(6.), ceiling: e(7.) })); + assert!(!FloatRange { floor: e(0.), ceiling: e(1.) } + .above(FloatRange { floor: e(0.), ceiling: e(5.) })); } #[test] fn below() { - assert!(FloatRange::Exclusive { floor: 0.into(), ceiling: 4.into() } - .below(FloatRange::Exclusive { floor: 6.into(), ceiling: 7.into() })); - assert!(!FloatRange::Exclusive { floor: 0.into(), ceiling: 1.into() } - .below(FloatRange::Exclusive { floor: 0.into(), ceiling: 5.into() })); + assert!(FloatRange { floor: e(0.), ceiling: e(4.) } + .below(FloatRange { floor: e(6.), ceiling: e(7.) })); + assert!(!FloatRange { floor: e(0.), ceiling: e(1.) } + .below(FloatRange { floor: e(0.), ceiling: e(5.) })); } #[test] fn space_addition() { - assert_eq!( - FloatRange::Exclusive { floor: 0.into(), ceiling: 4.into() } - .space_addition(FloatRange::Exclusive { floor: 6.into(), ceiling: 7.into() }), - FloatRange::Exclusive { floor: 6.into(), ceiling: 11.into() } - ); + let lhs = FloatRange { floor: e(0.), ceiling: e(4.) } + .space_addition(FloatRange { floor: e(6.), ceiling: e(7.) }); + assert_eq!(lhs, FloatRange { floor: e(6.), ceiling: e(11.) }); } #[test] fn space_multiplication() { - assert_eq!( - FloatRange::Exclusive { floor: 0.into(), ceiling: 4.into() } - .space_multiplication(FloatRange::Exclusive { floor: 6.into(), ceiling: 7.into() }), - FloatRange::Exclusive { floor: 0.into(), ceiling: 28.into() } - ); - assert_eq!( - FloatRange::Exclusive { floor: BetterF64::from(-2i32), ceiling: 4.into() } - .space_multiplication(FloatRange::Exclusive { - floor: BetterF64::from(-10i32), - ceiling: 1.into() - }), - FloatRange::Exclusive { floor: BetterF64::from(-40i32), ceiling: 20.into() } - ); + let lhs = FloatRange { floor: e(0.), ceiling: e(4.) } + .space_multiplication(FloatRange { floor: e(6.), ceiling: e(7.) }); + assert_eq!(lhs, FloatRange { floor: e(0.), ceiling: e(28.) }); + + let lhs = FloatRange { floor: e(-2.), ceiling: e(4.) } + .space_multiplication(FloatRange { floor: e(-10.), ceiling: e(1.) }); + assert_eq!(lhs, FloatRange { floor: e(-40.), ceiling: e(20.) }); + } + + #[test] + fn multiple_of() { + let lhs = FloatRange { floor: e(30.), ceiling: e(34.) }; + assert!(lhs.contains_multiple_of(4.into())); // 32 + assert!(!lhs.contains_multiple_of(7.into())); // 28 -- 35 } } diff --git a/checker/src/utilities/mod.rs b/checker/src/utilities/mod.rs index 222c487a..4e59012d 100644 --- a/checker/src/utilities/mod.rs +++ b/checker/src/utilities/mod.rs @@ -3,6 +3,7 @@ mod debugging; pub mod float_range; pub mod map; +pub mod modulo_class; pub mod range_map; pub mod serialization; diff --git a/checker/src/utilities/modulo_class.rs b/checker/src/utilities/modulo_class.rs new file mode 100644 index 00000000..e5753d50 --- /dev/null +++ b/checker/src/utilities/modulo_class.rs @@ -0,0 +1,162 @@ +type BetterF64 = ordered_float::NotNan; + +/// x ≡ *class* [mod *modulo*] +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ModuloClass { + pub modulo: BetterF64, + pub offset: BetterF64, +} + +// TODO more operations +impl ModuloClass { + #[must_use] + pub fn new(modulo: BetterF64, offset: BetterF64) -> Self { + debug_assert!(modulo != 0.); + if modulo > 0f64.try_into().unwrap() { + Self { offset: offset % modulo, modulo } + } else { + // TODO think this is correct. [1]_{-3} = [2]_{3} + let modulo = -modulo; + Self { offset: modulo - (offset % modulo), modulo } + } + } + + #[must_use] + pub fn contains(self, value: BetterF64) -> bool { + // Note -0. = 0. + (value - self.offset) % self.modulo == 0. + } + + /// WIP + #[must_use] + pub fn disjoint(self, other: Self) -> bool { + if let Ok(gcd) = gcd_of_float(self.modulo, other.modulo) { + crate::utilities::notify!("{:?}", gcd); + (self.offset - other.offset) % gcd != 0. + } else { + crate::utilities::notify!("Here"); + true + } + } + + #[must_use] + pub fn intersection(self, _other: Self) -> Option { + todo!() + } + + #[must_use] + pub fn get_cover(self, _other: Self) -> Option { + todo!() + } + + #[must_use] + pub fn offset(self, offset: BetterF64) -> Self { + // TODO temp fix + if self.is_default() { + self + } else { + Self::new(self.modulo, self.offset + offset) + } + } + + #[must_use] + pub fn multiply(self, multiple: BetterF64) -> Self { + // TODO temp fix + if self.is_default() { + self + } else { + Self::new(self.modulo * multiple, self.offset) + } + } + + #[must_use] + pub fn negate(self) -> Self { + // TODO temp fix + if self.is_default() { + self + } else { + Self::new(self.modulo, self.modulo - self.offset) + } + } + + #[must_use] + pub fn is_default(self) -> bool { + self.modulo == f64::EPSILON + } +} + +/// Using Farey algoirthm +/// TODO is there a faster way implemntation +/// Note that numerator and denominator are coprime +fn try_get_numerator_denominator(input: BetterF64) -> Result<(i32, i32), ()> { + const STEPS: usize = 50; + const MARGIN: f64 = 1e-4; + + let integer_part = input.trunc() as i32; + let fractional_part = input.fract(); + if fractional_part == 0. { + return Ok((integer_part, 1)); + } + + let (mut a, mut b, mut c, mut d) = (0, 1, 1, 1); + + for _ in 0..STEPS { + let mediant_float = (f64::from(a) + f64::from(b)) / (f64::from(c) + f64::from(d)); + if (fractional_part - mediant_float).abs() < MARGIN { + let numerator = a + b + integer_part * (c + d); + let denominator = c + d; + return Ok((numerator, denominator)); + } else if fractional_part > mediant_float { + a += b; + c += d; + } else { + b += a; + d += c; + } + } + + Err(()) +} + +fn gcd_of_float(n1: BetterF64, n2: BetterF64) -> Result { + fn gcd(mut n1: i32, mut n2: i32) -> i32 { + while n2 != 0 { + let t = n2; + n2 = n1 % n2; + n1 = t; + } + n1 + } + + /// n1*n2 = gcd(n1, n2)*lcm(n1, n2) + fn lcm(n1: i32, n2: i32) -> i32 { + (n1 * n2) / gcd(n1, n2) + } + + let (a, b) = try_get_numerator_denominator(n1)?; + let (c, d) = try_get_numerator_denominator(n2)?; + + // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d) + Ok(BetterF64::new(f64::from(gcd(a, c)) / f64::from(lcm(b, d))).unwrap()) +} + +// hmmm +impl Default for ModuloClass { + fn default() -> Self { + Self { modulo: f64::EPSILON.try_into().unwrap(), offset: BetterF64::new(0.).unwrap() } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // TODO test negatives etc + #[test] + fn gcd() { + assert_eq!( + gcd_of_float(BetterF64::new(1. / 3.).unwrap(), BetterF64::new(3. / 2.).unwrap()), + Ok(BetterF64::new(0.16666666666666666).unwrap()) + ); + } +} diff --git a/parser/examples/code_blocks_to_script.rs b/parser/examples/code_blocks_to_script.rs index 4f0a4fea..4abf44f4 100644 --- a/parser/examples/code_blocks_to_script.rs +++ b/parser/examples/code_blocks_to_script.rs @@ -13,6 +13,7 @@ fn main() -> Result<(), Box> { let replace_satisfies_with_as = args.iter().any(|item| item == "--satisfies-with-as"); let add_headers_as_comments = args.iter().any(|item| item == "--comment-headers"); + // let declare_to_function = args.iter().any(|item| item == "--declare-to-function"); let into_files_directory_and_extension = args.windows(3).find_map(|item| { matches!(item[0].as_str(), "--into-files").then_some((item[1].clone(), item[2].clone())) @@ -35,7 +36,7 @@ fn main() -> Result<(), Box> { let content = std::fs::read_to_string(&path)?; - let filters: Vec<&str> = vec!["import", "export", "declare"]; + let filters: Vec<&str> = vec!["import", "export"]; let blocks = if path.ends_with(".md") { let mut blocks = Vec::new(); @@ -98,104 +99,193 @@ fn main() -> Result<(), Box> { } } } - return Ok(()); - } - - // Else bundle into one, bound in arrow functions to prevent namespace collision - let mut final_blocks: Vec<(HashSet, String)> = Vec::new(); - for (header, code) in blocks { - let module = Module::from_string(code.clone(), Default::default()).map_err(Box::new)?; - - let mut names = HashSet::new(); - - let mut visitors = Visitors { - expression_visitors: Default::default(), - statement_visitors: Default::default(), - variable_visitors: vec![Box::new(NameFinder)], - block_visitors: Default::default(), - }; - - module.visit::>( - &mut visitors, - &mut names, - &VisitOptions { visit_nested_blocks: false, reverse_statements: false }, - source_map::Nullable::NULL, - ); - - // TODO quick fix to also register interface and type alias names to prevent conflicts - for item in module.items { - match item { - StatementOrDeclaration::Declaration(Declaration::TypeAlias(TypeAlias { - name: StatementPosition { identifier: VariableIdentifier::Standard(s, _), .. }, - .. - })) => { - names.insert(s.clone()); + } else { + // Else bundle into one, bound in arrow functions to prevent namespace collision + let mut final_blocks: Vec<(HashSet, String)> = Vec::new(); + for (header, mut code) in blocks { + // TODO clone + let module = match Module::from_string(code.clone(), Default::default()) { + Ok(module) => module, + Err(err) => { + return Err(From::from(format!("Parse error on {code}: {err:?}"))); } - StatementOrDeclaration::Declaration(Declaration::Interface(Decorated { - on: - InterfaceDeclaration { - name: - StatementPosition { - identifier: VariableIdentifier::Standard(s, _), .. - }, - .. - }, - .. - })) => { - names.insert(s.clone()); + }; + + let mut names = HashSet::new(); + + let mut visitors = Visitors { + expression_visitors: Default::default(), + statement_visitors: Default::default(), + variable_visitors: vec![Box::new(NameFinder)], + block_visitors: Default::default(), + }; + + module.visit::>( + &mut visitors, + &mut names, + &VisitOptions { visit_nested_blocks: false, reverse_statements: false }, + source_map::Nullable::NULL, + ); + + let mut declare_lets = Vec::new(); + + // TODO quick fix to also register interface and type alias names to prevent conflicts + for item in &module.items { + match item { + StatementOrDeclaration::Declaration(Declaration::TypeAlias(TypeAlias { + name: + StatementPosition { identifier: VariableIdentifier::Standard(s, _), .. }, + .. + })) => { + names.insert(s.clone()); + } + StatementOrDeclaration::Declaration(Declaration::Interface(Decorated { + on: + InterfaceDeclaration { + name: + StatementPosition { + identifier: VariableIdentifier::Standard(s, _), + .. + }, + .. + }, + .. + })) => { + names.insert(s.clone()); + } + StatementOrDeclaration::Declaration(Declaration::DeclareVariable( + declare_variable, + )) => { + for declaration in &declare_variable.declarations { + declare_lets.push(( + declaration.name.clone(), + declaration.type_annotation.clone(), + )); + } + } + _ => {} } - _ => {} } - } - // If available block add to that, otherwise create a new one - if let Some((items, block)) = - final_blocks.iter_mut().find(|(uses, _)| uses.is_disjoint(&names)) - { - items.extend(names.into_iter()); - if add_headers_as_comments { - block.push_str("\n\t// "); - block.push_str(&header); - } - for line in code.lines() { - block.push_str("\n\t"); - block.push_str(&line); - } - // If the block is not terminated, it can change the parsing of the next one - if block.ends_with(')') { - block.push(';'); - } - block.push('\n'); - } else { - let mut block = String::new(); - if add_headers_as_comments { - block.push_str("\t// "); - block.push_str(&header); + if !declare_lets.is_empty() { + use source_map::{Nullable, Span}; + let (mut top_level, mut inside) = (Vec::new(), Vec::new()); + for item in module.items { + match item { + StatementOrDeclaration::Declaration(Declaration::TypeAlias( + TypeAlias { .. }, + )) => { + top_level.push(item); + } + StatementOrDeclaration::Declaration(Declaration::Interface( + Decorated { .. }, + )) => { + top_level.push(item); + } + StatementOrDeclaration::Declaration(Declaration::DeclareVariable(..)) => {} + item => { + inside.push(item); + } + } + } + + use ezno_parser::{ast, expressions::operators, functions, Expression, Statement}; + + let parameters = declare_lets + .into_iter() + .map(|(name, type_annotation)| functions::Parameter { + visibility: (), + name, + type_annotation, + additionally: None, + position: Span::NULL, + }) + .collect(); + + let function = Expression::ArrowFunction(ast::ArrowFunction { + // TODO maybe async + header: false, + name: (), + parameters: functions::FunctionParameters { + parameters, + rest_parameter: Default::default(), + position: Span::NULL, + leading: (), + }, + return_type: None, + type_parameters: None, + position: Span::NULL, + body: ast::ExpressionOrBlock::Block(ast::Block(inside, Span::NULL)), + }); + + // void is temp fix + top_level.push( + Statement::Expression( + Expression::UnaryOperation { + operator: operators::UnaryOperator::Void, + operand: Box::new(function), + position: Span::NULL, + } + .into(), + ) + .into(), + ); + + let module = Module { hashbang_comment: None, items: top_level, span: Span::NULL }; + + code = module.to_string(&ezno_parser::ToStringOptions::typescript()); } - for line in code.lines() { - block.push_str("\n\t"); - block.push_str(&line); + + // If available block add to that, otherwise create a new one + if let Some((items, block)) = + final_blocks.iter_mut().find(|(uses, _)| uses.is_disjoint(&names)) + { + items.extend(names.into_iter()); + if add_headers_as_comments { + block.push_str("\n\t// "); + block.push_str(&header); + } + for line in code.lines() { + block.push_str("\n\t"); + block.push_str(&line); + } + // If the block is not terminated, it can change the parsing of the next one + if block.ends_with(')') { + block.push(';'); + } + block.push('\n'); + } else { + let mut block = String::new(); + if add_headers_as_comments { + block.push_str("\t// "); + block.push_str(&header); + } + for line in code.lines() { + block.push_str("\n\t"); + block.push_str(&line); + } + block.push('\n'); + final_blocks.push((names, block)); } - block.push('\n'); - final_blocks.push((names, block)); } - } - // eprintln!("Generated {:?} blocks", final_blocks.len()); + // eprintln!("Generated {:?} blocks", final_blocks.len()); - if let Some(out) = out_file { - let mut out = std::fs::File::create(out)?; - for (_items, block) in final_blocks { - writeln!(out, "() => {{\n{block}}};\n")?; - } - } else { - let mut out = std::io::stdout(); - for (_items, block) in final_blocks { - // eprintln!("block includes: {items:?}\n{block}\n---"); - writeln!(out, "() => {{\n{block}}};\n")?; + eprintln!("Bundled into {} functions", final_blocks.len()); + + if let Some(out) = out_file { + let mut out = std::fs::File::create(out)?; + for (_items, block) in final_blocks { + writeln!(out, "() => {{\n{block}}};\n")?; + } + } else { + let mut out = std::io::stdout(); + for (_items, block) in final_blocks { + // eprintln!("block includes: {items:?}\n{block}\n---"); + writeln!(out, "() => {{\n{block}}};\n")?; + } } } - Ok(()) } diff --git a/parser/src/expressions/arrow_function.rs b/parser/src/expressions/arrow_function.rs index 043ebc85..e30a80b1 100644 --- a/parser/src/expressions/arrow_function.rs +++ b/parser/src/expressions/arrow_function.rs @@ -91,17 +91,19 @@ impl FunctionBased for ArrowFunctionBase { local: crate::LocalToStringInformation, ) { // Use shorthand if one parameter with no declared type - if let (true, [Parameter { name, .. }]) = - (parameters.rest_parameter.is_none(), parameters.parameters.as_slice()) + if let ([Parameter { name, type_annotation, .. }], None) = + (parameters.parameters.as_slice(), ¶meters.rest_parameter) { - if let VariableField::Name(name, ..) = name.get_ast_ref() { - name.to_string_from_buffer(buf, options, local); - } else { - parameters.to_string_from_buffer(buf, options, local); + let is_printing_type_annotation = + options.include_type_annotations && type_annotation.is_some(); + if !is_printing_type_annotation { + if let VariableField::Name(name, ..) = name.get_ast_ref() { + name.to_string_from_buffer(buf, options, local); + return; + } } - } else { - parameters.to_string_from_buffer(buf, options, local); } + parameters.to_string_from_buffer(buf, options, local); } fn parameter_body_boundary_token_to_string_from_buffer( diff --git a/src/cli.rs b/src/cli.rs index a62dab08..fbdd1de4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -121,6 +121,9 @@ pub(crate) struct CheckArguments { /// compact diagnostics #[argh(switch)] pub compact_diagnostics: bool, + /// more behavior for numbers + #[argh(switch)] + pub advanced_numbers: bool, /// maximum diagnostics to print (defaults to 30, pass `all` for all and `0` to count) #[argh(option, default = "MaxDiagnostics::default()")] pub max_diagnostics: MaxDiagnostics, @@ -262,12 +265,17 @@ pub fn run_cli( timings, compact_diagnostics, max_diagnostics, + advanced_numbers, } = check_arguments; let type_check_options: TypeCheckOptions = if cfg!(target_family = "wasm") { Default::default() } else { - TypeCheckOptions { measure_time: timings, ..TypeCheckOptions::default() } + TypeCheckOptions { + measure_time: timings, + advanced_numbers, + ..TypeCheckOptions::default() + } }; let entry_points = match get_entry_points(input) { @@ -293,7 +301,7 @@ pub fn run_cli( new_debouncer(Duration::from_millis(200), None, tx).unwrap(); for e in &entry_points { - _ = debouncer.watcher().watch(e, notify::RecursiveMode::Recursive).unwrap(); + debouncer.watcher().watch(e, notify::RecursiveMode::Recursive).unwrap(); } let _ = run_checker( @@ -359,7 +367,7 @@ pub fn run_cli( source_maps: build_config.source_maps, type_definition_module: build_config.definition_file, // TODO not sure - output_path: PathBuf::from(output_path), + output_path, other_transformers: None, lsp_mode: false, }; diff --git a/src/playground/main.js b/src/playground/main.js index 9a7065b5..0aa7f379 100644 --- a/src/playground/main.js +++ b/src/playground/main.js @@ -5,7 +5,7 @@ import { defaultKeymap, indentWithTab, toggleLineComment } from "@codemirror/com import { parser as jsParser } from "@lezer/javascript"; import { tags } from "@lezer/highlight"; import { HighlightStyle, syntaxHighlighting, LanguageSupport, LRLanguage } from "@codemirror/language"; -import { init as init_ezno, check_with_options, get_version } from "ezno"; +import { init as init_ezno, check, get_version } from "ezno"; const diagnosticsEntry = document.querySelector(".diagnostics"); const editorParent = document.querySelector("#editor"); @@ -65,6 +65,14 @@ if (id) { let currentState = null; const ROOT_PATH = "index.tsx"; +const options = { + // Allow partial syntax + lsp_mode: true, + // For hover + store_type_mappings: true, + // For showing off + number_intrinsics: true +}; async function setup() { await init_ezno(); @@ -74,7 +82,7 @@ async function setup() { text = args.state.doc.text.join("\n"); try { const start = performance.now(); - currentState = check_with_options(ROOT_PATH, (_) => text, { lsp_mode: true, store_type_mappings: true }); + currentState = check(ROOT_PATH, (_) => text, options); const elapsed = performance.now() - start; timeOutput.innerText = `Parsed & checked in ${Math.trunc(elapsed)}ms`; From f270633ae11c65e8444c89981c7f2c1fc379f3c5 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 13 Nov 2024 19:56:02 +0000 Subject: [PATCH 8/9] Release: ezno-parser-visitable-derive to 0.0.9, ezno-parser to 0.1.7, ezno-ast-generator to 0.0.15, binary-serialize-derive to 0.0.3, ezno-checker to 0.0.18 and ezno to 0.0.23 --- Cargo.lock | 58 +++++++++++----------- Cargo.toml | 6 +-- checker/Cargo.toml | 6 +-- checker/binary-serialize-derive/Cargo.toml | 2 +- parser/Cargo.toml | 4 +- parser/generator/Cargo.toml | 4 +- parser/visitable-derive/Cargo.toml | 2 +- src/js-based-plugin/package-lock.json | 12 ++--- src/js-based-plugin/package.json | 4 +- src/js-cli-and-library/package-lock.json | 24 ++------- src/js-cli-and-library/package.json | 4 +- 11 files changed, 54 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6be9713..133a8497 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,9 +16,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "argh" @@ -65,7 +65,7 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "binary-serialize-derive" -version = "0.0.2" +version = "0.0.3" dependencies = [ "syn-helpers", ] @@ -96,9 +96,9 @@ checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" [[package]] name = "cc" -version = "1.1.36" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" +checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" dependencies = [ "shlex", ] @@ -281,7 +281,7 @@ dependencies = [ [[package]] name = "ezno" -version = "0.0.22" +version = "0.0.23" dependencies = [ "argh", "base64", @@ -310,7 +310,7 @@ dependencies = [ [[package]] name = "ezno-ast-generator" -version = "0.0.14" +version = "0.0.15" dependencies = [ "ezno-parser", "pretty_assertions", @@ -321,7 +321,7 @@ dependencies = [ [[package]] name = "ezno-checker" -version = "0.0.17" +version = "0.0.18" dependencies = [ "binary-serialize-derive", "derive-debug-extras", @@ -354,7 +354,7 @@ dependencies = [ [[package]] name = "ezno-parser" -version = "0.1.6" +version = "0.1.7" dependencies = [ "derive-debug-extras", "derive-enum-from-into", @@ -378,7 +378,7 @@ dependencies = [ [[package]] name = "ezno-parser-visitable-derive" -version = "0.0.8" +version = "0.0.9" dependencies = [ "string-cases", "syn-helpers", @@ -386,17 +386,17 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "file-id" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6584280525fb2059cba3db2c04abf947a1a29a45ddae89f3870f8281704fafc9" +checksum = "6bc904b9bbefcadbd8e3a9fb0d464a9b979de6324c03b3c663e8994f46a5be36" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -557,9 +557,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libredox" @@ -686,9 +686,9 @@ dependencies = [ [[package]] name = "notify-debouncer-full" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154" +checksum = "fb7fd166739789c9ff169e654dc1501373db9d80a4c3f972817c8a4d7cf8f34e" dependencies = [ "crossbeam-channel", "file-id", @@ -877,9 +877,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.39" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -933,9 +933,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -974,9 +974,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -994,9 +994,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -1099,9 +1099,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", diff --git a/Cargo.toml b/Cargo.toml index 7f0a5fd1..cdd8e08d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ members = [ name = "ezno" description = "A fast and correct TypeScript type checker and compiler with additional experiments. For use as a library or through the CLI" authors = ["Ben "] -version = "0.0.22" +version = "0.0.23" edition = "2021" license = "MIT" homepage = "https://kaleidawave.github.io/posts/introducing-ezno/" @@ -64,13 +64,13 @@ glob = "0.3" [dependencies.checker] path = "./checker" -version = "0.0.17" +version = "0.0.18" features = ["ezno-parser", "serde-serialize"] package = "ezno-checker" [dependencies.parser] path = "./parser" -version = "0.1.6" +version = "0.1.7" features = ["extras"] package = "ezno-parser" diff --git a/checker/Cargo.toml b/checker/Cargo.toml index 2b7614b9..ba58e6fe 100644 --- a/checker/Cargo.toml +++ b/checker/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ezno-checker" description = "A fast and correct TypeScript type checker with additional experiments" -version = "0.0.17" +version = "0.0.18" license = "MIT" repository = "https://github.com/kaleidawave/ezno" homepage = "https://kaleidawave.github.io/posts/introducing-ezno" @@ -26,7 +26,7 @@ source-map = { version = "0.15", features = [ "self-rust-tokenize", ] } -binary-serialize-derive = { path = "./binary-serialize-derive", version = "0.0.2" } +binary-serialize-derive = { path = "./binary-serialize-derive", version = "0.0.3" } temporary-annex = "0.1" derive-enum-from-into = "0.2" @@ -52,7 +52,7 @@ wasm-bindgen = "=0.2.89" [dependencies.parser] path = "../parser" optional = true -version = "0.1.6" +version = "0.1.7" features = ["extras"] package = "ezno-parser" diff --git a/checker/binary-serialize-derive/Cargo.toml b/checker/binary-serialize-derive/Cargo.toml index 937e9fa2..4aff0603 100644 --- a/checker/binary-serialize-derive/Cargo.toml +++ b/checker/binary-serialize-derive/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "binary-serialize-derive" description = "A small representation for objects in the Ezno checker, used for caching to make checking faster" -version = "0.0.2" +version = "0.0.3" edition = "2021" license = "MIT" repository = "https://github.com/kaleidawave/ezno" diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 0e5ef0ed..dcd3cf21 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -2,7 +2,7 @@ name = "ezno-parser" description = "Parser and AST definitions for Ezno" authors = ["Ben "] -version = "0.1.6" +version = "0.1.7" edition = "2021" license = "MIT" repository = "https://github.com/kaleidawave/ezno" @@ -32,7 +32,7 @@ extras = [] full-typescript = [] [dependencies] -visitable-derive = { path = "./visitable-derive", version = "0.0.8", package = "ezno-parser-visitable-derive" } +visitable-derive = { path = "./visitable-derive", version = "0.0.9", package = "ezno-parser-visitable-derive" } derive-finite-automaton = "0.2" derive-debug-extras = { version = "0.3", features = [ diff --git a/parser/generator/Cargo.toml b/parser/generator/Cargo.toml index dc885813..3b5e1670 100644 --- a/parser/generator/Cargo.toml +++ b/parser/generator/Cargo.toml @@ -2,7 +2,7 @@ name = "ezno-ast-generator" description = "Quasi-quoted constant compiled TS definitions for Ezno AST" authors = ["Ben "] -version = "0.0.14" +version = "0.0.15" edition = "2021" license = "MIT" repository = "https://github.com/kaleidawave/ezno" @@ -17,7 +17,7 @@ proc-macro = true quote = "1.0" proc-macro2 = "1.0" self-rust-tokenize = "0.3.3" -ezno-parser = { path = "..", version = "0.1.6", features = [ +ezno-parser = { path = "..", version = "0.1.7", features = [ "self-rust-tokenize", ] } diff --git a/parser/visitable-derive/Cargo.toml b/parser/visitable-derive/Cargo.toml index 7399ee0d..bed94783 100644 --- a/parser/visitable-derive/Cargo.toml +++ b/parser/visitable-derive/Cargo.toml @@ -2,7 +2,7 @@ name = "ezno-parser-visitable-derive" description = "Derives 'Visitable' trait for Ezno AST" authors = ["Ben "] -version = "0.0.8" +version = "0.0.9" edition = "2021" license = "MIT" repository = "https://github.com/kaleidawave/ezno" diff --git a/src/js-based-plugin/package-lock.json b/src/js-based-plugin/package-lock.json index 7d8dc8f3..52ed989a 100644 --- a/src/js-based-plugin/package-lock.json +++ b/src/js-based-plugin/package-lock.json @@ -1,15 +1,15 @@ { "name": "unplugin-ezno", - "version": "0.0.22", + "version": "0.0.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "unplugin-ezno", - "version": "0.0.22", + "version": "0.0.23", "license": "MIT", "dependencies": { - "ezno": "^0.0.22", + "ezno": "^0.0.23", "unplugin": "^1.3.1" }, "devDependencies": { @@ -865,9 +865,9 @@ "license": "MIT" }, "node_modules/ezno": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/ezno/-/ezno-0.0.22.tgz", - "integrity": "sha512-xAAmsBS7Wzf+bPN6pzCeP4kR4DyIOcJe/aMf2cGBXGjXnYFBNsnbUtupyMHP8m4D+zc1w+BDDZhgax5imosGkA==", + "version": "0.0.23", + "resolved": "https://registry.npmjs.org/ezno/-/ezno-0.0.23.tgz", + "integrity": "sha512-H0m/ZuSWMx9NTKyV18NZWUSwF39SENhyGp6TGx5n0vDvlqvG9s+fMsAxOoMom9CjbG6YW8CYtslcIaAS25SdlQ==", "bin": { "ezno": "dist/cli.mjs" }, diff --git a/src/js-based-plugin/package.json b/src/js-based-plugin/package.json index b4951f50..df0974b1 100644 --- a/src/js-based-plugin/package.json +++ b/src/js-based-plugin/package.json @@ -1,6 +1,6 @@ { "name": "unplugin-ezno", - "version": "0.0.22", + "version": "0.0.23", "description": "Ezno as a plugin", "main": "./dist/index.cjs", "module": "./dist/index.mjs", @@ -14,7 +14,7 @@ } }, "dependencies": { - "ezno": "^0.0.22", + "ezno": "^0.0.23", "unplugin": "^1.3.1" }, "keywords": [ diff --git a/src/js-cli-and-library/package-lock.json b/src/js-cli-and-library/package-lock.json index 87ad852d..a2d8e9aa 100644 --- a/src/js-cli-and-library/package-lock.json +++ b/src/js-cli-and-library/package-lock.json @@ -1,12 +1,12 @@ { "name": "ezno", - "version": "0.0.22", + "version": "0.0.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ezno", - "version": "0.0.22", + "version": "0.0.23", "license": "MIT", "dependencies": { "snapshot-fixtures": "^1.2.0" @@ -921,9 +921,6 @@ "engines": { "node": ">=12" }, - "engines": { - "node": ">=12" - }, "funding": { "url": "https://github.com/chalk/ansi-regex?sponsor=1" } @@ -3355,21 +3352,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/stylehacks": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.2.tgz", - "integrity": "sha512-00zvJGnCu64EpMjX8b5iCZ3us2Ptyw8+toEkb92VdmkEaRaSGBNKAoK6aWZckhXxmQP8zWiTaFaiMGIU8Ve8sg==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/stylehacks": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.4.tgz", @@ -3673,4 +3655,4 @@ "dev": true } } -} \ No newline at end of file +} diff --git a/src/js-cli-and-library/package.json b/src/js-cli-and-library/package.json index 5653a853..f6c46ad3 100644 --- a/src/js-cli-and-library/package.json +++ b/src/js-cli-and-library/package.json @@ -1,6 +1,6 @@ { "name": "ezno", - "version": "0.0.22", + "version": "0.0.23", "description": "A JavaScript compiler and TypeScript checker written in Rust with a focus on static analysis and runtime performance", "license": "MIT", "repository": "https://github.com/kaleidawave/ezno", @@ -79,4 +79,4 @@ "wasm-pack": "^0.13.0", "snapshot-fixtures": "^1.2.0" } -} \ No newline at end of file +} From 16f7779b157dea4da2b618d8e192492a09d1acb9 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 13 Nov 2024 20:19:16 +0000 Subject: [PATCH 9/9] Update ezno library for the playground - Add fix workflow so should not happen again --- .github/workflows/deploy-playground.yml | 8 +- src/playground/package-lock.json | 480 +++++++++++++----------- src/playground/package.json | 8 +- 3 files changed, 263 insertions(+), 233 deletions(-) diff --git a/.github/workflows/deploy-playground.yml b/.github/workflows/deploy-playground.yml index e711d2cc..fca5a546 100644 --- a/.github/workflows/deploy-playground.yml +++ b/.github/workflows/deploy-playground.yml @@ -12,12 +12,10 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: '20.11.1' + node-version: 22 - - name: build - run: | - npm i - npm run build + - name: Install (inc latest Ezno) and build + run: npm i; npm i ezno@latest; npm run build; working-directory: src/playground - name: Deploy to GitHub Pages diff --git a/src/playground/package-lock.json b/src/playground/package-lock.json index 7e9928c4..99b10c61 100644 --- a/src/playground/package-lock.json +++ b/src/playground/package-lock.json @@ -16,7 +16,7 @@ "@codemirror/view": "^6.23.0", "@lezer/highlight": "^1.2.0", "@lezer/javascript": "^1.4.13", - "ezno": "^0.0.22" + "ezno": "^0.0.23" }, "devDependencies": { "lz-string": "^1.5.0", @@ -27,9 +27,9 @@ } }, "node_modules/@codemirror/autocomplete": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.12.0.tgz", - "integrity": "sha512-r4IjdYFthwbCQyvqnSlx0WBHRHi8nBvU+WjJxFUij81qsBfhNudf/XKKmmC2j3m0LaOYUQTf3qiEK1J8lO1sdg==", + "version": "6.18.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.3.tgz", + "integrity": "sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -44,20 +44,20 @@ } }, "node_modules/@codemirror/commands": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz", - "integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.7.1.tgz", + "integrity": "sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "node_modules/@codemirror/lang-javascript": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz", - "integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz", + "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.6.0", @@ -69,9 +69,9 @@ } }, "node_modules/@codemirror/language": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.0.tgz", - "integrity": "sha512-2vaNn9aPGCRFKWcHPFksctzJ8yS5p7YoaT+jHpc0UGKzNuAIx4qy6R5wiqbP+heEEdyaABA582mNqSHzSoYdmg==", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.3.tgz", + "integrity": "sha512-kDqEU5sCP55Oabl6E7m5N+vZRoc0iWqgDVhEKifcHzPzjqCegcO4amfrYVL9PmPZpl4G0yjkpTpUO/Ui8CzO8A==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -82,9 +82,9 @@ } }, "node_modules/@codemirror/lint": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", - "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.2.tgz", + "integrity": "sha512-PDFG5DjHxSEjOXk9TQYYVjZDqlZTFaDBfhQixHnQOEVDDNHUbEh/hstAjcQJaA6FQdZTD1hquXTK0rVBLADR1g==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", @@ -92,14 +92,14 @@ } }, "node_modules/@codemirror/state": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.0.tgz", - "integrity": "sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==" + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", + "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" }, "node_modules/@codemirror/view": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.23.0.tgz", - "integrity": "sha512-/51px9N4uW8NpuWkyUX+iam5+PM6io2fm+QmRnzwqBy5v/pwGg9T0kILFtYeum8hjuvENtgsGNKluOfqIICmeQ==", + "version": "6.34.2", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.34.2.tgz", + "integrity": "sha512-d6n0WFvL970A9Z+l9N2dO+Hk9ev4hDYQzIx+B9tCyBP0W5wPEszi1rhuyFesNSkLZzXbQE5FPH7F/z/TMJfoPA==", "dependencies": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", @@ -107,9 +107,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -123,9 +123,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -139,9 +139,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -155,9 +155,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -171,9 +171,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -187,9 +187,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -203,9 +203,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -219,9 +219,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -235,9 +235,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -251,9 +251,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -267,9 +267,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -299,9 +299,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -315,9 +315,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -331,9 +331,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -347,9 +347,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -363,9 +363,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -379,9 +379,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -395,9 +395,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -411,9 +411,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -427,9 +427,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -443,9 +443,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -459,9 +459,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -475,22 +475,22 @@ } }, "node_modules/@lezer/common": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.0.tgz", - "integrity": "sha512-Wmvlm4q6tRpwiy20TnB3yyLTZim38Tkc50dPY8biQRwqE+ati/wD84rm3N15hikvdT4uSg9phs9ubjvcLmkpKg==" + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==" }, "node_modules/@lezer/highlight": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", - "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", "dependencies": { "@lezer/common": "^1.0.0" } }, "node_modules/@lezer/javascript": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.13.tgz", - "integrity": "sha512-5IBr8LIO3xJdJH1e9aj/ZNLE4LSbdsx25wFmGRAZsj2zSmwAYjx26JyU/BYOCpRQlu1jcv1z3vy4NB9+UkfRow==", + "version": "1.4.19", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.19.tgz", + "integrity": "sha512-j44kbR1QL26l6dMunZ1uhKBFteVGLVCBGNUD2sUaMnic+rbTviVuoK0CD1l9FTW31EueWvFFswCKMH7Z+M3JRA==", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.1.3", @@ -498,17 +498,17 @@ } }, "node_modules/@lezer/lr": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz", - "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", "dependencies": { "@lezer/common": "^1.0.0" } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.26.0.tgz", + "integrity": "sha512-gJNwtPDGEaOEgejbaseY6xMFu+CPltsc8/T+diUTTbOQLqD+bnrJq9ulH6WD69TqwqWmrfRAtUv30cCFZlbGTQ==", "cpu": [ "arm" ], @@ -519,9 +519,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.26.0.tgz", + "integrity": "sha512-YJa5Gy8mEZgz5JquFruhJODMq3lTHWLm1fOy+HIANquLzfIOzE9RA5ie3JjCdVb9r46qfAQY/l947V0zfGJ0OQ==", "cpu": [ "arm64" ], @@ -532,9 +532,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.26.0.tgz", + "integrity": "sha512-ErTASs8YKbqTBoPLp/kA1B1Um5YSom8QAc4rKhg7b9tyyVqDBlQxy7Bf2wW7yIlPGPg2UODDQcbkTlruPzDosw==", "cpu": [ "arm64" ], @@ -545,9 +545,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.26.0.tgz", + "integrity": "sha512-wbgkYDHcdWW+NqP2mnf2NOuEbOLzDblalrOWcPyY6+BRbVhliavon15UploG7PpBRQ2bZJnbmh8o3yLoBvDIHA==", "cpu": [ "x64" ], @@ -557,10 +557,36 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.26.0.tgz", + "integrity": "sha512-Y9vpjfp9CDkAG4q/uwuhZk96LP11fBz/bYdyg9oaHYhtGZp7NrbkQrj/66DYMMP2Yo/QPAsVHkV891KyO52fhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.26.0.tgz", + "integrity": "sha512-A/jvfCZ55EYPsqeaAt/yDAG4q5tt1ZboWMHEvKAH9Zl92DWvMIbnZe/f/eOXze65aJaaKbL+YeM0Hz4kLQvdwg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.26.0.tgz", + "integrity": "sha512-paHF1bMXKDuizaMODm2bBTjRiHxESWiIyIdMugKeLnjuS1TCS54MF5+Y5Dx8Ui/1RBPVRE09i5OUlaLnv8OGnA==", "cpu": [ "arm" ], @@ -571,9 +597,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.26.0.tgz", + "integrity": "sha512-cwxiHZU1GAs+TMxvgPfUDtVZjdBdTsQwVnNlzRXC5QzIJ6nhfB4I1ahKoe9yPmoaA/Vhf7m9dB1chGPpDRdGXg==", "cpu": [ "arm" ], @@ -584,9 +610,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.26.0.tgz", + "integrity": "sha512-4daeEUQutGRCW/9zEo8JtdAgtJ1q2g5oHaoQaZbMSKaIWKDQwQ3Yx0/3jJNmpzrsScIPtx/V+1AfibLisb3AMQ==", "cpu": [ "arm64" ], @@ -597,9 +623,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.26.0.tgz", + "integrity": "sha512-eGkX7zzkNxvvS05ROzJ/cO/AKqNvR/7t1jA3VZDi2vRniLKwAWxUr85fH3NsvtxU5vnUUKFHKh8flIBdlo2b3Q==", "cpu": [ "arm64" ], @@ -610,9 +636,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.26.0.tgz", + "integrity": "sha512-Odp/lgHbW/mAqw/pU21goo5ruWsytP7/HCC/liOt0zcGG0llYWKrd10k9Fj0pdj3prQ63N5yQLCLiE7HTX+MYw==", "cpu": [ "ppc64" ], @@ -623,9 +649,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.26.0.tgz", + "integrity": "sha512-MBR2ZhCTzUgVD0OJdTzNeF4+zsVogIR1U/FsyuFerwcqjZGvg2nYe24SAHp8O5sN8ZkRVbHwlYeHqcSQ8tcYew==", "cpu": [ "riscv64" ], @@ -636,9 +662,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.26.0.tgz", + "integrity": "sha512-YYcg8MkbN17fMbRMZuxwmxWqsmQufh3ZJFxFGoHjrE7bv0X+T6l3glcdzd7IKLiwhT+PZOJCblpnNlz1/C3kGQ==", "cpu": [ "s390x" ], @@ -649,9 +675,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.26.0.tgz", + "integrity": "sha512-ZuwpfjCwjPkAOxpjAEjabg6LRSfL7cAJb6gSQGZYjGhadlzKKywDkCUnJ+KEfrNY1jH5EEoSIKLCb572jSiglA==", "cpu": [ "x64" ], @@ -662,9 +688,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.26.0.tgz", + "integrity": "sha512-+HJD2lFS86qkeF8kNu0kALtifMpPCZU80HvwztIKnYwym3KnA1os6nsX4BGSTLtS2QVAGG1P3guRgsYyMA0Yhg==", "cpu": [ "x64" ], @@ -675,9 +701,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.26.0.tgz", + "integrity": "sha512-WUQzVFWPSw2uJzX4j6YEbMAiLbs0BUysgysh8s817doAYhR5ybqTI1wtKARQKo6cGop3pHnrUJPFCsXdoFaimQ==", "cpu": [ "arm64" ], @@ -688,9 +714,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.26.0.tgz", + "integrity": "sha512-D4CxkazFKBfN1akAIY6ieyOqzoOoBV1OICxgUblWxff/pSjCA2khXlASUx7mK6W1oP4McqhgcCsu6QaLj3WMWg==", "cpu": [ "ia32" ], @@ -701,9 +727,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.26.0.tgz", + "integrity": "sha512-2x8MO1rm4PGEP0xWbubJW5RtbNLk3puzAMaLQd3B3JHVw4KcHlmXcO+Wewx9zCoo7EUFiMlu/aZbCJ7VjMzAag==", "cpu": [ "x64" ], @@ -714,9 +740,9 @@ ] }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/ansi-sequence-parser": { @@ -731,9 +757,9 @@ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "bin": { @@ -743,35 +769,35 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/ezno": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/ezno/-/ezno-0.0.22.tgz", - "integrity": "sha512-xAAmsBS7Wzf+bPN6pzCeP4kR4DyIOcJe/aMf2cGBXGjXnYFBNsnbUtupyMHP8m4D+zc1w+BDDZhgax5imosGkA==", + "version": "0.0.23", + "resolved": "https://registry.npmjs.org/ezno/-/ezno-0.0.23.tgz", + "integrity": "sha512-H0m/ZuSWMx9NTKyV18NZWUSwF39SENhyGp6TGx5n0vDvlqvG9s+fMsAxOoMom9CjbG6YW8CYtslcIaAS25SdlQ==", "bin": { "ezno": "dist/cli.mjs" }, @@ -840,15 +866,15 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -866,20 +892,20 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.26.0.tgz", + "integrity": "sha512-ilcl12hnWonG8f+NxU6BlgysVA0gvY2l8N0R84S1HcINbW20bvwuCngJkkInV6LXhwRpucsW5k1ovDwEdBVrNg==", "dev": true, "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -889,22 +915,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.26.0", + "@rollup/rollup-android-arm64": "4.26.0", + "@rollup/rollup-darwin-arm64": "4.26.0", + "@rollup/rollup-darwin-x64": "4.26.0", + "@rollup/rollup-freebsd-arm64": "4.26.0", + "@rollup/rollup-freebsd-x64": "4.26.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.26.0", + "@rollup/rollup-linux-arm-musleabihf": "4.26.0", + "@rollup/rollup-linux-arm64-gnu": "4.26.0", + "@rollup/rollup-linux-arm64-musl": "4.26.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.26.0", + "@rollup/rollup-linux-riscv64-gnu": "4.26.0", + "@rollup/rollup-linux-s390x-gnu": "4.26.0", + "@rollup/rollup-linux-x64-gnu": "4.26.0", + "@rollup/rollup-linux-x64-musl": "4.26.0", + "@rollup/rollup-win32-arm64-msvc": "4.26.0", + "@rollup/rollup-win32-ia32-msvc": "4.26.0", + "@rollup/rollup-win32-x64-msvc": "4.26.0", "fsevents": "~2.3.2" } }, @@ -921,18 +949,18 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/style-mod": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", - "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" }, "node_modules/typescript": { "version": "5.3.3", @@ -948,14 +976,14 @@ } }, "node_modules/vite": { - "version": "5.2.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", - "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -974,6 +1002,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -991,6 +1020,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, diff --git a/src/playground/package.json b/src/playground/package.json index 318d64f9..4a8b8b1c 100644 --- a/src/playground/package.json +++ b/src/playground/package.json @@ -10,11 +10,11 @@ "preview": "vite preview" }, "devDependencies": { - "vite": "^5.2.12", "lz-string": "^1.5.0", "marked": "^10.0.0", "shiki": "^0.14.7", - "typescript": "5.3" + "typescript": "5.3", + "vite": "^5.2.12" }, "dependencies": { "@codemirror/commands": "^6.3.3", @@ -25,6 +25,6 @@ "@codemirror/view": "^6.23.0", "@lezer/highlight": "^1.2.0", "@lezer/javascript": "^1.4.13", - "ezno": "^0.0.22" + "ezno": "^0.0.23" } -} \ No newline at end of file +}