diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dc69c2d9..432c88b5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,19 +10,18 @@ We use UV for dependency management, running tests, and pre-commit hooks. ## Fork and Clone the Repository -1. Fork the repository: `https://github.com/clause/471c/fork` - -2. Choose your personal namespace or group for the fork. +1. Fork the repository on GitHub (use your account or organization). -3. Add `clause@udel.edu` and `wahid@udel.edu` as project members with the developer role. +2. Clone your fork locally: -4. Clone your fork locally either via command line or by using your preferred IDE. - -5. Link [CodeCov](https://about.codecov.io/) to your GitHub account (login to codecov using your github credentials). +```bash +git clone https://github.com//471c.git +cd 471c +``` -6. Update the badge URLs in README.md by replacing `clause` with `` and `471c` with ``. +3. Enable GitHub Actions for your fork if desired (Actions → Enable workflows). -7. Enable workflows for your fork by accessing the "actions" tab. +4. If you use CodeCov, connect it to your GitHub account separately. Update badge URLs in `README.md` as needed for your fork. ## Install project dependencies diff --git a/README.md b/README.md index 46ac9e85..a4154130 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Status -[![.github/workflows/ci.yml](https://github.com/clause/471c/actions/workflows/ci.yml/badge.svg)](https://github.com/clause/471c/actions/workflows/ci.yml) -[![Coverage](https://codecov.io/gh/clause/471c/branch/main/graph/badge.svg)](https://codecov.io/gh/clause/471c) +[![.github/workflows/ci.yml](https://github.com/JTFulkerson/471c/actions/workflows/ci.yml/badge.svg)](https://github.com/JTFulkerson/471c/actions/workflows/ci.yml) +[![Coverage](https://codecov.io/gh/JTFulkerson/471c/branch/main/graph/badge.svg)](https://codecov.io/gh/JTFulkerson/471c) # Contributing @@ -12,3 +12,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details. ![license](https://img.shields.io/badge/license-MIT-green.svg) This project is licensed under the MIT License. See [LICENSE](LICENSE) for details. + +## About + +`471c` is a multi-level language compiler project (L0..L4) with tooling and a VS Code language server that provides syntax and semantic diagnostics for L3/L4. See [lsp/README.md](lsp/README.md) for extension development instructions and the `packages/` directory for individual language packages. diff --git a/lsp/.gitignore b/lsp/.gitignore new file mode 100644 index 00000000..44b80760 --- /dev/null +++ b/lsp/.gitignore @@ -0,0 +1,20 @@ +out +node_modules +client/server +.vscode-test# Node dependencies +node_modules/ + +# TypeScript build output +out/ +dist/ +build/ +*.tsbuildinfo + +# Test and coverage output +coverage/ +.vscode-test/ + +# Local environment and editor files +.env +.env.* +.DS_Store diff --git a/lsp/.vscode/extensions.json b/lsp/.vscode/extensions.json new file mode 100644 index 00000000..af515502 --- /dev/null +++ b/lsp/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "dbaeumer.vscode-eslint" + ] +} \ No newline at end of file diff --git a/lsp/.vscode/launch.json b/lsp/.vscode/launch.json new file mode 100644 index 00000000..d6838f0c --- /dev/null +++ b/lsp/.vscode/launch.json @@ -0,0 +1,37 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +{ + "version": "0.2.0", + "configurations": [ + { + "type": "extensionHost", + "request": "launch", + "name": "Launch Client", + "env": { + "PYTHONPATH": "${workspaceFolder}/../packages/L4/src:${workspaceFolder}/../packages/L3/src:${workspaceFolder}/../packages/L2/src:${workspaceFolder}/../packages/util/src" + }, + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceRoot}"], + "outFiles": [ + "${workspaceRoot}/client/out/**/*.js", + "${workspaceRoot}/server/out/**/*.js" + ], + "autoAttachChildProcesses": true, + "preLaunchTask": { + "type": "npm", + "script": "watch" + } + }, + { + "name": "Language Server E2E Test", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}", + "--extensionTestsPath=${workspaceRoot}/client/out/test/index", + "${workspaceRoot}/client/testFixture" + ], + "outFiles": ["${workspaceRoot}/client/out/test/**/*.js"] + } + ] +} diff --git a/lsp/.vscode/settings.json b/lsp/.vscode/settings.json new file mode 100644 index 00000000..390d2993 --- /dev/null +++ b/lsp/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.insertSpaces": false, + "typescript.tsc.autoDetect": "off", + "typescript.preferences.quoteStyle": "single", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + } +} \ No newline at end of file diff --git a/lsp/.vscode/tasks.json b/lsp/.vscode/tasks.json new file mode 100644 index 00000000..070d88eb --- /dev/null +++ b/lsp/.vscode/tasks.json @@ -0,0 +1,33 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "compile", + "group": "build", + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": [ + "$tsc" + ] + }, + { + "type": "npm", + "script": "watch", + "isBackground": true, + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": [ + "$tsc-watch" + ] + } + ] +} \ No newline at end of file diff --git a/lsp/.vscodeignore b/lsp/.vscodeignore new file mode 100644 index 00000000..2a6b6a99 --- /dev/null +++ b/lsp/.vscodeignore @@ -0,0 +1,15 @@ +.vscode/** +**/*.ts +**/*.map +.gitignore +**/tsconfig.json +**/tsconfig.base.json +contributing.md +.travis.yml +client/node_modules/** +!client/node_modules/vscode-jsonrpc/** +!client/node_modules/vscode-languageclient/** +!client/node_modules/vscode-languageserver-protocol/** +!client/node_modules/vscode-languageserver-types/** +!client/node_modules/{minimatch,brace-expansion,concat-map,balanced-match}/** +!client/node_modules/{semver,lru-cache,yallist}/** \ No newline at end of file diff --git a/lsp/README.md b/lsp/README.md new file mode 100644 index 00000000..ec1428df --- /dev/null +++ b/lsp/README.md @@ -0,0 +1,48 @@ +# LSP for L3/L4 + +Language Server Protocol extension providing syntax and semantic diagnostics, semantic token highlighting for L3 and L4 language files (`.l3`, `.l4`). + +## Functionality + +This Language Server provides: +- **Syntax diagnostics** — parser errors (e.g., unmatched parentheses) +- **Semantic diagnostics** — type checking, unbound variables, duplicate bindings +- **Semantic token highlighting** — keywords and types +- **End-to-End tests** — regression tests for diagnostics and highlighting + +## Structure + +``` +. +├── client // VSCode extension client +│ ├── src +│ │ ├── extension.ts // Extension entry point +│ │ └── test/ // End-to-end tests +│ └── testFixture/ // L3/L4 test files +├── server // Language server (Node.js) +│ └── src/server.ts // Server logic; calls L3/L4 Python diagnostics CLI +├── package.json // Extension manifest +└── README.md // This file +``` + +## Running the Extension + +- Run `npm install` in this folder to install dependencies +- Open VS Code on this folder (or the repo root) +- Press F5 to launch the Extension Development Host (or go to Run → Launch Client) +- Open an `.l3` or `.l4` file to see diagnostics and highlighting +- Run `npm test` to run end-to-end tests + +## L4 Development notes + +When working on the L4 compiler and server integration you will often need the local Python packages to be importable by the language server. + +- The `Launch Client` launch configuration now sets `PYTHONPATH` so the Extension Development Host can run the L4 diagnostics CLI against workspace sources. +- To run the L4 CLI directly from the repo root (recommended): + +```bash +cd /Users/johnfulkerson/src/CISC471/471c +PYTHONPATH=packages/L4/src:packages/L3/src:packages/L2/src:packages/util/src .venv/bin/python -m L4.main packages/L4/examples/two.l4 --output /tmp/l4_two.py && .venv/bin/python /tmp/l4_two.py +``` + +- The server publishes a quick parenthesis syntax diagnostic immediately and replaces it with richer diagnostics produced by `python -m L4.main --diagnostics-json ` when available. diff --git a/lsp/client/package-lock.json b/lsp/client/package-lock.json new file mode 100644 index 00000000..e486e7e8 --- /dev/null +++ b/lsp/client/package-lock.json @@ -0,0 +1,1286 @@ +{ + "name": "lsp-sample-client", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "lsp-sample-client", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "glob": "^11.0.0", + "vscode-languageclient": "^9.0.1" + }, + "devDependencies": { + "@types/vscode": "^1.100.0", + "@vscode/test-electron": "^2.3.9" + }, + "engines": { + "vscode": "^1.100.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/vscode": { + "version": "1.102.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.102.0.tgz", + "integrity": "sha512-V9sFXmcXz03FtYTSUsYsu5K0Q9wH9w9V25slddcxrh5JgORD14LpnOA7ov0L9ALi+6HrTjskLJ/tY5zeRF3TFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/test-electron": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz", + "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "jszip": "^3.10.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@vscode/test-electron/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@vscode/test-electron/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@vscode/test-electron/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@vscode/test-electron/node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@vscode/test-electron/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/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "dependencies": { + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" + }, + "engines": { + "vscode": "^1.82.0" + } + }, + "node_modules/vscode-languageclient/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + }, + "dependencies": { + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@types/vscode": { + "version": "1.102.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.102.0.tgz", + "integrity": "sha512-V9sFXmcXz03FtYTSUsYsu5K0Q9wH9w9V25slddcxrh5JgORD14LpnOA7ov0L9ALi+6HrTjskLJ/tY5zeRF3TFA==", + "dev": true + }, + "@vscode/test-electron": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz", + "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==", + "dev": true, + "requires": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "jszip": "^3.10.1", + "semver": "^7.5.2" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "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 + } + } + }, + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==" + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + } + }, + "glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + } + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "requires": { + "@isaacs/cliui": "^8.0.2" + } + }, + "jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" + }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "requires": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "dependencies": { + "lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==" + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==" + }, + "vscode-languageclient": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "requires": { + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" + }, + "dependencies": { + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "requires": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/lsp/client/package.json b/lsp/client/package.json new file mode 100644 index 00000000..c0380784 --- /dev/null +++ b/lsp/client/package.json @@ -0,0 +1,19 @@ +{ + "name": "cisc471c-lsp-client", + "description": "VSCode part of a language server", + "author": "cisc471c", + "license": "MIT", + "version": "0.0.1", + "publisher": "cisc471c", + "engines": { + "vscode": "^1.100.0" + }, + "dependencies": { + "glob": "^11.0.0", + "vscode-languageclient": "^9.0.1" + }, + "devDependencies": { + "@types/vscode": "^1.100.0", + "@vscode/test-electron": "^2.3.9" + } +} \ No newline at end of file diff --git a/lsp/client/src/extension.ts b/lsp/client/src/extension.ts new file mode 100644 index 00000000..7601e386 --- /dev/null +++ b/lsp/client/src/extension.ts @@ -0,0 +1,60 @@ +import * as path from 'path'; +import { workspace, ExtensionContext } from 'vscode'; + +import { + LanguageClient, + LanguageClientOptions, + ServerOptions, + TransportKind +} from 'vscode-languageclient/node'; + +let client: LanguageClient; + +export function activate(context: ExtensionContext) { + // The server is implemented in node + const serverModule = context.asAbsolutePath( + path.join('server', 'out', 'server.js') + ); + + // If the extension is launched in debug mode then the debug server options are used + // Otherwise the run options are used + const serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { + module: serverModule, + transport: TransportKind.ipc, + } + }; + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for l0, l1, l2, l3, and l4 documents along with the unsaved buffers + // so language features work before first save + documentSelector: [ + { scheme: 'file', language: 'lsp-471c' }, + { scheme: 'untitled', language: 'lsp-471c' } + ], + synchronize: { + // Notify the server about file changes to '.clientrc files contained in the workspace + fileEvents: workspace.createFileSystemWatcher('**/.clientrc') + } + }; + + // Create the language client and start the client. + client = new LanguageClient( + '471cLSP', + 'Language Server for the 471c language', + serverOptions, + clientOptions + ); + + // Start the client. This will also launch the server + client.start(); +} + +export function deactivate(): Thenable | undefined { + if (!client) { + return undefined; + } + return client.stop(); +} diff --git a/lsp/client/src/test/completion.test.ts b/lsp/client/src/test/completion.test.ts new file mode 100644 index 00000000..8db15434 --- /dev/null +++ b/lsp/client/src/test/completion.test.ts @@ -0,0 +1,43 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import * as vscode from 'vscode'; +import * as assert from 'assert'; +import { getDocUri, activate } from './helper'; + +suite('Should do completion', () => { + const docUri = getDocUri('highlighting.l4'); + + test('Completes JS/TS in txt file', async () => { + await testCompletion(docUri, new vscode.Position(0, 0), { + items: [ + { label: 'JavaScript', kind: vscode.CompletionItemKind.Text }, + { label: 'TypeScript', kind: vscode.CompletionItemKind.Text } + ] + }); + }); +}); + +async function testCompletion( + docUri: vscode.Uri, + position: vscode.Position, + expectedCompletionList: vscode.CompletionList +) { + await activate(docUri); + + // Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion + const actualCompletionList = (await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + docUri, + position + )) as vscode.CompletionList; + + assert.ok(actualCompletionList.items.length >= 2); + expectedCompletionList.items.forEach((expectedItem, i) => { + const actualItem = actualCompletionList.items[i]; + assert.equal(actualItem.label, expectedItem.label); + assert.equal(actualItem.kind, expectedItem.kind); + }); +} diff --git a/lsp/client/src/test/diagnostics.test.ts b/lsp/client/src/test/diagnostics.test.ts new file mode 100644 index 00000000..d5b31e48 --- /dev/null +++ b/lsp/client/src/test/diagnostics.test.ts @@ -0,0 +1,75 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import * as vscode from 'vscode'; +import * as assert from 'assert'; +import { getDocUri, activate, waitForDiagnostics } from './helper'; + +suite('Should get diagnostics', () => { + const docUri = getDocUri('diagnostics.l3'); + const l4DocUri = getDocUri('diagnostics.l4'); + + test('Diagnoses unbound variables', async () => { + await testDiagnostics(docUri, [ + { message: 'Unbound variable: x', range: toRange(0, 7, 0, 8), severity: vscode.DiagnosticSeverity.Error, source: 'l3' } + ]); + }); + + test('Diagnoses malformed L4 syntax', async () => { + await activate(l4DocUri); + + const actualDiagnostics = vscode.languages.getDiagnostics(l4DocUri); + + assert.ok(actualDiagnostics.length > 0); + assert.equal(actualDiagnostics[0].severity, vscode.DiagnosticSeverity.Error); + assert.equal(actualDiagnostics[0].source, 'l4'); + assert.equal(actualDiagnostics[0].message, 'Missing closing parenthesis.'); + }); + + test('Diagnoses L4 type mismatch in if condition', async () => { + const ifTypeDocUri = getDocUri('diagnostics-if-type-mismatch.l4'); + await activate(ifTypeDocUri); + + const actualDiagnostics = await waitForDiagnostics(ifTypeDocUri); + + assert.ok(actualDiagnostics.length > 0, 'Expected at least one diagnostic'); + assert.equal(actualDiagnostics[0].severity, vscode.DiagnosticSeverity.Error); + assert.equal(actualDiagnostics[0].source, 'l4'); + // Just verify a diagnostic was reported; L4 type errors are semantic diagnostics + }); + + test('Diagnoses L4 type mismatch in primitive operands', async () => { + const primitiveTypeDocUri = getDocUri('diagnostics-primitive-type-mismatch.l4'); + await activate(primitiveTypeDocUri); + + const actualDiagnostics = await waitForDiagnostics(primitiveTypeDocUri); + + assert.ok(actualDiagnostics.length > 0, 'Expected at least one diagnostic'); + assert.equal(actualDiagnostics[0].severity, vscode.DiagnosticSeverity.Error); + assert.equal(actualDiagnostics[0].source, 'l4'); + // Just verify a diagnostic was reported; L4 type errors are semantic diagnostics + }); +}); + +function toRange(sLine: number, sChar: number, eLine: number, eChar: number) { + const start = new vscode.Position(sLine, sChar); + const end = new vscode.Position(eLine, eChar); + return new vscode.Range(start, end); +} + +async function testDiagnostics(docUri: vscode.Uri, expectedDiagnostics: vscode.Diagnostic[]) { + await activate(docUri); + + const actualDiagnostics = vscode.languages.getDiagnostics(docUri); + + assert.equal(actualDiagnostics.length, expectedDiagnostics.length); + + expectedDiagnostics.forEach((expectedDiagnostic, i) => { + const actualDiagnostic = actualDiagnostics[i]; + assert.equal(actualDiagnostic.message, expectedDiagnostic.message); + assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range); + assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity); + }); +} \ No newline at end of file diff --git a/lsp/client/src/test/helper.ts b/lsp/client/src/test/helper.ts new file mode 100644 index 00000000..b416bfbc --- /dev/null +++ b/lsp/client/src/test/helper.ts @@ -0,0 +1,62 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import * as vscode from 'vscode'; +import * as path from 'path'; + +export let doc: vscode.TextDocument; +export let editor: vscode.TextEditor; +export let documentEol: string; +export let platformEol: string; + +/** + * Activates the cisc471c.cisc471c-lsp-server extension + */ +export async function activate(docUri: vscode.Uri) { + // The extensionId is `publisher.name` from package.json + const ext = vscode.extensions.getExtension('cisc471c.cisc471c-lsp-server')!; + await ext.activate(); + try { + doc = await vscode.workspace.openTextDocument(docUri); + editor = await vscode.window.showTextDocument(doc); + await sleep(2000); // Wait for server activation + } catch (e) { + console.error(e); + } +} + +/** + * Wait for diagnostics to appear (for async operations like L4 type checking) + */ +export async function waitForDiagnostics(docUri: vscode.Uri, timeoutMs: number = 10000): Promise { + const startTime = Date.now(); + while (Date.now() - startTime < timeoutMs) { + const diags = vscode.languages.getDiagnostics(docUri); + if (diags.length > 0) { + return diags; + } + await sleep(100); + } + return []; +} + +async function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export const getDocPath = (p: string) => { + return path.resolve(__dirname, '../../testFixture', p); +}; +export const getDocUri = (p: string) => { + return vscode.Uri.file(getDocPath(p)); +}; + +export async function setTestContent(content: string): Promise { + const all = new vscode.Range( + doc.positionAt(0), + doc.positionAt(doc.getText().length) + ); + return editor.edit(eb => eb.replace(all, content)); +} diff --git a/lsp/client/src/test/index.ts b/lsp/client/src/test/index.ts new file mode 100644 index 00000000..ee3de701 --- /dev/null +++ b/lsp/client/src/test/index.ts @@ -0,0 +1,40 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +import * as path from 'path'; +import Mocha = require('mocha'); +import { glob } from 'glob'; + +export function run(): Promise { + // Create the mocha test + const mocha = new Mocha({ + ui: 'tdd', + color: true + }); + mocha.timeout(100000); + + const testsRoot = __dirname; + + return glob.glob('**.test.js', { cwd: testsRoot }).then(async files => { + + // Add files to the test suite + files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); + + try { + // Run the mocha test + await new Promise((resolve, reject) => { + mocha.run((failures: number) => { + if (failures > 0) { + reject(`${failures} tests failed.`); + } else { + resolve(); + } + }); + }); + } catch (err) { + console.error(err); + throw err; + } + }); +} \ No newline at end of file diff --git a/lsp/client/src/test/runTest.ts b/lsp/client/src/test/runTest.ts new file mode 100644 index 00000000..f8d2985f --- /dev/null +++ b/lsp/client/src/test/runTest.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; + +import { runTests } from '@vscode/test-electron'; + +async function main() { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, '../../../'); + + // The path to test runner + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, './index'); + + // Download VS Code, unzip it and run the integration test + await runTests({ extensionDevelopmentPath, extensionTestsPath }); + } catch { + console.error('Failed to run tests'); + process.exit(1); + } +} + +main(); diff --git a/lsp/client/src/test/semanticTokens.test.ts b/lsp/client/src/test/semanticTokens.test.ts new file mode 100644 index 00000000..ef2c6253 --- /dev/null +++ b/lsp/client/src/test/semanticTokens.test.ts @@ -0,0 +1,19 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { activate, getDocUri } from './helper'; + +suite('Should provide semantic tokens', () => { + const docUri = getDocUri('highlighting.l4'); + + test('Highlights L4 keywords and types', async () => { + await activate(docUri); + + const tokens = (await vscode.commands.executeCommand( + 'vscode.provideDocumentSemanticTokens', + docUri + )) as vscode.SemanticTokens | undefined; + + assert.ok(tokens); + assert.ok(tokens.data.length > 0); + }); +}); \ No newline at end of file diff --git a/lsp/client/testFixture/diagnostics-if-type-mismatch.l4 b/lsp/client/testFixture/diagnostics-if-type-mismatch.l4 new file mode 100644 index 00000000..ffdd6454 --- /dev/null +++ b/lsp/client/testFixture/diagnostics-if-type-mismatch.l4 @@ -0,0 +1,3 @@ +(l4 () + (let ((x 1)) + (if x 2 3))) diff --git a/lsp/client/testFixture/diagnostics-primitive-type-mismatch.l4 b/lsp/client/testFixture/diagnostics-primitive-type-mismatch.l4 new file mode 100644 index 00000000..31b1bed9 --- /dev/null +++ b/lsp/client/testFixture/diagnostics-primitive-type-mismatch.l4 @@ -0,0 +1,3 @@ +(l4 () + (let ((f (\ (x) x))) + (+ f 1))) diff --git a/lsp/client/testFixture/diagnostics.l3 b/lsp/client/testFixture/diagnostics.l3 new file mode 100644 index 00000000..118d8840 --- /dev/null +++ b/lsp/client/testFixture/diagnostics.l3 @@ -0,0 +1 @@ +(l3 () x) \ No newline at end of file diff --git a/lsp/client/testFixture/diagnostics.l4 b/lsp/client/testFixture/diagnostics.l4 new file mode 100644 index 00000000..e3bb8944 --- /dev/null +++ b/lsp/client/testFixture/diagnostics.l4 @@ -0,0 +1,3 @@ +(the + (-> Int (-> Int Int)) + (lambda (x) (lambda (y) (if (> (+ x y) 0) 1 0))) \ No newline at end of file diff --git a/lsp/client/testFixture/highlighting.l4 b/lsp/client/testFixture/highlighting.l4 new file mode 100644 index 00000000..4dea60ca --- /dev/null +++ b/lsp/client/testFixture/highlighting.l4 @@ -0,0 +1 @@ +l4 let letrec lambda if allocate load store begin and sole tuple tuple-get join project Int Bool Trivial Product \ No newline at end of file diff --git a/lsp/client/tsconfig.json b/lsp/client/tsconfig.json new file mode 100644 index 00000000..9ed6e579 --- /dev/null +++ b/lsp/client/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2024", + "lib": [ + "ES2024" + ], + "outDir": "out", + "rootDir": "src", + "sourceMap": true, + "strict": true + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + ".vscode-test" + ] +} \ No newline at end of file diff --git a/lsp/client/tsconfig.tsbuildinfo b/lsp/client/tsconfig.tsbuildinfo new file mode 100644 index 00000000..22f9b14b --- /dev/null +++ b/lsp/client/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./src/extension.ts","./src/test/completion.test.ts","./src/test/diagnostics.test.ts","./src/test/helper.ts","./src/test/index.ts","./src/test/runtest.ts","./src/test/semantictokens.test.ts"],"version":"5.9.2"} \ No newline at end of file diff --git a/lsp/eslint.config.mjs b/lsp/eslint.config.mjs new file mode 100644 index 00000000..ad264657 --- /dev/null +++ b/lsp/eslint.config.mjs @@ -0,0 +1,44 @@ +/** + * ESLint configuration for the project. + * + * See https://eslint.style and https://typescript-eslint.io for additional linting options. + */ +// @ts-check +import js from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import stylistic from '@stylistic/eslint-plugin'; + +export default tseslint.config( + { + ignores: [ + '**/.vscode-test', + '**/out', + ] + }, + js.configs.recommended, + ...tseslint.configs.recommended, + ...tseslint.configs.stylistic, + { + plugins: { + '@stylistic': stylistic + }, + rules: { + 'curly': 'warn', + '@stylistic/semi': ['warn', 'always'], + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/naming-convention': [ + 'warn', + { + 'selector': 'import', + 'format': ['camelCase', 'PascalCase'] + } + ], + '@typescript-eslint/no-unused-vars': [ + 'error', + { + 'argsIgnorePattern': '^_' + } + ] + } + } +); \ No newline at end of file diff --git a/lsp/package-lock.json b/lsp/package-lock.json new file mode 100644 index 00000000..bf68f125 --- /dev/null +++ b/lsp/package-lock.json @@ -0,0 +1,3750 @@ +{ + "name": "lsp-sample", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "lsp-sample", + "version": "1.0.0", + "hasInstallScript": true, + "license": "MIT", + "devDependencies": { + "@eslint/js": "^9.13.0", + "@stylistic/eslint-plugin": "^2.9.0", + "@types/mocha": "^10.0.6", + "@types/node": "^22", + "eslint": "^9.13.0", + "mocha": "^10.3.0", + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0" + }, + "engines": { + "vscode": "^1.100.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@stylistic/eslint-plugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.9.0.tgz", + "integrity": "sha512-OrDyFAYjBT61122MIY1a3SfEgy3YCMgt2vL4eoPmvTwDBwyQhAXurxNQznlRD/jESNfYWfID8Ej+31LljvF7Xg==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^8.8.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "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": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@types/estree": { + "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/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz", + "integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz", + "integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/type-utils": "8.39.0", + "@typescript-eslint/utils": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.39.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz", + "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", + "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.39.0", + "@typescript-eslint/types": "^8.39.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", + "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", + "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz", + "integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", + "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", + "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.39.0", + "@typescript-eslint/tsconfig-utils": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", + "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", + "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "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.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "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, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.31.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "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, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.0.tgz", + "integrity": "sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.39.0", + "@typescript-eslint/parser": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.6.tgz", + "integrity": "sha512-PlVX4Y0lDTN6E2V4ES2tEdyvXkeKzxa8c/vo0pxPr/TqbztddTP0yn7zZylIyiAuxerqj0Q5GhpJ1YJCP8LaZQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, + "@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.4.3" + } + }, + "@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true + }, + "@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "requires": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + } + }, + "@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true + }, + "@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.15" + } + }, + "@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@eslint/js": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "dev": true + }, + "@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true + }, + "@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "dev": true, + "requires": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + } + }, + "@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true + }, + "@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "requires": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "dependencies": { + "@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true + } + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@stylistic/eslint-plugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.9.0.tgz", + "integrity": "sha512-OrDyFAYjBT61122MIY1a3SfEgy3YCMgt2vL4eoPmvTwDBwyQhAXurxNQznlRD/jESNfYWfID8Ej+31LljvF7Xg==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "^8.8.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "dev": true + }, + "picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true + } + } + }, + "@types/estree": { + "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 + }, + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "@types/mocha": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", + "dev": true + }, + "@types/node": { + "version": "22.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz", + "integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==", + "dev": true, + "requires": { + "undici-types": "~6.21.0" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz", + "integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/type-utils": "8.39.0", + "@typescript-eslint/utils": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "dependencies": { + "ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true + } + } + }, + "@typescript-eslint/parser": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz", + "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/project-service": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", + "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", + "dev": true, + "requires": { + "@typescript-eslint/tsconfig-utils": "^8.39.0", + "@typescript-eslint/types": "^8.39.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", + "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0" + } + }, + "@typescript-eslint/tsconfig-utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", + "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", + "dev": true, + "requires": {} + }, + "@typescript-eslint/type-utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz", + "integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + } + }, + "@typescript-eslint/types": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", + "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", + "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.39.0", + "@typescript-eslint/tsconfig-utils": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", + "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", + "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.39.0", + "eslint-visitor-keys": "^4.2.1" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + } + } + }, + "acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.31.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "requires": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true + } + } + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "requires": { + "flat-cache": "^4.0.0" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "requires": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + } + }, + "flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true + }, + "import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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 + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true + }, + "serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "requires": {} + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true + }, + "typescript-eslint": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.0.tgz", + "integrity": "sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "8.39.0", + "@typescript-eslint/parser": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.0" + } + }, + "undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "y18n": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.6.tgz", + "integrity": "sha512-PlVX4Y0lDTN6E2V4ES2tEdyvXkeKzxa8c/vo0pxPr/TqbztddTP0yn7zZylIyiAuxerqj0Q5GhpJ1YJCP8LaZQ==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/lsp/package.json b/lsp/package.json new file mode 100644 index 00000000..68c1ae08 --- /dev/null +++ b/lsp/package.json @@ -0,0 +1,81 @@ +{ + "name": "cisc471c-lsp-server", + "description": "Language Server Protocol extension for L3/L4 languages", + "publisher": "cisc471c", + "license": "MIT", + "version": "1.0.0", + "categories": [ + "Programming Languages" + ], + "keywords": [ + "L3", + "L4", + "language", + "lsp" + ], + "engines": { + "vscode": "^1.100.0" + }, + "activationEvents": [ + "onLanguage:lsp-471c" + ], + "main": "./client/out/extension", + "contributes": { + "languages": [ + { + "id": "lsp-471c", + "extensions": [".l3", ".l4"] + } + ], + "semanticTokenScopes": [ + { + "language": "lsp-471c", + "scopes": { + "keyword": ["keyword.control.l4"], + "type": ["storage.type.l4"] + } + } + ], + "configuration": { + "type": "object", + "title": "Language Server for 471c configuration", + "properties": { + "471cLSP.maxNumberOfProblems": { + "scope": "resource", + "type": "number", + "default": 1000, + "description": "Controls the maximum number of diagnostics produced by the server." + }, + "471cLSP.trace.server": { + "scope": "window", + "type": "string", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "off", + "description": "Traces the communication between VS Code and the language server." + } + } + } + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -b", + "watch": "tsc -b -w", + "lint": "eslint", + "postinstall": "cd client && npm install && cd ../server && npm install && cd ..", + "test": "sh ./scripts/e2e.sh" + }, + "devDependencies": { + "@eslint/js": "^9.13.0", + "@stylistic/eslint-plugin": "^2.9.0", + "@types/mocha": "^10.0.6", + "@types/node": "^22", + "eslint": "^9.13.0", + "mocha": "^10.3.0", + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0" + } +} \ No newline at end of file diff --git a/lsp/scripts/e2e.sh b/lsp/scripts/e2e.sh new file mode 100755 index 00000000..860c62ef --- /dev/null +++ b/lsp/scripts/e2e.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +export CODE_TESTS_PATH="$(pwd)/client/out/test" +export CODE_TESTS_WORKSPACE="$(pwd)/client/testFixture" + +node "$(pwd)/client/out/test/runTest" \ No newline at end of file diff --git a/lsp/server/package-lock.json b/lsp/server/package-lock.json new file mode 100644 index 00000000..2ca80d82 --- /dev/null +++ b/lsp/server/package-lock.json @@ -0,0 +1,92 @@ +{ + "name": "lsp-sample-server", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "lsp-sample-server", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-textdocument": "^1.0.11" + }, + "engines": { + "node": "*" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", + "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + } + }, + "dependencies": { + "vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==" + }, + "vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "requires": { + "vscode-languageserver-protocol": "3.17.5" + } + }, + "vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "requires": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "vscode-languageserver-textdocument": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", + "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" + }, + "vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + } + } +} diff --git a/lsp/server/package.json b/lsp/server/package.json new file mode 100644 index 00000000..d60e63af --- /dev/null +++ b/lsp/server/package.json @@ -0,0 +1,19 @@ +{ + "name": "lsp-sample-server", + "description": "Example implementation of a language server in node.", + "version": "1.0.0", + "author": "cisc471c", + "license": "MIT", + "engines": { + "node": "*" + }, + "repository": { + "type": "git", + "url": "https://github.com/JTFulkerson/471c" + }, + "dependencies": { + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-textdocument": "^1.0.11" + }, + "scripts": {} +} diff --git a/lsp/server/src/server.ts b/lsp/server/src/server.ts new file mode 100644 index 00000000..32d5e562 --- /dev/null +++ b/lsp/server/src/server.ts @@ -0,0 +1,673 @@ +import { + createConnection, + TextDocuments, + Diagnostic, + DiagnosticSeverity, + ProposedFeatures, + InitializeParams, + DidChangeConfigurationNotification, + CompletionItem, + CompletionItemKind, + TextDocumentPositionParams, + TextDocumentSyncKind, + InitializeResult, + SemanticTokensBuilder, + SemanticTokensRequest +} from 'vscode-languageserver/node'; +import { execFile } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import { mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { promisify } from 'node:util'; + +import { + TextDocument +} from 'vscode-languageserver-textdocument'; + +const execFileAsync = promisify(execFile); +const repositoryRootPath = dirname(dirname(dirname(__dirname))); + +// Create a connection for the server, using Node's IPC as a transport. +// Also include all preview / proposed LSP features. +const connection = createConnection(ProposedFeatures.all); + +// Create a simple text document manager. +const documents = new TextDocuments(TextDocument); + +let hasConfigurationCapability = false; +let hasWorkspaceFolderCapability = false; +let hasDiagnosticRelatedInformationCapability = false; +let workspaceRootPath: string | undefined; + +interface L3Diagnostic { + stage: 'syntax' | 'semantic' | 'internal'; + severity: 'error' | 'warning' | 'information'; + code: string; + message: string; + line?: number; + column?: number; + end_line?: number; + end_column?: number; + snippet?: string; +} + +interface L3DiagnosticsReport { + ok: boolean; + diagnostics: L3Diagnostic[]; +} + +interface ExecFailure { + code?: string | number; + stdout?: string; + stderr?: string; +} + +connection.onInitialize((params: InitializeParams) => { + const capabilities = params.capabilities; + workspaceRootPath = extractWorkspaceRootPath(params); + + connection.console.log('[Initialize] Server initializing'); + connection.console.log(`[Initialize] Client supports textDocument.semanticTokens: ${!!capabilities.textDocument?.semanticTokens}`); + + // Does the client support the `workspace/configuration` request? + // If not, we fall back using global settings. + hasConfigurationCapability = !!( + capabilities.workspace && !!capabilities.workspace.configuration + ); + hasWorkspaceFolderCapability = !!( + capabilities.workspace && !!capabilities.workspace.workspaceFolders + ); + hasDiagnosticRelatedInformationCapability = !!( + capabilities.textDocument && + capabilities.textDocument.publishDiagnostics && + capabilities.textDocument.publishDiagnostics.relatedInformation + ); + + const result: InitializeResult = { + capabilities: { + textDocumentSync: TextDocumentSyncKind.Incremental, + // Tell the client that this server supports code completion. + completionProvider: { + resolveProvider: true + }, + // Semantic tokens for simple keyword/type highlighting in .l4 files + semanticTokensProvider: { + legend: { + tokenTypes: ['keyword', 'type'], + tokenModifiers: [] + }, + full: true + } + } + }; + + connection.console.log('[Initialize] Advertising semanticTokensProvider capability'); + + if (hasWorkspaceFolderCapability) { + result.capabilities.workspace = { + workspaceFolders: { + supported: true + } + }; + } + return result; +}); + +connection.onInitialized(() => { + connection.console.log('[Initialized] Server initialized'); + if (hasConfigurationCapability) { + // Register for all configuration changes. + connection.client.register(DidChangeConfigurationNotification.type, undefined); + } + if (hasWorkspaceFolderCapability) { + connection.workspace.onDidChangeWorkspaceFolders(_event => { + connection.console.log('Workspace folder change event received.'); + }); + } +}); + +// The example settings +interface ExampleSettings { + maxNumberOfProblems: number; +} + +// The global settings, used when the `workspace/configuration` request is not supported by the client. +// Please note that this is not the case when using this server with the client provided in this example +// but could happen with other clients. +const defaultSettings: ExampleSettings = { maxNumberOfProblems: 1000 }; +let globalSettings: ExampleSettings = defaultSettings; + +// Cache the settings of all open documents +const documentSettings = new Map>(); + +connection.onDidChangeConfiguration(change => { + if (hasConfigurationCapability) { + // Reset all cached document settings + documentSettings.clear(); + } else { + globalSettings = ( + (change.settings['471cLSP'] || defaultSettings) + ); + } +}); + +function getDocumentSettings(resource: string): Thenable { + if (!hasConfigurationCapability) { + return Promise.resolve(globalSettings); + } + let result = documentSettings.get(resource); + if (!result) { + result = connection.workspace.getConfiguration({ + scopeUri: resource, + section: '471cLSP' + }); + documentSettings.set(resource, result); + } + return result; +} + +// Only keep settings for open documents +documents.onDidClose(e => { + documentSettings.delete(e.document.uri); + connection.sendDiagnostics({ uri: e.document.uri, diagnostics: [] }); +}); + +// The content of a text document has changed. This event is emitted +// when the text document first opened or when its content has changed. +documents.onDidChangeContent(change => { + void publishDiagnostics(change.document); +}); + +documents.onDidOpen(event => { + connection.console.log(`[DocumentOpen] Opened: ${event.document.uri}, languageId: ${event.document.languageId}`); + void publishDiagnostics(event.document); +}); + +async function publishDiagnostics(textDocument: TextDocument): Promise { + const diagnostics = await validateTextDocument(textDocument); + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); +} + +function extractWorkspaceRootPath(params: InitializeParams): string | undefined { + const workspaceFolderUri = params.workspaceFolders && params.workspaceFolders.length > 0 + ? params.workspaceFolders[0].uri + : undefined; + const rootUri = workspaceFolderUri ?? params.rootUri ?? undefined; + if (!rootUri || !rootUri.startsWith('file://')) { + return undefined; + } + + try { + return fileURLToPath(rootUri); + } catch { + return undefined; + } +} + +function toFilePath(uri: string): string { + if (!uri.startsWith('file://')) { + throw new Error(`Unsupported URI scheme: ${uri}`); + } + return fileURLToPath(uri); +} + +function toSeverity(severity: L3Diagnostic['severity']): DiagnosticSeverity { + switch (severity) { + case 'warning': + return DiagnosticSeverity.Warning; + case 'information': + return DiagnosticSeverity.Information; + default: + return DiagnosticSeverity.Error; + } +} + +function toPosition(value: number | undefined): number { + if (typeof value !== 'number' || Number.isNaN(value)) { + return 0; + } + return Math.max(0, value - 1); +} + +function toLspDiagnostic(uri: string, diagnostic: L3Diagnostic): Diagnostic { + const startLine = toPosition(diagnostic.line); + const startCharacter = toPosition(diagnostic.column); + const endLine = typeof diagnostic.end_line === 'number' ? toPosition(diagnostic.end_line) : startLine; + const endCharacter = typeof diagnostic.end_column === 'number' + ? toPosition(diagnostic.end_column) + : startCharacter + 1; + + const lspDiagnostic: Diagnostic = { + severity: toSeverity(diagnostic.severity), + range: { + start: { line: startLine, character: startCharacter }, + end: { line: endLine, character: Math.max(endCharacter, startCharacter + 1) } + }, + message: diagnostic.message, + source: 'l3', + code: diagnostic.code + }; + + if (hasDiagnosticRelatedInformationCapability && diagnostic.snippet) { + lspDiagnostic.relatedInformation = [ + { + location: { + uri, + range: lspDiagnostic.range + }, + message: `Source: ${diagnostic.snippet}` + } + ]; + } + + return lspDiagnostic; +} + +// Allow mapping diagnostics for multiple languages (L3/L4) +function toLspDiagnosticFor(uri: string, diagnostic: L3Diagnostic, source: string): Diagnostic { + const d = toLspDiagnostic(uri, diagnostic); + d.source = source; + return d; +} + +function toL4SyntaxDiagnostic(uri: string, text: string): Diagnostic | null { + const stack: Array<{ line: number; character: number }> = []; + const lines = text.split(/\r?\n/); + + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + const line = lines[lineIndex]; + for (let character = 0; character < line.length; character++) { + const ch = line[character]; + if (ch === '(') { + stack.push({ line: lineIndex, character }); + } else if (ch === ')') { + if (stack.length === 0) { + return { + severity: DiagnosticSeverity.Error, + range: { + start: { line: lineIndex, character }, + end: { line: lineIndex, character: character + 1 } + }, + message: 'Unmatched closing parenthesis.', + source: 'l4', + code: 'L4_SYNTAX_ERROR' + }; + } + stack.pop(); + } + } + } + + if (stack.length === 0) { + return null; + } + + const lastLineIndex = lines.length - 1; + const lastCharacter = lines[lastLineIndex]?.length ?? 0; + return { + severity: DiagnosticSeverity.Error, + range: { + start: { line: lastLineIndex, character: lastCharacter }, + end: { line: lastLineIndex, character: lastCharacter + 1 } + }, + message: 'Missing closing parenthesis.', + source: 'l4', + code: 'L4_SYNTAX_ERROR' + }; +} + +async function createTempL3File(text: string): Promise<{ dirPath: string; filePath: string }> { + const dirPath = await mkdtemp(join(tmpdir(), 'l3-lsp-')); + const filePath = join(dirPath, 'document.l3'); + await writeFile(filePath, text, 'utf8'); + return { dirPath, filePath }; +} + +async function runL3Diagnostics(filePath: string): Promise { + const projectRootCandidates = [workspaceRootPath, repositoryRootPath].filter((candidate): candidate is string => { + return typeof candidate === 'string' && existsSync(join(candidate, 'packages', 'L3')); + }); + const workingDirectory = projectRootCandidates[0] ?? workspaceRootPath ?? dirname(filePath); + const commandCandidates: Array<{ command: string; args: string[] }> = []; + + for (const rootPath of projectRootCandidates) { + const venvL3 = join(rootPath, '.venv', 'bin', 'l3'); + if (existsSync(venvL3)) { + commandCandidates.push({ command: venvL3, args: ['--diagnostics-json', filePath] }); + } + + const venvPython = join(rootPath, '.venv', 'bin', 'python'); + if (existsSync(venvPython)) { + commandCandidates.push({ command: venvPython, args: ['-m', 'L3.main', '--diagnostics-json', filePath] }); + } + } + + commandCandidates.push({ command: 'l3', args: ['--diagnostics-json', filePath] }); + commandCandidates.push({ command: 'python3', args: ['-m', 'L3.main', '--diagnostics-json', filePath] }); + + let lastError = ''; + + // Build a PYTHONPATH from several candidate roots so `-m L4.main` can import local package sources. + const rootCandidates = new Set(projectRootCandidates); + if (repositoryRootPath) rootCandidates.add(repositoryRootPath); + if (workspaceRootPath) rootCandidates.add(workspaceRootPath); + rootCandidates.add(process.cwd()); + + const pyPathEntries: string[] = []; + for (const root of rootCandidates) { + if (!root) continue; + pyPathEntries.push(join(root, 'packages', 'L4', 'src')); + pyPathEntries.push(join(root, 'packages', 'L3', 'src')); + pyPathEntries.push(join(root, 'packages', 'L2', 'src')); + pyPathEntries.push(join(root, 'packages', 'util', 'src')); + } + + // Keep only unique and existing paths (but allow non-existing to avoid being too strict) + const unique = Array.from(new Set(pyPathEntries)); + const env = { ...(process.env as NodeJS.ProcessEnv) }; + env.PYTHONPATH = unique.join(':'); + + for (const candidate of commandCandidates) { + try { + const result = await execFileAsync(candidate.command, candidate.args, { cwd: workingDirectory, env }); + const parsed = JSON.parse(result.stdout) as L3DiagnosticsReport; + if (Array.isArray(parsed.diagnostics)) { + return parsed; + } + lastError = `Invalid diagnostics JSON from ${candidate.command}`; + } catch (error) { + const failure = error as ExecFailure; + if (failure.code === 'ENOENT') { + continue; + } + + const stdout = typeof failure.stdout === 'string' ? failure.stdout : ''; + if (stdout.trim().length > 0) { + try { + const parsed = JSON.parse(stdout) as L3DiagnosticsReport; + if (Array.isArray(parsed.diagnostics)) { + return parsed; + } + lastError = `Invalid diagnostics JSON from ${candidate.command}`; + continue; + } catch { + lastError = stdout.trim(); + continue; + } + } + + lastError = typeof failure.stderr === 'string' && failure.stderr.trim().length > 0 + ? failure.stderr.trim() + : `Failed to run ${candidate.command}`; + } + } + + return { + ok: false, + diagnostics: [ + { + stage: 'internal', + severity: 'error', + code: 'L3_DIAGNOSTICS_UNAVAILABLE', + message: lastError || 'Could not execute L3 diagnostics command.' + } + ] + }; +} + +async function createTempL4File(text: string): Promise<{ dirPath: string; filePath: string }> { + const dirPath = await mkdtemp(join(tmpdir(), 'l4-lsp-')); + const filePath = join(dirPath, 'document.l4'); + await writeFile(filePath, text, 'utf8'); + return { dirPath, filePath }; +} + +async function runL4Diagnostics(filePath: string): Promise { + const projectRootCandidates = [workspaceRootPath, repositoryRootPath].filter((candidate): candidate is string => { + return typeof candidate === 'string' && existsSync(join(candidate, 'packages', 'L4')); + }); + const workingDirectory = projectRootCandidates[0] ?? workspaceRootPath ?? dirname(filePath); + const commandCandidates: Array<{ command: string; args: string[] }> = []; + + for (const rootPath of projectRootCandidates) { + const venvL4 = join(rootPath, '.venv', 'bin', 'l4'); + if (existsSync(venvL4)) { + commandCandidates.push({ command: venvL4, args: ['--diagnostics-json', filePath] }); + } + + const venvPython = join(rootPath, '.venv', 'bin', 'python'); + if (existsSync(venvPython)) { + commandCandidates.push({ command: venvPython, args: ['-m', 'L4.main', '--diagnostics-json', filePath] }); + } + } + + commandCandidates.push({ command: 'l4', args: ['--diagnostics-json', filePath] }); + commandCandidates.push({ command: 'python3', args: ['-m', 'L4.main', '--diagnostics-json', filePath] }); + + let lastError = ''; + + for (const candidate of commandCandidates) { + try { + const result = await execFileAsync(candidate.command, candidate.args, { cwd: workingDirectory }); + const parsed = JSON.parse(result.stdout) as L3DiagnosticsReport; + if (Array.isArray(parsed.diagnostics)) { + return parsed; + } + lastError = `Invalid diagnostics JSON from ${candidate.command}`; + } catch (error) { + const failure = error as ExecFailure; + if (failure.code === 'ENOENT') { + continue; + } + + const stdout = typeof failure.stdout === 'string' ? failure.stdout : ''; + if (stdout.trim().length > 0) { + try { + const parsed = JSON.parse(stdout) as L3DiagnosticsReport; + if (Array.isArray(parsed.diagnostics)) { + return parsed; + } + lastError = `Invalid diagnostics JSON from ${candidate.command}`; + continue; + } catch { + lastError = stdout.trim(); + continue; + } + } + + lastError = typeof failure.stderr === 'string' && failure.stderr.trim().length > 0 + ? failure.stderr.trim() + : `Failed to run ${candidate.command}`; + } + } + + return { + ok: false, + diagnostics: [ + { + stage: 'internal', + severity: 'error', + code: 'L4_DIAGNOSTICS_UNAVAILABLE', + message: lastError || 'Could not execute L4 diagnostics command.' + } + ] + }; +} + +async function validateTextDocument(textDocument: TextDocument): Promise { + // Get settings for every validate run (used for both L3 and L4) + const settings = await getDocumentSettings(textDocument.uri); + + if (textDocument.uri.endsWith('.l4')) { + // Publish a quick syntax-only diagnostic immediately for fast feedback. + const quick = toL4SyntaxDiagnostic(textDocument.uri, textDocument.getText()); + + // Asynchronously run the L4 diagnostics CLI and replace diagnostics when it returns. + (async () => { + let tempDirPath: string | undefined; + try { + toFilePath(textDocument.uri); + const temp = await createTempL4File(textDocument.getText()); + tempDirPath = temp.dirPath; + + const report = await runL4Diagnostics(temp.filePath); + let diags: Diagnostic[]; + if (!report.ok) { + // If CLI unavailable, prefer quick syntax diagnostic if present + const syntaxDiagnostic = toL4SyntaxDiagnostic(textDocument.uri, textDocument.getText()); + diags = syntaxDiagnostic ? [syntaxDiagnostic] : report.diagnostics + .slice(0, settings.maxNumberOfProblems) + .map(diagnostic => toLspDiagnosticFor(textDocument.uri, diagnostic, 'l4')); + } else { + diags = report.diagnostics + .slice(0, settings.maxNumberOfProblems) + .map(diagnostic => toLspDiagnosticFor(textDocument.uri, diagnostic, 'l4')); + } + + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: diags }); + } catch (error) { + // If running the CLI fails, keep the quick syntax diagnostic (no-op) + connection.console.log(`[L4 Diagnostics] error: ${error instanceof Error ? error.message : String(error)}`); + } finally { + if (tempDirPath) { + await rm(tempDirPath, { recursive: true, force: true }); + } + } + })(); + + return quick ? [quick] : []; + } + + if (!textDocument.uri.endsWith('.l3')) { + return []; + } + + // In this example we get the settings for every validate run. + let tempDirPath: string | undefined; + try { + toFilePath(textDocument.uri); + const temp = await createTempL3File(textDocument.getText()); + tempDirPath = temp.dirPath; + + const report = await runL3Diagnostics(temp.filePath); + return report.diagnostics + .slice(0, settings.maxNumberOfProblems) + .map(diagnostic => toLspDiagnostic(textDocument.uri, diagnostic)); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown diagnostics error'; + return [ + { + severity: DiagnosticSeverity.Error, + range: { + start: { line: 0, character: 0 }, + end: { line: 0, character: 1 } + }, + message, + source: 'l3', + code: 'L3_DIAGNOSTICS_RUNTIME_ERROR' + } + ]; + } finally { + if (tempDirPath) { + await rm(tempDirPath, { recursive: true, force: true }); + } + } +} + +connection.onDidChangeWatchedFiles(_change => { + // Monitored files have change in VSCode + connection.console.log('We received a file change event'); +}); + +// This handler provides the initial list of the completion items. +connection.onCompletion( + (_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => { + // The pass parameter contains the position of the text document in + // which code complete got requested. For the example we ignore this + // info and always provide the same completion items. + return [ + { + label: 'TypeScript', + kind: CompletionItemKind.Text, + data: 1 + }, + { + label: 'JavaScript', + kind: CompletionItemKind.Text, + data: 2 + } + ]; + } +); + +// This handler resolves additional information for the item selected in +// the completion list. +connection.onCompletionResolve( + (item: CompletionItem): CompletionItem => { + if (item.data === 1) { + item.detail = 'TypeScript details'; + item.documentation = 'TypeScript documentation'; + } else if (item.data === 2) { + item.detail = 'JavaScript details'; + item.documentation = 'JavaScript documentation'; + } + return item; + } +); + + // Semantic tokens: simple keyword/type highlighting for .l4 files +// Semantic tokens: simple keyword/type highlighting for .l4 files +connection.onRequest(SemanticTokensRequest.type, params => { + const uri = params.textDocument.uri; + const doc = documents.get(uri); + connection.console.log(`[SemanticTokens] Received request for uri=${uri}`); + connection.console.log(`[SemanticTokens] Document exists: ${!!doc}, ends with .l4: ${uri.endsWith('.l4')}`); + + if (!uri.endsWith('.l4') || !doc) { + connection.console.log(`[SemanticTokens] Skipping - not .l4 or no document`); + return { data: [] }; + } + + // Keywords and types derived from packages/L4/src/L4/L4.lark + const typeKeywords = ['Int', 'Bool', 'Trivial', 'Product']; + const syntaxKeywords = [ + 'l4', 'let', 'letrec', 'lambda', 'if', 'allocate', 'load', 'store', 'begin', + 'and', 'sole', 'tuple', 'tuple-get', 'join', 'project' + ]; + + const all = [...typeKeywords, ...syntaxKeywords]; + // Build a regex that matches whole words; escape names just in case. + const escaped = all.map(s => s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')); + const regex = new RegExp(`\\b(?:${escaped.join('|')})\\b`, 'g'); + + const builder = new SemanticTokensBuilder(); + const lines = doc.getText().split(/\r?\n/); + connection.console.log(`[SemanticTokens] Processing ${lines.length} lines from document`); + + for (let line = 0; line < lines.length; line++) { + const text = lines[line]; + let match: RegExpExecArray | null; + regex.lastIndex = 0; + while ((match = regex.exec(text)) !== null) { + const word = match[0]; + const startChar = match.index; + const length = word.length; + const tokenType = typeKeywords.includes(word) ? 1 /* 'type' */ : 0 /* 'keyword' */; + connection.console.log(`[SemanticTokens] Token: "${word}" at ${line}:${startChar}, type=${tokenType}`); + builder.push(line, startChar, length, tokenType, 0); + } + } + + const result = builder.build(); + connection.console.log(`[SemanticTokens] Returning ${result.data.length} data elements`); + return result; +}); + +// Make the text document manager listen on the connection +// for open, change and close text document events +documents.listen(connection); + +// Listen on the connection +connection.listen(); diff --git a/lsp/server/tsconfig.json b/lsp/server/tsconfig.json new file mode 100644 index 00000000..e0e47954 --- /dev/null +++ b/lsp/server/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2024", + "lib": [ + "ES2024" + ], + "module": "commonjs", + "sourceMap": true, + "strict": true, + "outDir": "out", + "rootDir": "src" + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + ".vscode-test" + ] +} \ No newline at end of file diff --git a/lsp/server/tsconfig.tsbuildinfo b/lsp/server/tsconfig.tsbuildinfo new file mode 100644 index 00000000..e1008e3e --- /dev/null +++ b/lsp/server/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./src/server.ts"],"version":"5.9.2"} \ No newline at end of file diff --git a/lsp/themes/l4-theme.json b/lsp/themes/l4-theme.json new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/lsp/themes/l4-theme.json @@ -0,0 +1 @@ + diff --git a/lsp/tsconfig.json b/lsp/tsconfig.json new file mode 100644 index 00000000..af46f354 --- /dev/null +++ b/lsp/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2024", + "lib": [ + "ES2024" + ], + "outDir": "out", + "rootDir": "src", + "sourceMap": true + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + ".vscode-test" + ], + "references": [ + { + "path": "./client" + }, + { + "path": "./server" + } + ] +} \ No newline at end of file diff --git a/packages/L0/README.md b/packages/L0/README.md index e69de29b..da6e2bc1 100644 --- a/packages/L0/README.md +++ b/packages/L0/README.md @@ -0,0 +1,3 @@ +L0 + +L0 is now a procedure based language as compared to our previous expression and statement languages. Here, a program has a sequence of named procedures each containing its own parameters and a statement body. A statement can have copy, immediate, primative, branch, allocate, load, store, address, call, and hault. The main differences are that L0 organizes code into multiple named procedures whereas L1 had a single extry program with a statement body. L1 also uses abstract and apply for its functions where L0 replaces them with address and call removing a layer of abstraction. L0 also makes procedures ex plicity and predefined rather than in others where they were constructred functions at runtime. \ No newline at end of file diff --git a/packages/L1/README.md b/packages/L1/README.md index e69de29b..6eaeb810 100644 --- a/packages/L1/README.md +++ b/packages/L1/README.md @@ -0,0 +1,3 @@ +L1 + +Now in L1 it is a statement based language with very explicit flow control. There exists a program which has parameters and a body, and the statements are similar to L2 and L3 with a few differences. We have copy, immediate, primative, branch, abstract, apply, allocate, load, store, and hault. The differences with L1 as compared to L2 and L3 is that L2 used nested terms as expressions while now L1 replaces all of those with sequential statements. Doing this makes flow control explicit as each statement has a then continuation, except for apply and hault. L1 also adds a copy ability to move values between identifiers, and hault to end the program. L1 removes the idea of let, reference, and begin. \ No newline at end of file diff --git a/packages/L1/src/L1/close.py b/packages/L1/src/L1/close.py new file mode 100644 index 00000000..953915bd --- /dev/null +++ b/packages/L1/src/L1/close.py @@ -0,0 +1,202 @@ +from functools import partial +from typing import Callable + +from L0 import syntax as L0 + +from . import syntax as L1 + + +def free_variables(statement: L1.Statement) -> set[L1.Identifier]: + match statement: + case L1.Copy(destination=destination, source=source, then=then): + return ({source} | free_variables(then)) - {destination} + case L1.Abstract(destination=destination, parameters=parameters, body=body, then=then): + return (free_variables(body) - set(parameters) | free_variables(then)) - {destination} + case L1.Apply(target=target, arguments=arguments): + return {target} | set(arguments) + case L1.Immediate(destination=destination, then=then): + return free_variables(then) - {destination} + case L1.Primitive(destination=destination, left=left, right=right, then=then): + return ({left, right} | free_variables(then)) - {destination} + case L1.Branch(left=left, right=right, then=then, otherwise=otherwise): + return {left, right} | free_variables(then) | free_variables(otherwise) + case L1.Allocate(destination=destination, then=then): + return free_variables(then) - {destination} + case L1.Load(destination=destination, base=base, then=then): + return {base} | free_variables(then) - {destination} + case L1.Store(base=base, value=value, then=then): + return {base, value} | free_variables(then) + case L1.Halt(value=value): + return {value} + case _: # pragma: no cover + return set() + + +def close_term( + statement: L1.Statement, + lift: Callable[[L0.Procedure], None], + fresh: Callable[[str], str], +) -> L0.Statement: + recur = partial(close_term, lift=lift, fresh=fresh) # noqa: F841 + + match statement: + case L1.Copy(destination=destination, source=source, then=then): + return L0.Copy( + destination=destination, + source=source, + then=recur(then), + ) + + case L1.Abstract(destination=destination, parameters=parameters, body=body, then=then): + # 1. Close the abstract / lift to top level + name = fresh("proc") + env_p = fresh("env") + + fvs = list(free_variables(body) - set(parameters)) + + result = recur(body) + for i, fv in enumerate(fvs): + result = L0.Load( + destination=fv, + base=env_p, + index=i, + then=result, + ) + + lift( + L0.Procedure( + name=name, + parameters=[*parameters, env_p], + body=result, + ) + ) + + # 2. Create the closure (tuple of code and environment) + env = fresh("env") + code = fresh("code") + result = L0.Address( + destination=code, + name=name, + then=L0.Allocate( + destination=destination, + count=2, + then=L0.Store( + base=destination, + index=0, + value=code, + then=L0.Store( + base=destination, + index=1, + value=env, + then=recur(then), + ), + ), + ), + ) + + for i, fv in enumerate(fvs): + result = L0.Store( + base=env, + index=i, + value=fv, + then=result, + ) + + return L0.Allocate( + destination=env, + count=len(fvs), + then=result, + ) + + case L1.Apply(target=target, arguments=arguments): + # 1. Seperate code and environment from the closure + # 2. Call the code with the argument and environment + code = fresh("code") + env = fresh("env") + return L0.Load( + destination=code, + base=target, + index=0, + then=L0.Load( + destination=env, + base=target, + index=1, + then=L0.Call( + target=code, + arguments=[*arguments, env], + ), + ), + ) + + case L1.Immediate(destination=destination, value=value, then=then): + return L0.Immediate( + destination=destination, + value=value, + then=recur(then), + ) + + case L1.Primitive(destination=destination, left=left, right=right, operator=operator, then=then): + return L0.Primitive( + destination=destination, + left=left, + right=right, + operator=operator, + then=recur(then), + ) + + case L1.Branch(left=left, right=right, operator=operator, then=then, otherwise=otherwise): + return L0.Branch( + left=left, + right=right, + operator=operator, + then=recur(then), + otherwise=recur(otherwise), + ) + + case L1.Allocate(destination=destination, count=count, then=then): + return L0.Allocate( + destination=destination, + count=count, + then=recur(then), + ) + + case L1.Load(destination=destination, base=base, index=index, then=then): + return L0.Load( + destination=destination, + base=base, + index=index, + then=recur(then), + ) + + case L1.Store(base=base, index=index, value=value, then=then): + return L0.Store( + base=base, + index=index, + value=value, + then=recur(then), + ) + + case L1.Halt(value=value): # pragma: no branch + return L0.Halt(value=value) + + +def close_program(program: L1.Program, fresh: Callable[[str], str]) -> L0.Program: + match program: + case L1.Program(parameters=parameters, body=body): # pragma: no branch + procedures = list[L0.Procedure]() + + body = close_term( + body, + procedures.append, + fresh, + ) + return L0.Program( + procedures=[ + *procedures, + L0.Procedure( + name="l0", + parameters=parameters, + body=body, + ), + ], + ) diff --git a/packages/L1/test/L1/test_close.py b/packages/L1/test/L1/test_close.py new file mode 100644 index 00000000..ad9b55cb --- /dev/null +++ b/packages/L1/test/L1/test_close.py @@ -0,0 +1,231 @@ +from L0 import syntax as L0 +from L1 import syntax as L1 +from L1.close import close_program, close_term, free_variables +from util.sequential_name_generator import SequentialNameGenerator + + +def test_free_variables_copy(): + statement = L1.Copy( + destination="x", + source="y", + then=L1.Halt(value="x"), + ) + + assert free_variables(statement) == {"y"} + + +def test_free_variables_abstract(): + statement = L1.Abstract( + destination="f", + parameters=["x"], + body=L1.Primitive( + destination="t", + operator="+", + left="x", + right="z", + then=L1.Halt(value="t"), + ), + then=L1.Halt(value="f"), + ) + + assert free_variables(statement) == {"z"} + + +def test_free_variables_apply(): + statement = L1.Apply(target="f", arguments=["x", "y"]) + + assert free_variables(statement) == {"f", "x", "y"} + + +def test_free_variables_immediate(): + statement = L1.Immediate( + destination="x", + value=0, + then=L1.Halt(value="x"), + ) + + assert free_variables(statement) == set() + + +def test_free_variables_primitive(): + statement = L1.Primitive( + destination="x", + operator="+", + left="a", + right="b", + then=L1.Halt(value="x"), + ) + + assert free_variables(statement) == {"a", "b"} + + +def test_free_variables_branch(): + statement = L1.Branch( + operator="<", + left="a", + right="b", + then=L1.Halt(value="x"), + otherwise=L1.Halt(value="y"), + ) + + assert free_variables(statement) == {"a", "b", "x", "y"} + + +def test_free_variables_allocate(): + statement = L1.Allocate( + destination="p", + count=1, + then=L1.Halt(value="p"), + ) + + assert free_variables(statement) == set() + + +def test_free_variables_load(): + statement = L1.Load( + destination="x", + base="p", + index=0, + then=L1.Halt(value="x"), + ) + + assert free_variables(statement) == {"p"} + + +def test_free_variables_store(): + statement = L1.Store( + base="p", + index=0, + value="x", + then=L1.Halt(value="x"), + ) + + assert free_variables(statement) == {"p", "x"} + + +def test_free_variables_halt(): + statement = L1.Halt(value="x") + + assert free_variables(statement) == {"x"} + + +def test_close_term_non_abstract_forms(): + fresh = SequentialNameGenerator() + procedures: list[L0.Procedure] = [] + + statement = L1.Copy( + destination="x", + source="y", + then=L1.Immediate( + destination="z", + value=1, + then=L1.Primitive( + destination="w", + operator="+", + left="x", + right="z", + then=L1.Branch( + operator="<", + left="x", + right="w", + then=L1.Allocate( + destination="p", + count=1, + then=L1.Load( + destination="q", + base="p", + index=0, + then=L1.Store( + base="p", + index=0, + value="q", + then=L1.Halt(value="q"), + ), + ), + ), + otherwise=L1.Apply(target="f", arguments=["x"]), + ), + ), + ), + ) + + actual = close_term(statement, procedures.append, fresh) + + assert procedures == [] + assert isinstance(actual, L0.Copy) + assert isinstance(actual.then, L0.Immediate) + assert isinstance(actual.then.then, L0.Primitive) + assert isinstance(actual.then.then.then, L0.Branch) + assert isinstance(actual.then.then.then.then, L0.Allocate) + assert isinstance(actual.then.then.then.then.then, L0.Load) + assert isinstance(actual.then.then.then.then.then.then, L0.Store) + assert isinstance(actual.then.then.then.then.then.then.then, L0.Halt) + assert isinstance(actual.then.then.then.otherwise, L0.Load) + + +def test_close_term_abstract_lifts_and_builds_closure(): + fresh = SequentialNameGenerator() + procedures: list[L0.Procedure] = [] + + statement = L1.Abstract( + destination="f", + parameters=["x"], + body=L1.Primitive( + destination="y", + operator="+", + left="x", + right="z", + then=L1.Halt(value="y"), + ), + then=L1.Halt(value="f"), + ) + + actual = close_term(statement, procedures.append, fresh) + + assert len(procedures) == 1 + lifted = procedures[0] + + assert lifted.name == "proc0" + assert list(lifted.parameters) == ["x", "env0"] + assert isinstance(lifted.body, L0.Load) + assert lifted.body.destination == "z" + assert lifted.body.base == "env0" + assert lifted.body.index == 0 + assert isinstance(lifted.body.then, L0.Primitive) + assert isinstance(actual, L0.Allocate) + assert actual.destination == "env1" + + +def test_close_program_wraps_main_procedure(): + fresh = SequentialNameGenerator() + program = L1.Program( + parameters=["x"], + body=L1.Halt(value="x"), + ) + + actual = close_program(program, fresh) + + assert isinstance(actual, L0.Program) + assert len(actual.procedures) == 1 + assert actual.procedures[0].name == "l0" + assert list(actual.procedures[0].parameters) == ["x"] + assert isinstance(actual.procedures[0].body, L0.Halt) + + +def test_close_program_includes_lifted_procedures_before_main(): + fresh = SequentialNameGenerator() + program = L1.Program( + parameters=[], + body=L1.Abstract( + destination="f", + parameters=[], + body=L1.Halt(value="x"), + then=L1.Halt(value="f"), + ), + ) + + actual = close_program(program, fresh) + + assert len(actual.procedures) == 2 + assert actual.procedures[0].name.startswith("proc") + assert actual.procedures[1].name == "l0" diff --git a/packages/L2/README.md b/packages/L2/README.md index e69de29b..5fba267f 100644 --- a/packages/L2/README.md +++ b/packages/L2/README.md @@ -0,0 +1,3 @@ +L2 + +L2 has most of the same characteristics as L3 except letrec is missing. Functions, arithmetic, branching, and heap management all remain the same but not having letrec means that recursive bindings are not avalible. \ No newline at end of file diff --git a/packages/L2/src/L2/constant_folding.py b/packages/L2/src/L2/constant_folding.py new file mode 100644 index 00000000..c360fd86 --- /dev/null +++ b/packages/L2/src/L2/constant_folding.py @@ -0,0 +1,180 @@ +from functools import partial + +from .syntax import ( + Abstract, + Allocate, + Apply, + Begin, + Branch, + Immediate, + Let, + Load, + Primitive, + Reference, + Store, + Term, +) +from .util import ( + Context, + extend_context_with_bindings, + recur_terms, +) + + +def _normalize_commutative_immediate_left( + operator: str, + left: Term, + right: Term, +) -> Primitive: + return Primitive( + operator=operator, # type: ignore[arg-type] + left=right, + right=left, + ) + + +def constant_folding_term( + term: Term, + context: Context, +) -> Term: + recur = partial(constant_folding_term, context=context) # noqa: F841 + + match term: + case Let(bindings=bindings, body=body): + new_bindings, new_context = extend_context_with_bindings(bindings, context, recur) + return Let( + bindings=new_bindings, + body=constant_folding_term(body, new_context), + ) + + case Reference(name=name): + if name in context: + return context[name] + return term + + case Abstract(parameters=parameters, body=body): + return Abstract(parameters=parameters, body=recur(body)) + + case Apply(target=target, arguments=arguments): + return Apply( + target=recur(target), + arguments=recur_terms(arguments, recur), + ) + + case Immediate(value=_value): + return term + + case Primitive(operator=operator, left=left, right=right): + match operator: + case "+": + match recur(left), recur(right): + case Immediate(value=i1), Immediate(value=i2): + return Immediate(value=i1 + i2) + + case Immediate(value=0), right: + return right + + case [ + Primitive(operator="+", left=Immediate(value=i1), right=left), + Primitive(operator="+", left=Immediate(value=i2), right=right), + ]: + return Primitive( + operator="+", + left=Immediate(value=i1 + i2), + right=Primitive( + operator="+", + left=left, + right=right, + ), + ) + + case left, Immediate() as right: + return _normalize_commutative_immediate_left("+", left, right) + + # Coverage reports a synthetic exit arc on this fallback match arm. + # The arm is intentionally reachable and returns the non-folded primitive. + case left, right: # pragma: no branch + return Primitive( + operator="+", + left=left, + right=right, + ) + + case "-": + match recur(left), recur(right): + case Immediate(value=i1), Immediate(value=i2): + return Immediate(value=i1 - i2) + + # Coverage reports a synthetic exit arc on this fallback match arm. + # The arm is intentionally reachable and returns the non-folded primitive. + case left, right: # pragma: no branch + return Primitive(operator="-", left=left, right=right) + + # Coverage may report an extra arc on this literal case label under pattern matching. + # Runtime terms validated by the syntax model still follow normal folding logic below. + case "*": # pragma: no branch + match recur(left), recur(right): + case Immediate(value=i1), Immediate(value=i2): + return Immediate(value=i1 * i2) + + case Immediate(value=0), _: + return Immediate(value=0) + + case _, Immediate(value=0): + return Immediate(value=0) + + case Immediate(value=1), right: + return right + + case left, Immediate(value=1): + return left + + case left, Immediate() as right: + return _normalize_commutative_immediate_left("*", left, right) + + # Coverage reports a synthetic exit arc on this fallback match arm. + # The arm is intentionally reachable and returns the non-folded primitive. + case left, right: # pragma: no branch + return Primitive(operator="*", left=left, right=right) + + case Branch(operator=operator, left=left, right=right, consequent=consequent, otherwise=otherwise): + folded_left = recur(left) + folded_right = recur(right) + folded_consequent = recur(consequent) + folded_otherwise = recur(otherwise) + match operator: + case "<": + match folded_left, folded_right: + case Immediate(value=i1), Immediate(value=i2): + return folded_consequent if i1 < i2 else folded_otherwise + case _: + pass + # Coverage may report an extra arc on this literal case label under pattern matching. + # Runtime terms validated by the syntax model use only "<" and "==". + case "==": # pragma: no branch + match folded_left, folded_right: + case Immediate(value=i1), Immediate(value=i2): + return folded_consequent if i1 == i2 else folded_otherwise + case _: + pass + return Branch( + operator=operator, + left=folded_left, + right=folded_right, + consequent=folded_consequent, + otherwise=folded_otherwise, + ) + + case Allocate(count=count): + return Allocate(count=count) + + case Load(base=base, index=index): + return Load(base=recur(base), index=index) + + case Store(base=base, index=index, value=value): + return Store(base=recur(base), index=index, value=recur(value)) + + # Coverage may report an extra structural arc for this match arm. + # Semantically this always returns the reconstructed Begin node. + case Begin(effects=effects, value=value): # pragma: no branch + return Begin(effects=recur_terms(effects, recur), value=recur(value)) diff --git a/packages/L2/src/L2/constant_propogation.py b/packages/L2/src/L2/constant_propogation.py new file mode 100644 index 00000000..b5ea6d5f --- /dev/null +++ b/packages/L2/src/L2/constant_propogation.py @@ -0,0 +1,88 @@ +from functools import partial + +from .syntax import ( + Abstract, + Allocate, + Apply, + Begin, + Branch, + Immediate, + Let, + Load, + Primitive, + Reference, + Store, + Term, +) +from .util import ( + Context, + extend_context_with_bindings, + recur_terms, +) + + +def constant_propogation_term( + term: Term, + context: Context, +) -> Term: + recur = partial(constant_propogation_term, context=context) + + match term: + case Let(bindings=bindings, body=body): + new_bindings, new_context = extend_context_with_bindings(bindings, context, recur) + return Let( + bindings=new_bindings, + body=constant_propogation_term(body, new_context), + ) + + case Reference(name=name): + if name in context: + return context[name] + return term + + case Abstract(parameters=parameters, body=body): + abstract_context = {name: value for name, value in context.items() if name not in parameters} + return Abstract( + parameters=parameters, + body=constant_propogation_term(body, abstract_context), + ) + + case Apply(target=target, arguments=arguments): + return Apply( + target=recur(target), + arguments=recur_terms(arguments, recur), + ) + + case Immediate(value=_value): + return term + + case Primitive(operator=operator, left=left, right=right): + return Primitive( + operator=operator, + left=recur(left), + right=recur(right), + ) + + case Branch(operator=operator, left=left, right=right, consequent=consequent, otherwise=otherwise): + return Branch( + operator=operator, + left=recur(left), + right=recur(right), + consequent=recur(consequent), + otherwise=recur(otherwise), + ) + + case Allocate(count=count): + return Allocate(count=count) + + case Load(base=base, index=index): + return Load(base=recur(base), index=index) + + case Store(base=base, index=index, value=value): + return Store(base=recur(base), index=index, value=recur(value)) + + case Begin(effects=effects, value=value): # pragma: no branch + return Begin( + effects=recur_terms(effects, recur), + value=recur(value), + ) diff --git a/packages/L2/src/L2/cps_convert.py b/packages/L2/src/L2/cps_convert.py index ee729ade..b9f1b2ad 100644 --- a/packages/L2/src/L2/cps_convert.py +++ b/packages/L2/src/L2/cps_convert.py @@ -8,7 +8,7 @@ def cps_convert_term( term: L2.Term, - k: Callable[[L1.Identifier], L1.Statement], + m: Callable[[L1.Identifier], L1.Statement], fresh: Callable[[str], str], ) -> L1.Statement: _term = partial(cps_convert_term, fresh=fresh) @@ -16,37 +16,110 @@ def cps_convert_term( match term: case L2.Let(bindings=bindings, body=body): - pass + result = _term(body, m) + + for name, value in reversed(bindings): + result = _term(value, lambda value: L1.Copy(destination=name, source=value, then=result)) + + return result case L2.Reference(name=name): - pass + return m(name) case L2.Abstract(parameters=parameters, body=body): - pass + tmp = fresh("t") + k = fresh("k") + return L1.Abstract( + destination=tmp, + parameters=[*parameters, k], + body=_term(body, lambda body: L1.Apply(target=k, arguments=[body])), + then=m(tmp), + ) case L2.Apply(target=target, arguments=arguments): - pass + k = fresh("k") + tmp = fresh("t") + return L1.Abstract( + destination=k, + parameters=[tmp], + body=m(tmp), + then=_term( + target, + lambda target: _terms( + arguments, + lambda arguments: L1.Apply(target=target, arguments=[*arguments, k]), + ), + ), + ) case L2.Immediate(value=value): - pass + tmp = fresh("t") + return L1.Immediate(destination=tmp, value=value, then=m(tmp)) case L2.Primitive(operator=operator, left=left, right=right): - pass + tmp = fresh("t") + return _term( + left, + lambda left: _term( + right, + lambda right: L1.Primitive(destination=tmp, operator=operator, left=left, right=right, then=m(tmp)), + ), + ) case L2.Branch(operator=operator, left=left, right=right, consequent=consequent, otherwise=otherwise): - pass + j = fresh("j") + tmp = fresh("t") + return L1.Abstract( + destination=j, + parameters=[tmp], + body=m(tmp), + then=_term( + left, + lambda left: _term( + right, + lambda right: L1.Branch( + operator=operator, + left=left, + right=right, + then=_term(consequent, lambda consequent: L1.Apply(target=j, arguments=[consequent])), + otherwise=_term(otherwise, lambda otherwise: L1.Apply(target=j, arguments=[otherwise])), + ), + ), + ), + ) case L2.Allocate(count=count): - pass + tmp = fresh("t") + return L1.Allocate(destination=tmp, count=count, then=m(tmp)) case L2.Load(base=base, index=index): - pass + tmp = fresh("t") + return _term( + base, + lambda base: L1.Load(destination=tmp, base=base, index=index, then=m(tmp)), + ) + # Should double check this case L2.Store(base=base, index=index, value=value): - pass + tmp = fresh("t") + return _term( + base, + lambda base: _term( + value, + lambda value: L1.Store( + base=base, index=index, value=value, then=L1.Immediate(destination=tmp, value=0, then=m(tmp)) + ), + ), + ) case L2.Begin(effects=effects, value=value): # pragma: no branch - pass + return _terms( + effects, + lambda effects: _term( + value, + lambda value: m(value), + ), + ) def cps_convert_terms( diff --git a/packages/L2/src/L2/dead_code_elimination.py b/packages/L2/src/L2/dead_code_elimination.py new file mode 100644 index 00000000..4bf2d16b --- /dev/null +++ b/packages/L2/src/L2/dead_code_elimination.py @@ -0,0 +1,180 @@ +from functools import partial + +from .syntax import ( + Abstract, + Allocate, + Apply, + Begin, + Branch, + Identifier, + Immediate, + Let, + Load, + Primitive, + Reference, + Store, + Term, +) +from .util import ( + Context, + recur_terms, +) + + +def is_pure(term: Term) -> bool: + match term: + case Immediate(): + return True + + case Reference(): + return True + + case Primitive(left=left, right=right): + return is_pure(left) and is_pure(right) + + case Abstract(body=body): + return is_pure(body) + + case Let(bindings=bindings, body=body): + return all(is_pure(value) for _, value in bindings) and is_pure(body) + + case Branch(left=left, right=right, consequent=consequent, otherwise=otherwise): + return is_pure(left) and is_pure(right) and is_pure(consequent) and is_pure(otherwise) + + case Load(base=base): + return is_pure(base) + + case Begin(effects=effects, value=value): + return all(is_pure(effect) for effect in effects) and is_pure(value) + + case Apply(): + return False + + case Allocate(): + return False + + case Store(): + return False + + +def free_vars(term: Term) -> set[Identifier]: + match term: + case Immediate(): + return set() + + case Reference(name=name): + return {name} + + case Primitive(left=left, right=right): + return free_vars(left) | free_vars(right) + + case Apply(target=target, arguments=arguments): + result = free_vars(target) + for argument in arguments: + result |= free_vars(argument) + return result + + case Abstract(parameters=parameters, body=body): + return free_vars(body) - set(parameters) + + case Branch(left=left, right=right, consequent=consequent, otherwise=otherwise): + return free_vars(left) | free_vars(right) | free_vars(consequent) | free_vars(otherwise) + + case Load(base=base): + return free_vars(base) + + case Store(base=base, value=value): + return free_vars(base) | free_vars(value) + + case Begin(effects=effects, value=value): + result = free_vars(value) + for effect in effects: + result |= free_vars(effect) + return result + + case Allocate(): + return set() + + case Let(bindings=bindings, body=body): + names = [name for name, _ in bindings] + result = free_vars(body) - set(names) + for _, value in bindings: + result |= free_vars(value) + return result + + +def dead_code_elimination_term( + term: Term, + context: Context, +) -> Term: + recur = partial(dead_code_elimination_term, context=context) + + match term: + case Let(bindings=bindings, body=body): + new_values = [(name, recur(value)) for name, value in bindings] + new_body = recur(body) + + live = free_vars(new_body) + kept_reversed: list[tuple[Identifier, Term]] = [] + + for name, value in reversed(new_values): + if name in live or not is_pure(value): + kept_reversed.append((name, value)) + live.discard(name) + live |= free_vars(value) + + kept = list(reversed(kept_reversed)) + if len(kept) == 0: + return new_body + + return Let(bindings=kept, body=new_body) + + case Reference(name=_name): + return term + + case Abstract(parameters=parameters, body=body): + return Abstract(parameters=parameters, body=recur(body)) + + case Apply(target=target, arguments=arguments): + return Apply( + target=recur(target), + arguments=recur_terms(arguments, recur), + ) + + case Immediate(value=_value): + return term + + case Primitive(operator=operator, left=left, right=right): + return Primitive( + operator=operator, + left=recur(left), + right=recur(right), + ) + + case Branch(operator=operator, left=left, right=right, consequent=consequent, otherwise=otherwise): + return Branch( + operator=operator, + left=recur(left), + right=recur(right), + consequent=recur(consequent), + otherwise=recur(otherwise), + ) + + case Allocate(count=count): + return Allocate(count=count) + + case Load(base=base, index=index): + return Load(base=recur(base), index=index) + + case Store(base=base, index=index, value=value): + return Store(base=recur(base), index=index, value=recur(value)) + + case Begin(effects=effects, value=value): # pragma: no branch + new_effects = [recur(effect) for effect in effects] + kept_effects = [effect for effect in new_effects if not is_pure(effect)] + new_value = recur(value) + + if len(kept_effects) == 0: + return new_value + + return Begin(effects=kept_effects, value=new_value) diff --git a/packages/L2/src/L2/optimize.py b/packages/L2/src/L2/optimize.py index 77ef7c65..6972975c 100644 --- a/packages/L2/src/L2/optimize.py +++ b/packages/L2/src/L2/optimize.py @@ -1,7 +1,32 @@ +from .constant_folding import constant_folding_term +from .constant_propogation import constant_propogation_term +from .dead_code_elimination import dead_code_elimination_term from .syntax import Program +def optimize_program_step( + program: Program, +) -> tuple[Program, bool]: + propagated = Program( + parameters=program.parameters, + body=constant_propogation_term(program.body, {}), + ) + folded = Program( + parameters=propagated.parameters, + body=constant_folding_term(propagated.body, {}), + ) + eliminated = Program( + parameters=folded.parameters, + body=dead_code_elimination_term(folded.body, {}), + ) + return eliminated, eliminated != program + + def optimize_program( program: Program, ) -> Program: - return program + current = program + while True: + current, changed = optimize_program_step(current) + if not changed: + return current diff --git a/packages/L2/src/L2/util.py b/packages/L2/src/L2/util.py new file mode 100644 index 00000000..2dbfff98 --- /dev/null +++ b/packages/L2/src/L2/util.py @@ -0,0 +1,32 @@ +from collections.abc import Callable, Sequence + +from .syntax import ( + Identifier, + Immediate, + Reference, + Term, +) + +type Context = dict[Identifier, Term] + + +def recur_terms( + terms: Sequence[Term], + recur: Callable[[Term], Term], +) -> list[Term]: + return [recur(term) for term in terms] + + +def extend_context_with_bindings( + bindings: Sequence[tuple[Identifier, Term]], + context: Context, + recur: Callable[[Term], Term], +) -> tuple[list[tuple[Identifier, Term]], Context]: + new_bindings: list[tuple[Identifier, Term]] = [] + new_context = dict(context) + for name, value in bindings: + result = recur(value) + new_bindings.append((name, result)) + if isinstance(result, Immediate | Reference): + new_context[name] = result + return new_bindings, new_context diff --git a/packages/L2/test/L2/test_optimize.py b/packages/L2/test/L2/test_optimize.py index 716e9cc3..a0189410 100644 --- a/packages/L2/test/L2/test_optimize.py +++ b/packages/L2/test/L2/test_optimize.py @@ -1,9 +1,27 @@ -from L2.optimize import optimize_program +from typing import cast + +from L2.constant_folding import constant_folding_term +from L2.constant_propogation import constant_propogation_term +from L2.dead_code_elimination import dead_code_elimination_term, free_vars, is_pure +from L2.optimize import optimize_program, optimize_program_step from L2.syntax import ( + Abstract, + Allocate, + Apply, + Begin, + Branch, Immediate, + Let, + Load, Primitive, Program, + Reference, + Store, + Term, ) +from pydantic import ValidationError + +# Used copilot to help with writing these tests, gave it my expected input and output and it generated tests that I then modified to be more comprehensive and cover more edge cases. def test_optimize_program(): @@ -24,3 +42,552 @@ def test_optimize_program(): actual = optimize_program(program) assert actual == expected + + +def test_constant_propogation_term_all_cases(): + term = Let( + bindings=[ + ("x", Immediate(value=1)), + ("y", Reference(name="x")), + ], + body=Begin( + effects=[ + Apply(target=Reference(name="f"), arguments=[Reference(name="y")]), + Store(base=Reference(name="arr"), index=0, value=Reference(name="x")), + ], + value=Branch( + operator="<", + left=Primitive(operator="+", left=Reference(name="x"), right=Immediate(value=2)), + right=Immediate(value=5), + consequent=Load(base=Allocate(count=1), index=0), + otherwise=Abstract(parameters=["x"], body=Reference(name="x")), + ), + ), + ) + + expected = Let( + bindings=[ + ("x", Immediate(value=1)), + ("y", Reference(name="x")), + ], + body=Begin( + effects=[ + Apply(target=Reference(name="f"), arguments=[Reference(name="x")]), + Store(base=Reference(name="arr"), index=0, value=Immediate(value=1)), + ], + value=Branch( + operator="<", + left=Primitive(operator="+", left=Immediate(value=1), right=Immediate(value=2)), + right=Immediate(value=5), + consequent=Load(base=Allocate(count=1), index=0), + otherwise=Abstract(parameters=["x"], body=Reference(name="x")), + ), + ), + ) + + actual = constant_propogation_term(term, context={"f": Reference(name="f")}) + + assert actual == expected + + +def test_constant_folding_plus_cases(): + term = Primitive( + operator="+", + left=Primitive(operator="+", left=Immediate(value=2), right=Reference(name="a")), + right=Primitive(operator="+", left=Immediate(value=3), right=Reference(name="b")), + ) + + expected = Primitive( + operator="+", + left=Immediate(value=5), + right=Primitive( + operator="+", + left=Reference(name="a"), + right=Reference(name="b"), + ), + ) + + actual = constant_folding_term(term, context={}) + + assert actual == expected + + actual_immediate = constant_folding_term( + Primitive(operator="+", left=Immediate(value=1), right=Immediate(value=2)), + context={}, + ) + assert actual_immediate == Immediate(value=3) + + actual_zero = constant_folding_term( + Primitive(operator="+", left=Immediate(value=0), right=Reference(name="x")), + context={}, + ) + assert actual_zero == Reference(name="x") + + actual_normalize = constant_folding_term( + Primitive(operator="+", left=Reference(name="x"), right=Immediate(value=9)), + context={}, + ) + assert actual_normalize == Primitive( + operator="+", + left=Immediate(value=9), + right=Reference(name="x"), + ) + + +def test_constant_folding_minus_and_multiply_cases(): + actual_subtract = constant_folding_term( + Primitive(operator="-", left=Immediate(value=9), right=Immediate(value=4)), + context={}, + ) + assert actual_subtract == Immediate(value=5) + + actual_subtract_not_folded = constant_folding_term( + Primitive(operator="-", left=Reference(name="x"), right=Immediate(value=4)), + context={}, + ) + assert actual_subtract_not_folded == Primitive( + operator="-", + left=Reference(name="x"), + right=Immediate(value=4), + ) + + actual_multiply = constant_folding_term( + Primitive(operator="*", left=Immediate(value=3), right=Immediate(value=4)), + context={}, + ) + assert actual_multiply == Immediate(value=12) + + actual_mul_left_zero = constant_folding_term( + Primitive(operator="*", left=Immediate(value=0), right=Reference(name="x")), + context={}, + ) + assert actual_mul_left_zero == Immediate(value=0) + + actual_mul_right_zero = constant_folding_term( + Primitive(operator="*", left=Reference(name="x"), right=Immediate(value=0)), + context={}, + ) + assert actual_mul_right_zero == Immediate(value=0) + + actual_mul_left_one = constant_folding_term( + Primitive(operator="*", left=Immediate(value=1), right=Reference(name="x")), + context={}, + ) + assert actual_mul_left_one == Reference(name="x") + + actual_mul_right_one = constant_folding_term( + Primitive(operator="*", left=Reference(name="x"), right=Immediate(value=1)), + context={}, + ) + assert actual_mul_right_one == Reference(name="x") + + actual_mul_normalize = constant_folding_term( + Primitive(operator="*", left=Reference(name="x"), right=Immediate(value=7)), + context={}, + ) + assert actual_mul_normalize == Primitive( + operator="*", + left=Immediate(value=7), + right=Reference(name="x"), + ) + + actual_mul_not_folded = constant_folding_term( + Primitive(operator="*", left=Reference(name="x"), right=Reference(name="y")), + context={}, + ) + assert actual_mul_not_folded == Primitive( + operator="*", + left=Reference(name="x"), + right=Reference(name="y"), + ) + + +def test_constant_folding_non_primitive_cases_and_branches(): + actual_reference_hit = constant_folding_term(Reference(name="x"), context={"x": Immediate(value=4)}) + assert actual_reference_hit == Immediate(value=4) + + actual_reference_miss = constant_folding_term(Reference(name="y"), context={"x": Immediate(value=4)}) + assert actual_reference_miss == Reference(name="y") + + actual_abstract = constant_folding_term( + Abstract(parameters=["x"], body=Primitive(operator="+", left=Reference(name="x"), right=Immediate(value=1))), + context={}, + ) + assert actual_abstract == Abstract( + parameters=["x"], + body=Primitive(operator="+", left=Immediate(value=1), right=Reference(name="x")), + ) + + actual_apply = constant_folding_term( + Apply( + target=Reference(name="f"), + arguments=[Primitive(operator="+", left=Immediate(value=2), right=Immediate(value=3))], + ), + context={}, + ) + assert actual_apply == Apply(target=Reference(name="f"), arguments=[Immediate(value=5)]) + + actual_immediate = constant_folding_term(Immediate(value=11), context={}) + assert actual_immediate == Immediate(value=11) + + actual_lt_true = constant_folding_term( + Branch( + operator="<", + left=Immediate(value=1), + right=Immediate(value=2), + consequent=Immediate(value=7), + otherwise=Immediate(value=8), + ), + context={}, + ) + assert actual_lt_true == Immediate(value=7) + + actual_lt_false = constant_folding_term( + Branch( + operator="<", + left=Immediate(value=4), + right=Immediate(value=2), + consequent=Immediate(value=7), + otherwise=Immediate(value=8), + ), + context={}, + ) + assert actual_lt_false == Immediate(value=8) + + actual_lt_fallback = constant_folding_term( + Branch( + operator="<", + left=Reference(name="x"), + right=Immediate(value=2), + consequent=Immediate(value=7), + otherwise=Immediate(value=8), + ), + context={}, + ) + assert actual_lt_fallback == Branch( + operator="<", + left=Reference(name="x"), + right=Immediate(value=2), + consequent=Immediate(value=7), + otherwise=Immediate(value=8), + ) + + actual_eq_true = constant_folding_term( + Branch( + operator="==", + left=Immediate(value=3), + right=Immediate(value=3), + consequent=Immediate(value=9), + otherwise=Immediate(value=10), + ), + context={}, + ) + assert actual_eq_true == Immediate(value=9) + + actual_eq_false = constant_folding_term( + Branch( + operator="==", + left=Immediate(value=3), + right=Immediate(value=4), + consequent=Immediate(value=9), + otherwise=Immediate(value=10), + ), + context={}, + ) + assert actual_eq_false == Immediate(value=10) + + actual_eq_fallback = constant_folding_term( + Branch( + operator="==", + left=Reference(name="x"), + right=Immediate(value=4), + consequent=Immediate(value=9), + otherwise=Immediate(value=10), + ), + context={}, + ) + assert actual_eq_fallback == Branch( + operator="==", + left=Reference(name="x"), + right=Immediate(value=4), + consequent=Immediate(value=9), + otherwise=Immediate(value=10), + ) + + actual_allocate = constant_folding_term(Allocate(count=3), context={}) + assert actual_allocate == Allocate(count=3) + + actual_load = constant_folding_term( + Load(base=Primitive(operator="+", left=Immediate(value=1), right=Immediate(value=2)), index=0), context={} + ) + assert actual_load == Load(base=Immediate(value=3), index=0) + + actual_store = constant_folding_term( + Store( + base=Primitive(operator="+", left=Immediate(value=1), right=Immediate(value=2)), + index=0, + value=Primitive(operator="+", left=Immediate(value=3), right=Immediate(value=4)), + ), + context={}, + ) + assert actual_store == Store(base=Immediate(value=3), index=0, value=Immediate(value=7)) + + actual_begin = constant_folding_term( + Begin( + effects=[Primitive(operator="+", left=Immediate(value=1), right=Immediate(value=2))], + value=Primitive(operator="+", left=Immediate(value=3), right=Immediate(value=4)), + ), + context={}, + ) + assert actual_begin == Begin(effects=[Immediate(value=3)], value=Immediate(value=7)) + + +def test_dead_codeis_pure_andfree_vars_cases(): + assert is_pure(Immediate(value=1)) is True + assert is_pure(Reference(name="x")) is True + assert is_pure(Primitive(operator="+", left=Immediate(value=1), right=Immediate(value=2))) is True + assert is_pure(Abstract(parameters=["x"], body=Reference(name="x"))) is True + assert is_pure(Let(bindings=[("x", Immediate(value=1))], body=Reference(name="x"))) is True + assert ( + is_pure( + Branch( + operator="<", + left=Immediate(value=1), + right=Immediate(value=2), + consequent=Immediate(value=3), + otherwise=Immediate(value=4), + ) + ) + is True + ) + assert is_pure(Load(base=Reference(name="x"), index=0)) is True + assert is_pure(Begin(effects=[Immediate(value=1)], value=Reference(name="x"))) is True + assert is_pure(Apply(target=Reference(name="f"), arguments=[])) is False + assert is_pure(Allocate(count=1)) is False + assert is_pure(Store(base=Reference(name="x"), index=0, value=Immediate(value=1))) is False + + assert free_vars(Immediate(value=1)) == set() + assert free_vars(Reference(name="x")) == {"x"} + assert free_vars(Primitive(operator="+", left=Reference(name="x"), right=Reference(name="y"))) == {"x", "y"} + assert free_vars(Apply(target=Reference(name="f"), arguments=[Reference(name="x")])) == {"f", "x"} + assert free_vars( + Abstract(parameters=["x"], body=Primitive(operator="+", left=Reference(name="x"), right=Reference(name="y"))) + ) == {"y"} + assert free_vars( + Branch( + operator="<", + left=Reference(name="a"), + right=Reference(name="b"), + consequent=Reference(name="c"), + otherwise=Reference(name="d"), + ) + ) == {"a", "b", "c", "d"} + assert free_vars(Load(base=Reference(name="arr"), index=0)) == {"arr"} + assert free_vars(Store(base=Reference(name="arr"), index=0, value=Reference(name="v"))) == {"arr", "v"} + assert free_vars(Begin(effects=[Reference(name="u")], value=Reference(name="v"))) == {"u", "v"} + assert free_vars(Allocate(count=1)) == set() + assert free_vars( + Let( + bindings=[ + ("x", Reference(name="a")), + ("y", Primitive(operator="+", left=Reference(name="x"), right=Reference(name="b"))), + ], + body=Primitive(operator="+", left=Reference(name="y"), right=Reference(name="c")), + ) + ) == {"a", "b", "c", "x"} + + +def test_dead_code_elimination_term_all_cases(): + term_drop_let = Let( + bindings=[("x", Immediate(value=1))], + body=Immediate(value=7), + ) + expected_drop_let = Immediate(value=7) + actual_drop_let = dead_code_elimination_term(term_drop_let, context={}) + assert actual_drop_let == expected_drop_let + + term_keep_let = Let( + bindings=[("x", Store(base=Reference(name="arr"), index=0, value=Immediate(value=1)))], + body=Immediate(value=7), + ) + expected_keep_let = Let( + bindings=[("x", Store(base=Reference(name="arr"), index=0, value=Immediate(value=1)))], + body=Immediate(value=7), + ) + actual_keep_let = dead_code_elimination_term(term_keep_let, context={}) + assert actual_keep_let == expected_keep_let + + actual_reference = dead_code_elimination_term(Reference(name="x"), context={}) + assert actual_reference == Reference(name="x") + + actual_abstract = dead_code_elimination_term( + Abstract(parameters=["x"], body=Primitive(operator="+", left=Immediate(value=1), right=Immediate(value=2))), + context={}, + ) + assert actual_abstract == Abstract( + parameters=["x"], body=Primitive(operator="+", left=Immediate(value=1), right=Immediate(value=2)) + ) + + actual_apply = dead_code_elimination_term( + Apply( + target=Reference(name="f"), + arguments=[Primitive(operator="+", left=Immediate(value=1), right=Immediate(value=2))], + ), + context={}, + ) + assert actual_apply == Apply( + target=Reference(name="f"), + arguments=[Primitive(operator="+", left=Immediate(value=1), right=Immediate(value=2))], + ) + + actual_immediate = dead_code_elimination_term(Immediate(value=5), context={}) + assert actual_immediate == Immediate(value=5) + + actual_primitive = dead_code_elimination_term( + Primitive(operator="+", left=Reference(name="x"), right=Immediate(value=2)), + context={}, + ) + assert actual_primitive == Primitive(operator="+", left=Reference(name="x"), right=Immediate(value=2)) + + actual_branch = dead_code_elimination_term( + Branch( + operator="==", + left=Reference(name="x"), + right=Immediate(value=2), + consequent=Immediate(value=1), + otherwise=Immediate(value=0), + ), + context={}, + ) + assert actual_branch == Branch( + operator="==", + left=Reference(name="x"), + right=Immediate(value=2), + consequent=Immediate(value=1), + otherwise=Immediate(value=0), + ) + + actual_allocate = dead_code_elimination_term(Allocate(count=2), context={}) + assert actual_allocate == Allocate(count=2) + + actual_load = dead_code_elimination_term(Load(base=Reference(name="arr"), index=0), context={}) + assert actual_load == Load(base=Reference(name="arr"), index=0) + + actual_store = dead_code_elimination_term( + Store(base=Reference(name="arr"), index=0, value=Immediate(value=7)), + context={}, + ) + assert actual_store == Store(base=Reference(name="arr"), index=0, value=Immediate(value=7)) + + term_begin_drop = Begin(effects=[Immediate(value=1)], value=Reference(name="x")) + expected_begin_drop = Reference(name="x") + actual_begin_drop = dead_code_elimination_term(term_begin_drop, context={}) + assert actual_begin_drop == expected_begin_drop + + term_begin_keep = Begin( + effects=[Immediate(value=1), Store(base=Reference(name="arr"), index=0, value=Immediate(value=9))], + value=Reference(name="x"), + ) + expected_begin_keep = Begin( + effects=[Store(base=Reference(name="arr"), index=0, value=Immediate(value=9))], + value=Reference(name="x"), + ) + actual_begin_keep = dead_code_elimination_term(term_begin_keep, context={}) + assert actual_begin_keep == expected_begin_keep + + +def test_optimize_program_step_and_optimize_program(): + program_change = Program( + parameters=[], + body=Let( + bindings=[ + ("x", Immediate(value=1)), + ("y", Reference(name="x")), + ("z", Primitive(operator="+", left=Reference(name="y"), right=Immediate(value=2))), + ], + body=Reference(name="z"), + ), + ) + + expected_step_program = Program( + parameters=[], + body=Let( + bindings=[ + ("x", Immediate(value=1)), + ("y", Reference(name="x")), + ("z", Primitive(operator="+", left=Immediate(value=2), right=Reference(name="y"))), + ], + body=Reference(name="z"), + ), + ) + + actual_step_program, changed = optimize_program_step(program_change) + + assert actual_step_program == expected_step_program + assert changed is True + + program_no_change = Program( + parameters=["x"], + body=Reference(name="x"), + ) + + actual_same_program, changed_same = optimize_program_step(program_no_change) + + assert actual_same_program == program_no_change + assert changed_same is False + + actual_optimize = optimize_program(program_change) + + assert actual_optimize == expected_step_program + + actual_optimize_no_change = optimize_program(program_no_change) + + assert actual_optimize_no_change == program_no_change + + +def test_dead_code_helper_fallthrough_cases(): + invalid_term = cast(Term, object()) + + actualis_pure = is_pure(invalid_term) + assert actualis_pure is None + + actualfree_vars = free_vars(invalid_term) + assert actualfree_vars is None + + +def test_constant_folding_edge_fallthrough_cases(): + plus_invalid = Primitive.model_construct( + operator="+", + left=Reference(name="x"), + right=object(), + ) + with_validation_error_plus = False + try: + constant_folding_term(plus_invalid, context={}) + except ValidationError: + with_validation_error_plus = True + assert with_validation_error_plus is True + + minus_invalid = Primitive.model_construct( + operator="-", + left=Reference(name="x"), + right=object(), + ) + with_validation_error_minus = False + try: + constant_folding_term(minus_invalid, context={}) + except ValidationError: + with_validation_error_minus = True + assert with_validation_error_minus is True + + multiply_invalid = Primitive.model_construct( + operator="*", + left=Reference(name="x"), + right=object(), + ) + with_validation_error_multiply = False + try: + constant_folding_term(multiply_invalid, context={}) + except ValidationError: + with_validation_error_multiply = True + assert with_validation_error_multiply is True diff --git a/packages/L3/README.md b/packages/L3/README.md index e69de29b..c22df315 100644 --- a/packages/L3/README.md +++ b/packages/L3/README.md @@ -0,0 +1,11 @@ +L3 + +The L3 Compiler is an expression based language with named variables, functions, arithmetic, conditionals, and explicit heap operations. There is an Identifier that names variables and functions as well as a Program that contains parameters and a single term body. A term is a unioned type of all avalible constructs. + +Bindings and variables let and letrec bind different identifiers to terms; and then reference reads a bound name. Unlike let, letrec supports self-reference for recursive definitions. + +For functions, there is an abstract that defines the function with a list of parameters and a body. Apply is used to invoke a function with given arguments. + +For values and control we have immediate, primative, and branch. Immediate is an integer literal and primitive applies the +, -, or * operator to a left and right term. Branch is then able to be used to evaluate < or == and selects a consequent or otherwise. + +The last thing that L3 supports is memory and sequencing. Allocate reserves a block of memory, while load reads from the memory and store writes to memory. There is also begin which sequences terms before producing a final value. \ No newline at end of file diff --git a/packages/L3/src/L3/L3.lark b/packages/L3/src/L3/L3.lark index a7f21d7e..f2e0dc03 100644 --- a/packages/L3/src/L3/L3.lark +++ b/packages/L3/src/L3/L3.lark @@ -1,3 +1,6 @@ +%import common.WS +%ignore WS + program : "(" PROGRAM "(" parameters ")" term ")" parameters : IDENTIFIER* @@ -20,11 +23,40 @@ let : "(" LET "(" bindings ")" term ")" letrec : "(" LETREC "(" bindings ")" term ")" bindings : binding* -binding : IDENTIFIER term +binding : "(" IDENTIFIER term ")" + +reference : IDENTIFIER + +abstract : "(" LAMBDA "(" parameters ")" term ")" + +apply : "(" term term* ")" + +immediate : NUMBER + +primitive : "(" OPERATOR term term ")" + +branch : "(" IF "(" COMPARATOR term term ")" term term ")" + +allocate : "(" ALLOCATE immediate ")" + +load : "(" LOAD term immediate ")" + +store : "(" STORE term immediate term ")" + +begin : "(" BEGIN term+ ")" PROGRAM.2 : "l3" LET.2 : "let" LETREC.2 : "letrec" LAMBDA.2 : "\\" | "lambda" | "λ" +IF.2 : "if" +ALLOCATE.2 : "allocate" +LOAD.2 : "load" +STORE.2 : "store" +BEGIN.2 : "begin" + +OPERATOR : "+" | "-" | "*" +COMPARATOR : "<" | "==" -IDENTIFIER : /[a-zA-Z_][a-zA-Z0-9_]*/ \ No newline at end of file +IDENTIFIER : /[a-zA-Z_][a-zA-Z0-9_]*/ +NUMBER : /-?\d+/ \ No newline at end of file diff --git a/packages/L3/src/L3/check.py b/packages/L3/src/L3/check.py index 78af5b85..e9237852 100644 --- a/packages/L3/src/L3/check.py +++ b/packages/L3/src/L3/check.py @@ -1,6 +1,6 @@ -from collections import Counter from collections.abc import Mapping from functools import partial +from typing import Counter from .syntax import ( Abstract, @@ -27,14 +27,14 @@ def check_term( term: Term, context: Context, ) -> None: - recur = partial(check_term, context=context) + recur = partial(check_term, context=context) # noqa: F841 match term: case Let(bindings=bindings, body=body): counts = Counter(name for name, _ in bindings) duplicates = {name: count for name, count in counts.items() if count > 1} if duplicates: - raise ValueError(f"duplicate binders: {duplicates}") + raise ValueError(f"Duplicate bindings: {duplicates}") for _, value in bindings: recur(value) @@ -46,37 +46,36 @@ def check_term( counts = Counter(name for name, _ in bindings) duplicates = {name: count for name, count in counts.items() if count > 1} if duplicates: - raise ValueError(f"duplicate binders: {duplicates}") + raise ValueError(f"Duplicate bindings: {duplicates}") local = dict.fromkeys([name for name, _ in bindings]) for name, value in bindings: recur(value, context={**context, **local}) - check_term(body, {**context, **local}) + check_term(body, context={**context, **local}) - case Reference(name=name): + case Reference(name=name): # Leaf if name not in context: - raise ValueError(f"unknown variable: {name}") + raise ValueError(f"Unbound variable: {name}") - case Abstract(parameters=parameters, body=body): + case Abstract(parameters=parameters, body=body): # Done counts = Counter(parameters) duplicates = {name for name, count in counts.items() if count > 1} if duplicates: - raise ValueError(f"duplicate parameters: {duplicates}") - + raise ValueError(f"Duplicate parameters: {duplicates}") local = dict.fromkeys(parameters, None) - recur(body, context={**context, **local}) + check_term(body, context=local) - case Apply(target=target, arguments=arguments): + case Apply(target=target, arguments=arguments): # Done recur(target) for argument in arguments: recur(argument) - case Immediate(value=_value): + case Immediate(value=_value): # Leaf pass - case Primitive(operator=_operator, left=left, right=right): + case Primitive(operator=_operator, left=left, right=right): # Should be done recur(left) recur(right) @@ -87,13 +86,13 @@ def check_term( recur(otherwise) case Allocate(count=_count): - pass + pass # No need to check count, as it is a non-negative integer by construction case Load(base=base, index=_index): - recur(base) + recur(base) # No need to check index, as it is a non-negative integer by construction case Store(base=base, index=_index, value=value): - recur(base) + recur(base) # No need to check index, as it is a non-negative integer by construction recur(value) case Begin(effects=effects, value=value): # pragma: no branch @@ -102,15 +101,12 @@ def check_term( recur(value) -def check_program( - program: Program, -) -> None: +def check_program(program: Program) -> None: match program: case Program(parameters=parameters, body=body): # pragma: no branch counts = Counter(parameters) duplicates = {name for name, count in counts.items() if count > 1} if duplicates: - raise ValueError(f"duplicate parameters: {duplicates}") - + raise ValueError(f"Duplicate parameters: {duplicates}") local = dict.fromkeys(parameters, None) check_term(body, context=local) diff --git a/packages/L3/src/L3/diagnostics.py b/packages/L3/src/L3/diagnostics.py new file mode 100644 index 00000000..75584b82 --- /dev/null +++ b/packages/L3/src/L3/diagnostics.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +import re +from collections.abc import Sequence +from pathlib import Path +from typing import Literal + +from lark import UnexpectedInput +from pydantic import BaseModel + +from .check import check_program +from .parse import parse_program + +type DiagnosticStage = Literal["syntax", "semantic", "internal"] +type DiagnosticSeverity = Literal["error", "warning", "information"] + + +class Diagnostic(BaseModel, frozen=True): + stage: DiagnosticStage + severity: DiagnosticSeverity = "error" + code: str + message: str + line: int | None = None + column: int | None = None + end_line: int | None = None + end_column: int | None = None + snippet: str | None = None + + +class DiagnosticsReport(BaseModel, frozen=True): + ok: bool + diagnostics: Sequence[Diagnostic] + + +def _line_snippet(source: str, line: int | None) -> str | None: + if line is None: + return None + + lines = source.splitlines() + if line < 1 or line > len(lines): + return None + + return lines[line - 1] + + +def _position_for_identifier(source: str, identifier: str) -> tuple[int, int] | None: + pattern = re.compile(rf"\b{re.escape(identifier)}\b") + match = pattern.search(source) + if match is None: + return None + + before = source[: match.start()] + line = before.count("\n") + 1 + if "\n" in before: + column = len(before.rsplit("\n", maxsplit=1)[-1]) + 1 + else: + column = len(before) + 1 + + return line, column + + +def _identifier_from_message(message: str) -> str | None: + unbound = re.search(r"Unbound variable:\s*([A-Za-z_][A-Za-z0-9_]*)", message) + if unbound: + return unbound.group(1) + + duplicate = re.search(r"Duplicate (?:bindings|parameters):\s*(.+)$", message) + if duplicate is None: + return None + + names = re.findall(r"[A-Za-z_][A-Za-z0-9_]*", duplicate.group(1)) + if not names: + return None + + return names[0] + + +def _normalize_error_position(source: str, line: int | None, column: int | None) -> tuple[int, int]: + if isinstance(line, int) and isinstance(column, int) and line > 0 and column > 0: + return line, column + + lines = source.splitlines() + if not lines: + return 1, 1 + + last_line_number = len(lines) + last_column = len(lines[-1]) + 1 + return last_line_number, last_column + + +def _syntax_diagnostic(source: str, error: UnexpectedInput) -> Diagnostic: + expected = sorted(getattr(error, "expected", [])) + expected_message = "" + if expected: + expected_message = f" Expected one of: {', '.join(expected)}." + + line, column = _normalize_error_position( + source, + getattr(error, "line", None), + getattr(error, "column", None), + ) + + return Diagnostic( + stage="syntax", + code="L3_SYNTAX_ERROR", + message=f"{str(error).strip()}.{expected_message}", + line=line, + column=column, + end_line=line, + end_column=column + 1, + snippet=_line_snippet(source, line), + ) + + +def _semantic_diagnostic(source: str, message: str) -> Diagnostic: + identifier = _identifier_from_message(message) + position = _position_for_identifier(source, identifier) if identifier else None + + line: int | None = None + column: int | None = None + end_column: int | None = None + + if position is not None: + line, column = position + end_column = column + len(identifier) if identifier else None + + code = "L3_SEMANTIC_ERROR" + if message.startswith("Unbound variable"): + code = "L3_UNBOUND_VARIABLE" + elif message.startswith("Duplicate bindings"): + code = "L3_DUPLICATE_BINDINGS" + elif message.startswith("Duplicate parameters"): + code = "L3_DUPLICATE_PARAMETERS" + + return Diagnostic( + stage="semantic", + code=code, + message=message, + line=line, + column=column, + end_line=line, + end_column=end_column, + snippet=_line_snippet(source, line), + ) + + +def analyze_source(source: str) -> DiagnosticsReport: + try: + program = parse_program(source) + check_program(program) + return DiagnosticsReport(ok=True, diagnostics=[]) + except UnexpectedInput as error: + return DiagnosticsReport(ok=False, diagnostics=[_syntax_diagnostic(source, error)]) + except ValueError as error: + return DiagnosticsReport(ok=False, diagnostics=[_semantic_diagnostic(source, str(error))]) + except Exception as error: # pragma: no cover + return DiagnosticsReport( + ok=False, + diagnostics=[ + Diagnostic( + stage="internal", + code="L3_INTERNAL_ERROR", + message=str(error), + ) + ], + ) + + +def analyze_file(path: Path) -> DiagnosticsReport: + return analyze_source(path.read_text()) diff --git a/packages/L3/src/L3/eliminate_letrec.py b/packages/L3/src/L3/eliminate_letrec.py index 63ea854b..cc6b044c 100644 --- a/packages/L3/src/L3/eliminate_letrec.py +++ b/packages/L3/src/L3/eliminate_letrec.py @@ -1,6 +1,5 @@ # noqa: F841 from collections.abc import Mapping -from functools import partial from L2 import syntax as L2 @@ -13,39 +12,108 @@ def eliminate_letrec_term( term: L3.Term, context: Context, ) -> L2.Term: - recur = partial(eliminate_letrec_term, context=context) - match term: case L3.Let(bindings=bindings, body=body): - pass + return L2.Let( + bindings=[(name, eliminate_letrec_term(value, context)) for name, value in bindings], + body=eliminate_letrec_term(body, context), + ) case L3.LetRec(bindings=bindings, body=body): - pass + # Mark all binding names as recursive in the context + binding_names = [name for name, _ in bindings] + new_context: Context = {**context, **dict.fromkeys(binding_names)} # type: ignore + + # Check which bindings need heap allocation based on their values + # Simple values (Immediate, Allocate) can be stored directly + # Complex values (everything else) need Allocate + Store + simple_binding_indices: set[int] = set() + for i, (_, value) in enumerate(bindings): + match value: + case L3.Immediate() | L3.Allocate(): + simple_binding_indices.add(i) + case _: + pass + + # Separate simple and complex bindings + simple_bindings: list[tuple[str, L2.Term]] = [] + complex_bindings: list[tuple[str, L3.Term]] = [] + complex_binding_names: list[str] = [] + + for i, (name, value) in enumerate(bindings): + if i in simple_binding_indices: + transformed_value = eliminate_letrec_term(value, new_context) + simple_bindings.append((name, transformed_value)) + else: + complex_bindings.append((name, value)) + complex_binding_names.append(name) + + # Create stores for complex bindings + stores: list[L2.Term] = [] + for name, value in complex_bindings: + transformed_value = eliminate_letrec_term(value, new_context) + stores.append( + L2.Store( + base=L2.Reference(name=name), + index=0, + value=transformed_value, + ) + ) + + # Transform the body + transformed_body = eliminate_letrec_term(body, new_context) + + # Build the result + all_bindings = simple_bindings + [(name, L2.Allocate(count=1)) for name in complex_binding_names] + + if stores: + return L2.Let( + bindings=all_bindings, + body=L2.Begin( + effects=stores, + value=transformed_body, + ), + ) + else: + return L2.Let( + bindings=all_bindings, + body=transformed_body, + ) case L3.Reference(name=name): # if name is a recursive variable -> (Load (Reference name))) # else (Reference name) - pass + if name in context: + return L2.Load(base=L2.Reference(name=name), index=0) + else: + return L2.Reference(name=name) case L3.Abstract(parameters=parameters, body=body): - pass + return L2.Abstract(parameters=parameters, body=eliminate_letrec_term(body, context)) case L3.Apply(target=target, arguments=arguments): - pass + return L2.Apply( + target=eliminate_letrec_term(target, context), + arguments=[eliminate_letrec_term(argument, context) for argument in arguments], + ) case L3.Immediate(value=value): return L2.Immediate(value=value) - case L3.Primitive(operator=_operator, left=left, right=right): - pass + case L3.Primitive(operator=operator, left=left, right=right): + return L2.Primitive( + operator=operator, + left=eliminate_letrec_term(left, context), + right=eliminate_letrec_term(right, context), + ) case L3.Branch(operator=operator, left=left, right=right, consequent=consequent, otherwise=otherwise): return L2.Branch( operator=operator, - left=recur(left), - right=recur(right), - consequent=recur(consequent), - otherwise=recur(otherwise), + left=eliminate_letrec_term(left, context), + right=eliminate_letrec_term(right, context), + consequent=eliminate_letrec_term(consequent, context), + otherwise=eliminate_letrec_term(otherwise, context), ) case L3.Allocate(count=count): @@ -53,15 +121,22 @@ def eliminate_letrec_term( case L3.Load(base=base, index=index): return L2.Load( - base=recur(base), + base=eliminate_letrec_term(base, context), index=index, ) - case L3.Store(base=base, index=_index, value=value): - pass + case L3.Store(base=base, index=index, value=value): + return L2.Store( + base=eliminate_letrec_term(base, context), + index=index, + value=eliminate_letrec_term(value, context), + ) case L3.Begin(effects=effects, value=value): # pragma: no branch - pass + return L2.Begin( + effects=[eliminate_letrec_term(effect, context) for effect in effects], + value=eliminate_letrec_term(value, context), + ) def eliminate_letrec_program( diff --git a/packages/L3/src/L3/main.py b/packages/L3/src/L3/main.py index aaa9c6f6..99d26569 100644 --- a/packages/L3/src/L3/main.py +++ b/packages/L3/src/L3/main.py @@ -1,11 +1,13 @@ from pathlib import Path import click -from L1.to_python import to_ast_program -from L2.cps_convert import cps_convert_program + +# from L2.cps_convert import cps_convert_program from L2.optimize import optimize_program +from L2.to_python import to_ast_program from .check import check_program +from .diagnostics import analyze_source from .eliminate_letrec import eliminate_letrec_program from .parse import parse_program from .uniqify import uniqify_program @@ -17,6 +19,12 @@ max_content_width=120, ), ) +@click.option( + "--diagnostics-json", + is_flag=True, + default=False, + help="Emit parser/checker diagnostics as JSON for editor integrations", +) @click.option( "--check/--no-check", default=True, @@ -42,24 +50,32 @@ ) def main( output: Path | None, + diagnostics_json: bool, check: bool, optimize: bool, input: Path, ) -> None: - l3 = parse_program(input.read_text()) + source = input.read_text() + + if diagnostics_json: + report = analyze_source(source) + click.echo(report.model_dump_json(indent=2)) + raise SystemExit(0 if report.ok else 1) + + l3 = parse_program(source) if check: check_program(l3) - fresh, l3 = uniqify_program(l3) + fresh, l3 = uniqify_program(l3) # type: ignore l2 = eliminate_letrec_program(l3) if optimize: l2 = optimize_program(l2) - l1 = cps_convert_program(l2, fresh) + # l1 = cps_convert_program(l2, fresh) - module = to_ast_program(l1) + module = to_ast_program(l2) (output or input.with_suffix(".py")).write_text(module) diff --git a/packages/L3/src/L3/parse.py b/packages/L3/src/L3/parse.py index 8fbd08f6..5af096c4 100644 --- a/packages/L3/src/L3/parse.py +++ b/packages/L3/src/L3/parse.py @@ -5,9 +5,20 @@ from lark.visitors import v_args # pyright: ignore[reportUnknownVariableType] from .syntax import ( + Abstract, + Allocate, + Apply, + Begin, + Branch, Identifier, + Immediate, Let, + LetRec, + Load, + Primitive, Program, + Reference, + Store, Term, ) @@ -27,9 +38,9 @@ def program( def parameters( self, - parameters: Sequence[Identifier], + parameters: Sequence[Token], ) -> Sequence[Identifier]: - return parameters + return [str(p) for p in parameters] @v_args(inline=True) def term( @@ -57,7 +68,7 @@ def letrec( bindings: Sequence[tuple[Identifier, Term]], body: Term, ) -> Term: - return Let( + return LetRec( bindings=bindings, body=body, ) @@ -71,10 +82,127 @@ def bindings( @v_args(inline=True) def binding( self, - name: Identifier, + name: Token, value: Term, ) -> tuple[Identifier, Term]: - return name, value + return str(name), value + + @v_args(inline=True) + def reference( + self, + name: Token, + ) -> Term: + return Reference(name=str(name)) + + @v_args(inline=True) + def abstract( + self, + _lambda: Token, + parameters: Sequence[Identifier], + body: Term, + ) -> Term: + return Abstract( + parameters=parameters, + body=body, + ) + + @v_args(inline=True) + def apply( + self, + target: Term, + *arguments: Term, + ) -> Term: + return Apply( + target=target, + arguments=list(arguments), + ) + + @v_args(inline=True) + def immediate( + self, + value: Token, + ) -> Term: + return Immediate(value=int(value)) + + @v_args(inline=True) + def primitive( + self, + operator: Token, + left: Term, + right: Term, + ) -> Term: + return Primitive( + operator=str(operator), # type: ignore + left=left, + right=right, + ) + + @v_args(inline=True) + def branch( + self, + _if: Token, + operator: Token, + left: Term, + right: Term, + consequent: Term, + otherwise: Term, + ) -> Term: + return Branch( + operator=str(operator), # type: ignore + left=left, + right=right, + consequent=consequent, + otherwise=otherwise, + ) + + @v_args(inline=True) + def allocate( + self, + _allocate: Token, + count: Immediate, + ) -> Term: + return Allocate( + count=count.value, + ) + + @v_args(inline=True) + def load( + self, + _load: Token, + base: Term, + index: Immediate, + ) -> Term: + return Load( + base=base, + index=index.value, + ) + + @v_args(inline=True) + def store( + self, + _store: Token, + base: Term, + index: Immediate, + value: Term, + ) -> Term: + return Store( + base=base, + index=index.value, + value=value, + ) + + def begin( + self, + args: Sequence[Token | Term], + ) -> Term: + # Filter out Token objects (like BEGIN token), keep only Terms + terms = [arg for arg in args if not isinstance(arg, Token)] + if len(terms) == 0: + raise ValueError("begin requires at least one term") + return Begin( + effects=terms[:-1], + value=terms[-1], + ) def parse_term(source: str) -> Term: diff --git a/packages/L3/src/L3/uniqify.py b/packages/L3/src/L3/uniqify.py index 2e242d60..ba3180a4 100644 --- a/packages/L3/src/L3/uniqify.py +++ b/packages/L3/src/L3/uniqify.py @@ -9,6 +9,7 @@ Apply, Begin, Branch, + Identifier, Immediate, Let, LetRec, @@ -20,9 +21,10 @@ Term, ) -type Context = Mapping[str, str] +type Context = Mapping[Identifier, Identifier] +# This pass is responsible for renaming all identifiers in a program to be unique. This is necessary for the later stages of the compiler, which rely on the fact that all identifiers are unique. def uniqify_term( term: Term, context: Context, @@ -32,40 +34,95 @@ def uniqify_term( match term: case Let(bindings=bindings, body=body): - pass + new_bindings: list[tuple[Identifier, Term]] = [] + new_context = context + for name, value in bindings: + new_name = fresh(name) + new_bindings.append((new_name, _term(value))) + new_context = {**new_context, name: new_name} + + return Let( + bindings=new_bindings, + body=uniqify_term(body, new_context, fresh), + ) case LetRec(bindings=bindings, body=body): - pass + new_bindings: list[tuple[Identifier, Term]] = [] + new_context = context + for name, value in bindings: + new_name = fresh(name) + new_bindings.append((new_name, value)) + new_context = {**new_context, name: new_name} + + _new_term = partial(uniqify_term, context=new_context, fresh=fresh) + + return LetRec( + bindings=[(new_name, _new_term(value)) for new_name, value in new_bindings], + body=_new_term(body), + ) case Reference(name=name): - pass + if name in context: + return Reference(name=context[name]) + return term case Abstract(parameters=parameters, body=body): - pass + new_parameters = [fresh(parameter) for parameter in parameters] + new_context = { + **context, + **{parameter: new_parameter for parameter, new_parameter in zip(parameters, new_parameters)}, + } + return Abstract( + parameters=new_parameters, + body=uniqify_term(body, new_context, fresh), + ) case Apply(target=target, arguments=arguments): - pass + return Apply( + target=_term(target), + arguments=[_term(argument) for argument in arguments], + ) case Immediate(): - pass + return term case Primitive(operator=operator, left=left, right=right): - pass + return Primitive( + operator=operator, + left=_term(left), + right=_term(right), + ) case Branch(operator=operator, left=left, right=right, consequent=consequent, otherwise=otherwise): - pass + return Branch( + operator=operator, + left=_term(left), + right=_term(right), + consequent=_term(consequent), + otherwise=_term(otherwise), + ) case Allocate(): - pass + return term case Load(base=base, index=index): - pass + return Load( + base=_term(base), + index=index, + ) case Store(base=base, index=index, value=value): - pass + return Store( + base=_term(base), + index=index, + value=_term(value), + ) case Begin(effects=effects, value=value): # pragma: no branch - pass + return Begin( + effects=[_term(effect) for effect in effects], + value=_term(value), + ) def uniqify_program( diff --git a/packages/L3/test/L3/test_diagnostics.py b/packages/L3/test/L3/test_diagnostics.py new file mode 100644 index 00000000..26589b7e --- /dev/null +++ b/packages/L3/test/L3/test_diagnostics.py @@ -0,0 +1,35 @@ +from L3.diagnostics import analyze_source + + +def test_diagnostics_ok_program() -> None: + report = analyze_source("(l3 (x) x)") + + assert report.ok is True + assert report.diagnostics == [] + + +def test_diagnostics_reports_syntax_error() -> None: + report = analyze_source("(l3 (x) x") + + assert report.ok is False + assert len(report.diagnostics) == 1 + diagnostic = report.diagnostics[0] + + assert diagnostic.stage == "syntax" + assert diagnostic.code == "L3_SYNTAX_ERROR" + assert diagnostic.line == 1 + assert diagnostic.column is not None + + +def test_diagnostics_reports_unbound_variable() -> None: + report = analyze_source("(l3 () x)") + + assert report.ok is False + assert len(report.diagnostics) == 1 + diagnostic = report.diagnostics[0] + + assert diagnostic.stage == "semantic" + assert diagnostic.code == "L3_UNBOUND_VARIABLE" + assert diagnostic.line == 1 + assert diagnostic.column is not None + assert diagnostic.message == "Unbound variable: x" diff --git a/packages/L3/test/L3/test_eliminate_letrec.py b/packages/L3/test/L3/test_eliminate_letrec.py index d9d42d27..7cf8d36b 100644 --- a/packages/L3/test/L3/test_eliminate_letrec.py +++ b/packages/L3/test/L3/test_eliminate_letrec.py @@ -233,3 +233,203 @@ def test_eliminate_letrec_program(): ) assert actual == expected + + +def test_eliminate_letrec_reference_recursive(): + term = L3.LetRec( + bindings=[("x", L3.Immediate(value=0))], + body=L3.Reference(name="x"), + ) + expected = L2.Let( + bindings=[("x", L2.Immediate(value=0))], + body=L2.Load(base=L2.Reference(name="x"), index=0), + ) + actual = eliminate_letrec_term(term, context={}) + assert actual == expected + + +def test_eliminate_letrec_reference_non_recursive(): + term = L3.Reference(name="x") + + context: Context = {} + + expected = L2.Reference(name="x") + + actual = eliminate_letrec_term(term, context) + + assert actual == expected + + +def test_eliminate_letrec_body_uses_recursive_binding(): + term = L3.LetRec( + bindings=[("x", L3.Immediate(value=1))], + body=L3.Reference(name="x"), + ) + + expected = L2.Let( + bindings=[("x", L2.Immediate(value=1))], + body=L2.Load(base=L2.Reference(name="x"), index=0), + ) + + actual = eliminate_letrec_term(term, context={}) + + assert actual == expected + + +def test_eliminate_letrec_abstract_apply(): + term = L3.Apply( + target=L3.Abstract(parameters=["x"], body=L3.Reference(name="x")), + arguments=[L3.Immediate(value=1)], + ) + + expected = L2.Apply( + target=L2.Abstract(parameters=["x"], body=L2.Reference(name="x")), + arguments=[L2.Immediate(value=1)], + ) + + actual = eliminate_letrec_term(term, context={}) + + assert actual == expected + + +def test_eliminate_letrec_primitive_branch(): + term = L3.Branch( + operator="<", + left=L3.Primitive( + operator="+", + left=L3.Immediate(value=1), + right=L3.Immediate(value=2), + ), + right=L3.Immediate(value=4), + consequent=L3.Immediate(value=5), + otherwise=L3.Immediate(value=6), + ) + + expected = L2.Branch( + operator="<", + left=L2.Primitive( + operator="+", + left=L2.Immediate(value=1), + right=L2.Immediate(value=2), + ), + right=L2.Immediate(value=4), + consequent=L2.Immediate(value=5), + otherwise=L2.Immediate(value=6), + ) + + actual = eliminate_letrec_term(term, context={}) + + assert actual == expected + + +def test_eliminate_letrec_nested_primitives(): + term = L3.Primitive( + operator="+", + left=L3.Primitive( + operator="+", + left=L3.Immediate(value=1), + right=L3.Immediate(value=2), + ), + right=L3.Immediate(value=3), + ) + expected = L2.Primitive( + operator="+", + left=L2.Primitive( + operator="+", + left=L2.Immediate(value=1), + right=L2.Immediate(value=2), + ), + right=L2.Immediate(value=3), + ) + actual = eliminate_letrec_term(term, context={}) + assert actual == expected + + +def test_eliminate_letrec_branch_equals(): + term = L3.Branch( + operator="==", + left=L3.Immediate(value=1), + right=L3.Immediate(value=1), + consequent=L3.Immediate(value=7), + otherwise=L3.Immediate(value=8), + ) + + expected = L2.Branch( + operator="==", + left=L2.Immediate(value=1), + right=L2.Immediate(value=1), + consequent=L2.Immediate(value=7), + otherwise=L2.Immediate(value=8), + ) + + actual = eliminate_letrec_term(term, context={}) + + assert actual == expected + + +def test_eliminate_letrec_memory_and_begin(): + term = L3.Begin( + effects=[ + L3.Store( + base=L3.Reference(name="b"), + index=0, + value=L3.Immediate(value=2), + ), + ], + value=L3.Load( + base=L3.Allocate(count=1), + index=0, + ), + ) + + expected = L2.Begin( + effects=[ + L2.Store( + base=L2.Reference(name="b"), + index=0, + value=L2.Immediate(value=2), + ) + ], + value=L2.Load( + base=L2.Allocate(count=1), + index=0, + ), + ) + + actual = eliminate_letrec_term(term, context={}) + + assert actual == expected + + +def test_eliminate_letrec_allocate_and_load(): + term = L3.Load( + base=L3.Allocate(count=1), + index=0, + ) + + expected = L2.Load( + base=L2.Allocate(count=1), + index=0, + ) + + actual = eliminate_letrec_term(term, context={}) + + assert actual == expected + + +def test_eliminate_letrec_store(): + term = L3.Store( + base=L3.Allocate(count=1), + index=0, + value=L3.Immediate(value=42), + ) + + expected = L2.Store( + base=L2.Allocate(count=1), + index=0, + value=L2.Immediate(value=42), + ) + + actual = eliminate_letrec_term(term, context={}) + + assert actual == expected diff --git a/packages/L3/test/L3/test_parse.py b/packages/L3/test/L3/test_parse.py index f48391a5..e4aa85ae 100644 --- a/packages/L3/test/L3/test_parse.py +++ b/packages/L3/test/L3/test_parse.py @@ -1,4 +1,5 @@ -from L3.parse import parse_program, parse_term +import pytest +from L3.parse import AstTransformer, parse_program, parse_term from L3.syntax import ( Abstract, Allocate, @@ -14,6 +15,7 @@ Reference, Store, ) +from lark import Token # Let @@ -295,3 +297,10 @@ def test_parse_program_identity(): actual = parse_program(source) assert actual == expected + + +def test_begin_requires_at_least_one_term(): + transformer = AstTransformer() + + with pytest.raises(ValueError, match="begin requires at least one term"): + transformer.begin([Token("BEGIN", "begin")]) diff --git a/packages/L3/test/L3/test_uniqify.py b/packages/L3/test/L3/test_uniqify.py index e2243e9a..3fdaf62e 100644 --- a/packages/L3/test/L3/test_uniqify.py +++ b/packages/L3/test/L3/test_uniqify.py @@ -1,5 +1,19 @@ -from L3.syntax import Apply, Immediate, Let, Reference -from L3.uniqify import Context, uniqify_term +from L3.syntax import ( + Abstract, + Allocate, + Apply, + Begin, + Branch, + Immediate, + Let, + LetRec, + Load, + Primitive, + Program, + Reference, + Store, +) +from L3.uniqify import Context, uniqify_program, uniqify_term from util.sequential_name_generator import SequentialNameGenerator @@ -15,6 +29,18 @@ def test_uniqify_term_reference(): assert actual == expected +def test_uniqify_term_reference_not_in_context(): + term = Reference(name="x") + + context: Context = {} + fresh = SequentialNameGenerator() + actual = uniqify_term(term, context, fresh=fresh) + + expected = Reference(name="x") + + assert actual == expected + + def test_uniqify_immediate(): term = Immediate(value=42) @@ -59,3 +85,119 @@ def test_uniqify_term_let(): ) assert actual == expected + + +def test_uniqify_term_letrec_and_abstract_and_apply(): + term = LetRec( + bindings=[ + ( + "f", + Abstract( + parameters=["x"], + body=Apply( + target=Reference(name="f"), + arguments=[Reference(name="x")], + ), + ), + ), + ("y", Reference(name="f")), + ], + body=Reference(name="y"), + ) + + context: Context = {"f": "outer_f"} + fresh = SequentialNameGenerator() + actual = uniqify_term(term, context, fresh) + + expected = LetRec( + bindings=[ + ( + "f0", + Abstract( + parameters=["x0"], + body=Apply( + target=Reference(name="f0"), + arguments=[Reference(name="x0")], + ), + ), + ), + ("y0", Reference(name="f0")), + ], + body=Reference(name="y0"), + ) + + assert actual == expected + + +def test_uniqify_term_memory_and_control_forms(): + term = Begin( + effects=[ + Store( + base=Reference(name="ptr"), + index=0, + value=Primitive( + operator="+", + left=Reference(name="a"), + right=Reference(name="b"), + ), + ) + ], + value=Branch( + operator="<", + left=Reference(name="a"), + right=Immediate(value=0), + consequent=Allocate(count=1), + otherwise=Load(base=Reference(name="ptr"), index=1), + ), + ) + + context: Context = {"a": "a1", "b": "b1", "ptr": "p1"} + fresh = SequentialNameGenerator() + actual = uniqify_term(term, context, fresh) + + expected = Begin( + effects=[ + Store( + base=Reference(name="p1"), + index=0, + value=Primitive( + operator="+", + left=Reference(name="a1"), + right=Reference(name="b1"), + ), + ) + ], + value=Branch( + operator="<", + left=Reference(name="a1"), + right=Immediate(value=0), + consequent=Allocate(count=1), + otherwise=Load(base=Reference(name="p1"), index=1), + ), + ) + + assert actual == expected + + +def test_uniqify_program(): + program = Program( + parameters=["x", "y"], + body=Primitive( + operator="+", + left=Reference(name="x"), + right=Reference(name="y"), + ), + ) + + _, actual = uniqify_program(program) + + expected = Program( + parameters=["x0", "y0"], + body=Primitive( + operator="+", + left=Reference(name="x0"), + right=Reference(name="y0"), + ), + ) + + assert actual == expected diff --git a/packages/L4/examples/one.l4 b/packages/L4/examples/one.l4 new file mode 100644 index 00000000..9831504b --- /dev/null +++ b/packages/L4/examples/one.l4 @@ -0,0 +1,4 @@ +(l4 () + (let ((x 1)) + (let ((y 2)) + (+ x y)))) \ No newline at end of file diff --git a/packages/L4/examples/two.l4 b/packages/L4/examples/two.l4 new file mode 100644 index 00000000..54be36af --- /dev/null +++ b/packages/L4/examples/two.l4 @@ -0,0 +1,6 @@ +(l4 () + ((lambda (x) + (lambda (y) + (+ x y))) + 1 + 2)) \ No newline at end of file diff --git a/packages/L4/pyproject.toml b/packages/L4/pyproject.toml new file mode 100644 index 00000000..1acce68c --- /dev/null +++ b/packages/L4/pyproject.toml @@ -0,0 +1,8 @@ +[project] +name = "L4" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +authors = [{ name = "James Clause", email = "clause@udel.edu" }] +requires-python = ">=3.14" +dependencies = ["pydantic>=2.12.3", "util"] diff --git a/packages/L4/src/L4/L4.lark b/packages/L4/src/L4/L4.lark new file mode 100644 index 00000000..78df34b1 --- /dev/null +++ b/packages/L4/src/L4/L4.lark @@ -0,0 +1,65 @@ +%import common.WS +%ignore WS + +program : "(" PROGRAM "(" parameters ")" term ")" +parameters : IDENTIFIER* + +term : let + | reference + | abstract + | apply + | immediate + | primitive + | if_expr + | and_expr + | sole_expr + | tuple_expr + | tuple_get_expr + | join_expr + | project_expr + +let : "(" LET "(" bindings ")" term ")" + +bindings : binding* +binding : "(" IDENTIFIER term ")" + +reference : IDENTIFIER + +abstract : "(" LAMBDA "(" parameters ")" term ")" + +apply : "(" term term* ")" + +immediate : NUMBER + +primitive : "(" OPERATOR term term ")" + +if_expr : "(" IF term term term ")" + +and_expr : "(" AND term term ")" + +sole_expr : SOLE + +tuple_expr : "(" TUPLE term+ ")" + +tuple_get_expr : "(" TUPLE_GET term immediate ")" + +join_expr : "(" JOIN term+ ")" + +project_expr : "(" PROJECT term immediate ")" + +PROGRAM.2 : "l4" +LET.2 : "let" +LAMBDA.2 : "\\" | "lambda" | "λ" +IF.2 : "if" + +OPERATOR : "+" | "-" | "*" + +IDENTIFIER : /[a-zA-Z_][a-zA-Z0-9_]*/ +NUMBER : /-?\d+/ + +AND.2 : "and" +SOLE.2 : "sole" +TUPLE.2 : "tuple" +TUPLE_GET.2 : "tuple-get" +JOIN.2 : "join" +PROJECT.2 : "project" \ No newline at end of file diff --git a/packages/L4/src/L4/__init__.py b/packages/L4/src/L4/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/packages/L4/src/L4/check.py b/packages/L4/src/L4/check.py new file mode 100644 index 00000000..d4979f12 --- /dev/null +++ b/packages/L4/src/L4/check.py @@ -0,0 +1,218 @@ +from collections.abc import Mapping +from functools import partial + +from .syntax import ( + Abstraction, + And, + Application, + Arrow, + Bool, + Boolean, + Box, + BoxRead, + BoxWrite, + Branch, + Identifier, + If, + Immediate, + Int, + Join, + Let, + Primitive, + Product, + Program, + Project, + Reference, + Sole, + Term, + Trivial, + Tuple, + TupleGet, + Type, +) + + +def equivalent( + t1: Type, + t2: Type, +) -> bool: + recur = partial(equivalent) + + match t1, t2: + case Arrow(domain=d1, codomain=c1), Arrow(domain=d2, codomain=c2): + return ( + recur(d1, d2) # domain + and recur(c1, c2) # codomain + ) + + case Int(), Int(): + return True + + case Bool(), Bool(): + return True + + case Trivial(), Trivial(): + return True + + case Product(components=cs1), Product(components=cs2): + return ( + len(cs1) == len(cs2) and all(recur(c1, c2) for c1, c2 in zip(cs1, cs2)) # + ) + + case Box(content=c1), Box(content=c2): # 2 boxes are the same if they share contents + return recur(c1, c2) + + case _: + return False + + +def check_term( + term: Term, + expected: Type, + gamma: Mapping[Identifier, Type], +) -> Type: + infer = partial(infer_term, gamma=gamma) + + actual = infer(term) + if not equivalent(actual, expected): + raise ValueError(f"type mismatch: expected {expected} but got {actual}") + + return actual + + +def infer_term( + term: Term, + gamma: Mapping[Identifier, Type], +) -> Type: + _infer = partial(infer_term, gamma=gamma) + _check = partial(check_term, gamma=gamma) + + match term: + case Reference(name=name): + match gamma.get(name): + case None: + raise ValueError(f"unknown variable: {name}") + + case type: + return type + + case Abstraction(parameter=parameter, domain=domain, body=body): + return Arrow( + domain=domain, + codomain=_infer(body, gamma={**gamma, parameter: domain}), + ) + + case Application(target=target, argument=argument): + match _infer(target): + case Arrow(domain=domain, codomain=codomain): + _check(argument, domain) + return codomain + + case target_type: + raise ValueError(f"expected {target} to be {Arrow} not {target_type}") + + case Immediate(): + return Int() # passes value to the type version + + case Boolean(): + return Bool() # made it pass the value to the type + + case Primitive(operator=operator, left=left, right=right): # Should only add Ints + match operator: + case "+" | "-" | "*": + _check(left, Int()) + _check(right, Int()) + return Int() + + case Branch( + operator=operator, left=left, right=right, motive=motive, consequent=consequent, otherwise=otherwise + ): # branch now only covers the comparison of Int types + match operator: + case "<" | "==": + _check(left, Int()) + _check(right, Int()) + _check(consequent, motive) + _check(otherwise, motive) + return motive + + case If(test=test, consequent=consequent, otherwise=otherwise): + _check(test, Bool()) + consequent_type = _infer(consequent) + otherwise_type = _infer(otherwise) + if not equivalent(consequent_type, otherwise_type): + raise ValueError(f"branches have different types: {consequent_type} and {otherwise_type}") + return consequent_type + + case And(left=left, right=right): # used to be branch but for clarity now used to represent the and operator + # should only accept bools and return a bool + _check(left, Bool()) + _check(right, Bool()) + return Bool() + + case Sole(): + return Trivial() + + case Tuple(components=components): + return Product(components=[_infer(component) for component in components]) + + case TupleGet(target=target, index=index): + match _infer(target): + case Product(components=components): + if index not in range(len(components)): + raise ValueError(f"invalid index: {index} in {components}") + return components[index] + + case target_type: + raise ValueError(f"expected {target} to be {Product} not {target_type}") + + case Join(components=components): + return Product(components=[_infer(component) for component in components]) + + case Project(target=target, index=index): + match _infer(target): + case Product(components=components): + if index not in range(len(components)): + raise ValueError(f"invalid index: {index} in {components}") + + return components[index] + + case target_type: + raise ValueError(f"expected {target} to be {Product} not {target_type}") + + case BoxWrite(target=target, value=value): + # target has to be in box(content = T) and value has to be of type T + match _infer(target): + case Box(content=content_type): + _check(value, content_type) + return Trivial() # writing is a side effect dont need to return meaningful stuff + case target_type: # wrong T + raise ValueError(f"expected {target} to be {Box} not {target_type}") + + case BoxRead(target=target): # return T + match _infer(target): # target myst be a box with content=T + case Box(content=content_type): + return content_type + case target_type: + raise ValueError(f"expected {target} to be {Box} not {target_type}") + + case Let(bindings=bindings, body=body): + # Evaluate each binding in sequence with progressively extended context + extended_gamma = dict(gamma) + for identifier, value_term in bindings: + # Infer type of the binding value using current gamma + value_type = infer_term(value_term, extended_gamma) + # Add binding to gamma for next bindings and body + extended_gamma[identifier] = value_type + # Evaluate body with all bindings in scope + return infer_term(body, extended_gamma) + + case _: + raise ValueError(f"cannot infer type for term: {term}") + + +def check_program( + program: Program, +) -> None: + match program: + case Program(parameters=parameters, body=body): # pragma: no branch + check_term(body, Int(), dict.fromkeys(parameters, Int())) diff --git a/packages/L4/src/L4/diagnostics.py b/packages/L4/src/L4/diagnostics.py new file mode 100644 index 00000000..0ce00946 --- /dev/null +++ b/packages/L4/src/L4/diagnostics.py @@ -0,0 +1,174 @@ +from __future__ import annotations + +import re +from collections.abc import Sequence +from pathlib import Path +from typing import Literal + +from lark import UnexpectedInput +from pydantic import BaseModel + +from .check import check_program +from .parse import parse_program + +type DiagnosticStage = Literal["syntax", "semantic", "internal"] +type DiagnosticSeverity = Literal["error", "warning", "information"] + + +class Diagnostic(BaseModel, frozen=True): + stage: DiagnosticStage + severity: DiagnosticSeverity = "error" + code: str + message: str + line: int | None = None + column: int | None = None + end_line: int | None = None + end_column: int | None = None + snippet: str | None = None + + +class DiagnosticsReport(BaseModel, frozen=True): + ok: bool + diagnostics: Sequence[Diagnostic] + + +def _line_snippet(source: str, line: int | None) -> str | None: + if line is None: + return None + + lines = source.splitlines() + if line < 1 or line > len(lines): + return None + + return lines[line - 1] + + +def _position_for_identifier(source: str, identifier: str) -> tuple[int, int] | None: + pattern = re.compile(rf"\b{re.escape(identifier)}\b") + match = pattern.search(source) + if match is None: + return None + + before = source[: match.start()] + line = before.count("\n") + 1 + if "\n" in before: + column = len(before.rsplit("\n", maxsplit=1)[-1]) + 1 + else: + column = len(before) + 1 + + return line, column + + +def _identifier_from_message(message: str) -> str | None: + unbound = re.search(r"unknown variable:\s*([A-Za-z_][A-Za-z0-9_]*)", message) + if unbound: + return unbound.group(1) + + duplicate = re.search(r"Duplicate (?:bindings|parameters):\s*(.+)$", message) + if duplicate is None: + return None + + names = re.findall(r"[A-Za-z_][A-Za-z0-9_]*", duplicate.group(1)) + if not names: + return None + + return names[0] + + +def _normalize_error_position(source: str, line: int | None, column: int | None) -> tuple[int, int]: + if isinstance(line, int) and isinstance(column, int) and line > 0 and column > 0: + return line, column + + lines = source.splitlines() + if not lines: + return 1, 1 + + last_line_number = len(lines) + last_column = len(lines[-1]) + 1 + return last_line_number, last_column + + +def _syntax_diagnostic(source: str, error: UnexpectedInput) -> Diagnostic: + expected = sorted(getattr(error, "expected", [])) + expected_message = "" + if expected: + expected_message = f" Expected one of: {', '.join(expected)}." + + line, column = _normalize_error_position( + source, + getattr(error, "line", None), + getattr(error, "column", None), + ) + + return Diagnostic( + stage="syntax", + code="L4_SYNTAX_ERROR", + message=f"{str(error).strip()}.{expected_message}", + line=line, + column=column, + end_line=line, + end_column=column + 1, + snippet=_line_snippet(source, line), + ) + + +def _semantic_diagnostic(source: str, message: str) -> Diagnostic: + identifier = _identifier_from_message(message) + position = _position_for_identifier(source, identifier) if identifier else None + + line: int | None = None + column: int | None = None + end_column: int | None = None + + if position is not None: + line, column = position + end_column = column + len(identifier) if identifier else None + else: + # Default to first line if identifier not found (e.g., for type mismatch errors) + line, column = 1, 1 + end_column = None + + code = "L4_SEMANTIC_ERROR" + if message.startswith("unknown variable"): + code = "L4_UNBOUND_VARIABLE" + elif message.startswith("Duplicate bindings"): + code = "L4_DUPLICATE_BINDINGS" + elif message.startswith("Duplicate parameters"): + code = "L4_DUPLICATE_PARAMETERS" + + return Diagnostic( + stage="semantic", + code=code, + message=message, + line=line, + column=column, + end_line=line, + end_column=end_column, + snippet=_line_snippet(source, line), + ) + + +def analyze_source(source: str) -> DiagnosticsReport: + try: + program = parse_program(source) + check_program(program) + return DiagnosticsReport(ok=True, diagnostics=[]) + except UnexpectedInput as error: + return DiagnosticsReport(ok=False, diagnostics=[_syntax_diagnostic(source, error)]) + except ValueError as error: + return DiagnosticsReport(ok=False, diagnostics=[_semantic_diagnostic(source, str(error))]) + except Exception as error: # pragma: no cover + return DiagnosticsReport( + ok=False, + diagnostics=[ + Diagnostic( + stage="internal", + code="L4_INTERNAL_ERROR", + message=str(error), + ) + ], + ) + + +def analyze_file(path: Path) -> DiagnosticsReport: + return analyze_source(path.read_text()) diff --git a/packages/L4/src/L4/elaborate.py b/packages/L4/src/L4/elaborate.py new file mode 100644 index 00000000..22d37275 --- /dev/null +++ b/packages/L4/src/L4/elaborate.py @@ -0,0 +1,151 @@ +from collections.abc import Callable +from functools import partial + +from L3 import syntax as L3 +from util.sequential_name_generator import SequentialNameGenerator + +from . import syntax as L4 + +type FreshFunc = Callable[[str], str] + + +def elaborate_term(term: L4.Term, fresh: FreshFunc) -> L3.Term: + recur = partial(elaborate_term, fresh=fresh) + + match term: + case L4.Reference(name=name): + return L3.Reference(name=name) + + case L4.Abstraction(parameter=parameter, domain=_domain, body=body): + return L3.Abstract( + parameters=[parameter], + body=recur(body), + ) + + case L4.Application(target=target, argument=argument): + return L3.Apply( + target=recur(target), + arguments=[recur(argument)], + ) + + case L4.Boolean(value=value): + return L3.Immediate(value=1 if value else 0) + + case L4.If(test=test, consequent=consequent, otherwise=otherwise): + test_name = fresh("if") + return L3.Let( + bindings=[(test_name, recur(test))], + body=L3.Branch( + operator="==", + left=L3.Reference(name=test_name), + right=L3.Immediate(value=1), + consequent=recur(consequent), + otherwise=recur(otherwise), + ), + ) + + case L4.And(left=left, right=right): + left_name = fresh("and") + return L3.Let( + bindings=[(left_name, recur(left))], + body=L3.Branch( + operator="==", + left=L3.Reference(name=left_name), + right=L3.Immediate(value=1), + consequent=recur(right), + otherwise=L3.Immediate(value=0), + ), + ) + + case L4.Immediate(value=value): + return L3.Immediate(value=value) + + case L4.Primitive(operator=operator, left=left, right=right): + return L3.Primitive( + operator=operator, + left=recur(left), + right=recur(right), + ) + + case L4.Branch( + operator=operator, left=left, right=right, motive=_motive, consequent=consequent, otherwise=otherwise + ): + return L3.Branch( + operator=operator, + left=recur(left), + right=recur(right), + consequent=recur(consequent), + otherwise=recur(otherwise), + ) + + case L4.Sole(): + return L3.Immediate(value=0) + + case L4.Tuple(components=components): + tuple_name = fresh("tuple") + return L3.Let( + bindings=[(tuple_name, L3.Allocate(count=len(components)))], + body=L3.Begin( + effects=[ + L3.Store( + base=L3.Reference(name=tuple_name), + index=index, + value=recur(component), + ) + for index, component in enumerate(components) + ], + value=L3.Reference(name=tuple_name), + ), + ) + + case L4.TupleGet(target=target, index=index): + tuple_name = fresh("tuple") + return L3.Let( + bindings=[(tuple_name, recur(target))], + body=L3.Load( + base=L3.Reference(name=tuple_name), + index=index, + ), + ) + + case L4.Join(components=components): + return recur(L4.Tuple(components=components)) + + case L4.Project(target=target, index=index): + return recur(L4.TupleGet(target=target, index=index)) + + case L4.BoxWrite(target=target, value=value): + box_name = fresh("box") + return L3.Let( + bindings=[(box_name, recur(target))], + body=L3.Store( + base=L3.Reference(name=box_name), + index=0, + value=recur(value), + ), + ) + case L4.BoxRead(target=target): + box_name = fresh("box") + return L3.Let( + bindings=[(box_name, recur(target))], + body=L3.Load( + base=L3.Reference(name=box_name), + index=0, + ), + ) + case _: + raise ValueError(f"Unknown term for L4 elaboration {term}") + + +def elaborate_program(program: L4.Program, fresh: FreshFunc | None = None) -> L3.Program: + fresh = fresh or SequentialNameGenerator() + + match program: + case L4.Program(parameters=parameters, body=body): + return L3.Program( + parameters=parameters, + body=elaborate_term(body, fresh), + ) + + case _: + raise ValueError(f"unknown program for L4 elaboration: {program}") diff --git a/packages/L4/src/L4/main.py b/packages/L4/src/L4/main.py new file mode 100644 index 00000000..f21abf36 --- /dev/null +++ b/packages/L4/src/L4/main.py @@ -0,0 +1,82 @@ +from pathlib import Path + +import click +from L2.optimize import optimize_program +from L2.to_python import to_ast_program +from L3.eliminate_letrec import eliminate_letrec_program +from L3.uniqify import uniqify_program + +from .check import check_program +from .diagnostics import analyze_source +from .elaborate import elaborate_program +from .parse import parse_program + + +@click.command( + context_settings=dict( + help_option_names=["-h", "--help"], + max_content_width=120, + ), +) +@click.option( + "--diagnostics-json", + is_flag=True, + default=False, + help="Emit parser/checker diagnostics as JSON for editor integrations", +) +@click.option( + "--check/--no-check", + default=True, + show_default=True, + help="Enable or disable semantic analysis", +) +@click.option( + "--optimize/--no-optimize", + default=True, + show_default=True, + help="Enable or disable optimization", +) +@click.option( + "-o", + "--output", + type=click.Path(writable=True, dir_okay=False, path_type=Path), + default=None, + help="Output file (defaults to .py)", +) +@click.argument( + "input", + type=click.Path(exists=True, readable=True, dir_okay=False, path_type=Path), +) +def main( + output: Path | None, + diagnostics_json: bool, + check: bool, + optimize: bool, + input: Path, +) -> None: + source = input.read_text() + + if diagnostics_json: + report = analyze_source(source) + click.echo(report.model_dump_json(indent=2)) + raise SystemExit(0 if report.ok else 1) + + l4 = parse_program(source) + + if check: + check_program(l4) + + l3 = elaborate_program(l4) + fresh, l3 = uniqify_program(l3) # type: ignore + + l2 = eliminate_letrec_program(l3) + + if optimize: + l2 = optimize_program(l2) + + module = to_ast_program(l2) + (output or input.with_suffix(".py")).write_text(module) + + +if __name__ == "__main__": + main() diff --git a/packages/L4/src/L4/parse.py b/packages/L4/src/L4/parse.py new file mode 100644 index 00000000..89b90788 --- /dev/null +++ b/packages/L4/src/L4/parse.py @@ -0,0 +1,221 @@ +from collections.abc import Sequence +from pathlib import Path +from typing import Literal, cast + +from lark import Lark, Token, Transformer +from lark.visitors import v_args # pyright: ignore[reportUnknownVariableType] + +from .syntax import ( + Abstraction, + And, + Application, + Arrow, + Bool, + Boolean, + Box, + BoxRead, + BoxWrite, + Branch, + Identifier, + If, + Immediate, + Int, + Join, + Primitive, + Product, + Program, + Project, + Reference, + Sole, + Term, + Trivial, + Tuple, + TupleGet, + Type, +) + + +class AstTransformer(Transformer[Token, Program | Term]): + @v_args(inline=True) + def program( + self, + _program: Token, + parameters: Sequence[Identifier], + body: Term, + ) -> Program: + return Program(parameters=parameters, body=body) + + def parameters(self, parameters: Sequence[Token]) -> Sequence[Identifier]: + return [str(parameter) for parameter in parameters] + + @v_args(inline=True) + def term(self, term: Term) -> Term: + return term + + # --- Types --- + + @v_args(inline=True) + def int_type(self) -> Type: + return Int() + + @v_args(inline=True) + def bool_type(self) -> Type: + return Bool() + + @v_args(inline=True) + def trivial_type(self) -> Type: + return Trivial() + + @v_args(inline=True) + def arrow_type(self, domain: Type, codomain: Type) -> Type: + return Arrow(domain=domain, codomain=codomain) + + def product_type(self, args: Sequence[Type]) -> Type: + return Product(components=[component for component in args]) + + @v_args(inline=True) + def box_type(self, content: Type) -> Type: + return Box(content=content) + + # --- Terms --- + + @v_args(inline=True) + def let(self, _let: Token, bindings: Sequence[tuple[Identifier, Term]], body: Term) -> Term: + from .syntax import Let + + return Let(bindings=bindings, body=body) + + def bindings(self, bindings: Sequence[tuple[Identifier, Term]]) -> Sequence[tuple[Identifier, Term]]: + return bindings + + @v_args(inline=True) + def binding(self, name: Token, value: Term) -> tuple[Identifier, Term]: + return str(name), value + + @v_args(inline=True) + def reference(self, name: Token) -> Term: + return Reference(name=str(name)) + + @v_args(inline=False) + def abstract(self, args) -> Term: + # Grammar: abstract : "(" LAMBDA "(" parameters ")" term ")" + # args will contain processed children after Lark filters anonymous tokens + from .syntax import Int + + # Filter args into parameters and body + parameters = None + body = None + + for arg in args: + if isinstance(arg, (list, tuple)) and parameters is None: + parameters = arg + elif arg is not None and parameters is not None: # Body comes after parameters + body = arg + + if parameters is None or body is None: + raise ValueError(f"abstract requires parameters and body, got args: {args}") + + if not parameters: + raise ValueError("abstract requires at least one parameter") + + # Build nested Abstraction for multiple parameters + result = body + for param in reversed(parameters): + result = Abstraction(parameter=param, domain=Int(), body=result) + + return result + + def apply(self, args: Sequence[Term]) -> Term: + terms = list(args) + if len(terms) == 0: + raise ValueError("application requires at least one term") + + target = terms[0] + for argument in terms[1:]: + target = Application(target=target, argument=argument) + + return target + + @v_args(inline=True) + def immediate(self, value: Token) -> Term: + return Immediate(value=int(value)) + + @v_args(inline=True) + def boolean(self, value: Token) -> Term: + # Lark gives us the token "true" or "false" as a string + return Boolean(value=str(value) == "true") + + @v_args(inline=True) + def primitive(self, operator: Token, left: Term, right: Term) -> Term: + op = cast(Literal["+", "-", "*"], str(operator)) + return Primitive(operator=op, left=left, right=right) + + @v_args(inline=True) + def branch( + self, + _if: Token, + operator: Token, + left: Term, + right: Term, + motive: Type, + consequent: Term, + otherwise: Term, + ) -> Term: + # Branch: (if (< left right) motive consequent otherwise) + op = cast(Literal["<", "=="], str(operator)) + return Branch( + operator=op, + left=left, + right=right, + motive=motive, + consequent=consequent, + otherwise=otherwise, + ) + + @v_args(inline=True) + def if_expr(self, _if: Token, test: Term, consequent: Term, otherwise: Term) -> Term: + return If(test=test, consequent=consequent, otherwise=otherwise) + + @v_args(inline=True) + def and_expr(self, _and: Token, left: Term, right: Term) -> Term: + return And(left=left, right=right) + + @v_args(inline=True) + def sole_expr(self, _sole: Token) -> Term: + return Sole() + + def tuple_expr(self, components: Sequence[Term]) -> Term: + return Tuple(components=components) + + @v_args(inline=True) + def tuple_get_expr(self, _tuple_get: Token, target: Term, index: Immediate) -> Term: + return TupleGet(target=target, index=index.value) + + def join_expr(self, components: Sequence[Term]) -> Term: + return Join(components=components) + + @v_args(inline=True) + def project_expr(self, _project: Token, target: Term, index: Immediate) -> Term: + return Project(target=target, index=index.value) + + @v_args(inline=True) + def box_write_expr(self, _box_write: Token, target: Term, value: Term) -> Term: + return BoxWrite(target=target, value=value) + + @v_args(inline=True) + def box_read_expr(self, _box_read: Token, target: Term) -> Term: + return BoxRead(target=target) + + +def parse_term(source: str) -> Term: + grammar = Path(__file__).with_name("L4.lark").read_text() + parser = Lark(grammar, start="term") + tree = parser.parse(source) # pyright: ignore[reportUnknownMemberType] + return AstTransformer().transform(tree) # pyright: ignore[reportReturnType] + + +def parse_program(source: str) -> Program: + grammar = Path(__file__).with_name("L4.lark").read_text() + parser = Lark(grammar, start="program") + tree = parser.parse(source) # pyright: ignore[reportUnknownMemberType] + return AstTransformer().transform(tree) # pyright: ignore[reportReturnType] diff --git a/packages/L4/src/L4/syntax.py b/packages/L4/src/L4/syntax.py new file mode 100644 index 00000000..7f496c87 --- /dev/null +++ b/packages/L4/src/L4/syntax.py @@ -0,0 +1,192 @@ +from collections.abc import Sequence +from typing import Annotated, Literal + +from pydantic import BaseModel, Field + +type Identifier = Annotated[str, Field(min_length=1)] +# type Label = Annotated[str, Field(min_length=1)] +# idk why this is here so I've commented it out +type Nat = Annotated[int, Field(ge=0)] + + +class Program(BaseModel, frozen=True): + tag: Literal["program"] = "program" + parameters: Sequence[Identifier] + body: Term + + +type Type = Annotated[ + Arrow | Int | Bool | Trivial | Product | Box, + Field(discriminator="tag"), +] + + +class Arrow(BaseModel, frozen=True): # Arrow type used to represent function types, e.g. int -> int + tag: Literal["arrow"] = "arrow" + domain: Type + codomain: Type + + +class Int(BaseModel, frozen=True): # Int, its an integer dude. Immutable + tag: Literal["int"] = "int" + + +class Bool(BaseModel, frozen=True): # Bool, Truthiness Immutable + tag: Literal["bool"] = "bool" + + +class Trivial(BaseModel, frozen=True): + tag: Literal["trivial"] = "trivial" + + +# Can contain a product or a product that contains boxes if mutability is wanted there +# type ascription, bi-directional stuff + + +# Add a type of box, memory cell or box that goes down to memory +# Needs to store and hold a conversion of a type into a memory address +# "the transformation of placing a primitive type within an object so that the value can be used as a reference" +# Mutable +class Box(BaseModel, frozen=True): + tag: Literal["box"] = "box" + content: Type + + +class Product(BaseModel, frozen=True): # Tupl that holds a sequence of types + tag: Literal["product"] = "product" + components: Sequence[Type] + + +type Term = Annotated[ + # Lambda + Let + | Reference + | Abstraction + | Application + # Bool + | Boolean + | If + | And + # Int + | Immediate + | Primitive + | Branch + # Trivial + | Sole + # Product + | Tuple + | TupleGet + | Join + | Project + # Box + | BoxWrite + | BoxRead, + Field(discriminator="tag"), +] + + +class Reference(BaseModel, frozen=True): + tag: Literal["reference"] = "reference" + name: Identifier + + +class Abstraction(BaseModel, frozen=True): + tag: Literal["abstraction"] = "abstraction" + parameter: Identifier + domain: Type + body: Term + + +class Application(BaseModel, frozen=True): + tag: Literal["application"] = "application" + target: Term + argument: Term + + +class Boolean(BaseModel, frozen=True): # the expression of booleans + tag: Literal["boolean"] = "boolean" + value: bool + + +class If(BaseModel, frozen=True): # lacks a motive + tag: Literal["if"] = "if" + test: Term + consequent: Term + otherwise: Term + + +class And(BaseModel, frozen=True): # + tag: Literal["and"] = "and" + left: Term + right: Term + + +class Immediate(BaseModel, frozen=True): + tag: Literal["immediate"] = "immediate" + value: int + + +class Primitive(BaseModel, frozen=True): + tag: Literal["primitive"] = "primitive" + operator: Literal["+", "-", "*"] + left: Term + right: Term + + +class Branch(BaseModel, frozen=True): + tag: Literal["branch"] = "branch" + operator: Literal["<", "=="] + left: Term # any term currently, may want to make it unable to accept say a tuple + right: Term # because of this need to check the matter of + motive: Type # Expected return type of the branch + consequent: Term + otherwise: Term + + +class Sole(BaseModel, frozen=True): + tag: Literal["sole"] = "sole" + + +# class Successor(BaseModel, frozen=True): +# tag: Literal["successor"] = "successor" +# target: Term +# I have no idea what this is or what it is for but am not removing it in case its important + + +class Tuple(BaseModel, frozen=True): # tuples should be 2 types grouped together yea? + tag: Literal["tuple"] = "tuple" + components: Sequence[Term] + + +class TupleGet(BaseModel, frozen=True): # grabs from the tuple returning the type(s) within? + tag: Literal["tuple_get"] = "tuple_get" + target: Term + index: Nat + + +class Join(BaseModel, frozen=True): # Or is join the literal + tag: Literal["join"] = "join" + components: Sequence[Term] + + +class Project(BaseModel, frozen=True): + tag: Literal["project"] = "project" + target: Term + index: Annotated[int, Field(ge=0)] + + +class BoxWrite(BaseModel, frozen=True): # Write a Term to a box or overwrite the current value in the box + tag: Literal["box_write"] = "box_write" + target: Term + value: Term + + +class BoxRead(BaseModel, frozen=True): # Read a value from a box + tag: Literal["box_read"] = "box_read" + target: Term + + +class Let(BaseModel, frozen=True): + tag: Literal["let"] = "let" + bindings: Sequence[tuple[Identifier, Term]] + body: Term diff --git a/packages/L4/test/L4/highlighting.l4 b/packages/L4/test/L4/highlighting.l4 new file mode 100644 index 00000000..38e10268 --- /dev/null +++ b/packages/L4/test/L4/highlighting.l4 @@ -0,0 +1 @@ +l4 let letrec lambda if allocate load store begin and sole tuple tuple-get join project Int Bool Trivial Product diff --git a/pyproject.toml b/pyproject.toml index d7353169..a5feeaac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ [project] name = "471c" version = "0.1.0" -description = "Add your description here" -authors = [{ name = "James Clause", email = "clause@udel.edu" }] +description = "Multi-level language compiler and tooling for L0..L4 (includes VS Code LSP for L3/L4)" +authors = [{ name = "James Clause", email = "clause@udel.edu" }, { name = "John Fulkerson", email = "jtfulky@udel.edu" }] readme = "README.md" requires-python = ">=3.14" dependencies = [] @@ -26,6 +26,7 @@ members = ["packages/*"] [tool.uv.sources] util = { workspace = true } +l4 = { workspace = true } l3 = { workspace = true } l2 = { workspace = true } l1 = { workspace = true } diff --git a/uv.lock b/uv.lock index b1e5469e..b332fd18 100644 --- a/uv.lock +++ b/uv.lock @@ -9,6 +9,7 @@ members = [ "l1", "l2", "l3", + "l4", "util", ] @@ -227,6 +228,21 @@ requires-dist = [ { name = "util", editable = "packages/util" }, ] +[[package]] +name = "l4" +version = "0.1.0" +source = { editable = "packages/L4" } +dependencies = [ + { name = "pydantic" }, + { name = "util" }, +] + +[package.metadata] +requires-dist = [ + { name = "pydantic", specifier = ">=2.12.3" }, + { name = "util", editable = "packages/util" }, +] + [[package]] name = "lark" version = "1.3.1"